anbinder
anbinder

Reputation: 55

“The value of ESP was not properly saved across a function call.”

I have a DLL written in Delphi 7 that I need to use in Visual C++ 2008.

From documentation that came with DLL, I can see that function is declared as (Delphi 7):

function ReadInfo(pCOM, pBuf, pErr: Pointer):boolean;

where pCom is pointer to data structure:

TCOM = record
dwBaudRate: Longword;
nCom,
nErr,
nLang : Byte;
end;

pBuf is pointer to "array of byte" (as it is written in DLL's documentation).

pErr - not used.

So now in c++ (after successfully loading DLL with LoadLibrary), I call:

myFunc = (MY_FUNC_POINTER)GetProcAddress(dllHandle, "ReadInfo");

which also doesn't return any errors.

MY_FUNC_POINTER is defined as:

typedef bool (*MY_FUNC_POINTER)(TCOM*, BYTE*, void*);

where TCOM is:

struct TCOM
{
     unsigned long dwBaudRate;
     BYTE nComm;
     BYTE nError;
     BYTE nLanguage;
};

I defined:

TCOM MyCom;
BYTE *myRes;
myRes = new BYTE[1024*1024];

But after calling

myFunc(&MyCom, myRes, NULL)

I get “The value of ESP was not properly saved across a function call.” error.

Upvotes: 2

Views: 1783

Answers (1)

David Heffernan
David Heffernan

Reputation: 613302

There would appear to be a calling convention mismatch. On the face of it, the function declares no calling convention in the Delphi, so the default Borland register convention is used. Your C++ code does not declare a calling convention for the import so the default of cdecl is used. But it is plausible that the Delphi code and documentation are not aligned and the Delphi code actually uses a different calling convention. Check the Delphi code, or contact the vendor. No matter what, the error message that you report does indicate a binary mis-match across the boundary between your module and the other module.

If the function really does use the Borland register calling convention (but see below for more), then you cannot readily call the function from languages other than Delphi. In that case you'd need a bridge to adapt that to a standard calling convention such as stdcall. By that I mean a Delphi DLL that can call the original DLL and expose it's functionality a way suited to interop. A better solution would be to fix the root problem and build the DLL again using standard calling conventions.


In fact, I now suspect that all the other commentators are correct. I suspect that the Delphi documentation does not match the Delphi code. I suspect that the function really is stdcall. So you can, probably, solve your problem by changing the function pointer typedef to be as follows:

typedef bool (__stdcall *MY_FUNC_POINTER)(TCOM*, BYTE*, void*);

My reasoning for this is that in stdcall the callee is responsible for cleaning the stack. That's not the case for cdecl, and since all the parameters, and the return value, fit in registers, it's not the case for Delphi register calling convention, for this function. Since there is a stack pointer mis-match, it follows that the most likely explanation is that the Delphi function is stdcall.

All the same, it's not comfortable to be working out calling conventions this way. If you cannot get any help from the DLL vendor then I'd be inclined to dig a little deeper by looking at the DLL function's code under a disassembler.

Upvotes: 4

Related Questions