COM 进程内 DLL 中的 DllMain 存在问题

发布于 2024-12-27 16:44:25 字数 2730 浏览 4 评论 0原文

我开发了一个基于 C++ DLL 的进程内 COM 服务器,并使用 MinGW + MSYS 成功编译了它。如果我用 --enable-stdcall-fixup -Wl,DLLMain.def 编译它,那么与 COM 服务器相关的所有内容都会像魅力一样工作。但是,通过这种方式,某些符号被导出两次:

'dlltool -z output.def --export-all-symbols 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. 

I have developed a C++ DLL-based in-proc COM server and successfully compiled it with MinGW + MSYS. If I compile it with --enable-stdcall-fixup -Wl,DLLMain.def switches everything related to COM server works like a charm. However, this way some of the symbols got exported twice:

'dlltool -z output.def --export-all-symbols libCOMTest.dll' produces:

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
    ...

And the linker generates a few warnings:

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

If I omit these compiler options, the DllMain routine got unexported so I can't even register my COM server with regsvr32 utility.

Below are some of the exported symbols of libCOMTest.dll :

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

As you can see there is no DllMain routine in the list.

My .def file looks as follows:

LIBRARY         libCOMTest
DESCRIPTION     'libCOMTest in-proc server'

EXPORTS
            DllMain         @1  PRIVATE
            DllGetClassObject   @2  PRIVATE
            DllCanUnloadNow     @3  PRIVATE
            DllRegisterServer   @4  PRIVATE
            DllUnregisterServer @5  PRIVATE

What is the reason the DllMain routine got unexported without --enable-stdcall-fixup compile switch? Are there any special tricks for building of in-proc COM server with MinGW + MSYS?

The solution #1

As Hans suggested in his answer, it is possible to use the rename syntax in the .def file as follows:

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

The guide to building and using Win32 DLLs in Haskell suggests just the same.

The solution #2

The other way is to make use of the --kill-at linker switch to strip out @nn part:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

仲春光 2025-01-03 16:44:25

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

获得两个导出虽然很草率,但实际上并不是问题,任何使用这些入口点的代码总是会要求正确的入口点。一定要尝试不使用 --export-all-symbols 。您唯一可以尝试的另一件事是在 .def 文件中使用重命名语法,不确定您的链接器是否支持它:

        DllUnregisterServer=DllUnregisterServer@0  @5  PRIVATE

I don't know enough about your build tools but can extrapolate. The linker is complaining because you asked to export "DllRegisterServer" but the compiler actually generated the identifier "DllRegisterServer@0" as required for the stdcall calling convention. That's a mismatch. The --enable-stdcall-fixup option is a workaround for that, putting the linker in 'fuzzy search mode' and allowing it to find a match.

Getting two exports is sloppy but not actually a problem, whatever code uses these entrypoints is always going to ask for the correct one. Definitely try it without --export-all-symbols. The only other thing you could try is to use the rename syntax in the .def file, not sure if your linker supports it:

        DllUnregisterServer=DllUnregisterServer@0  @5  PRIVATE
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文