MinGW 与 MSVC 生成 DLL 各种情况的折腾笔记

??本博文由CSDN博主zuishikonghuan所作,版权归zuishikonghuan所有,转载请注明出处:http://blog.csdn.net/zuishikonghuan/article/details/51918076

写这篇博客,主要是刚折腾 MinGW,相关内容网上的资料不全,而且错误很多

其实之前我根本没把这个当回事,我就想 MinGW 跟 Linux 上的 GNU 编译器不会有差别,但是事实却不是这样。。。

提示:所有代码均使用 __stdcall

安装 MSVC 和 MinGW

MSVC:安装 Visual Studio,之后即可在开始菜单中找到“Visual Studio开发人员命令提示”,启动后会自动配制环境变量,不多说了(之前我写过提取 MSVC 编译器的博客)

MinGW:这真是一个悲伤的故事,官方的下载工具总是失败,看起来需要搭梯子,其实,有一种更简单的方法。。

http://www.mingw.org/wiki/InstallationHOWTOforMinGW 里面下载各个组件,然后自己解压到一起就行。注意上面的页面中有的组件的连接已经失效了(但放心并不多),所以只能在 MinGW 的 Sourceforge 上一点点找了。

MSYS 环境就不用了,这个下来不好用,版本很老,不知道官方为什么不更新,其实,只需要安装一个 msysgit,MSYS 环境就有了,版本也是最新的,不过 msysgit 在 AWS 上,还是需要搭梯子才能下载。

嘿嘿,写一个超简单的脚本

#!bash
export PATH="/c/Users/abc/Downloads/MinGW/MinGW/bin:$PATH"
bash

把 /c/Users/abc/Downloads/MinGW/MinGW/bin 换成你的 MinGW/bin 目录即可,双击打开一个可以用 MinGW GCC、G++ 的 Bash 终端。

MinGW 调用 MinGW 生成的 DLL

自家调用自家的,也会出现问题,别不信,比如这儿有 dll.cpp 和 dlluse.cpp

#include <Windows.h>

BOOL WINAPI DllMain(
    HINSTANCE hinstDLL,
    DWORD     fdwReason,
    LPVOID    lpvReserved
) {
    return TRUE;
}

extern "C" __declspec(dllexport) void WINAPI showMessage() {
    MessageBoxA(0, "I am showMessage", 0, 0);
}

extern "C" __declspec(dllexport) void WINAPI showMessage2() {
    MessageBoxA(0, "showMessage2", 0, 0);
}
#include <Windows.h>

extern "C" void WINAPI showMessage();
extern "C" void WINAPI showMessage2();

int main() {
    showMessage();
}

如果我们这样编译:

$ g++ -mwindows -static -shared -o 1.dll dll.cpp -Wl,--out-implib,lib1.a

$ g++ -mwindows -static dlluse.cpp -l1 -L.

$ ./a.exe

这样是没有问题的,但是,问题出现在了 –kill-at 选项上

我们先用微软的 dumpbin 工具来看一下导出表:

> dumpbin /exports 1.dll
Microsoft (R) COFF/PE Dumper Version 14.00.23918.0
Copyright (C) Microsoft Corporation.  All rights reserved.

Dump of file 1.dll

File Type: DLL

  Section contains the following exports for 1.dll

    //....

    ordinal hint RVA      name

          1    0 000012BB [email protected]0
          2    1 0000128C [email protected]0

  Summary

    //....

每个函数后面都出现了一个“@n”,如果我们不希望 DLL 的导出函数中还要带着一个 “@n” 标记(就像Windows的DLL一样都是没有的),在 MSVC 中我们可以通过 DEF 导出,MInGW 则提供了 –kill-at 选项。

你可能会问为什么非的不要这个呢,如果带上这个参数占用的堆栈大小,那么如果我们想动态调用 DLL 中的函数,就得自己计算这个大小,这是不能忍受的,OK,我们使用 –kill-at 来编译试试:

$ g++ -mwindows -static -shared -o 1.dll dll.cpp -Wl,--kill-at,--out-implib,lib1.a

来看看是否生成了不带“@n”的导出函数

> dumpbin /exports 1.dll
Microsoft (R) COFF/PE Dumper Version 14.00.23918.0
Copyright (C) Microsoft Corporation.  All rights reserved.

//....

    ordinal hint RVA      name

          1    0 0000128C showMessage
          2    1 000012BB showMessage2

//....

果然如此,那么我们尝试编译 dlluse.cpp,但看见结果的那一刻,真是吓死宝宝了。

$ g++ -mwindows -static dlluse.cpp -l1 -L.
C:\Users\abc\AppData\Local\Temp\cc3M0ER9.o:dlluse.cpp:(.text+0xc): undefined reference to `[email protected]‘
collect2.exe: error: ld returned 1 exit status

我明明有-l1 -L.,你告诉我“未定义的引用”(相当于MSVC的“无法解析的外部符号”),难不成是闹鬼了不成。。

于是吓得我赶紧 Google,但网上愣是没有一个人知道如何在 MinGW 中用导入库导入不带at的函数。

但是也有人(包括 MinGW 官方的手册)提供了一个方法,就是直接把 dll 输入进去:

$ g++ -mwindows -static dlluse.cpp 1.dll
Warning: resolving [email protected]0 by linking to _showMessage
Use --enable-stdcall-fixup to disable these warnings
Use --disable-stdcall-fixup to disable these fixups

出现了一个警告,如果想屏蔽这个警告,按照提示添加“–enable-stdcall-fixup”参数即可:

$ g++ -mwindows -static dlluse.cpp 1.dll -Wl,--enable-stdcall-fixup

这样虽然能解决问题,但是总是觉得怪怪的,毕竟把一个 PE 文件当作目标文件或源文件输入进去总是觉得不好,Linux 上的 GNU 编译器绝对不能直接把 so 库传进去的,MinGW 你是要闹哪样?

我就是想用导入库来调用不带at的DLL函数,MinGW你做不到吗。。

在接下来的时候,我一直在考虑这个问题,最终茶饭不思(咳咳,扯远了),不过最终还是让我找到了方法

$ g++ -mwindows -static -shared -o 1.dll dll.cpp -Wl,--output-def,1.def

$ g++ -mwindows -static -shared -o 1.dll dll.cpp -Wl,--kill-at

$ dlltool --kill-at -d 1.def --dllname 1.dll -l lib1.a

$ g++ -mwindows -static dlluse.cpp -l1 -L.

$ ./a.exe

解释:第一次编译,是按照有 at 的编译,生成 1.dll 和一个模块定义文件 1.def,第二次再编译,按照没有at的生成,第三步,使用第一步生成的 def 文件生成导入库,一定要添加 –kill-at 选项。

生成的 1.def 内容如下

EXPORTS
    showMessage2@0 @1
    showMessage@0 @2

后面的序号部分不是必要的,要的就是“@n”

总之我之前是很怀疑这样究竟行不行的,但事实告诉我这样可以,用 dumpbin 查 exports 和 imports 都是没有at的!

虽然解决了,但是心里并不高兴,在 MinGW 中为了达到这个目的,需要编译(连接)两次同一个代码,让我这个强迫症浑身不舒服。

MSVC 调用 MinGW 生成的 DLL

先来说说带 at 的怎么处理

$ g++ -mwindows -static -shared -o 1.dll dll.cpp -Wl,--output-def,1.def

然后就是在 MSVC 中编译 dlluse 了

> lib /machine:i386 /def:1.def
Microsoft (R) Library Manager Version 14.00.23918.0
Copyright (C) Microsoft Corporation.  All rights reserved.

  正在创建库 1.lib 和对象 1.exp

> cl /MT /c dlluse.cpp
用于 x86 的 Microsoft (R) C/C++ 优化编译器 19.00.23918 版
版权所有(C) Microsoft Corporation。保留所有权利。

dlluse.cpp

> link dlluse.obj 1.lib
Microsoft (R) Incremental Linker Version 14.00.23918.0
Copyright (C) Microsoft Corporation.  All rights reserved.

> dlluse.exe

首先利用 MinGW 生成的 def 文件创建了导入库,然后编译 dlluse,连接,运行

这样是没有问题的。但是莫名其妙的问题在不带 at 的DLL中出现了:

$ g++ -mwindows -static -shared -o 1.dll dll.cpp -Wl,--output-def,1.def,--kill-at

$ cat 1.def
EXPORTS
    showMessage = showMessage@0 @1
    showMessage2 = showMessage2@0 @2

> lib /machine:i386 /def:1.def
Microsoft (R) Library Manager Version 14.00.23918.0
Copyright (C) Microsoft Corporation.  All rights reserved.

  正在创建库 1.lib 和对象 1.exp

#刚才已经cl编译过了,现在直接连接就行
> link dlluse.obj 1.lib
Microsoft (R) Incremental Linker Version 14.00.23918.0
Copyright (C) Microsoft Corporation.  All rights reserved.

dlluse.obj : error LNK2019: 无法解析的外部符号 _showMessage@0,该符号在函数 _main 中被引用
dlluse.exe : fatal error LNK1120: 1 个无法解析的外部命令

这个问题又是闹得我茶饭不思啊(笑),这个问题真是麻烦,MSVC 的 link 是支持 def 文件中的“=”的,但 lib 不支持,于是我就想还是用带 at 的版本的 def,用不带 at 的DLL文件不就行了,就像上面编译两次的那个是一个道理

$ g++ -mwindows -static -shared -o 1.dll dll.cpp -Wl,--output-def,1.def

$ g++ -mwindows -static -shared -o 1.dll dll.cpp -Wl,--kill-at

#标准输出略
> lib /machine:i386 /def:1.def

> cl /MT /c dlluse.cpp

> link dlluse.obj 1.lib

> dlluse.exe

整个过程一气呵成,没有任何问题,正当我满心欢喜的查看成果的时候,惊呆了,弹出的是“showMessage2”而不是 “I am showMessage”。

这又是闹哪样啊,不过内心也在庆幸自己导出了两个函数,不然这个问题可能真发现不了。

不多说,dumpbin 走起

> dumpbin /exports 1.dll
Microsoft (R) COFF/PE Dumper Version 14.00.23918.0
Copyright (C) Microsoft Corporation.  All rights reserved.

Dump of file 1.dll

File Type: DLL

//....

    ordinal hint RVA      name

          1    0 0000128C showMessage
          2    1 000012BB showMessage2
//....

> dumpbin /exports 1.lib
Microsoft (R) COFF/PE Dumper Version 14.00.23918.0
Copyright (C) Microsoft Corporation.  All rights reserved.

Dump of file 1.lib

File Type: LIBRARY

     Exports

       ordinal    name

             1    [email protected]0
             2    [email protected]0

//....

> dumpbin /imports dlluse.exe
Microsoft (R) COFF/PE Dumper Version 14.00.23918.0
Copyright (C) Microsoft Corporation.  All rights reserved.

Dump of file dlluse.exe

File Type: EXECUTABLE IMAGE

  Section contains the following imports:

    1.dll
                40D000 Import Address Table
                412264 Import Name Table
                     0 time date stamp
                     0 Index of first forwarder reference

                      Ordinal     2
//....

MSVC 按照序号导入,但是为神马 DLL 里的序号和导入库里的不一样啊,我下意识就感觉是 def 出问题了,用vim 1.def看看def文件有没有问题,果然如此:

EXPORTS
    showMessage2@0 @1
    showMessage@0 @2

看来是用 MinGW 编译两次产生的后遗症

于是我就想,既然这样我就把def中的序号都删除了吧

$ sed -i ‘s/ @.*//g‘ 1.def

$ cat 1.def
EXPORTS
    showMessage2@0
    showMessage@0

这下连接是没有问题了,正当我满心欢喜运行之时,又是一盆冷水下来,csrss告诉我:无法定位程序输入点 [email protected] 于动态链接库 1.dll 上。

可恶,在导出函数不带 at 的情况下,如果 def 中不带 at,就会导致代码中生成的弱符号“[email protected]”找不到对应的强符号,如果def中带 at,link是没有问题的,但是却只能在 PE 文件的导入表中使用“[email protected]”而不能用“showMessage”

只能说微软的 lib.exe 功能太弱,生成的导入库很多功能比不上 link 生成DLL时生成的。

如果def中不带at,那么生成的导入库就不能与代码正常连接,如果带at,显然可以与代码连接,但却显然无法从DLL的不带at中找到相应的导出函数,归根到底,是 MSVC 的 lib.exe 不支持别名(即“=”后的内容被忽略),现在我们已经走入了死胡同。

但我突然灵光一闪(笑),刚刚的那个情况,不就说明了可以在 def 中使用带at的名字,而连接到正确的不带at的函数吗,那个不是通过别名来实现的(这一点和 MinGW 不同),而是靠的函数序号,之所以不行,是因为序号和DLL中导出的序号不一致而已

想明白了这一点,问题就简单了,我们可以这样:

$ pexports -h dlluse.cpp -o 1.dll > 1.def

$ cat 1.def
LIBRARY 1.dll
EXPORTS
[email protected]0                   @1
[email protected]0                  @2

> lib /machine:i386 /def:1.def

> dumpbin /exports 1.lib
Microsoft (R) COFF/PE Dumper Version 14.00.23918.0
Copyright (C) Microsoft Corporation.  All rights reserved.

Dump of file 1.lib

File Type: LIBRARY

     Exports

       ordinal    name

             2    [email protected]0
             1    [email protected]0

//....

其中的-h dlluse.cpp不能少,我们需要有一个包含函数声明的头文件,这样 pexports 才能正确计算at后面的堆栈字节数。

这样序号就对了,之后在 link 就行了!

MinGW 调用 MSVC 生成的 DLL

这个就非常简单了,我们先用 MSVC 编译这个 DLL(用 MSVC 编译器的估计大家都用 DEF 导出,也就是不带 at 的):

> cl /MT /c dll.cpp
用于 x86 的 Microsoft (R) C/C++ 优化编译器 19.00.23918 版
版权所有(C) Microsoft Corporation。保留所有权利。

dll.cpp

> link /dll dll.obj user32.lib /def:dll.def
Microsoft (R) Incremental Linker Version 14.00.23918.0
Copyright (C) Microsoft Corporation.  All rights reserved.

  正在创建库 dll.lib 和对象 dll.exp

最简单的办法就是上面说的 MinGW 的“个性”:

$ g++ -mwindows -static dlluse.cpp dll.dll -Wl,--enable-stdcall-fixup

如果不想这样,可以这样:

$ pexports -h dlluse.cpp dll.dll > dll.def

$ dlltool --kill-at -d dll.def --dllname dll.dll -l libdll.a

$ g++ -mwindows -static dlluse.cpp -ldll -L.

直接用原来的def生成导入库再使用会出现连接错误的,原因就不说了,因为道理和上面的情况是一个道理。

总算写完了,现在思路清晰多了(笑)

时间: 2024-10-07 07:34:28

MinGW 与 MSVC 生成 DLL 各种情况的折腾笔记的相关文章

利用openssl管理证书及SSL编程第3部分:将MinGW编译的openssl dll导出def和lib供MSVC使用

将MinGW编译的openssl dll导出def和lib供MSVC使用 前面我们用mingw把openssl 编译成了动态库,得到下面2个dll文件: libeay32.dll ssleay32.dll 然后用下面的脚本生成Windows MSVC需要的模块定义文件(.def, .lib和.exp), 然后就可以在VC中使用了. 前提系统要安装VS. 系统要求: Windows7+VS Studio (2008 and later)+MSYS 1) 根据32位dll生成模块定义文件的pytho

C++ 生成 dll 和调用 dll 的方法实例(转)

1)生成dll 建立两个文件 xxx.h , xxx.cpp xxx.h内容如下: #ifdef BUILD_XXX_DLL#define EXPORT __declspec(dllexport)#else#define EXPORT __declspec(dllimport)#endif extern "C"{EXPORT void example(void);... ...} xxx.cpp内容如下: #define BUILD_XXX_DLL#include "xxx.

MinGW 使用和创建 DLL 应注意的问题

MinGW 是 GCC 的 Windows 版本,稳定版已经到了 4.5.2,功能和性能上很好,感觉不比 Microsoft 自家的 VC 差啊.但是 MinGW 下使用和创建 DLL 倒是要特别注意,问题主要集中在 g++ 编译器(C++ 的 GNU 版本编译器)对于 DLL 的函数输入以及输出的名称修饰.调用协议上和 VC 编译器是有很大区别的. 1.MinGW 如何使用一个标准的 DLL.这里标准 DLL 指的是采用 __stdcall 调用协议.并且导出函数名称干干净净,没有函数名尾部的

(Unity)Unity自定义Debug日志文件,利用VS生成Dll文件并使用Dotfuscated进展混淆,避免被反编译

Unity自定义Debug日志文件,利用VS生成Dll文件并使用Dotfuscated进行混淆,避免被反编译. 1.打开VS,博主所用版本是Visual Studio 2013. 2.新建一个VC项目,选择类库,取名为JefferyChan,具体步骤如下图: 3.因为要调用Unity中的相关文件,所以这里要引入外部文件.首先在Unity的安装文件夹中找到UnityEngine.dll,我的路径是:D:\Program Files (x86)\Unity\Editor\Data\Managed 如

VS2008下WinRar源码生成dll和 lib总结

WinRar官方提供了源码(http://www.rarlab.com/rar_add.htm):如果自己想要修改里面的内容就要重新生成DLL和LIB,我在网上找了很多资料都没有说得很清楚.花一两天的时间才把他生成成功.网上的资料都给了一些误导,所以改了很久才完成.现在给出正确的步骤,给这方面的学习者提供一下思路. )建立一个空的win32 dll工程2)把.hpp和.cpp都添加到工程里3)从工程中移除不是删除arccmt.cpp.beosea.cpp.coder.cpp.model.cpp.

利用def生成dll文件

DLL中导出函数的声明有两种方式:一种为在函数声明中加上__declspec(dllexport),这里不再举例说明:另外一种方式是采用模块定义(.def) 文件声明,.def文件为链接器提供了有关被链接程序的导出.属性及其他方面的信息.            首先创建 一个DLL程序,.cpp中int __stdcall Add(int numa, int numb){       return (numa + numb);} int __stdcall Sub(int numa, int n

.net 调用 Matlab生成dll出现的问题(The type initializer for &#39;MathWorks?.MATLAB.NE?T.Utility.?MWMCR&#39; threw an exception.)

https://cn.mathworks.com/matlabcentral/answers/278399-i-get-an-error-saying-the-type-initializer-for-mathworks-matlab-net-utility-mwmcr-threw-an-except 这篇文章帮忙解决了问题 记录一下备忘: Hi Nug, Ensure that you have the MCR version corresponding to MATLAB 2012b ins

生成dll文件的示例

看了好多网上写的关于dll文件生成和实用的资料发现多尔不全,都是抄来抄去,有的干脆就是搬用msdn上的原文,实在没有创意和可看的东西.于是本着学和实用的目的自己实践的东西分享给大家. 大前提:使用VS2010作为dll生成工具 概述:主要通过构建一个解决方案中的一个项目来演示如何定义和生成dll文件,在同一个方案中在创建一个项目主要用来进行生成dll的使用. 简易结构图: testdll(解决方案名) |--makedll(生成dll项目名) |--testdll(使用dll项目名) maked

用Qt生成dll类库及调用方法

空白工程新建DLL后,将DLL和LIB文件放入需要调用的"指定目录" 项目->属性->连接器->常规->附加库目录->添加"指定目录" 项目->属性->连接器->输入->附加依赖项->添加"LIB文件" 添加头文件到项目中,则DLL可用! VC6.0: 使一个项目编译生成DLL库而不生成可执行文件: 删除main()方法: 将.pro项目文件中的TEMPLATE = app改为TEMPL