代码之家  ›  专栏  ›  技术社区  ›  SoLaR

在混合的LIB/DLL中,为什么导入混合的原生/CLR LIB/DLL的本地C++应用程序不调用外部的VARS上的CTOR/DTor

  •  -1
  • SoLaR  · 技术社区  · 7 年前

    到目前为止,当这个logger和importer/implementer/caller应用程序都用本机或托管代码编译时,一切都正常。但是如果我将Logger-Labor编译为用/CLR管理的,并在原生C++项目W/O/CLR中使用,则在DLL初始化期间,从“Login”库导出的类与“ExtLN”不调用日志记录DLL中的构造函数或析构函数。实际上,在logger库中从来没有调用构造函数/析构函数(注意,甚至连扩展类构造函数也没有调用),只有类的空shell存在,只有半初始化。

    现在我已经不是第一次遇到这样或类似的问题了,在过去的解决方案中,要么拆分为纯本机代码和纯托管代码,要么一个为另一个编写包装器(要维护两个项目,无可移植性)。或者编译两个版本的库一个用于本机应用程序,另一个用于托管应用程序。现在在这种情况下,这些不是解决方案,而是限制,因为我需要有代码通过相同的进程管道工作,无论是本机dll、本机应用程序、托管dll、托管应用程序,…,为了简单起见,我需要一个全合一。

    有人知道这背后的原因吗?

    1 回复  |  直到 6 年前
        1
  •  0
  •   SoLaR    7 年前

    有两个恶魔让代码丢失。

    第一个魔鬼:警告消息是第一个线索,即导出的初始值设定项将不会在托管代码首次执行之前运行(猜测编译托管代码时,加载DLL时将不会初始化托管内容)。 在测试混合代码的加载顺序时出现警告的示例:

    1>CBla.cpp(8): warning C4835: '_bla1_' : the initializer for exported data will not be run until managed code is first executed in the host assembly.
    

    第二个魔鬼:一些标准的c/c++代码无法编译为托管代码。虽然我觉得这不应该被编译成托管的。但我开始在使用变量参数的函数中接收警告消息,并开始到处放置本机代码pragma,以便将本机代码编译为本机!!!

    #pragma managed(push, off)
    // native code
    #pragma managed(pop)
    

    你只能用IDA Disassembler或类似的工具来发现。。。但在下一页的底部也作了简要说明: https://msdn.microsoft.com/en-us/library/ms173266.aspx?f=255&MSPPError=-2147217396 “混合程序集的初始化”。 因此,调用混合代码LIB/DLL的纯本机应用程序永远不会使用/触发CLR代码,也永远不会在本机编译类中调用构造函数/析构函数。

    解决方案: 克服此限制的唯一方法是在所有这些中放置一些托管函数以强制加载CLR,在这种情况下,您将在调试过程中注意到一个异常:

    First-chance exception at 0x7620c41f in ManagedNativeNatTest.exe: 0x04242420: CLRDBG_NOTIFICATION_EXCEPTION_CODE.
    

    这实际上是CLR代码收到初始化通知并开始为LIB/DLL加载导出的本机类的时刻。我能够通过将属于本机类的空函数放入托管代码中触发此操作:

    ...
    #pragma managed(push, off)
    ...
    #pragma managed(pop)
    
    void CBla1::ManagedCall()
    { }
    
    #pragma managed(push, off)
    ...
    

    调用此函数会导致执行CLR loader,以初始化外部变量。

    为什么这样做我不确定,也许是因为CLR从来没有加载任何东西,直到它被使用。不知道如果我用NGen把CLR编译成本地代码会不会也是这样,但这可能是另一种冒险。

    这就完成了我的回答:为什么外部变量不能在混合LIB/DLL中调用构造函数/析构函数。