I have an application with a ListView that contains some filenames and filepaths that I want to pass to a BackgroundWorker. I essentially want the worker thread to know the strings that this ListView contains one way or another.
While tinkering with this and searching around I have tried making a public ref Class so that I can send it to the RunWorkerAsync() function, which gives me a can not convert to System::Object error:
public ref class FileInfoContainer {
public:
cli::array<System::String^>^ filepaths;
cli::array<System::String^>^ filenames;
System::String^ outfile;
FileInfoContainer(cli::array<System::String^>^ filepaths, cli::array<System::String^>^ filenames, System::String^ outfile);
};
I have also tried passing the whole Form as an argument to RunWorkerAsync() which, during debugging, indeed passes every piece of data the Form has into a System::Object. The problem is that if I try to access an element of my form through this object I get a not member of System::Object error:
private: System::Void compworker_DoWork(System::Object^ sender, System::ComponentModel::DoWorkEventArgs^ e) {
BackgroundWorker^ worker = dynamic_cast<BackgroundWorker^>(sender);
System::Object^ arg = e->Argument;
arg->button1 //this fails
MainForm form1 = (MainForm)e->Argument; //this also fails, even though I've seen it suggested in numerous threads
}
Any good suggestions about how I can pass my arguments to the worker thread?
Related
I want to pass multiple optional objects in function as varags ?
Optional<ab> ab = Optional.of(ab);
Optional<cd> cd = Optional.of(cd);
Optional<dc> dc = Optional.of(dc);
Optional<ba> ba = Optional.of(ba);
data(ab, cd, dc, ba);
data(Optional<Object>... objects){...}
I am getting error if i don this, any suggestion how can be proceed?
It isn’t related to varargs. You can’t pass an Optional<SomeSpecificType> where an Optional<Object> is expected. They are not compatible.
Assume just (without varargs):
static void data(Optional<Object> object) {
// …
}
Now if we try
Optional<String> ab = Optional.of("");
data(ab);
In my Eclipse I get this error message:
The method data(Optional<Object>) in the type MyClass is not
applicable for the arguments (Optional<String>)
Java generics are defined with this restriction. You also cannot pass, for example a List<String> where a List<Object> is expected.
You can overcome the limitation by declaring the method generic too:
static <T> void data(Optional<T> object) {
// …
}
Or just like this:
static void data(Optional<?> object) {
// …
}
With any of these two declarations the call above is OK.
BTW, #HadiJ is correct in the comment: Optional is meant for return values for from methods that may or may not be there. They have very few other good uses, and as parameters is not one of them. It seems to me that for your use case you should just pass the arguments that are there and leave out those that aren’t. The your data method may receive a longer or shorter argument array, but will just have to handle all elements of the array without caring about Optional. And passing String, Integer, LocalDate, etc, to a method declared void data(Object... objs) is straightforward and poses no problem.
I have a lot of native classes that accept some form of callbacks, usually a boost::signals2::slot-object.
But for simplicity, lets assume the class:
class Test
{
// set a callback that will be invoked at an unspecified time
// will be removed when Test class dies
void SetCallback(std::function<void(bool)> callback);
}
Now I have a managed class that wraps this native class, and I would like to pass a callback method to the native class.
public ref class TestWrapper
{
public:
TestWrapper()
: _native(new Test())
{
}
~TestWrapper()
{
delete _native;
}
private:
void CallbackMethod(bool value);
Test* _native;
};
now usually what I would do is the following:
Declare a method in the managed wrapper that is the callback I want.
Create a managed delegate object to this method.
Use GetFunctionPointerForDelegate to obtain a pointer to a function
Cast the pointer to the correct signature
Pass the pointer to the native class as callback.
I also keep the delegate alive since I fear it will be garbage collected and I will have a dangling function pointer (is this assumption correct?)
this looks kind of like this:
_managedDelegateMember = gcnew ManagedEventHandler(this, &TestWrapper::Callback);
System::IntPtr stubPointer = Marshal::GetFunctionPointerForDelegate(_managedDelegateMember);
UnmanagedEventHandlerFunctionPointer functionPointer = static_cast<UnmanagedEventHandlerFunctionPointer >(stubPointer.ToPointer());
_native->SetCallback(functionPointer);
I Would like to reduce the amount of code and not have to perform any casts nor declare any delegate types. I want to use a lambda expression with no delegate.
This is my new approach:
static void SetCallbackInternal(TestWrapper^ self)
{
gcroot<TestWrapper^> instance(self);
self->_native->SetCallback([instance](bool value)
{
// access managed class from within native code
instance->Value = value;
}
);
}
Declare a static method that accepts this in order to be able to use C++11 lambda.
Use gcroot to capture the managed class in the lambda and extend its lifetime for as long as the lambda is alive.
No casts, no additional delegate type nor members, minimal extra allocation.
Question:
Is this approach safe? I'm fearing I'm missing something and that this can cause a memory leak / undefined behavior in some unanticipated scenario.
EDIT:
this approach leads to a MethodAccessException when the lambda calls a private method of its managed wrapper class. seems like this method must at least be internal.
I think that you should not be using gcroot but a shared pointer. Shared pointer are made to keep an object alive as long as someone is using it.
You should also use a more c++ style in your whole code by replacing raw pointer with smart pointer and template instead of std::function (a lambda can be stored in a compile time type).
For example using the code you posted :
class Test
{
// set a callback that will be invoked at an unspecified time
// will be removed when Test class dies
template <class T>
void SetCallback(T callback); // Replaced std::function<void(bool)> with T
}
public ref class TestWrapper
{
public:
TestWrapper()
: _native()
{}
private:
void CallbackMethod(bool value);
std::unique_ptr<Test> _native; // Replaced Test* with std::unique_ptr<Test>
};
After replacing the old method with this new method all over my code base, I can report that it is safe, more succinct, and as far as I can tell, no memory leaks occur.
Hence I highly recommend this method for passing managed callbacks to native code.
The only caveats I found were the following:
Using lambda expressions forces the use of a static method as a helper for the callback registration. This is kinda hacky. It is unclear to me why the C++-CLI compiler does no permit lambda expressions within standard methods.
The method invoked by the lambda must be marked internal so to not throw MethodAccessException upon invocation. This is sort of make sense as it is not called within the class scope itself. but still, delegates / lambdas with C# don't have that limitation.
I'm repeatedly running into the problem of accepting a non-const reference parameter, since it seems taking an rvalue parameter prevents accepting lvalues and vice versa. Here's an example
void read(File &file)// I want to modify file
{
SomeClass someObject;
file.readInto(&someObject);//readInto is a non-const method
// do something with the data populated in someObject
}
But when I try to call read I have a problem if I try two different calling conventions
//this works just fine
File f1 = File::open("some_file_path");
read(f1);
// However this fails
read( File::open("some_file_path") );//because open returns an rvalue
The problem I have is if I change the parameter to a non-const rvalue than I can't pass the lvalue anymore. Am I doomed to always provide an override (or template) that takes the rvalue reference type and simply calls out to the lvalue override?
Since you updated the question, I suggest doing this:
void read(File& file)
{
SomeClass someObject;
file.radInto(&someObject);
// ...
}
void read(File&& file) { read(file); }
That will handle both lvalues and rvalues with minimal code repetition.
I think your read function should simply take a File&:
void read(File& file) // I want to modify file
{
SomeClass someObject;
file.readInto(&someObject);//Modifies file
// do something with the data populated in someObject
}
Then you can call:
// OK
std::shared_ptr<File> f1 = File::open("some_file_path");
read(*f1);
// OK
read( *File::open("some_file_path") );
Added benefit: the function is not limited to shared_ptr, and works with any File independently of how its memory is managed.
Alternatively, use a forwarding reference:
template <typename T>
void read(T&& file)// I want to modify file
{
SomeClass someObject;
file->readInto(&someObject);//Modifies file
// do something with the data populated in someObject
}
Below is the code I used to access the asset file for a metro app I am working on.
async void readFileFromDisk (string fileName, string fileType)
{
string fileContent;
StorageFile file = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFileAsync(fileName);
using (IRandomAccessStream readStream = await file.OpenAsync(FileAccessMode.Read))
{
using (DataReader dataReader = new DataReader(readStream))
{
UInt32 numBytesLoaded = await dataReader.LoadAsync((UInt32)readStream.Size);
fileContent = dataReader.ReadString(numBytesLoaded);
}
}
This code is run in the handler for Loaded event for the page. I am currently getting an exception saying "Value does not fall in range". The error occurs at the first line itself, where I try to get storagefile handle from the installation folder.
On debugging, the fileName string comes out to be Null. I guess, I should be moving the code to some event which is fired at a later stage in page lifecycle, but can't seem to figure out what is the best place to do it. Suggestions??
P.S. I need to read this file before any interaction from user, as it reads the data for the level, that user will be interacting with.
Edit:
Missed a couple things.
The below function is called from the handler for loaded event.
void Game_Loaded(object sender, RoutedEventArgs e)
{
//read all level files to the strings
readFileFromDisk("//Assets/Levels/Start" + selectedLevel + ".txt", "Start");
This handler basically calls above function for different file paths, in similar manner. The string selected level is static variable, while the fileName string is created from the same.
Edit 2:
Found the issue, but solution is still far away. The return type of readFileFromDist method is causing trouble. Changed it to Task, and this part works fine, but I get "Object reference not set to an instance" error. Tried to convert Game_Loaded event handler to async too, to use await operators, but that gives me compiler error for "wrong return type".
SO, I tried removing async completely, but I guess I can't do that. There is no way to open files without using async function. So, I essentially need a way to call the readFileFromDisk function, using await, and continue with rest of the code execution once the task is completed. Something like, "IsCompleted" event for the awaited calls for the function.
Solved! Needed to use "ms:appx///Assets/filename.txt" instead of "//Assets/filename.txt".
I've made an extension inside a package and I am calling the following code (occurs when a user presses a button in the toolbar):
DocumentEvents documentEvents = (DTE2)GetService(typeof(DTE));
_dte.Events.DebuggerEvents.OnEnterBreakMode += DebuggerEvents_OnEnterBreakMode;
_dte.Events.DebuggerEvents.OnEnterDesignMode += DebuggerEvents_OnEnterDesignMode;
_dte.Events.DebuggerEvents.OnContextChanged += DebuggerEvents_OnContextChanged;
_dte.Events.DocumentEvents.DocumentSaved += new _dispDocumentEvents_DocumentSavedEventHandler(DocumentEvents_DocumentSaved);
_dte.Events.DocumentEvents.DocumentOpened += new _dispDocumentEvents_DocumentOpenedEventHandler(DocumentEvents_DocumentOpened);
void DocumentEvents_DocumentOpened(Document Document)
{
}
void DocumentEvents_DocumentSaved(Document Document)
{
}
void DebuggerEvents_OnEnterBreakMode(dbgEventReason Reason, ref dbgExecutionAction ExecutionAction)
{
}
void DebuggerEvents_OnContextChanged(Process NewProcess, Program NewProgram, Thread NewThread, StackFrame NewStackFrame)
{
}
private void DebuggerEvents_OnEnterDesignMode(dbgEventReason reason)
{
}
The first and the major problem is that the subscription to the event doesn't work. I've tried:
Opening new documents
Detaching from debug (thus supposedly triggering OnEnterDesignMode
Saving a document
None of these seem to have any effect and the callback functions were never called.
The second issue is that the subscription to the event line works USUALLY (the subscription itself, the callback doesn't work as described above) but after a while running the subscription line, e.g:
_dte.Events.DebuggerEvents.OnEnterBreakMode -= DebuggerEvents_OnEnterBreakMode;
Causes an exception:
Exception occured!
System.Runtime.InteropServices.InvalidComObjectException: COM object that has been separated from its underlying RCW cannot be used.
at System.StubHelpers.StubHelpers.StubRegisterRCW(Object pThis, IntPtr pThread)
at System.Runtime.InteropServices.UCOMIConnectionPoint.Unadvise(Int32 dwCookie)
at EnvDTE._dispDebuggerEvents_EventProvider.remove_OnEnterDesignMode(_dispDebuggerEvents_OnEnterDesignModeEventHandler A_1)
Any ideas will be welcome
Thanks!
Vitaly
Posting an answer that I got from MSDN forums, by Ryan Molden, in case it helps anyone:
I believe the problem here is how the
CLR handles COM endpoints (event
sinks). If I recall correctly when
you hit the
_applicationObject.Events.DebuggerEvents
part of your 'chain' the CLR will
create a NEW DebuggerEvents object for
the property access and WON'T cache
it, therefor it comes back to you, you
sign up an event handler to it (which
creates a strong ref between the
TEMPORARY object and your object due
to the delegate, but NOT from your
object to the temporary object, which
would prevent the GC). Then you don't
store that object anywhere so it is
immediately GC eligible and will
eventually be GC'ed.
I changed the code to store DebuggerEvents as a field and it all started to work fine.
Here is what #VitalyB means using code:
// list where we will place events.
// make sure that this variable is on global scope so that GC does not delete the evvents
List<object> events = new List<object>();
public void AddEvents(EnvDTE dte)
{
// create an event when a document is open
var docEvent = dte.Events.DocumentEvents;
// add event to list so that GC does not remove it
events.Add(docEvent );
docEvent.DocumentOpened += (document)=>{
Console.Write("document was opened!");
};
// you may add more events:
var commandEvent = dte.Events.CommandEvents;
events.Add(commandEvent );
commandEvent.AfterExecute+= etc...
}