从 C++ 调用导出的 delphi 函数的应用程序崩溃
Application crash calling exported delphi function from C++
我有一个在 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 := [=11=]415B30;
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;
}
C++ 代码在调用导出的 Delphi 函数时崩溃。
如何在不使应用程序崩溃的情况下正确执行操作?
您没有在 Delphi 代码中指定调用约定。 Delphi 中的默认调用约定是 register
(在 C++Builder 中称为 __fastcall
,并且不受任何其他 C++ 编译器支持)。您的 C++ 代码对导入的函数使用 __stdcall
(C++ 中的默认调用约定通常是 __cdecl
)。混合调用约定是未定义的行为,会导致各种问题,包括崩溃。您需要在两种语言中指定相同的调用约定。在这种情况下,您应该在 Delphi 代码中使用 stdcall
以匹配您在 C++ 代码中使用 __stdcall
:
procedure SystemReload(lpMessage: PAnsiChar; dwIcon: byte; dwColor: byte); stdcall;
此外,您的 Delphi 代码将导出函数声明为 procedure
,这意味着它没有 return 值。但是您的 C++ 代码将导入的函数声明为具有 int
return 类型。您应该更改您的 C++ 代码以使用 void
以匹配您在 Delphi 代码中对 procedure
的使用:
typedef void (__stdcall *SGMessage)(char*, BYTE, BYTE);
此外,与此无关,如果 LoadLibrary()
失败,您的 C++ 代码将调用 FreeLibrary()
两次。如果 LoadLibrary()
失败,您根本不应该调用 FreeLibrary()
。如果 LoadLibrary()
成功,只调用一次。您应该将对 FreeLibrary()
的调用移至 if (hLib)
块内:
void ShowGameMessage(char* Message, BYTE Icon, BYTE Color)
{
HMODULE hLib = LoadLibrary("SysReload.dll");
if (hLib)
{
typedef void (__stdcall *SGMessage)(char*, BYTE, BYTE);
SGMessage ShowGameMessage = (SGMessage) GetProcAddress(hLib, "SystemReload");
if (ShowGameMessage)
(*ShowGameMessage)(Message, Icon, Color);
FreeLibrary(hLib);
}
}
您的 Delphi 导出看起来不像 __stdcall
。
因此,如果您使用的是 C++Builder,请将其声明为 __fastcall
,或者在 DLL 中将其声明为 stdcall
。由于它是一个 DLL 导出,stdcall
可能是更好的选择。
如果您不使用 C++Builder,而是使用另一个 C++,那么 __fastcall
不是一个选项,因为那样的话,您的 __fastcall
与 Delphi 不兼容' s 默认 register
调用约定。最好将 DLL 函数声明为 stdcall
(或 cdecl
,尽管对于 DLL,stdcall
更常见)。
FWIW,出于上述原因,使用默认的 register
DLL 导出调用约定是禁忌。
我有一个在 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 := [=11=]415B30;
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;
}
C++ 代码在调用导出的 Delphi 函数时崩溃。
如何在不使应用程序崩溃的情况下正确执行操作?
您没有在 Delphi 代码中指定调用约定。 Delphi 中的默认调用约定是 register
(在 C++Builder 中称为 __fastcall
,并且不受任何其他 C++ 编译器支持)。您的 C++ 代码对导入的函数使用 __stdcall
(C++ 中的默认调用约定通常是 __cdecl
)。混合调用约定是未定义的行为,会导致各种问题,包括崩溃。您需要在两种语言中指定相同的调用约定。在这种情况下,您应该在 Delphi 代码中使用 stdcall
以匹配您在 C++ 代码中使用 __stdcall
:
procedure SystemReload(lpMessage: PAnsiChar; dwIcon: byte; dwColor: byte); stdcall;
此外,您的 Delphi 代码将导出函数声明为 procedure
,这意味着它没有 return 值。但是您的 C++ 代码将导入的函数声明为具有 int
return 类型。您应该更改您的 C++ 代码以使用 void
以匹配您在 Delphi 代码中对 procedure
的使用:
typedef void (__stdcall *SGMessage)(char*, BYTE, BYTE);
此外,与此无关,如果 LoadLibrary()
失败,您的 C++ 代码将调用 FreeLibrary()
两次。如果 LoadLibrary()
失败,您根本不应该调用 FreeLibrary()
。如果 LoadLibrary()
成功,只调用一次。您应该将对 FreeLibrary()
的调用移至 if (hLib)
块内:
void ShowGameMessage(char* Message, BYTE Icon, BYTE Color)
{
HMODULE hLib = LoadLibrary("SysReload.dll");
if (hLib)
{
typedef void (__stdcall *SGMessage)(char*, BYTE, BYTE);
SGMessage ShowGameMessage = (SGMessage) GetProcAddress(hLib, "SystemReload");
if (ShowGameMessage)
(*ShowGameMessage)(Message, Icon, Color);
FreeLibrary(hLib);
}
}
您的 Delphi 导出看起来不像 __stdcall
。
因此,如果您使用的是 C++Builder,请将其声明为 __fastcall
,或者在 DLL 中将其声明为 stdcall
。由于它是一个 DLL 导出,stdcall
可能是更好的选择。
如果您不使用 C++Builder,而是使用另一个 C++,那么 __fastcall
不是一个选项,因为那样的话,您的 __fastcall
与 Delphi 不兼容' s 默认 register
调用约定。最好将 DLL 函数声明为 stdcall
(或 cdecl
,尽管对于 DLL,stdcall
更常见)。
FWIW,出于上述原因,使用默认的 register
DLL 导出调用约定是禁忌。