|
I am using C# class library in C++ using C++/CLI mode.
I have declared a class member as follows
gcroot <<jobresults^>> m_objTest;
And the following call gives compilation error
m_myInterface->GetResults(m_objTest);
Error 1 error C2664 :
'cannot convert parameter 1 from 'gcroot<T>' to 'System::IO::Stream ^%'
The following is the prototype of GetResults function in C#
void GetResults(ref JobResults jobRes);
Help me to solve this error.
modified on Monday, June 28, 2010 12:35 AM
|
|
|
|
|
A gcroot is not a System::IO::Stream ^ . You need to convert your object into the correct type required by the function.
It's time for a new signature.
|
|
|
|
|
Before passing it to the by-ref function, you need to use a temp variable. Example:
ref class Derived : Base
{
String^ _s;
public:
Derived(String^ s):_s(s){}
};
void Foo(Derived^% derived)
{
derived = gcnew Derived("From Foo");
}
class Native
{
gcroot<Derived^> _derived;
public:
void Bar()
{
_derived = gcnew Derived("Before");
Derived^ tmp = _derived;
Foo(tmp);
_derived = tmp;
}
};
|
|
|
|
|
Yes Exactly i did the same. So there is no option to pass gcroot object as reference to other function?
|
|
|
|
|
As far as I know, no. Remember that the by-ref language behavior is implemented at runtime by the CLR whereas the gcroot is an unmanaged wrapper class. So unless the callee is a C++/CLI method expecting a gcroot argument, what you want to do is quite impossible (in my opinion anyway).
|
|
|
|
|
Thanks for your information.
|
|
|
|
|
I am creating a custom combobox derived from CComboBox. What I need is to put the image on the arrow button of the combo box. Please help me in this regards.
Thanks
modified on Thursday, June 24, 2010 3:08 AM
|
|
|
|
|
some line of code of what you have done till now would help
modified on Friday, June 25, 2010 7:48 AM
|
|
|
|
|
I am building a program which is to run time-domain simulations. I made a form with a start button which kicks-off the simulation. I now want to make a button to stop the simulation.
I made a boolean which is set to true when the simulation is started, and the simulation runs in a while loop as long as this boolean is true. Then I made a stop button which sets the boolean to false. However, when I click the stop button nothing happens. It seems as if this event is only dealt with after the simulation is ended.
Does anyone have an idea on how to deal with this? Any idea is very welcome!
ATJA
|
|
|
|
|
you should not run your simulation on the main thread, as it blocks the GUI and prevents all interactions (an event handler should last no more than 20 milliseconds).
You need another thread to perform long-wielding calculations; a BackgroundWorker may be handy as you likely want your GUI to be updated while progress is made and new results become available. Look for an example in MSDN.
|
|
|
|
|
Luc,
Thanks for your answer! I'm trying to use a backgroundworker, but I'm having some trouble with getting it to work. I followed the example on MSDN, and first put:
System::ComponentModel::BackgroundWorker^ worker
in the code. Then I try to initialize the worker by:
worker->DoWork += gcnew DoWorkEventHandler(this, &form::worker_doWork);
But now I get an error on this last line while running the program: "Object reference not set to an instance of an object". Any idea what the problem could be?
ATJA
|
|
|
|
|
I guess you'd get that if you don't have executed a worker=gcnew BackgroundWorker somewhere before.
|
|
|
|
|
Thanks, it works now!
However, there is one very strange thing. When I compile the code everything works, but in debug mode nothing happens if I click the button that triggers the backgroundworker events. Any idea what this problem could be?
|
|
|
|
|
Arjen Tjallema wrote: Any idea what this problem could be?
without seeing the relevant code, no.
|
|
|
|
|
That makes sense... Here is the relevant part of my code.
I made a BackgroundWorker called simWorker, and a start and stop button which should, obviously, start and stop my simulation. Starting it works fine when I compile the code, but nothing happens when I run in debug mode. The stop button is not working yet, I'm still finding out how to deal with that.
Hope you can think of something to make this also run in debug mode, that would make life much easier.
Thanks!
namespace uDP2 {
public ref class DPSim : public System::Windows::Forms::Form
{
static clModel^ model = gcnew clModel;
public:
DPSim(clModel^ mdl)
{
InitializeComponent();
model = mdl;
InitializeSimWorker();
}
protected:
~DPSim()
{
if (components)
{
delete components;
}
}
private: System::ComponentModel::IContainer^ components;
protected:
private:
System::ComponentModel::BackgroundWorker^ simWorker;
void InitializeSimWorker(){
simWorker = gcnew BackgroundWorker;
simWorker->DoWork += gcnew DoWorkEventHandler( this, &DPSim::simWorker_DoWork );
}
void simWorker_DoWork( Object^ sender, DoWorkEventArgs^ e ){
BackgroundWorker^ worker = dynamic_cast<BackgroundWorker^>(sender);
e->Result = simulate(model, worker, e);
}
#pragma region Windows Form Designer generated code
{
}
#pragma endregion
private: clModel^ simulate(clModel^ model, BackgroundWorker^ worker, DoWorkEventArgs^ e){
return model;
}
private: System::Void butStart_Click(System::Object^ sender, System::EventArgs^ e) {
simWorker->RunWorkerAsync(model);
}
private: System::Void butStop_Click(System::Object^ sender, System::EventArgs^ e) {
model->state->modelRunning = Convert::ToInt32(0);
}
};
}
|
|
|
|
|
Hi,
that code is not really OK. I do not know what is the exact cause of your symptoms, however I'll offer some comments that should bring you closer to success:
1.
I would defer all initialization of the simulator till the start button is pressed. You don't need a BGW if no simulation is ordered.
2.
I would then also disable the start button (and enable the stop button) for as long as the simulator is running.
3.
a BGW needs more initialization; have a look at one of the examples, e.g. here[^]. You probably want to include (this is C#, change syntax as required):
bw.WorkerSupportsCancellation = true;
bw.WorkerReportsProgress = true;
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
4.
You should provide a RunWorkerCompleted handler; that is where the results become available, the exceptions of DoWork are available (through e->Error), and the start button could be re-enabled (and the stop button disabled again).
5.
You could use the built-in BGW cancellation mechanism, although having your own could probably be equally good.
6.
FYI: other threads are not allowed to touch GUI parts, however both the ProgressChanged and RunWorkerCompleted handlers run on the main thread (assuming you created the BGW on the main thread), and so are allowed to modify the GUI. Your model might take advantage of that.
7.
a BGW uses a ThreadPool thread, i.e. its thread gets recycled automatically, you don't need to worry about that. Use a new BGW each time! Maybe (your symptom description was a bit cryptic here) together with (1) above, that is what caused your discomfort in compiling/running/debugging.
|
|
|
|
|
Thanks a lot for your comments! I've used them in my code, but the problem with debugging is still not solved.
My start button contains the following code:
simWorker = gcnew BackgroundWorker;
simWorker->WorkerSupportsCancellation = true;
simWorker->WorkerReportsProgress = true;
simWorker->DoWork += gcnew DoWorkEventHandler(this, &uDP2::DPSim::simWorker_DoWork);
simWorker->ProgressChanged += gcnew ProgressChangedEventHandler(this, &uDP2::DPSim::simWorker_ProgressChanged);
simWorker->RunWorkerCompleted += gcnew RunWorkerCompletedEventHandler(this, &uDP2::DPSim::simWorker_RunWorkerCompleted);
butReset->Enabled = false;
butPause->Enabled = true;
butStart->Enabled = false;
simWorker->RunWorkerAsync(model);
When I compile and run the program this works fine, the last line in this code runs the actual model. However, when I step through the code in debug mode the last line seems to be skipped; it passes this line without running the model. I don't have a clue what's going on here, this seems to be very strange to me...
Update:
It seems that the code which updates the data on my Graphical User Interface in each timestep causes the problem. I have an "updateGUI" function which is called in each timestep from the simulation, which is again called by RunWorkerAsync. The updateGUI function contains:
lblTime->Text = Convert::ToString(Math::Round(*model->state->t,2));
txtX->Text = Convert::ToString(Math::Round(model->state->eta[0,0],2));
txtY->Text = Convert::ToString(Math::Round(model->state->eta[1,0],2));
txtPsi->Text = Convert::ToString(Math::Round(model->state->eta[5,0],2));
lblWindDirSim->Text = Convert::ToString(*model->environment->windDir / Math::PI * 180);
If I comment this out the code runs fine in debug mode, otherwise it doesn't. Any idea what the problem could be?
modified on Thursday, June 24, 2010 5:41 AM
|
|
|
|
|
I see two possibilities:
1.
you did not follow my earlier point #6, so the offending code is in DoWork, rather than in the ProgressChanged handler.
2.
that code is throwing an exception; put it all in a try-catch and look at Exception.ToString().
BTW: your RunWorkerCompleted should check e.Error, which would contain any uncaught exception that has occured while the BGW was running.
|
|
|
|
|
Luc,
I found out I did indeed not follow your point #6, I guess I didn't completely understand.
Now it works fine, thanks a lot for all your quick and useful help!!
Regards,
ATJA
|
|
|
|
|
You're welcome.
You may want to read this[^] to know more about GUI and threads.
|
|
|
|
|
That indeed looks interesting for what I'm doing, I'll have a look at it.
Thanks again!
|
|
|
|
|
I add reference to BOOST thread header and do not even use it yet. Then when I run my application I immediately get the error message
"The application failed to initialize properly. Click on OK to terminate the application."
I found a possible solution while googling, but am not sure if it is safe. Their is a run-time error at startup depending on the #pragma managed and #pragma unmanaged usage.
"Alas, the default compiler settings may lead to trouble:
Statically linking to Boost libraries may yield an executable
which chokes on startup with a System.BadImageFormatException, even
if the compiler and linker run without warning (a bug, IMHO).
Explanantion:
<cite author="Anthony Williams" date="2009-02-04 15:54:40 GMT">
The problem is that the static boost thread library tries to hook the
native win32 PE TLS callbacks in order to ensure that the thread-local
data used by boost thread is cleaned up correctly. This is not
compatible with a C++/CLI executable.
</cite>
Solution: In Configuration Properties -> C/C++ -> Preprocessor ->
Preprocessor Definitions add BOOST_ALL_DYN_LINK in order to
force the usage of the DLLs.
In addition copy the necessary DLLs to the directory where the
executable resides. E.g. copy boost_thread-vc90-mt-gd-1_XX.dll
to MyApp/bin/Debug.
Enters the next bug of Visual Studio:
The C++/CLI compiler deliberately chooses to ignore the
#pragma managed(push, off) / #pragma managed(pop) PREPRO directives
for files which are compiled with /clr command line flag.
You obtain a plethora of warnings of the form
warning C4793: 'boost::[..]::`vcall'{0}'' : function compiled as native :
For the executable this means that on termination another exception
is thrown, which adds to the confusion:
Unhandled exception at 0x7c812a7b in XXX.exe: 0xC0020001:
"Die Zeichenfolgenbindung ist unzulässig." or in english:
Error 1700 "The string binding is invalid" or
RPC_S_INVALID_STRING_BINDING.
Solution: Put managed (CLI) and unmanaged C++ code into different
compilation units. For unmanaged code files change the compiler
settings this way:
In "Configuration Properties -> C/C++ -> General -> Compiler with
Common Language" support choose "No Common Language Support".
"
modified on Tuesday, June 22, 2010 9:06 AM
|
|
|
|
|
So what is your question? Yes, mixing boost.thread which uses the native Win32 threading API with CLR code is not without problems. You definitely will need to re-compile boost.threading such that it uses dynamic binding to the multi-threading version of c-runtime libs. Then choose compatible settings when building your C++/CLI project.
|
|
|
|
|
Thanks for the reply.
I have written a program that works as a CLR executable with boost threads, but when I make it a DLL I get an immediate crash. I have not been able to figure this out and have posted to the boost community without resolution.
Source code for a simple example is below
//CLR_DLL.def
LIBRARY CLR_DLL
EXPORTS
CLR_TEST
//CLR_DLL.h
#ifndef CLR_DLL_H
#define CLR_DLL_H
//define CLR_DLL in the CLR_DLL project, but not the executable
#ifndef CLR_DLL
#pragma comment(lib, "CLR.lib")
#pragma message("Using Library CLR.lib")
#endif
void _stdcall CLR_TEST();
#endif
//CLR_DLL.cpp
#ifndef CLR_DLL_H
#include <clr_dll.h>
#endif
//include directory C:\Program Files\boost\boost_1_43_0
//lib directory C:\Program Files\boost\boost_1_43_0\stage\lib
#pragma managed(push, off)
//dll will not compile without next line, but executable would crash without it!
//extern "C" void tss_cleanup_implemented(void) {}
#include <boost thread="" thread.hpp="">
#pragma managed(pop)
#pragma unmanaged
namespace boost {
struct thread::dummy {};
}
namespace boost {
namespace detail {
namespace win32 {
struct _SECURITY_ATTRIBUTES {};
};
};
};
void run() {}
void test() {
boost::thread t(run);
}
void _stdcall CLR_TEST() {
test();
}
//MAIN.cpp in exe project
#include <clr.h>
int main(array<system:tring ^=""> ^args) {
CLR_TEST();
return 0;
}
|
|
|
|
|
Hm, as far as I can tell you need to get the managed/unmanaged #pragmas right and define a conformant dll entry point. But in general, I would not do it that way.
Create two distinct projects:
1. a native c++ dll project which uses boost and exports the functionality you need encapsulated in an interface, which has no direct references to boost::thread. Take care to choose multi-threaded and dll version of the runtime libraries. Don't forget to compile boost.threading accordingly!
class IMyUnmanagedAPI {
public:
virtual DoMyUnmanagedStuff() = 0;
};
class MyUnmanagedAPIFactory {
public:
static IMyUnmanagedAPI* Create();
static void Destroy(IMyUnmanagedAPI*);
};
2. a c++ dll project with clr support which includes your interface declaration - but nothing else! Make this project use the unmanaged import library generated by project 1. Use a clr wrapper around your unmanaged interface, following the recommended patterns. In your clr project, you only communicate with a pointer to IMyUnmanagedAPI object which (or, better, implementation of which) encapsulates all the boost:: stuff.
modified on Sunday, August 22, 2010 5:47 AM
|
|
|
|
|