C回调函数在Object Pascal中崩溃

6
我正在创建一个类似下面的C语言dll。
#ifdef TEST_EXPORTS
#define TEST_API __declspec(dllexport)
#else
#define TEST_API __declspec(dllimport)
#endif

#ifdef __cplusplus
extern "C" {
#endif
    /* fun accept an argument status_callback which is a function pointer*/
    TEST_API void fun(void(*status_callback)(int));
#ifdef __cplusplus
}
#endif

并且fun函数的实现如下:

void fun(void(*status_callback)(int)) {
   status_callback(0);
}

当在Delphi XE等Object Pascal上使用此dll时,如下所示,我遇到了一个崩溃,其中回调函数的地址出现访问冲突。
type
 TStatusCallBack = procedure( Value: Integer ); stdcall;
 T_fun = procedure(cb : TStatusCallback );

procedure BCallBack( Value: Integer ); stdcall;
begin
   BStatus := value;
end;

begin
 _fun(BCallBack);
end;

实例化代码:

DLLHandle := LoadLibrary( ‘mylib.dll’ );
if (DLLHandle > HDLL_ERROR) then
begin
  _fun := T_fun(GetProcAddress( DLLHandle, 'fun');
end;

问题是什么?由于我对C语言是新手,有什么建议吗?

TEST_API 是否表示 stdcall 而不是 cdecl - MBo
1
@MBo 不是的。提供的代码中宏是可见的。 - David Heffernan
1
我正在使用Visual Studio创建这个dll,我认为默认的调用约定是cdecl。所以我甚至尝试在Pascal中使用cdecl。但仍然无法工作。顺便说一下,我对C和Pascal都不熟悉。 - krishnakumarcn
1
代码的第二部分不是我写的,而是一个懂Pascal的人写的。这就是原因。 - krishnakumarcn
函数fun会导致GPF而不是回调函数吗?请将fun函数中除了回调函数以外的所有内容注释掉,然后再试一次。 - SOLID Developper
显示剩余2条评论
1个回答

5
我们缺少重要细节。我们看不到C++端的fun实现,也看不到你在Delphi侧如何声明_fun。
但是有一个错误是我们可以看到的。回调函数的调用约定不匹配。C++代码将回调定义为__cdecl。您需要在Delphi侧使用cdecl而不是stdcall来匹配,否则会出错。
同样,我希望您的Delphi代码对_fun的调用约定也有错误。同样,它应该为cdecl,但错误消息表明您的声明实际上是register。
总之,请对Delphi代码进行以下更改:
  • 将回调函数类型声明为cdecl。
  • 声明实现回调的函数为cdecl。
  • 声明import fun为cdecl。

查看您的编辑,您对LoadLibrary返回值的错误检查是错误的。如果返回值为零,则表示出现错误。要测试失败,请将其与零进行比较以检查相等性。
您还未对调用GetProcAddress时出现的错误进行检查。您需要处理返回nil的情况。同样,这可能会导致您的代码出现运行时错误。
而且,您的编辑代码显示了我的所有猜测都是正确的。解决方案如上所述。

1
你展示了如何声明T_fun,但没有展示_fun是如何实例化的:是使用“static” DLL链接作为外部声明,还是使用LoadLibrary和GetProcAddress?如果是这样,请展示一下。并且调用约定是否与David所说的匹配,即对于所有函数都是__cdecl? - Rudy Velthuis
是的。这正是我预测的。请按照答案末尾的配方进行操作。 - David Heffernan
谢谢。我会尝试这个。我需要在我的C++库中明确使用__cdecl吗? - krishnakumarcn
1
不需要。这是默认的调用约定。然而,最好明确指出。选择一个调用约定并在所有地方明确指定它。 - David Heffernan
1
您可以使用任何调用约定,只要在两侧匹配即可。没有规定Windows库应该使用stdcall。使用cdecl或stdcall完全是合理的。 - David Heffernan
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接