COM进程中DLL中的DllMain出现问题

An issue with DllMain in COM in-proc DLL

本文关键字:问题 DllMain 中的 进程 DLL COM      更新时间:2023-10-16

我开发了一个基于proc COM服务器的C++DLL,并成功地用MinGW+MSYS编译了它。如果我用--enable-stdcall-fixup -Wl,DLLMain.def开关编译它,那么与COM服务器相关的一切都会像符咒一样工作。然而,通过这种方式,一些符号被导出了两次:

"dlltool-z output.def--导出libCOMTest.dll生成的所有符号:

EXPORTS
    ...
    DeleteCriticalSection@4 @ 14
    DllCanUnloadNow @ 15 DATA
    DllCanUnloadNow@0 @ 16
    DllGetClassObject @ 17 DATA
    DllGetClassObject@12 @ 18
    DllMain @ 19 DATA
    DllMainCRTStartup@12 @ 20
    DllRegisterServer @ 21 DATA
    DllRegisterServer@0 @ 22
    DllUnregisterServer @ 23 DATA
    DllUnregisterServer@0 @ 24
    EnterCriticalSection@4 @ 25
    ...

链接器会生成一些警告:

Warning: resolving _DllMain by linking to _DllMain@12
Use --enable-stdcall-fixup to disable these warnings
Use --disable-stdcall-fixup to disable these fixups
Warning: resolving _DllGetClassObject by linking to _DllGetClassObject@12
Warning: resolving _DllCanUnloadNow by linking to _DllCanUnloadNow@0
Warning: resolving _DllRegisterServer by linking to _DllRegisterServer@0
Warning: resolving _DllUnregisterServer by linking to _DllUnregisterServer@0

如果我省略了这些编译器选项,DllMain例程将无法导出,因此我甚至无法使用regsvr32实用程序注册COM服务器。

以下是libCOMTest.dll的一些导出符号:

EXPORTS
    ...
    DeleteCriticalSection@4 @ 14
    DllCanUnloadNow@0 @ 15
    DllGetClassObject@12 @ 16
    DllMainCRTStartup@12 @ 17
    DllRegisterServer@0 @ 18
    DllUnregisterServer@0 @ 19
    EnterCriticalSection@4 @ 20
    ...

如您所见,列表中没有DllMain例程。

我的.def文件如下所示:

LIBRARY         libCOMTest
DESCRIPTION     'libCOMTest in-proc server'
EXPORTS
            DllMain         @1  PRIVATE
            DllGetClassObject   @2  PRIVATE
            DllCanUnloadNow     @3  PRIVATE
            DllRegisterServer   @4  PRIVATE
            DllUnregisterServer @5  PRIVATE

在没有--enable-stdcall-fixup编译开关的情况下,DllMain例程未导出的原因是什么?使用MinGW+MSYS构建进程内COM服务器有什么特别的技巧吗?

解决方案#1

正如Hans在回答中所建议的那样,可以在.def文件中使用重命名语法,如下所示:

EXPORTS
    DllGetClassObject   = DllGetClassObject@12
    DllCanUnloadNow     = DllCanUnloadNow@0
    DllRegisterServer   = DllRegisterServer@0
    DllUnregisterServer = DllUnregisterServer@0

在Haskell中构建和使用Win32 DLL的指南也提出了同样的建议。

解决方案#2

另一种方法是利用--kill-at链接器开关剥离@nn部分:

--kill-at
    If given, the stdcall suffixes (@nn) will be stripped from symbols 
    before they are exported. 

我对您的构建工具了解不够,但可以推断。链接器正在抱怨,因为您要求导出"DllRegisterServer",但编译器实际生成了标识符DllRegisterServer@0"根据stdcall调用约定的要求。这是不匹配的。--enable stdcall fixup选项是一种解决方法,将链接器置于"模糊搜索模式",并允许它查找匹配项。

获得两个导出是草率的,但实际上并不是问题,无论代码使用这些入口点,都会要求正确的入口点。绝对不要尝试--导出所有符号。您唯一可以尝试的另一件事是在.def文件中使用重命名语法,不确定您的链接器是否支持它:

        DllUnregisterServer=DllUnregisterServer@0  @5  PRIVATE