minGW 调用MSVC的DLL, 无法传递参数....求高人解...

Aisy_D 2010-09-20 01:21:00
MSVC2005 部分:

// XyzLibrary.h

#ifdef XYZLIBRARY_EXPORTS
#define XYZAPI __declspec(dllexport)
#else
#define XYZAPI __declspec(dllimport)
#endif

#ifdef __cplusplus
# define EXTERN_C extern "C"
# define XYZHANDLE IXyz*

struct IXyz
{
virtual int Foo(int n) = 0;
virtual void Release() = 0;
};

#endif

EXTERN_C XYZAPI int hzf (int);
EXTERN_C XYZAPI XYZHANDLE hzfXyz (VOID);




// XyzLibrary.cpp

#include "stdafx.h"
#include "stdio.h"
#include "XyzLibrary.h"


class XyzImpl : public IXyz
{
public:
int Foo(int n);
void Release();
};

int XyzImpl::Foo(int n)
{
printf("Foo called....%d\n",n);
return n * n;
}

void XyzImpl::Release()
{
delete this;
}


XYZHANDLE hzfXyz()
{
printf("\n\nhzfXyz() called ....\n");
return new XyzImpl;
}

int hzf(int n)
{
printf("\n\nhzf(int) called ....\n");
XyzImpl *xyz = new XyzImpl;
return xyz->Foo(n);
}


以上编译得到 XyzLibrary.dll



minGW 代码部分:

///mian.cpp

    
#include <stdio.h>
#include "XyzLibrary.h"


int main(int ,char** )
{
IXyz *xyz = hzfXyz();
printf(" hzfXyz call: %d\n" , xyz->Foo(5));

printf(" hzf call: %d\n" , hzf(8));

return 0;
}




////下面是输出:
hzfXyz() called ....
Foo called....169091488 /// 这里的数值应该是 5 , 却得到了 169091488
hzfXyz call: -982866944 ///这里的数值应该是 25, 却得到了 169091488 * 169091488 ,溢出....


hzf(int)
Foo called....8
hzf call: 64


问题:
在 mian 中获取 XyzImpl的指针,并调用 XyzImpl 的 Foo 函数, Foo 函数能正确调用,但是参数(上例在是 5) 却无法正确传递,而是传递了一个随机值....

我在 main 在调用 hzf(int) 函数时, 因为 XyzImpl 是在 MSVC 的 DLL 中创建的,所以函数运行结果一正确....

现在我就搞不懂, 为什么在 minGW中 的 XyzImpl 指针无法正确传递 int 这样的标准 C 类型数据呢??? 如果我要传递 STL 的数据类型, 例如 std::string, std::list 等等 ,不是更不可能了吗??

请问处理这个问题呢...望高人解答 ...谢谢!



...全文
481 14 打赏 收藏 转发到动态 举报
写回复
用AI写文章
14 条回复
切换为时间正序
请发表友善的回复…
发表回复
Aisy_D 2010-09-20
  • 打赏
  • 举报
回复
8051 那些简单的汇编我能看懂, 但是上面的汇编我确实搞不懂,要系统学习一下才行.....

visualwind 2010-09-20
  • 打赏
  • 举报
回复
你在调foo的时候,传的eax对不对,eax里面应该是5。然后在printf之前,压入eax里面的值是不是5,从这个图可以看到进入printf后eax里面变成了乱码
Aisy_D 2010-09-20
  • 打赏
  • 举报
回复
不是printf的问题,看下图:
visualwind 2010-09-20
  • 打赏
  • 举报
回复
调用约定应该不会影响DLL输出函数名字。DLL输出函数名字是由.DEF文件 __declspec(dllexport) 以及extern "C"影响的。
visualwind 2010-09-20
  • 打赏
  • 举报
回复
应该是printf时,两种编译器对可变参数的处理不一样,导致没有取到正确的n的位置,LZ可以先分别在两种编译器中试试printf的效果
Aisy_D 2010-09-20
  • 打赏
  • 举报
回复
调用约定会影响 DLL 中函数的导出的命名, __stdcall 导出的命名是 _Function@n 而 __cdecl的规则是 Function ....
详细内容请参看:
http://wyw.dcweb.cn/stdcall.htm

我上面的例子, 函数能正确调用, 说明应该不是调用约定的问题了吧......但是也有可能...
Aisy_D 2010-09-20
  • 打赏
  • 举报
回复
下面是我在 QT Creator2 中的部分 反汇编...


61
62 // GetXyz();
63 IXyz *xxx = hzfXyz();
0x00401c1e <+778>: mov 0x41a398,%eax
0x00401c23 <+783>: call *%eax
0x00401c25 <+785>: mov %eax,-0x20(%ebp)

64 int aa = xxx->Foo(5);
0x00401c28 <+788>: mov -0x20(%ebp),%eax
0x00401c2b <+791>: mov (%eax),%eax
0x00401c2d <+793>: mov (%eax),%edx
0x00401c2f <+795>: movl $0x5,0x4(%esp)
0x00401c37 <+803>: mov -0x20(%ebp),%eax
0x00401c3a <+806>: mov %eax,(%esp)
0x00401c3d <+809>: call *%edx
0x00401c3f <+811>: mov %eax,-0x1c(%ebp)

65
66
67 printf(" aa vla: %d\n", aa);
0x00401c42 <+814>: mov -0x1c(%ebp),%eax
0x00401c45 <+817>: mov %eax,0x4(%esp)
0x00401c49 <+821>: movl $0x413af6,(%esp)
0x00401c50 <+828>: call 0x4066dc <printf>

68 printf(" aa hzf call val: %d\n", hzf(8));
0x00401c55 <+833>: movl $0x8,(%esp)
0x00401c5c <+840>: mov 0x41a394,%eax
0x00401c61 <+845>: call *%eax
0x00401c63 <+847>: mov %eax,0x4(%esp)
0x00401c67 <+851>: movl $0x413b03,(%esp)
0x00401c6e <+858>: call 0x4066dc <printf>

69 printf("\n" );
visualwind 2010-09-20
  • 打赏
  • 举报
回复
调用约定只是函数返回时的栈清理工作问题,和调用时压栈的问题没有关系。
Aisy_D 2010-09-20
  • 打赏
  • 举报
回复
指定成 __stdcall 还是 __cdecl 呢....
好像指定为 __stdcall 的时候, 连函数都不能正确调用了.....
skyworth98 2010-09-20
  • 打赏
  • 举报
回复
指定调用约定试试看
Aisy_D 2010-09-20
  • 打赏
  • 举报
回复
我是在 虚拟机中安装 了 VC2005, 编译了 MSVC 的 XyzLibrary.dll ,

因为主程序用 minGW , 用的 IDE 是 Qt Creator2, 在真机的 windows 系统中编译了 minGW 的 main.exe, 没有使用 OD, 我看看 Qt Creator 的汇编中能不能查到问题.....
visualwind 2010-09-20
  • 打赏
  • 举报
回复
虽然是两种编译器,但都是在windows平台上,编译后当然都是win平台的格式,编译器会自动按平台生成汇编。你可以直接用OD加载你的EXE,然后找到call hzf的位置(如果对反汇编不熟,可以在call hzf之前加上一个call win32 API,或者加个字符串(比如char* p="hello"),然后编译生成EXE,这样只要在OD中找到win32 API或者找到这个字符串,就可以很方便定位call hzf的位置),然后下断点,然后就可以单步查看压栈的参数了。
Aisy_D 2010-09-20
  • 打赏
  • 举报
回复
不好意思,现在功力尚浅, 可能看不懂汇编, 因为涉及两种 compiler 编译器, 有两种风格的汇编, 要看 intel 那种,还是另外一种呢...
visualwind 2010-09-20
  • 打赏
  • 举报
回复
可能是调用函数时压栈出了问题,建议用OD反汇编看看
MSVC vs. MinGW 之 (lib,dll,def,obj,exe) vs (a,dll,def,o,exe) 玩转攻略手记 一份粗糙的研究记录,有待补完和整理。 MinGW: c -> o gcc -c a.c c -> exe gcc a.c libs.o -o a.exe (从主程序a.c,附加libs,生成a.exe) o -> exe gcc a.o b.o ... -o main.exe c -> dll,def,a gcc a.c -shared -o a.dll -Wl,--output-def,a.def,--out-implib,liba.a a -> dll a2dll liba.a dll -> a: dlltool --dllname a.dll --def a.def --output-lib liba.a (需要def文件) a -> def: dumpbin /exports lib.a > lib.def (在windows上调用,def需要修改) dll -> def : pexports a.dll -o > a.def (这里的-o是指给函数标序号) lib -> def : reimp -d a.lib lib -> a: (for __cdecl functions in most case) reimp a.lib; (for __stdcall functions) MSVC: c -> lib cl /LD a.c (注意已经定义了export列表) c -> dll cl /LD a.c c -> obj cl /c a.c c -> exe cl a.c /out:a.exe dll ->lib lib /machine:ix86 /def:a.def /out:a.lib (需要def文件) obj ->lib lib a.obj b.obj... /out:mylib.lib dll ->def DUMPBIN a.dll /EXPORTS /OUT:a.def (生成的def需要做修正) lib ->def reimp -d a.lib (这个要在MSYS+MinGW下用) 关于这些工具的适用范围可以很容易的理解和记忆。 dll和exe都是PE文件,所以可以使用pexports. lib和a是静态库文件,都是归档类型,不是PE格式。所以不能使用pexports. dll可以使用dlltool. lib可以使用lib, 和reimp(lib->a工具) 所有的bin文件,包括dll,exe,lib,a都可以使用dumpbin. 参考: http://hi.baidu.com/kaien_space/blog/item/5e77fafa2ba9ff16a8d3110a.html Mingw官网文档: http://www.mingw.org/wiki/MSVC_and_MinGW_DLLs http://oldwiki.mingw.org/index.php/CreateImportLibraries http://www.mingw.org/wiki/FAQ http://hi.baidu.com/opaquefog/blog/item/9b21b6deb324e25dccbf1ab7.html http://qzone.qq.com/blog/8330936-1238659272 http://hi.baidu.com/jzinfo/blog/item/b0aa1d308de99f9da8018e00.html 本篇测试用代码: 1. main.cpp #include #include #include "mylib.h" using namespace std; int main() { char str[]="Hello world!"; printhello(str); return 0; } 2. mylib.cpp #include #include #include "mylib.h" using namespace std; void EXPORT printhello(char *str) { cout << str << endl; } 3. mylib.h #define EXPORT __declspec(
学习并掌握C++2.0(11+14+17+20)的新特性,学习线程及线程池的应用 ---------------------------------------------------给小白学员的3年学习路径及计划技术方面分三块:1.纯开发技术方向2.音视频流媒体专业方向3.项目实战---------------------------------------------------1.纯开发技术方向(1) C++必须要过硬(至少学会10本经典好书)(2) 系统级编程(Windows、Linux),必须特别熟练系统API,灵活运用(3) 框架与工具(Qt、MFC):必须精通其中一种。(4) 架构与设计模式:需要提升一个高度,不再是简单的编码,而是思维模式。(5) 驱动级别(如果有兴趣,可以深入到驱动级:包括Windows、Linux)(6) 最好学习点Java+Html+javascript等WEB技术。2.音视频流媒体专业方向(1) 音视频流媒体基础理论:   必须认真学会,否则看代码就是看天书(2) 编解码方向:精通h.264,h.265(hevc), 包括理论和各个开源库(ffmpeg,libx264,libx265,...)。(3) 直播方向:  精通各种直播协议(rtsp,rtmp,hls,http-flv,...), 钻研各个开源库(live555,darwin,srs,zlmediakit,crtmpserver,...)(4) 视频监控:  理论+开源库(onvif+281818)(EasyMonitor、iSpy、ZoneMinder(web)、...) 3.项目实战(1) Qt项目:  至少要亲手练习10个实战项目(网络服务器、多线程、数据库、图像处理、多人聊天、等等)(2)音视频项目:包括编解码、视频监控、直播等各个方向,都需要亲手实战项目,包括视频服务器、后台管理系统、前端播放器(多端)---------------------------------------------------  第1章 C++11新特性 41). nullptr关键字与新语法 42). auto和decltype类型推导 6 auto讲解 6 auto示例 7 decltype 83). for区间迭代 94). 初始化列表 105). 模板增强 11外部模板 11类型别名模板 12默认模板参数 126). 构造函数 13委托构造 13继承构造 147). Lambda 表达式 158). 新增容器 20std::array 20std::forward_list 21无序容器 22元组 std::tuple 239). 正则表达式 2610). 语言级线程支持 28多线程库简介 2811). 右值引用和move语义 31右值引用和move语义 32转移左值 3412). constexpr 35第2章 C++14新特性 36Lambda 函数 36类型推导 37返回值类型推导(Return type deduction) 37泛型lambda 39[[弃用的]]  [[deprecated]]属性 40二进制数字和数字分隔符 41第3章 C++17新特性 42安装GCC10.2 42安装msys2-x86_64-20200720 42更新镜像 42更新软件库 43安装 MinGW64 等必要的软件 43环境变量Path 43编译命令 43constexpr 44typename 45折叠表达式 47结构化绑定 48条件分支语句初始化 49聚合初始化 50嵌套命名空间 52lambda表达式捕获*this的值 53改写/继承构造函数 54用auto作为非类型模板参数 55__has_include 56fallthrough 57nodiscard 57maybe_unused 58第4章 C++20新特性 59编译命令 59concept 59typename 60explicit 61constinit 62位域变量的默认成员初始化 62指定初始化 63基于范围的for循环初始化 64放宽基于范围的for循环,新增自定义范围方法 65嵌套内联命名空间 66允许用圆括弧的值进行聚合初始化 67unicode字符串字面量 68允许转换成未知边界的数组 68likely和unlikely 69第5章 C++2.0(11/14/17/20)总结与分析 705.1 C语言与C++ 715.2 语言可用性的强化 725.2.1 常量 725.2.2 变量及其初始化 735.2.3 类型推导 745.2.4 控制流 765.2.5 模板 775.2.6 面向对象 815.3 语言运行期的强化 835.3.1 Lambda 表达式 835.3.2 右值引用 865.4 容器 885.4.1 线性容器 885.4.2 无序容器 895.4.3 元组 895.5 智能指针与内存管理 905.5.1 RAII 与引用计数 905.5.2 std::shared_ptr 905.5.3 std::unique_ptr 915.5.4 std::weak_ptr 91第6章 C++2.0多线程原理与实战 93什么是并发 93并发的方式 93为什么使用并发 95线程简介 96创建线程的三种方式 971. 通过函数 972.通过类对象创建线程 993.通过lambda表达式创建线程 101thread线程的使用 101互斥量与临界区 105期物Future 111条件变量 112原子操作 114内存模型 118第7章 C++2.0线程池原理与实战 120线程与线程池的基本原理 1201)、线程 1202)、线程的生命周期 1213)、什么是单线程和多线程 1214)、线程池 1225)、四种常见的线程池 123线程池的架构与流程 123线程池代码实战 125    

64,637

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧