DELPHI调用DLL时的回调函数问题

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了DELPHI调用DLL时的回调函数问题相关的知识,希望对你有一定的参考价值。

DLL是用C++编写的 要用DELPHI调用 可是其中的回调函数一直没反应啊

C/C++ code#ifndef _FSC_MOTOSRV_H_
#define _FSC_MOTOSRV_H_

#ifdef WIN32
#ifdef MOTO_SRV_EXPORTS
#define MOTOSRV_API extern "C" __declspec(dllexport)
#else
#define MOTOSRV_API __declspec(dllimport)
#endif

#define _CALL __cdecl
#endif //WIN32

typedef int bool;

typedef void (WINAPI *MOTO_Callback)(char *msg, int msglen);

//注册接到对方数据时的回调函数
MOTOSRV_API void _CALL MOTO_SetCallBackFun(MOTO_Callback cb);

以上是DLL的头文件

C/C++ code
void __stdcall display(char *msg, int len)

int i;
for(i =0; i<len; ++i)

printf("0x%02x ",msg[i]);

printf("\n");
return;


int main(int argc, char **argv)


//////////////////////// 接收数据 ////////////////////////////
//设置回调函数
MOTO_SetCallBackFun(display);

//开始接收数据
MOTO_StartRecv(Port);


以上是C++调用DLL时候的回调函数部分,运行正常。

Delphi(Pascal) code
type
PCALLBACK = function(msg:Pchar;n:integer):boolean;stdcall;

procedure MOTO_SetCallBackFun(cbf: PCALLBACK);cdecl; external 'moto_srv.dll';

//function CallBack(msg:Pchar;n:integer):boolean;stdcall;

function CallBack(msg:Pchar;n:integer):boolean;stdcall;
begin
showmessage('11');
form1.memo1.text:='wert';
result:=true;
end;

function MOTO_SendData(str:Pchar; n:integer):boolean;cdecl;external'moto_srv.dll' ;
procedure MOTO_SetIDParam(m:integer; n:integer);cdecl;external'moto_srv.dll' ;
procedure MOTO_SetNetParam(str:string; n:integer);cdecl;external'moto_srv.dll' ;
function MOTO_StartRecv(n:integer):boolean;cdecl;external'moto_srv.dll' ;
procedure MOTO_StopRecv();cdecl;external'moto_srv.dll' ;

procedure TForm1.Button1Click(Sender: TObject);
var
i,k:integer;
aa:array of byte;
str:string;
begin str:='0101DD99004703000011072215293403D5BEB5F7000001000100000000C0EED4CBC7BF0000D2B6CFB2B3AC00000CD5BED5FB393939393939394603443180334603443180334603443180330FE8';
k:=trunc(length(str)/2);
setlength(aa,k);
for i:=0 to k-1 do
aa[i]:=byte(strtoint('$'+str[2*i+1]+str[2*i+2])) ;
sendaaa(aa,k);
end;

procedure TForm1.Sendaaa(aa:array of byte;n:integer);
var
callbackFun: PCALLBACK;
begin
callbackFun:= CallBack;
MOTO_SetNetParam('12.0.0.111',4004);
MOTO_SendData(@aa,n);
MOTO_SetCallBackFun(@callbackFun);
MOTO_StartRecv(4004);
end;

求高手帮帮忙啊,为什么我的回调函数里面一直没有反应呢?showmessage('11');没反应啊,C++的程序里面就算 void __stdcall display(char *msg, int len)
这里的数据类型不对,回调函数收到的数据不正确,但至少还会有反应的,请问我的程序哪出问题了?请高手帮帮忙,谢谢了!

哪位高手能帮帮我 留下邮箱 我把C++的DEMO和DLL发过去

参考技术A DLL是用C++编写的 要用DELPHI调用 可是其中的回调函数一直没反应啊

C/C++ code#ifndef _FSC_MOTOSRV_H_
#define _FSC_MOTOSRV_H_

#ifdef WIN32
#ifdef MOTO_SRV_EXPORTS
#define MOTOSRV_API extern "C" __declspec(dllexport)
#else
#define MOTOSRV_API __declspec(dllimport)
#endif

#define _CALL __cdecl
#endif //WIN32

typedef int bool;

typedef void (WINAPI *MOTO_Callback)(char *msg, int msglen);

//注册接到对方数据时的回调函数
MOTOSRV_API void _CALL MOTO_SetCallBackFun(MOTO_Callback cb);

以上是DLL的头文件

C/C++ code
void __stdcall display(char *msg, int len)

int i;
for(i =0; i<len; ++i)

printf("0x%02x ",msg[i]);

printf("\n");
return;


int main(int argc, char **argv)


//////////////////////// 接收数据 ////////////////////////////
//设置回调函数
MOTO_SetCallBackFun(display);

//开始接收数据
MOTO_StartRecv(Port);


以上是C++调用DLL时候的回调函数部分,运行正常。

Delphi(Pascal) code
type
PCALLBACK = function(msg:Pchar;n:integer):boolean;stdcall;

procedure MOTO_SetCallBackFun(cbf: PCALLBACK);cdecl; external 'moto_srv.dll';

//function CallBack(msg:Pchar;n:integer):boolean;stdcall;

function CallBack(msg:Pchar;n:integer):boolean;stdcall;
begin
showmessage('11');
form1.memo1.text:='wert';
result:=true;
end;

function MOTO_SendData(str:Pchar; n:integer):boolean;cdecl;external'moto_srv.dll' ;
procedure MOTO_SetIDParam(m:integer; n:integer);cdecl;external'moto_srv.dll' ;
procedure MOTO_SetNetParam(str:string; n:integer);cdecl;external'moto_srv.dll' ;
function MOTO_StartRecv(n:integer):boolean;cdecl;external'moto_srv.dll' ;
procedure MOTO_StopRecv();cdecl;external'moto_srv.dll' ;

procedure TForm1.Button1Click(Sender: TObject);
var
i,k:integer;
aa:array of byte;
str:string;
begin str:='0101DD99004703000011072215293403D5BEB5F7000001000100000000C0EED4CBC7BF0000D2B6CFB2B3AC00000CD5BED5FB393939393939394603443180334603443180334603443180330FE8';
k:=trunc(length(str)/2);
setlength(aa,k);
for i:=0 to k-1 do
aa[i]:=byte(strtoint('$'+str[2*i+1]+str[2*i+2])) ;
sendaaa(aa,k);
end;

procedure TForm1.Sendaaa(aa:array of byte;n:integer);
var
callbackFun: PCALLBACK;
begin
callbackFun:= CallBack;
MOTO_SetNetParam('12.0.0.111',4004);
MOTO_SendData(@aa,n);
MOTO_SetCallBackFun(@callbackFun);
MOTO_StartRecv(4004);
end;

求高手帮帮忙啊,为什么我的回调函数里面一直没有反应呢?showmessage('11');没反应啊,C++的程序里面就算 void __stdcall display(char *msg, int len)
这里的数据类型不对,回调函数收到的数据不正确,但至少还会有反应的,请问我的程序哪出问题了?请高手帮帮忙,谢谢了!

哪位高手能帮帮我 留下邮箱 我把C++的DEMO和DLL发过去
参考技术B zxd4g@tom.com追问

已发送,请帮忙看一下,谢谢!

追答

DLL中的输出函数的调用风格不是cdecl,而是pascal风格(windowsAPI多采用这种),通过反汇编moto_srv.dll,发现几个函数的尾部都是ret n的方式返回,即由被调用者负责清理堆栈,而不是cdecl的由调用者通过add esp, n的方式清理堆栈。
以上是主要问题,改正方法是把函数原型定义的cdecl去掉,或者改写为pascal(缺省如此,直接去掉cdecl同此);次要问题(或许不是问题),在display中,先不要访问VCL等有关窗体的资源,用writeln输出到屏幕,或者保存到一个文件,看看是否成功。

追问

感谢您的回答,可是按照您说的做 还是不行
以下是我的程序:
type
PCALLBACK = procedure(msg:Pchar;n:integer);stdcall;

procedure MOTO_SetCallBackFun(cbf: PCALLBACK);pascal; external 'moto_srv.dll';

procedure display(msg:Pchar;n:integer);stdcall;
begin
writeln('xxx');
//writeln(msg);
end;

writeln('xxx');这句没反应,

追答

抱歉,关于清理堆栈的问题,我看错了,几个输出函数的风格还是cdecl,其内部没有输出的函数才是自行清理堆栈的,函数声明还是用cdecl吧。
由于不能运行,所以困难了很多,你做做如下试验:
1、在writeln行加上断点,看看能否执行到此
2、在调用MOTO_SetCallBackFun行加上断点,断下来后,切换到CPU模式,在汇编模式下跟踪看看能否执行到display中去。

追问

writeln('xxx');这行应该是收到数据才运行的,还有切换到CPU模式我看不懂。。。
做DLL的人不会DELPHI,用DELPHI的我又比较菜,请问该怎么跟做DLL的人调试才能找到错误?
还有可不可以加QQ或者百度HI,我再详细请教下,我的QQ号就是刚刚发给你的邮箱的QQ号,谢谢!

本回答被提问者采纳
参考技术C 是 速度

异步回调函数

我想写一个函数指针,异步执行完就调用该函数。可是,我不大明白,要是这个函数是某个类中的成员方法,该怎么写这个函数指针声明?

回调函数是应用程序提供给Windows系统DLL或其它DLL调用的函数,一般用于截获消息、获取系统信息或处理异步事件。应用程序把回调函数的地址指针告诉DLL,而DLL在适当的时候会调用该函数。回调函数必须遵守事先规定好的参数格式和传递方式,否则DLL一调用它就会引起程序或系统的崩溃。通常情况下,回调函数采用标准WindowsAPI的调用方式,即__stdcall,当然,DLL编制者可以自已定义调用方式,但客户程序也必须遵守相同的规定。在__stdcall方式下,函数的参数按从右到左的顺序压入堆栈,除了明确指明是指针或引用外,参数都按值传递,函数返回之前自己负责把参数从堆栈中弹出。程序在调用一个函数(function)时(通常指api).相当于程序(program)呼叫(Call)了一个函数(function)关系
当你调用的函数在传递返回值给回调函数时,你就可以利用回调函数来处理或完成一定的操作。至于如何定义自己的回调函数,跟具体使用的API函数有关,很多不同类别的回调函数有各种各样的参数,有关这些参数的描述一般在帮助中有说明回调函数的参数和返回值等.其实简单说回调函数就是你所写的函数满足一定条件后,被DLL调用!

Windows 系统还包含着另一种更为广泛的回调机制,即消息机制。消息本是 Windows 的基本控制手段,是一种变相的函数调用。发送消息的目的是通知收方运行一段预先准备好的代码,相当于调用一个函数。消息所附带的 WParam 和 LParam 相当于函数的参数,应用程序可以主动发送消息,更多情况下是坐等 Windows 发送消息。一旦消息进入所属消息队列,便检感兴趣的那些,跳转去执行相应的消息处理代码。操作系统本是为应用程序服务,由应用程序来调用。而应用程序一旦启动,却要反过来等待操作系统的调用。这分明也是一种回调,或者说是一种广义回调。其实,应用程序之间也可以形成这种回调。假如进程 B 收到进程 A 发来的消息,启动了一段代码,其中又向进程 A 发送消息,这就形成了回调。这种回调比较隐蔽,弄不好会搞成递归调用,若缺少终止条件,将会循环不已,直至把程序搞垮。利用消息也可以构成狭义回调。把回调函数地址换成窗口 handle。如此,当需要比较数据大小时,不是去调用回调函数,而是借 API 函数 SendMessage 向指定窗口发送消息。收到消息方负责比较数据大小,把比较结果通过消息本身的返回值传给消息发送方。所实现的功能与回调函数并无不同。当然,此例中改为消息纯属画蛇添脚,反倒把程序搞得很慢。但其他情况下并非总是如此,特别是需要异步调用时,发送消息是一种不错的选择。假如回调函数中包含文件处理之类的低速处理,调用方等不得,需要把同步调用改为异步调用,去启动一个单独的线程,然后马上执行后续代码,其余的事让线程慢慢去做。一个替代办法是借 API 函数 PostMessage 发送一个异步消息,然后立即执行后续代码。这要比自己搞个线程省事许多,而且更安全。

回调用于层间协作,上层将本层函数安装在下层,这个函数就是回调,而下层在一定条件下触发回调,例如作为一个驱动,是一个底层,他在收到一个数据时,除了完成本层的处理工作外,还将进行回调,将这个数据交给上层应用层来做进一步处理,这在分层的数据通信中很普遍。其实回调和API非常接近,他们的共性都是跨层调用的函数。但区别是API是低层提供给高层的调用,一般这个函数对高层都是已知的;而回调正好相反,他是高层提供给底层的调用,对于低层他是未知的,必须由高层进行安装,这个安装函数其实就是一个低层提供的API,安装后低层不知道这个回调的名字,但它通过一个函数指针来保存这个回调,在需要调用时,只需引用这个函数指针和相关的参数指针。 其实:回调就是该函数写在高层,低层通过一个函数指针保存这个函数,在某个事件的触发下,低层通过该函数指针调用高层那个函数。
参考技术A void a()


void b(void *a)


void c()
b((void * ) a);



大致如此,你调试调试本回答被提问者采纳

以上是关于DELPHI调用DLL时的回调函数问题的主要内容,如果未能解决你的问题,请参考以下文章

Delphi DLL 和 Delphi EXE 之间的回调功能

关于delphi回调函数

请大家解释一下Delphi的回调函数

什么是回调函数,如何写一个回调函数

如何将c/c++回调函数转换为delphi

Delphi回调函数及其使用