Best Practices for DllMain
DllMain keeps on popping up as a pain point for developers. Heck, just yesterday we found an old bug where a component was calling CoFreeUnusedLibraries in its DllMain function.
So it was really cool that this morning I came in and discovered an email (from the PM who owns the DLL loader) about a new article that was posted with best practices for authoring DLLs (in particular DllMain).
You can find the article here.
Comments
- Anonymous
June 15, 2006
Great reference. It's too bad it's hidden away deep within device driver development resources. - Anonymous
June 15, 2006
Very interesting document. But I think it's not clear enough regards the use of CRT functions:
- it says you cannot use the CRT functions dealing with memory (so I suppose malloc, free, but also strdup, which is in any case DEPRECATED).
- it says that you can "open, read from, write to file". Can I use the CRT file functions like fopen/fclose? Or just the CreateFile/ReadFile/WriteFile ones? My suspect is that you cannot use fopen/... functions are they are NOT inside kernel32.dll.
My rule of thumb is usually use only the functions exported by Kernel32.dll (and then follow the other restrictions). - Anonymous
June 15, 2006
I agree with Peter Ritchie - this should be in MSDN - possibly added to the DllMain document itself.
As Gianluca Varenni implies, the item about not using CRT memory alloc functions should be expanded to simply say that CRT routines should be avoided - calling a CRT function before the CRT DLL has been initialized can cause the deadlock.
I like the list of what you can do - but I think it basically boils down to don't call other DLLs except kernel32.
Also, the document says that the "loader lock must be at the bottom" of the lock acquisition hierarchy. Unless I'm confused on the terminology, it seems that the loader lock must be at the top of the heirarchy, since it is by definition aqcuired by the DllMain thread before that thread has an opportunity to acquire any other locks. - Anonymous
June 15, 2006
> DllMain keeps on popping up as a pain point for developers.
Not for some. Consider Visual Studio 2005. If you use the wizard to generate a skeleton project, it generates code with the first parameter having type HANDLE. If you change the first parameter to have type HINSTANCE, Visual Studio gives compile-time errors. So is this painful for Visual Studio developers? Not at all. Not only is it a "won't fix" for the present version of Visual Studio, it's already a "won't fix" for the next version. No one's going to do any painful exercise fixing this.
Of course it really is less painful than some other "won't fixes", but I don't know of others involving DllMain at the moment. - Anonymous
June 15, 2006
The comment has been removed - Anonymous
June 16, 2006
The comment has been removed - Anonymous
June 16, 2006
Say you do not wish to distribute a dll as a separate file, but you can't compile it into the executable as it's written in a different programming language.
The only way to load the DLL via api functions is to store the DLL as a resource, then when your application runs save it to disc and load it from there. You can't directly load it from memory.
There are ways to manually load and initialize a dll located in memory, but none are provided by the API as far as I know.
Considering how often I've seen different solutions to being able to include a DLL inside the executable I think more people than I wonder why you can only load DLLs from a storage medium. (a simple search gives quite a long list of results) - Anonymous
June 16, 2006
Why NOT distribute it as a separate file? MSI is a stable reliable technology, free for redistribution (as far as I know), and adds you to ARP for easy discovery.
Zip also works pretty well.
The scenario you're describing is pretty fringe, IMHO. - Anonymous
June 16, 2006
PetrieW, I think what Larry is saying, is; if you have code you want to run but don't want it in a physical DLL on disk then just run it as part of your application. I.e. don't put it in a separate DLL.
If you don't have the source for the DLL but still want to use it, then that's a different story--and I would echo Larry, that's pretty fringe. - Anonymous
June 16, 2006
Well, I started looking into it after people replaced customized DLLs with other DLLs (opensource project stuff and they know what I use) and caused data corruption. Some people seem willing to go to great lengths to do stuff the way they want to.
(In this case they replaced the DLL to be able to do inserts in a encrypted flatfile database.)
While loading from memory would in this case not be 100% remedy to that, I've found that encrypting the dll and simply loading it manually into memory proved to be too big a hurdle to make it worthwhile to even try. (Probably helps a bit that it doesn't actually call winapi to find dll functions.)
I know anything placed on someone elses pc is essentially unprotectable but that doesn't mean I feel like making it dead easy.
And yes, this is very fringe and probably there's some much better way to do it, I just happened to find the code for loading it from memory and stuck with that. ;) - Anonymous
June 17, 2006
There's a slightly less obscure reason for having memory-based DLLs: global hooks. Often, the type of program that uses global hooks is a relatively small utility-type program: e.g. global hotkey utilities, mouse/keyboard recording utilities, window management changes ("true X-mouse", theme programs) etc. The hook DLL isn't needed outside the lifetime of the process that installed the hook. Many of these programs are small enough that they could be distributed as one file, and would benefit from a memory-based DLL. The image of the DLL could be backed by the swap file like an anonymous memory-mapped file. - Anonymous
June 20, 2006
I found that paper pretty much useless. It contains a very poor overview, in my estimation. A much better resource is this blog: http://blogs.msdn.com/mgrier/default.aspx
He goes much further in depth than the paper. - Anonymous
June 20, 2006
I do not think that configuration file loading and varification should be recommended, as people may use complex configuration files including having XML files and if the XML parser loads any DLL, it may creat problem. - Anonymous
June 29, 2006
Being able to call a sort of "LoadLibraryInMemory()" would actually be quite useful for various things: UPX-like compression (currently, everything has to be decompressed into a temporary file instead), packaging Java applications with their own JVM (you can't go hacking the JVM to embed more neatly - but linking against an unmodified DLL in memory should be OK)...
ISTR someone somewhere had a (proprietary) tool which claimed to do this; I could see some promising directions in the native API, but not enough documentation to make it an easy route. - Anonymous
July 05, 2006
One of the things one cannot do in DllMain or in static objects (on Windows) is to perform network operations (with other threads or processes). This is allowed on other platforms. Can you shed some light on which we have this restriction on DllMain?
~ash - Anonymous
July 05, 2006
Ash, the simple answer is "the loader lock". This is the root of the problem with DllMain - the loader lock is held across the DllMain calls for all Dlls. Essentially this puts the loader lock at the top of the lock hierarchy, which means that you can't take any other locks while inside it.
Mike Grier wrote an insanely detailed series on the loader lock a while ago, it's referenced in Jeff's comment above. - Anonymous
July 05, 2006
[1] I wrote:
> Consider Visual Studio 2005. If you use the wizard to
> generate a skeleton project, it generates code with the
> first parameter having type HANDLE. If you change the
> first parameter to have type HINSTANCE, Visual Studio
> gives compile-time errors.
Today I had occasion to discover that Visual Studio 2005 doesn't have that bug with plain old Win32 DLLs. So two of my DLLs now have code like this:
BOOL APIENTRY DllMain(
#ifdef UNDER_CE
HANDLE hModule,
#else // WIN32
HMODULE hModule,
#endif // CE vs. WIN32
DWORD ul_reason_for_call,
LPVOID lpReserved)
{
[....]
It's still been resolved as a "Won't fix" for the next release, so it's not painful for Microsoft's Visual Studio development team.
[2] Earlier today I happened to notice one of Mr. Osterman's blog entries from year 2004, in which a comment by Mr. Grier mentioned that memory allocation calls from DllMain can cause deadlock. This made me wonder yet again how to allocate memory for Thread Local Storage. Is GlobalAlloc really safer than malloc? - Anonymous
July 14, 2006
The comment has been removed - Anonymous
June 08, 2009
PingBack from http://quickdietsite.info/story.php?id=1523