吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 2866|回复: 12
收起左侧

[C&C++ 转载] MingW编译Dll文件遇到的几个问题

[复制链接]
seeyou_shj 发表于 2020-10-16 08:49
本人学习过程中的一点理解。原帖我发在QQ了,原帖地址:https://user.qzone.qq.com/37505515/blog/1602725485
内容如下:
visual studio很好用,不过对于我来说总觉得很受限制。起码程序编译的时候,那些环境设置牛让我头疼不已,苦于找不到相关资料,只能一点点摸索。于是那些耐心就逐渐消磨在这样的摸索过程中。

感觉90%以上的时间都浪费在摸索上了。

于是就去找替代,于是就找到了mingw,另一款C++编译器。于是就去摸索,于是遇到了诸多困难。

Dll文件编译,就是其中之一。

写一个简单的dll文件,内容如下(main.cpp):

int sub(int a,int b){

    return a-b;

}

int add(int a,int b, int c){

    return sub(a,b)+c;

}
编译:g++ -shared main.cpp -o main.dll,ok,得到main.dll文件了。转到python的ide,打开此dll:
>>> import ctypes
>>> m = ctypes.cdll.LoadLibrary("d:/temp/main.dll")
>>> m.add(5,3,8)
(此处一大串错误)
咋回事?打开dos环境,运行vs自带的dll输出函数查看器dumpbin,看看咋回事:
D:\temp>"D:\Microsoft Visual Studio 14.0\Common7\Tools\vsvars32.bat"
D:\temp>dumpbin main.dll -exports
(此处省略一大串字符)
1    0 000013C2 _Z3addiii
2    1 000013B0 _Z3subii
(此处省略一大串字符)
原来如此。导出来的函数名称都被赋值了下划线前缀和后缀,所以名称变了。python环境测试一下:
>>> m._Z3addiii(5,3,4)
6
结果正常。

找到原因了,那么就想办法还原函数名吧。查找了一下相关资料,找到原因:
================================================
以下内容来自https://www.linuxidc.com/Linux/2015-12/126123.htm,原文如下:
MinGW 是 GCC 的 Windows 版本,稳定版已经到了 4.5.2,功能和性能上很好,感觉不比 Microsoft 自家的 VC 差啊。但是 MinGW 下使用和创建 DLL 倒是要特别注意,问题主要集中在 g++ 编译器(C++ 的 GNU 版本编译器)对于 DLL 的函数输入以及输出的名称修饰、调用协议上和 VC 编译器是有很大区别的。1、MinGW 如何使用一个标准的 DLL。这里标准 DLL 指的是采用 __stdcall 调用协议、并且导出函数名称干干净净,没有函数名尾部的 @nn、没有函数名头部的下划线的。MinGW 的 g++ 程序中,对于需要从 DLL 中导入的函数,声明时必须要用 extern "C",但不要用 __declspec(dllimport),虽然 g++ 支持,但是一旦你使用,则 g++ 在链接时会自动强制在需要导入的函数名前加 _imp__ 前缀,导致链接时找不到函数名错误。所以,我们其实完全不需要用 __declspec(dllimport)。另一方面,gcc/g++ 编译器遇到代码中 __stdcall 修饰的函数名,会自动将其函数名在链接时设置为函数名@nn,nn 是函数参数栈字节数。另外,gcc/g++ 编译器/链接器在链接时其实不需要 DLL 的导入库(import lib),因为它们可以直接从 DLL 链接,这样更加方便,省去了很多从 DLL 如何生成符合格式要求的 .a 导入库等问题。只需要在 gcc/g++ 参数中加入 -Wl,--enable-stdcall-fixup -L../../Bin/ -lDLL文件名(不含.dll)即可直接从 DLL 文件本身完成链接。这里要注意,-Wl参数指示 g++ 链接器需要采用后面的链接控制参数(以逗号分隔),--enable-stdcall-fixup 告诉 g++ 链接器需要导入的 DLL 函数的名字需要自动在尾部加上 @nn 格式的后缀,以便符合 gcc/g++ 对 __stdcall 函数名的扩展规范。-L 指定 DLL 文件所在目录,-l 指定 DLL 文件名称,不带 .dll。如果不加 --enable-stdcall-fixup,gcc/g++ 总是会报链接错误,因为 gcc/g++ 将代码中需要从 DLL 导入的函数名后面都强制加了 @nn,但是 DLL 中的函数名不带 @nn,没有 --enable-stdcall-fixup,很有可能就会出错。即时不出错,也会有很多警告,很有可能会导致程序 crash!注,在 NetBeans CDT 中,-Wl,--enable-stdcall-fixup -L -l 这样的参数在链接器参数设置中指定。2、MinGW 如何创建一个标准的 DLL。MinGW 中创建标准 DLL,应该使用 __declspec(dllexport),包括 extern "C" 等都是和 VC 一样的。但要注意,这样编译链接生成的 DLL,导出的函数名尾部都带有 @nn,为了要去除它们,必须在链接器参数设置中使用 -Wl,--kill-at,它告诉链接器创建 DLL 时导出的函数名尾部不要带有 @nn。注,在 NetBeans CDT 中,上面这个参数同样是在链接器参数设置中指定。综上,如果创建一个 DLL,同时这个 DLL 又需要导入其他 DLL 的函数,参数就可以统一为 -Wl,--kill-at,--enable-stdcall-fixup -L -l 这样了,很方便。感觉 MinGW 确实相当强悍,跨平台,可调性很灵活,性能也很强劲,更难能可贵的是,它是一个免费而又强大的编译器!当然,搭配 NetBeans CDT 更是相当好的一款 C/C++ 开发利器。MinGW 创建的程序或 DLL 脱离 libgcc-xx-xx.dll 和 libstdc++-x.dll 运行库的方法
MinGW 沿袭了 Linux 下 gcc/g++ 的习惯,编译出的程序或者动态链接库(共享库)总是默认采用动态链接方式,需要系统中附带运行时库文件 libgcc-xx-xx.dll 和 libstdc++-xx.dll。那么如果我们不想发布程序时还要附带这两个运行库,该如何操作呢?通过参考 gcc/g++ 用户手册,发现只需要在编译器或链接器参数设置中使用 -static-libgcc 和 -static-libstdc++ 即可实现对这两个运行库的静态库链接,不必附带运行库了。如果采用参数 -static,那么表示所有涉及到的外部共享库都采用静态链接方式了。不过 MinGW 始终还是要依赖 msvcrt.dll 这个在 Windows 平台上的基本运行库,但相信任何 Windows 平台都不会少了 msvcrt.dll 这个文件的,这是 Windows 必然自带的系统文件啦。================================================

解决方法找到了,将main.cpp内容更改为如下:

extern "C" int sub(int a,int b){
    return a-b;
}
extern "C" int add(int a,int b, int c){
    return sub(a,b)+c;
}

仍然用前面的命令编译:

g++ -shared main.cpp -o main.dll

在python环境中测试一下,结果正常:

>>> import ctypes
>>> m = ctypes.cdll.LoadLibrary("d:/temp/main.dll")
>>> m.add(5,3,8)
10

ok,顺利解决!

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

JuncoJet 发表于 2020-10-16 08:57
用gcc就可以不需要extern "C"
c03xp 发表于 2020-10-16 09:03
如果安装了vs,可以在命令行使用cl编译

cl  /LD  main.cpp

有可能报错,那就先运行一下"C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\Tools\vsvars32.bat"

参考cl的文档 https://docs.microsoft.com/en-us/cpp/build/reference/compiler-options-listed-by-category?view=vs-2019
 楼主| seeyou_shj 发表于 2020-10-19 10:39
JuncoJet 发表于 2020-10-16 08:57
用gcc就可以不需要extern "C"

可以不需要extern c,不过生成的dll引用的函数名称会变化。
 楼主| seeyou_shj 发表于 2020-10-19 10:40
我现在在学习ime输入法编程,研究了很久了,一直没找到思路。现在生成dll更改为ime后,可以在加载的时候提示dllmain函数运行正常,但是不知道怎么调用imeinquire和其他函数。有高手请指点一下
JuncoJet 发表于 2020-10-19 10:55
seeyou_shj 发表于 2020-10-19 10:39
可以不需要extern c,不过生成的dll引用的函数名称会变化。

用def文件来做导出
 楼主| seeyou_shj 发表于 2020-10-20 08:40
JuncoJet 发表于 2020-10-19 10:55
用def文件来做导出

可以用def文件做,不知道用g++怎么编译?能指点一下吗?
JuncoJet 发表于 2020-10-20 09:20
seeyou_shj 发表于 2020-10-20 08:40
可以用def文件做,不知道用g++怎么编译?能指点一下吗?

g++ 源文件.cpp -o 输出.exe -l导入库
大概就这样的编译参数把,用C-Free、Qt Creator一键编译也可以
 楼主| seeyou_shj 发表于 2020-10-20 14:15
JuncoJet 发表于 2020-10-20 09:20
g++ 源文件.cpp -o 输出.exe -l导入库
大概就这样的编译参数把,用C-Free、Qt Creator一键编译也可以

我知道直接编译是用

g++ -shared 文件名.c -o 文件名.dll

如果附加上def文件,需要加什么参数?
JuncoJet 发表于 2020-10-20 14:54
seeyou_shj 发表于 2020-10-20 14:15
我知道直接编译是用

g++ -shared 文件名.c -o 文件名.dll

dlltool --dllname zlib1.dll --def zlib1.def --output-lib libz.dll.a
好像需要独立的命令
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-5-5 23:33

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表