应用程序崩溃从C ++调用导出的delphi函数

时间:2018-02-04 00:37:52

标签: function delphi call

我在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 ++代码崩溃了。

如何在不崩溃应用程序的情况下做正确的事情?

2 个答案:

答案 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导出是一个禁忌,原因如上所述。

更多信息:DLL dos and don'ts -- Calling convention