Delphi调用Dll返回结构体的问题?

伟志 2014-11-04 09:55:00
C写的一个动态库,其中有些函数的返回值为结构体(非指针),然后在Delphi里调用,得到的返回结果是错误的。例如,在C里面:
结构体:
typedef struct uv_buf_t {
ULONG len; char* base;
} uv_buf_t;

导出的函数:
uv_buf_t uv_buf_init(char* base, unsigned int len) {
uv_buf_t buf;
buf.base = base;
buf.len = len;
return buf;
}

在Delphi里:
对应C的结构体:
uv_buf_t = record
len: ULONG;
base: PAnsiChar;
end;
函数声明为:
uv_buf_init: function(base: PAnsiChar; len: Cardinal): uv_buf_t; cdecl;
在Delphi中调用:
procedure test2;
var
buf: uv_buf_t;
s: PAnsiChar;
begin
s := 'abcde';
buf := uv_buf_init(@s, 5);
Writeln('buf的值:len=' + IntToStr(buf.len) + ' base=' + buf.base);
end;
最终的结果是错误的,而且会导致传到C中的参数的值也不对,如果把C库中函数的返回值和Delph声明函数的返回值都改为指针就一点问题都没有,但是我们是不能动C代码的,只能接收返回的结构体,不知哪位高手有解决办法或思路,在此先谢过了。
uv_buf_init: function(base: PAnsiChar; len: Cardinal): uv_buf_t; stdcall;
调用约定都试过了,还是不行。
...全文
855 12 打赏 收藏 转发到动态 举报
写回复
用AI写文章
12 条回复
切换为时间正序
请发表友善的回复…
发表回复
caonumber 2014-11-07
  • 打赏
  • 举报
回复
frtrnr ,说的可以尝试下
伟志 2014-11-07
  • 打赏
  • 举报
回复
没有人遇到过这样的问题吗?
伟志 2014-11-07
  • 打赏
  • 举报
回复
不错,这样也行。
  • 打赏
  • 举报
回复
既然你确定了结构体是通过edx:eax返回的,那就可以在Delphi中返回int64/uint64: uv_buf_init: function(base: PAnsiChar; len: Cardinal): uint64; cdecl; 然后: PUInt64(@buf)^ := uv_buf_init(s, 5);
伟志 2014-11-07
  • 打赏
  • 举报
回复
其实这是Delphi编译器和VS编译器造成的,其实只有返回值是8字节才有这样的问题,其它字节的话就不会有问题。 如果返回值是8个字节,Delphi会把参数先压栈,然后把返回值的变量地址当作最后一个参数压栈,如果Dll也是Delphi写的话,那么在Dll里,Delphi会把栈顶的值当作返回值的地址,栈顶往下依次为参数,然后把结果写到返回值的地址里。但现在的Dll是C写的,C的处理方试为(针对返回值是8字节的),C会把栈顶往下依次当作参数,所以在C里面接收到参数会错位,因为Delphi传过来的栈顶是返回值的指针。那么C处理回返回值是把低4位放到EAX里,把高4位放到EDX里,Delphi里根本没有读取这两个寄存器,所以返回值也得不到。问题就是这样的,解决方法,在Delphi里嵌入汇编可以解决这样的问题。
引用 8 楼 DelphiGuy 的回复:
你把C代码用bcc编译再试试。
这个项目是VS的项目,不知道用BCC能不能编译,如果能编译的话,应该可,毕竟和Delphi同一个家族的产品,传参数的方式应该是一致的。
引用 9 楼 DelphiGuy 的回复:
你也可以这样声明试试: uv_buf_init: procedure(base: PAnsiChar; len: Cardinal; var p: uv_buf_t); cdecl;
这样肯定也是不行的,我最初就用这样的方法试过了,现在想想,也知道为什么不行了。
  • 打赏
  • 举报
回复
你也可以这样声明试试: uv_buf_init: procedure(base: PAnsiChar; len: Cardinal; var p: uv_buf_t); cdecl;
  • 打赏
  • 举报
回复
你把C代码用bcc编译再试试。
Playmaster 2014-11-07
  • 打赏
  • 举报
回复
为了加快调用速度,参数也可能放在寄存器了传进去。
Playmaster 2014-11-07
  • 打赏
  • 举报
回复
编译优化时delphi会根据结构的大小把返回值放到堆栈或是寄存器里,可能8字节就刚好放到寄存器里了(DX:AX)。 还有windows下C函数用stdcall调用模式的也很多。
frtrnr 2014-11-06
  • 打赏
  • 举报
回复
随C(++)编译器 版本不同,struct 可能是字节对齐的,也可能没有对齐(也可能在“项目”中设置) 所以,对应的delphi record,你试试加上 packed
伟志 2014-11-06
  • 打赏
  • 举报
回复
这些都试了,包括强制字节对齐(从1到16),还是不行。 如果返回的结构体是8字节就有问题,如果不是8字节就没有问题(比如删除或添加一个成员,或修改原有成员的类型)。 现在看来,是Delphi和C在这方面的处理方式不同,从压栈的数据来看,返回值的地址会被压在栈顶,在C里面却把栈顶当做第一个参数,而不当作返回值,举个例子: Delphi: procedure test2; var buf: uv_buf_t; s: PAnsiChar; begin s := 'abcde'+#0; Writeln('uv_buf_t大小:' + IntToStr(Sizeof(uv_buf_t))); Writeln('s的地址:' + IntToHex(Integer(@s), 8)); Writeln('buf的地址:' + IntToHex(Integer(@buf), 8)); buf := uv_buf_init(s, 5); Writeln('buf的值:len=' + IntToStr(buf.len) + ' base=' + buf.base); end; 那么在C里跟踪调试,运行结果如下: 按照从右到左的压栈顺序,先压右边的参数,如下的顺序: 0x0012FF00 40 ff 12 00 @... 这是返回值的地址 0x0012FF04 b4 9d 41 00 ??A. 这是参数base的值 0x0012FF08 05 00 00 00 .... 这是参数len的值 问题就在这里了,现在base指向了0x0012FF00 地址,即指向了返回值所在的栈,而len指向base数据存放的栈,即0x0012FF04,整好偏移了4个字节。不知道这是什么原因。
s11ss 2014-11-05
  • 打赏
  • 举报
回复
调试,看cpu窗口

1,183

社区成员

发帖
与我相关
我的任务
社区描述
Delphi Windows SDK/API
社区管理员
  • Windows SDK/API社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

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