Pregunta

I am loading a native control (C++) into a WPF control using HwndHost. The HwndHost is defined as follows:

class ControlHost : System.Windows.Interop.HwndHost
{
    public IntPtr Handle;
    protected override HandleRef BuildWindowCore(HandleRef hwndParent)
    {
        // instantiate the native control
        Handle = control.Handle;
        return new HandleRef(this, control.Handle);
    }
    ...
}

My WPF project has a System.Windows.Controls.Border named ControlHostElement. The general pattern is to get the handle for the ControlHostElement, instantiate the native control and and set it as a child element of the WPF control. This pattern is prescribed by MSDN here. I am triggering this with a button on the WPF page:

private void btnHwndHost_OnClick(object sender, RoutedEventArgs e)
{
    myControlHost = new ControlHost();
    ControlHostElement.Child = myControlHost;
}

The problem is that when I instantiate my native control, I get an OS Loader Lock error at the line where Child is assigned:

DLL 'my.dll' is 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'm not sure how I'm inside the loader thread at this point, but I figure I should just spin up a new thread to perform the initialization and window handle assignment:

private void btnHwndHost_OnClick(object sender, RoutedEventArgs e)
{
    Thread loadControlHostThread = new Thread(
        new ThreadStart(this.loadControlHostThread_DoWork));
    loadControlHostThread.SetApartmentState(ApartmentState.STA);
    loadControlHostThread.Start();
}

void loadControlHostThread_DoWork()
{
    myControlHost = new ControlHost();
    ControlHostElement.Child = myControlHost;
}

No dice:

An unhandled exception of type 'System.InvalidOperationException' occurred in WindowsBase.dll

Additional information: The calling thread cannot access this object because a different thread owns it.

Fair enough. Maybe I should try asking the UI thread to do this work:

void loadControlHostThread_DoWork()
{
    this.Dispatcher.Invoke((Action)(() =>
        {
            myControlHost = new ControlHost();
            ControlHostElement.Child = myControlHost;
        }));
}

That results in the same OS Loader Lock error. What is the correct way for me to initialize my native control?

¿Fue útil?

Solución

I get an OS Loader Lock error

It is not an error, it is a warning. From an MDA, a Managed Debugger Assistant. They are little slivers of code that Microsoft inserted into the CLR and the debugger to produce warnings when it looks like your program is doing something wrong. The kind that doesn't produce an exception but makes your program hang-up or fail in a very difficult to diagnose way.

Loader lock certainly fits that pattern, it is a deadlock buried inside Windows internals. Associated with the loader, the part of the operating system that's responsible for loading DLLs and calling their DllMain() entrypoint. It takes an internal lock to ensure that the DllMain() functions are called one-at-a-time. It prevents re-entrancy problems, pretty comparable to the kind of trouble Application.DoEvents() causes. A deadlock on that lock is pretty hard to debug, the code is completely buried inside operating system as well as mysterious DllMain() functions you don't know anything about. Very high odds that a real deadlock would get you to tear your head-hair out in major clumps with little to show for it than a bald spot without that MDA.

Unfortunately the MDA tends to produce false warnings. It is not always aware that deadlock cannot actually happen. It is over-eager, a side-effect of it having to predict, crystal-ball style, that it might happen. Without otherwise being able to wire itself into the operating system internals to give you a guaranteed warning. The Windows group at Microsoft hasn't ever been that happy about accommodating managed code, Longhorn was a sore spot for quite a while. Loader lock was a big, big issue in .NET 1.0

It almost certainly is a false warning in your case, you can be dead-sure that the CLR is already loaded, your program could not possibly start otherwise.

Fortunately it is very simple to make it stop bugging you: Debug + Exceptions, open the Managed Debugging Assistants node and untick the "LoaderLock" checkbox. Very high odds that it will leave you in peace from there, allowing you to focus on testing your program.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top