|
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
|
|
|
|
|
Thanks for the suggestion. This week, I am going to take your suggestion of two separate projects, but I will have to have the native C++ dll call the dll with clr support. The reason is that my boost thread code needs to call .NET functionality such as ADO and WebClient.
|
|
|
|
|
Well, the point is to have the functionality of either worlds compiled separately in distinct translation units. However, the way you seem to be going is a bit harder: You will need to provide abstractions of .NET entities usable in native c++ program.
I don't know the background of what you are trying to accomplish, but I would suggest to stay "at one side of the line" (either .NET or native) as long as possible. What you have sketched above (using boost.threading with ADO and Web) smells like problems... There should be a very, very serious reason to do it that way.
|
|
|
|
|
Thanks for the input.
The code works after I separate it into two dlls. The particular function queries multiple webpages on separate threads and webclient was the only CLR functionality I utilized. I then processes the data after all of the threads come back since the processing time is small fraction of the runtime.
I also am thinking of building XLL's with functions that retrieve data from the web, require multiple database calls/queries, and do array function computations simultaneously. The .NET threads cannot be utilized in the XLL project, so I have to use Boost threads or some other thread library.
The Boost threads cannot be utilized inside the CLR project, but I am not using much more native than .NET code. So I created another dll that makes a native wrapper around the small # of functions I use with CLR.
|
|
|
|
|
I am trying to read the article 'Unraveling the Mysteries of Writing a Winsock 2 Layered Service Provider' that microsoft published in may MSDN in 1999.
The article http://www.microsoft.com/msj/0599/LayeredService/LayeredService.aspx[^] is old I admit but I could not find anything more recent that use that technique.
The goal is to write a web filter that would prevents words like ***, **** and ***** to appear in web pages.
I need to be able to rewrite the content of web pages on the fly and hopefully messengers too.
Did anyone manage to make this run in a recent Visual Studio?
I would prefer to have this written in MC++ but I fear it is beyond my C++ capabilities at this stage.
This is a compelling area of development, I am surprised I can't find an article here that would extend this sample.
I certainly hope I overlooked it.
The code is available here : http://download.microsoft.com/download/0/6/7/0678184e-905e-4783-9511-d4dca1f492b4/MSJMay99.exe[^]
|
|
|
|
|
After a lot of effort (and downloading the Windows Vista/7 SDK) I am slowly progressing on this.
|
|
|
|
|
There is a book that explains how to write one.
The book is called Network Programming for Windows - Second Edition
by Anthony Jones and Jim Ohlund
This is regarded as a classic book, so you might have to pay a high price for it.
|
|
|
|
|
Hello,
I made a DLL wrapper in managed code which is merely a wrapper function around a native C++ DLL. The DLL works perfectly except I can not see the assembly info in explorer. Using ILDASM, I can see the fields in the manifest. What could be wrong?
Thanks in advance
|
|
|
|
|
The C# and VB compilers will pull the info from assemblyinfo.cs and create the required version resource. Since C++ supports native resources it will not do this automatically. You will have to create a native version resource manually like you would with a native project. There will be some information duplication of course, but it's not a big deal I guess.
|
|
|
|
|
OK, thanks Nishant, I added a resource to VS2010 project which created the info. I needed. So in AssemblyInfo.cpp the only information that I can affect is for signing the assembly and where the key is located. Can I then remove the other fields or are they used?
|
|
|
|
|
alleyes wrote: Can I then remove the other fields or are they used?
Yeah you can remove them if you don't want to have that info in the assembly metadata (which is query-able from .NET).
|
|
|
|
|