Larry Osterman's WebLog

Confessions of an Old Fogey
Blog - Title

Best Practices for DllMain

Best Practices for DllMain

  • Comments 19

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.

  • Great reference.  It's too bad it's hidden away deep within device driver development resources.
  • 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).
  • 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.
  • > 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.
  • Something I've always wondered, why are there no functions to load a dll from memory rather than from disc?
    Sure, you can set it up manually but well... that's error prone. ;)
  • What does it mean to load a DLL from memory?

    A DLL is a file on the disk that contains code, just like an EXE.  Executable code is loaded from some storage medium.  

    If it was in memory, you could just execute it.

  • 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)
  • 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.
  • 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.
  • 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. ;)
  • 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.
  • 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.
  • 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.
  • 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.
  • 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
Page 1 of 2 (19 items) 12