views:

197

answers:

1

We have a managed app, that uses an assembly. That assembly uses some unmanaged C++ code.

The Managed C++ code is in a dll, that depends on several other dlls. All of those Dlls are loaded by this code. (We load all the dll's that ImageCore.dll depends on first, so we can tell which ones are missing, otherwise it would just show up as ImageCore.dll failed to load, and the log file would give no clues as to why).

class Interop
{
    private const int DONT_RESOLVE_DLL_REFERENCES = 1;
    private static log4net.ILog log = log4net.LogManager.GetLogger("Imagecore.NET");

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr LoadLibraryEx(string fileName, IntPtr dummy, int flags);
    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr FreeLibrary(IntPtr hModule);

    static private String[] libs = { "log4cplus.dll", "yaz.dll", "zlib1.dll", "libxml2.dll" };

    public static void PreloadAssemblies()
    {
        for (int i=0; i < libs.Length; ++i) {
            String libname = libs[i];

            IntPtr hModule = LoadLibraryEx(libname, IntPtr.Zero, DONT_RESOLVE_DLL_REFERENCES);
            if(hModule == IntPtr.Zero) {
                log.Error("Unable to pre-load '" + libname + "'");
                throw new DllNotFoundException("Unable to pre-load '" + libname + "'");
            } else {
                FreeLibrary(hModule);
            }
        }

        IntPtr h = LoadLibraryEx("ImageCore.dll", IntPtr.Zero, 0);
        if (h == IntPtr.Zero) {
            throw new DllNotFoundException("Unable to pre-load ImageCore.dll");
        }
    }
}

And this code is called by

public class ImageDoc : IDisposable {
    static ImageDoc()
    {
        ImageHawk.ImageCore.Utility.Interop.PreloadAssemblies();
    }
    ...
}

Which is static constructor.

As near as I can understand it, as soon as we attempt to use an ImageDoc object, the dll that contains that assembly is loaded and as part of that load, the static constructor is called which in turn causes several other DLLs to be loaded as well. What I'm trying to figure out, is how do we defer loading of those DLLs so that we don't run smack dab into this loader lock that is being kicked out because of the static constructor.

I've pieced this much together by looking at:

  1. http://social.msdn.microsoft.com/Forums/en-US/vsto/thread/dd192d7e-ce92-49ce-beef-3816c88e5a86
  2. http://msdn.microsoft.com/en-us/library/aa290048%28VS.71%29.aspx
  3. http://forums.devx.com/showthread.php?t=53529
  4. http://www.yoda.arachsys.com/csharp/beforefieldinit.html

But I just can't seem to find a way to get these external DLLs to load without it happening at the point the class is loading. I think I need to get these LoadLibrary calls out of the static constructor, but don't know how to get them called before they are needed (except for how it is done here). I would prefer to not have to put this kind of knowledge of the dlls into every app that uses this assembly. (And I'm not sure that would even fix the problem....

The strange thing is that the exception only appears to be happening while running within the debugger, not while running outside the debugger.

How do I manage to Load those DLLs without running afoul of:

LoadLibrary <- .NET loads the class from assembly dll 
DllMain 
LoadLibrary <- -Due to Static ctor 
DllMain
+2  A: 

LoaderLock is an MDA (Managed Debugging Assistant) warning from the debugger. It tells you that there might be a problem with the code. It only happens when running under the debugger because it's the debugger that is making the MDA checks to inform you that under some circumstances a deadlock "may occur".

Unfortunately I can't help you much further than that. My experience of LoaderLock is that (a) it is a mysterious warning that VS gives you, but there is precious little support telling you what to actually do to resolve it, and (b) our application has been running for 4 years with a LoaderLock going off (in DirectX, so it's not even in our code) and it's never actually caused a problem, other than being an annoying hassle every time we run under the debugger. YMMV of course.

(You can disable the MDA in Debug -> Exceptions in the Managed Debugging Assistants section, but every time you Reset those settings, the damn MDA turns back on)

Jason Williams