托管c ++ dll的加载程序锁定错误,静态链接到本机c ++ lib

时间:2011-08-17 14:20:14

标签: c++ dll managed loaderlock

我有一个托管的c ++ dll,其中几个托管类反过来调用我已经静态链接到dll的库中的本机c ++代码。但是,如果我尝试在dll上运行RegAsm.exe,该工具会正确报告“我们没有注册的类型”,但挂起。我非常确定这是一个loader lock问题,当RegAsm尝试加载它时我的dll会挂起。我正在使用Visual Studio 2008,快速版。

让我感到困惑的是,在将本机代码放入dll时,一切正常,但在将它从库中静态链接时却没有。我知道这篇文章与this question类似但我在我的dll中没有DllMain ,我没有从DllMain运行MSIL代码的风险。另外,遵循在单个文件上设置/ clr的建议没有帮助。

使用/ NOENTRY编译dll会修复锁定问题,但会导致应用程序中断Type initializer for <Module> threw exception异常,显然只会推荐使用.NET 2003.

我怀疑静态成员的初始化可能是一个可能的罪魁祸首,但为什么在我的静态库中编译成MSIL会超出我的范围。

  

只是为了澄清:虽然我不需要在dll上运行RegAsm.exe,但我正在使用它来检查加载程序锁定问题。实际上我在c#程序集中使用了dll,它实现了很少的COM可见类 - 所以我需要在那个上进行COM注册。最后,C#IDE在注册COM互操作时崩溃,报告'R6033 c ++运行时错误:在本机代码初始化期间尝试使用此程序集中的MSIL代码。这表示您的应用程序中存在错误。它很可能是从本机构造函数或DllMain调用MSIL编译(/ clr)函数的结果。

解决了这个问题,但很少有事情不清楚,我很好奇:

我注意到在停止工作的时候,静态链接的lib中的头文件中添加了两个静态变量,如下所示:

// The whole header is forced to compile as native 
#pragma managed(push, off)
....
static const std::locale commaSeparator(std::locale::classic(), 
                                        new DecimalSeparator<char>(','));;
....
#pragma managed(pop)

将初始化移动到.cpp文件(并将static更改为extern)可修复加载程序锁定。任何人都可以指出为什么初始化程序会被编译为MSIL?

在修复之前,如果我只是#included来自托管dll的头文件,那么一切正常。但是,如果我包含标题AND也链接到lib,事情就不起作用了。由于lib也在内部使用头,我最终得到了静态变量的两个实例吗?无论如何,为什么要投诉运行MSIL代码?

虽然现在情况有效,但欢迎提出任何见解。

2 个答案:

答案 0 :(得分:1)

Compiiler使用自己的代码包装用户定义的DllMain,该代码用于初始化运行时库和全局变量(这样用户定义的DllMain可以使用已创建的全局对象运行并可以使用标准库)。即使你没有DllMain,仍然会有编译器生成的。使用/ NOENTRY指示链接器忽略它。

因此,在静态变量的构造函数中执行某些操作实际上与在DllMain中执行此操作相同,而且似乎就是这种情况。

答案 1 :(得分:1)

following page,(尤其是初始化静态对象标题中的实现部分)具有以下内容:

  

因为启用和禁用/ clr的CPP文件可以包含相同的头,或者#include可以包含在#pragma非托管块内,因此可以同时提供MSIL和本机版本的函数标题中的实现。

  

在Visual C ++ 2005中,为了方便用户处理加载程序锁定,链接器将在显示两者时选择托管的本机实现。 ...但是,由于编译器存在两个未解决的问题,此版本中的此规则有两个例外:
  ...
   - 对内联函数的调用是通过全局静态函数指针。这种情况特别值得注意,因为虚函数是通过全局函数指针调用的。

当将静态变量直接放在托管dll中时(或者在使用\ clr进行编译时),可以避免加载器锁定,因为在DllMain中只发生本机静态变量的初始化。但是,任何编译为native的静态变量在执行MSIL代码时都会死锁。在我们的例子中,MSIL是为静态对象的构造函数使用的STL的一部分生成的,因为当提供本机和MSIL实现时,链接器的行为令人惊讶。

解决方案是:

  • 使用/clr编译所有内容(因为我们需要使用静态库,所以不可能)
  • 确保所有#included第三方标题(STL)前面都有#pragma unmanaged(太复杂了)
  • 从标题中删除静态变量初始化(通过外部链接)

    // Solved by replacing initialization in header file
    static const std::locale commaSeparator(...);
    // with 
    extern const std::locale commaSeparator;
    // and doing initialization in a cpp file