I am currently fighting with calling an unmanaged C DLL from my C# Winforms application. Basicaly, it works, but sometimes it seems like the DLL looses its context, so I have to restart my app to be able to work with it again.
Are there any rules when the unmanaged DLL is loaded and when released from memory during my app is running? Or can the garbage collector randomly release the DLL and then initialize it again when the DLL is called next time?
I am using the standard approach to call DLL functions:
[DllImport("Bat1Dll.dll")]
public static extern uint GetDeviceVersion();
The DLL reads a block of data from a device connected via USB. The data is kept in the DLL, I can edit it using several functions and finaly send the data back to the device.
Thanks for any suggestions,
Petr
PetrPosted Aug 8, 2008, 7:27 AM
Thanks Alan,
that's what I don't understand too. In C#, it was also much harder to get the errors in a console application, compared to a WinForms app, where it was showing errors all the time.
Petr
AlanPosted Aug 8, 2008, 6:57 AM
Whilst I can understand why not allowing for the value of the dwReason parameter would cause the variables to be re-initialized each time the system called DllMain, I can't really see why the problem would be any worse for a managed consumer as opposed to an unmanaged consumer of the dll :-/
Anyway, no matter, I'm pleased you've solved it :)
You've allowed for all possible dwReason values so there shouldn't be anything else to worry about.
PetrPosted Aug 8, 2008, 6:08 AM
So, we have probably found a solution. Originally, our DllMain function in the DLL looked like this:
BOOL APIENTRY DllMain( HANDLE hModule, DWORD dwReason, LPVOID lpReserved) {
// Create devices :
Adapter = new TPktAdapter;
Bat1 = new TBat1;
Bat1->Adapter = Adapter;
return 1;
}
We thought the DllMain function will be called only once during DLL load, so we initialized all our modules here without any care about the dwReason parameter.
As it turned out, the DllMain is called more often for several reasons like DLL_PROCESS_ATTACH, DLL_PROCESS_DETACH, DLL_THREAD_ATTACH etc. That's why our modules were initialized every time the DllMain was called, which damaged our data.
Now the DllMain looks like this and everything seems to be working:
BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved) {
if (dwReason == DLL_PROCESS_ATTACH) {
// Create devices :
Adapter = new TPktAdapter;
Bat1 = new TBat1;
Bat1->Adapter = Adapter;
} else if (dwReason == DLL_PROCESS_DETACH) {
delete Logger;
delete Adapter;
delete Bat1;
} else if (dwReason == DLL_THREAD_ATTACH) {
// Don't care
} else if (dwReason == DLL_THREAD_DETACH) {
// Don't care
} else {
// Unknown reason
}
return 1;
}
Are there any other thing we should take into consideration?
Petr
AlanPosted Aug 5, 2008, 6:46 PM
All I can think of is that the cumulative overhead of repeatedly calling the dll functions via the P/Invoke mechanism is causing a synchronization problem in communications with the USB port.
Would it be feasible for you to try calling the dll from some other .NET language (such as C++/CLI or VB.Net) to see whether you experience the same problem as when calling it from C#?
PetrPosted Aug 5, 2008, 3:55 AM
What can be the problem if I work with the DLL exactly the same way both in C++ and C#? Does it mean that the DLL is somehow dependent on the C#?
Petr
PetrPosted Aug 5, 2008, 1:14 AM
Thanks Alan,
nice to hear that the DLL will not be released until my app closes, so I can focus on the DLL itself. I have made some tests so far and it seems that it works perfectly when called from C++, I have problems only when calling from C#. We are going to test it more.
Petr
AlanPosted Aug 4, 2008, 6:24 PM
The dll will be loaded (if it hasn't already been loaded by another running process) when the first call is made via P/Invoke to the unmanaged function and, AFAIK, will remain loaded until (no sooner than) the application ends.
The only thing you could do to unload it before then would be to P/Invoke the API function FreeLibrary but it's not really a good idea to mess with the internal workings of the CLR.
So, if you're making repeated calls to GetDeviceVersion() then I think you can discount the possibility of any problems being caused by having to reload the library.There is a slight performance hit with any P/Invoke call but it's rarely noticeable.
As no parameters are being passed to the function, I can only think that the problem must lie with the dll itself. Does it always work faultlessly from C itself?