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

时间:2014-05-15 22:10:12

标签: 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);
    }
    ...
}

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

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

问题是,当我实例化我的本机控件时,我在分配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;
}

没有骰子:

  

未处理的类型' System.InvalidOperationException'   发生在WindowsBase.dll

中      

其他信息:调用线程无法访问此对象   因为另一个线程拥有它。

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

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

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

1 个答案:

答案 0 :(得分:5)

  

我收到OS Loader Lock错误

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

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

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

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

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