我在Delphi 7中编译了一个导出函数的.DLL。我试图从C ++调用该函数。
procedure SystemReload(lpMessage: PAnsiChar; dwIcon: byte; dwColor: byte);
var
dwMessage: cardinal;
procedure SystemReload_Real(lpMessage: PAnsiChar); assembler;
asm
...
end;
begin
dwMessage := $00415B30;
ShowGameMessage_Real(lpMessage);
end;
exports SystemReload name 'SystemReload';
begin
end.
然后我用来调用函数的C ++代码:
int ShowGameMessage(char* Message, BYTE Icon, BYTE Color)
{
int ret;
if (exist("SysReload.dll"))
{
HMODULE hLib = LoadLibrary("SysReload.dll");
if (hLib)
{
typedef int(__stdcall *SGMessage)(char*, BYTE, BYTE);
SGMessage ShowGameMessage = (SGMessage)GetProcAddress(hLib, "SystemReload");
ret = (*ShowGameMessage)(Message, Icon, Color);
} else { FreeLibrary(hLib); }
FreeLibrary(hLib);
}
return ret;
}
调用导出的Delphi函数时,C ++代码崩溃了。
如何在不崩溃应用程序的情况下做正确的事情?
答案 0 :(得分:3)
您没有在Delphi代码中指定调用约定。 Delphi中的默认调用约定是//calling the method
double choice = getMenuOption();
(在C ++ Builder中称为long intCastedChoice = (long)choice;
,任何其他C ++编译器都不支持)。您的C ++代码使用register
作为导入的函数(C ++中的默认调用约定通常为__fastcall
)。混合调用约定是未定义的行为,可能导致各种问题,包括崩溃。您需要在两种语言中指定相同的调用约定。在这种情况下,您应该在Delphi代码中使用__stdcall
来匹配您在C ++代码中使用__cdecl
:
stdcall
此外,您的Delphi代码将导出的函数声明为__stdcall
,这意味着它没有返回值。但是您的C ++代码将导入的函数声明为具有procedure SystemReload(lpMessage: PAnsiChar; dwIcon: byte; dwColor: byte); stdcall;
返回类型。您应该更改C ++代码以使用procedure
来匹配您在Delphi代码中使用int
:
void
另外,在一个不相关的注释中,如果procedure
失败,您的C ++代码将调用typedef void (__stdcall *SGMessage)(char*, BYTE, BYTE);
两次。如果FreeLibrary()
失败,您根本不应该致电LoadLibrary()
。如果FreeLibrary()
成功,则只调用一次。您应该将来电LoadLibrary()
移至LoadLibrary()
区块内:
FreeLibrary()
答案 1 :(得分:1)
您的Delphi导出看起来不像是__stdcall
。
因此,如果您使用的是C ++ Builder,则将其声明为__fastcall
,或者在DLL中将其声明为stdcall
。由于它是DLL导出,stdcall
可能是更好的选择。
如果你不使用C ++ Builder,而是使用另一个C ++,则__fastcall
不是一个选项,因为那样,你的__fastcall
与Delphi的默认register
调用不兼容惯例。最好将DLL函数声明为stdcall
(或cdecl
,尽管对于DLL,stdcall
更常见。)
FWIW,使用默认的register
调用约定进行DLL导出是一个禁忌,原因如上所述。