在进行托管到本机的互操作时,操作系统加载器锁定

本文关键字:操作系统 加载 锁定 互操作 本机 | 更新日期: 2023-09-27 18:03:50

我正在使用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规定的。我是用WPF页面上的一个按钮触发的:

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

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

DLL 'my.dll'试图在操作系统加载器锁定内托管执行。做不要尝试在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错误。初始化本机控件的正确方法是什么?

在进行托管到本机的互操作时,操作系统加载器锁定

我得到一个OS Loader Lock错误

这不是错误,这是一个警告。来自MDA,托管调试器助手。它们是微软插入到CLR和调试器中的一小段代码,当看起来像您的程序正在做错误的事情时,它们会产生警告。这种类型不会产生异常,但会使程序挂起或以非常难以诊断的方式失败。

Loader锁当然符合这种模式,它是一个死锁隐藏在Windows内部。与加载程序相关联,这是操作系统中负责加载dll并调用它们的dlmain()入口点的部分。它使用一个内部锁来确保每次只调用一个DllMain()函数。它可以防止重入问题,这与Application.DoEvents()引起的麻烦非常相似。锁上的死锁很难调试,代码完全隐藏在操作系统和神秘的DllMain()函数中,您对此一无所知。真正的僵局很有可能会让你把你的头发撕成大块,除了没有MDA的秃斑之外,几乎没有什么可显示的。

不幸的是,MDA倾向于产生错误的警告。它并不总是意识到死锁实际上不可能发生。这是过于急切的,这是它必须像水晶球一样预测它可能发生的副作用。而不能将自己连接到操作系统内部,从而给您一个保证的警告。微软的Windows团队对托管代码一直不太满意,Longhorn在很长一段时间内都是一个痛处。加载器锁是。net 1.0中一个很大的很大的问题

在你的情况下,这几乎肯定是一个错误的警告,你可以完全确定CLR已经加载,否则你的程序不可能启动。

幸运的是,让它停止打扰你很简单:Debug + Exceptions,打开Managed Debugging Assistants节点,取消选中"LoaderLock"复选框。它很有可能会让您从此平静下来,让您专注于测试您的程序。