OS Loader在执行托管到本机互操作时锁定

Jus*_* R. 2 c# c++ wpf multithreading interop

我正在使用HwndHost将本机控件(C++)加载到WPF控件中.HwndHost的定义如下:

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);
    }
    ...
}
Run Code Online (Sandbox Code Playgroud)

我的WPF项目有一个System.Windows.Controls.Border名为ControlHostElement.一般模式是获取ControlHostElement的句柄,实例化本机控件并将其设置为WPF控件的子元素.这种模式由MSDN 此规定.我通过WPF页面上的按钮触发了这个:

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

问题是,当我实例化我的本机控件时,我在分配Child的行中收到OS Loader Lock错误:

DLL'my.dll'正在尝试在OS Loader锁中执行托管执行.不要尝试在DllMain或图像初始化函数中运行托管代码,因为这样做会导致应用程序挂起.

我不知道我在这一点上是如何进入加载程序线程的,但我想我应该启动一个新线程来执行初始化和窗口句柄赋值:

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;
}
Run Code Online (Sandbox Code Playgroud)

没有骰子:

WindowsBase.dll中出现未处理的"System.InvalidOperationException"类型异常

附加信息:调用线程无法访问此对象,因为另一个线程拥有它.

很公平.也许我应该尝试让UI线程完成这项工作:

void loadControlHostThread_DoWork()
{
    this.Dispatcher.Invoke((Action)(() =>
        {
            myControlHost = new ControlHost();
            ControlHostElement.Child = myControlHost;
        }));
}
Run Code Online (Sandbox Code Playgroud)

这导致相同的OS Loader Lock错误.我初始化本机控件的正确方法是什么?

Han*_*ant 6

我收到OS Loader Lock错误

这不是错误,而是一个警告.从MDA,托管调试器助手.它们是微软插入CLR和调试器的一小段代码,当你的程序看起来出错时会发出警告.这种类型不会产生异常但会使您的程序挂起或以非常难以诊断的方式失败.

Loader lock肯定适合这种模式,它是Windows内部隐藏的死锁.与加载器相关联,操作系统的一部分负责加载DLL并调用其DllMain()入口点.它需要一个内部锁来确保一次一个地调用DllMain()函数.它可以防止重入问题,与Application.DoEvents()导致的那种麻烦相当.锁上的死锁很难调试,代码完全隐藏在操作系统内,以及你不知道的神秘的DllMain()函数.非常高的几率,真正的僵局会让你在没有MDA的情况下将你的头发撕成大块而没有显示出来.

不幸的是,MDA往往会产生错误的警告.它并不总是意识到死锁实际上不会发生.它过于渴望,它必须预测,水晶球风格,它可能发生的副作用.否则无法将自身连接到操作系统内部,以便为您提供有保证的警告.微软的Windows组织对于容纳托管代码并不高兴,Longhorn在很长一段时间内都是一个痛处.加载程序锁定是.NET 1.0中的一个重大问题

在您的情况下,几乎可以肯定是一个错误的警告,您可能已经确定已经加载了CLR,否则您的程序无法启动.

幸运的是,让它停止烦扰是非常简单的:Debug + Exceptions,打开Managed Debugging Assistants节点并取消选中"LoaderLock"复选框.非常高的几率,它会让你安静下来,让你专注于测试你的程序.