我有一个用Delphi 7编写的DLL,我需要在Visual C ++ 2008中使用。
从DLL附带的文档中,我可以看到该函数被声明为(Delphi 7):
function ReadInfo(pCOM, pBuf, pErr: Pointer):boolean;
其中 pCom 是指向数据结构的指针:
TCOM = record
dwBaudRate: Longword;
nCom,
nErr,
nLang : Byte;
end;
pBuf 是指向“数组字节”的指针(因为它是在DLL的文档中编写的)。
pErr - 未使用。
现在在c ++中(在使用LoadLibrary成功加载DLL之后),我调用:
myFunc = (MY_FUNC_POINTER)GetProcAddress(dllHandle, "ReadInfo");
也不会返回任何错误。
MY_FUNC_POINTER定义为:
typedef bool (*MY_FUNC_POINTER)(TCOM*, BYTE*, void*);
TCOM是:
struct TCOM
{
unsigned long dwBaudRate;
BYTE nComm;
BYTE nError;
BYTE nLanguage;
};
我定义了:
TCOM MyCom;
BYTE *myRes;
myRes = new BYTE[1024*1024];
但是在打电话之后
myFunc(&MyCom, myRes, NULL)
我得到“ESP的值未在函数调用中正确保存。”错误。
答案 0 :(得分:4)
似乎存在调用约定不匹配。从表面上看,该函数在Delphi中没有声明调用约定,因此使用了默认的Borland寄存器约定。您的C ++代码未声明导入的调用约定,因此使用了默认的cdecl。但是Delphi代码和文档没有对齐并且Delphi代码实际上使用不同的调用约定似乎是合理的。查看Delphi代码,或联系供应商。无论如何,您报告的错误消息都表明模块与其他模块之间的边界存在二进制不匹配。
如果该函数确实使用了Borland寄存器调用约定(但请参见下面的更多内容),那么您无法从Delphi以外的语言中轻松调用该函数。在这种情况下,您需要一个桥接器来使其适应标准调用约定,例如stdcall。我的意思是一个Delphi DLL,它可以调用原始DLL并以适合互操作的方式公开它的功能。更好的解决方案是修复根问题并使用标准调用约定再次构建DLL。
事实上,我现在怀疑所有其他评论员都是正确的。我怀疑Delphi文档与Delphi代码不匹配。我怀疑该函数确实是stdcall
。因此,您可以通过将函数指针typedef更改为如下来解决您的问题:
typedef bool (__stdcall *MY_FUNC_POINTER)(TCOM*, BYTE*, void*);
我的理由是,在stdcall
中,被调用者负责清理堆栈。对于cdecl
来说情况并非如此,并且由于所有参数和返回值都适合寄存器,因此对于此函数,Delphi寄存器调用约定不是这种情况。由于存在堆栈指针不匹配,因此最可能的解释是Delphi函数为stdcall
。
同样,以这种方式制定调用约定并不舒服。如果你无法从DLL供应商那里得到任何帮助,那么我倾向于通过查看反汇编程序下的DLL函数代码来深入挖掘。