C++ DLL does not unload with AppDomain - c#

I have a C# plugin that uses a separate C++ DLL. The only reference to that DLL is from within the plugin itself. The parent application loads all plugins in their own AppDomain and unloads this AppDomain when the plugin is unloaded.
I have checked, and I definitely see the application's memory drop when I unload the plugin. I am also able to delete all of the managed assemblies that were loaded. The problem is that when I try to delete the native DLL I just keep getting Access Denied until I close the whole application.
I've been looking at this for a while, but I still can't figure out why just this DLL stays in memory.

AppDomains are a pure managed code construct. Nothing like that exists in native code, nor does Windows have any idea about it. So the scope for a loaded native DLL is the process. Technically, the pinvoke marshaller could reference count the DLL and keep track exactly which AppDomain triggered the load of the DLL. It however cannot tell whether any native code is running that uses that DLL. Native code that could be started by a call made from code in another AppDomain, possibly indirectly through a marshaled delegate.
Clearly disaster strikes if the AppDomain manager unloads a DLL that's used that way, that's a nasty and impossible to diagnose AccessViolation. Particularly nasty since it can trigger a long time after the AppDomain got unloaded.
So the marshaller doesn't implement that kind of counting, the DLL stays loaded. Only you can provide the guarantee that this can't happen, you have some measure of control over exactly what code runs in the DLL and how it gets started. You can force the DLL to unload but it requires a hack. Pinvoke LoadLibrary() yourself to get a handle to the DLL. And pinvoke FreeLibrary() twice to get it unloaded forcibly. Neither Windows nor the CLR can't see that you are cheating. You must ensure that the DLL can't be used after this.

AFAIK (under the hood) native DLLs need to be loaded via Win32 API LoadLibrary... which loads them directly into the process memory - in case of a .NET application that is not specific to an AppDomain... LoadLibrary knows absolutely nothing about AppDomain (which is purely .NET-specific)... thus unloading the AppDomain doesn't necessarily unload native DLLs...
Interesting discussions regarding this situation:
http://clrinterop.codeplex.com/discussions/61817
http://www.mofeel.net/61-microsoft-public-dotnet-framework-interop/1940.aspx
IF you can change the implementation of the respective plugin then you would implement "late native binding" which would solve the problem you see:
http://blogs.msdn.com/b/junfeng/archive/2004/07/14/181932.aspx
http://www.codeproject.com/KB/cs/dyninvok.aspx
http://pinvoke.net/default.aspx/kernel32/GetPRocAddress.html
http://www.pinvoke.net/default.aspx/kernel32.loadlibrary

Related

Implicit Linking and FreeLibrary in C#

I just found out I could unload DLL which is implicit linking with the function FreeLibrary() in C#. I remember I couldn't do this in C++, but it works well in my simple test project. I wonder if this would be okay in my real projects too. Is it safe to use this method?
Fairly vague, I'll have to assume you talk about DLLs that got loaded through pinvoke. Yes, there is no protection against calling FreeLibrary() twice. Works in C++ as well btw for a DLL that's loaded explicitly. Not for implicitly loaded DLLs, they get a reference count of "infinity".
The pinvoke marshaller uses LoadLibrary() under the hood, happens when the very first [DllImport] function gets executed. The OS loader simply keeps a reference count, every LoadLibrary() call increments it and FreeLibrary() decrements. When it reaches 0 then it gets unloaded. So if you pinvoke LoadLibrary() yourself and call FreeLibrary() twice then the DLL does get unloaded. The virtual address space formerly used by the memory-mapped file that maps the code in the DLL is released and can be used again by subsequent allocations.
Safe, no, that's not a word that jumps to mind. When you accidentally call an entrypoint in the DLL then your program is going to behave very poorly. The pinvoke marshaller cannot do anything about it, the stub for the native method was already generated. Odds for an AccessViolationException are decent but not guaranteed. Arbitrary code execution is technically possible.
The only truly safe way to do this is to ensure that the AppDomain that contains the pinvoke code is unloaded. You get no help with this, just a rule you have to implement yourself.

Calling a c# .dll from native visual c++ code

the system I'm working with consists of:
A front-end application written in most likely VB or else VC++ (don't know, don't and can't have the sources for it)
An unmanaged VC++ .dll
A C# .dll
The application calls the first dll, the first dll calls different methods from the second one.
In order to make the first dll able to see and call the C# code I followed this guide:
http://support.microsoft.com/kb/828736
The only difference is that i am not compiling with /clr:OldSyntax, if I do then changing the other dependant compiling options makes the first dll load incorrectly from the application.
Everything compiles smoothly; the whole setup even worked fine initially, however after completely developing my code across the two dlls I now get an error in the application. The error is:
Run-time error '-2147417848 (80010108)':
Automation Error
The object invoked has disconnected from its clients.
And occurs when the following line is executed in the first dll:
MyManagedInterfacePtr ptrName(__uuidof(MyManagedClass));
I tried reproducing a fully working setup but without success.
Any ideas on how the heck I managed to do it in the first place?
Or alternatively on other approaches for making the two dlls work together?
Thanks in advance!
It is a low-level COM error, associated with RPC. That gets normally used in out-of-process servers, but that doesn't sound like your setup. It would also be used if you make calls on a COM interface from another thread. One possible cause is that the thread that created the COM object was allowed to exit, calling CoUninitialize and tearing down the COM object. A subsequent call made from another thread would generate this error. Getting reference counting wrong (calling Release too often) could cause this too.
Tackle this by carefully tracing which threads create a COM object and how long they survive.

How do I protect my C# app from crashing when calling a method in an unmanaged DLL?

I have an unmanaged DLL written by another developer in unmanaged C++. My application is a WinForms application written in C#. I am using interop to call a method (function) in the native DLL. The call is causing my application to crash upon executing the method.
How does one safely call a method using interop, so that it does not bring the app down?
You could create a seperate AppDomain and load the unmanaged DLL in it. If the call crashes, it will just crash the AppDomain and your application will still live

How to write a lib in C#, the lib can be run without any invoked?

i want to write a C# lib, or a reference service,
so that if a application reference my lib, and when the application runs,
the function in my function can be run without any invoked?
for example, i got a lib, keep reading the memory usage of the platform,
when the memory was up to 80%, i pop up a message to the user, it is time to close the application.
so..when i create a another application, and want my new application has the mem check feature, i just need to reference it then after my new application run, the mem check will be fired together..
so..how can i do these?
Create a static constructor for your class - this constructor will be run once, and only once, the first time the host application references anything related to your class.
You could create a thread to run some background checking process, hook into an event of the Application object, or whatever you need.
Note however, that you'll need to do more than just add a project reference to your assembly - an assembly is only loaded into an App Domain when something in that assembly is referenced by existing code.
The term you're looking for is "Win32 application hook." There's a decent introduction to managed hook injection here: http://msdn.microsoft.com/en-us/magazine/cc188966.aspx
I'm not sure you can monitor system memory consumption with an application hook, though. And unfortunately, you cannot write a global hook in managed code because global hooks require a well-defined entry point (a DLL export). This is really something best suited for C++.
It is possible to create traditional DLL exports in an assembly (via ILASM or the now defunct Delphi.NET) but not really recommended. (search for reverse p/invoke)
Another approach is to create a C++/CLI intermediate dll to call your managed code. But to be perfectly honest, I reckon you are simply better off just creating a native DLL with C++ or Delphi.
Edit:
Ok, firstly a disclaimer, I work for Quest Software (the company that makes this tool that I am about to plug). That said...
OS Monitoring is actually not as straight forward as you might think, things like memory consumption, process monitoring etc is...well, pernickety. You may find that somthing like Spotlight on Windows (Freeware for first 10 licences) would suit your purpose ?

Loader lock error

I am building on C++ dll, by writing code in C#.
I get an error, saying
LoaderLock was detected Message:
Attempting managed execution inside OS
Loader lock. Do not attempt to run
managed code inside a DllMain or image
initialization function since doing so
can cause the application to hang.
I tried seraching what this error exactly means, but I am drawing pointless articles, mostly saying that it's just a warning, and I should switch that off in Visual Studio.
The other solutions seem to be due to ITunes, or this problem occurring when programming with DirectX. My problem is connected to neither.
Can anybody explain, what this actually means?
you need to go to menu Debug -> Exceptions, open the Managed Debugging Assistants, find LoaderLock and uncheck
The general idea of loader lock:
The system runs the code in DllMain inside a lock (as in - synchronization lock). Therefore, running non-trivial code inside DllMain is "asking for a deadlock", as described here.
The question is, why are you trying to run code inside DllMain? Is it crucial that this code run inside the context of DllMain or can you spawn a new thread and run the code in it, and not wait for the code to finish execution inside DllMain?
I believe that the problem with manged code specifically, is that running managed code might involves loading the CLR and suchlike and there's no knowing what could happen there that would result in a deadlock... I would not heed the advice of "disable this warning" if I were you because most chances are you'll find your applications hangs unexpectedly under some scenarios.
UPDATE FOR .NET 4.0 AND MORE RECENT FRAMEWORKS
This is an old question asked at the time of .Net 2.0, when support for mixed mode DLLs had serious initialization problems, prone to random deadlocks. As of .Net 4.0, the initialization of mixed mode DLLs has changed. Now there are two separate stages of initialization:
Native initialization, called at the DLL's entry point, which includes native C++ run-time setup and execution of your DllMain method.
Managed initialization, executed automatically by system loader.
Since step #2 is performed outside of the Loader Lock, there is no deadlocks. The details are described at Initialization of Mixed Assemblies.
To ensure your mixed mode assembly can be loaded from a native executable, the only thing you need to check is that DllMain method is declared as native code. #pragma unmanaged could help here:
#pragma unmanaged
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
... // your implementation here
}
It is also important that any code that DllMain might call directly or indirectly is also unmanaged. It makes sense to limit the type of functionality used by DllMain so you trace all code reachable from DllMain and ensure it is all compiled with #pragma unmanaged.
Compiler helps a little by giving you warining C4747 if it detects that DllMain is not declared as unmanaged:
1> Generating Code...
1>E:\src\mixedmodedll\dllmain.cpp : warning C4747: Calling managed 'DllMain': Managed code may not be run under loader lock, including the DLL entrypoint and calls reached from the DLL entrypoint
However compiler won't generate any warnings if DllMain indirectly calls some other managed function, so you need to ensure that never happens, otherwise your application could deadlock randomly.
kindly remind those VS2017 users that you need to disable "exception helper" instead of "exception assistant"(before VS2017) to prevent from loader lock error, which setting path is Debug->Exception. Just ran int to this problem and wasted 2 hours searching for solutions...
Press ctr d+e Then Expend Managed Debugging Assistants Node. Then Unchecked the LoaderLock.
Hope this will help you.
I recently got this error while creating an instance of an COM-Object written in native code:
m_ComObject = Activator.CreateInstance(Type.GetTypeFromProgID("Fancy.McDancy"));
This led to the described error. A "LoaderLock was detected"-Exception was thrown.
I overcame this error by creating the object-instance in an extra thread:
ThreadStart threadRef = new ThreadStart(delegate { m_ComObject = Activator.CreateInstance(Type.GetTypeFromProgID("Fancy.McDancy")); });
Thread myThread = new Thread(threadRef);
myThread.Start();
myThread.Join(); // for synchronization
I'm building a C++ CLR DLL (MSVS2015) that has to make calls into an unmanaged DLL and define unmanaged code. I use #pragma managed and #pragma unmanaged to control what mode it is in for a given area of the code.
In my case I simply put #pragma unmanaged in front of my DllMain() and this solved the problem.
It seemed to be thinking I wanted a managed version of DllMain().
The setting path in my visual studio 2017 instance is Debug -> Windows -> Exception Settings . The exception settings "window" showed up in the bottom tab group (as opposed to a separate window), took me a while to notice it. Search for "loader".
This problem occurs because of the way in which the debugger in Visual Studio runs managed applications that use Microsoft Foundation Classes version 8.0 in one or more DLL files.
Have a thorough reading at: http://msdn.microsoft.com/en-us/library/aa290048(vs.71).aspx

Categories

Resources