C++ dll function calling in delphi7 - c++

I am using Delphi7 and I am new in it.
I want to use function of Dll(Implemented in C++) in my Delphi Project.
I have a function declaration in C++ like- (given by third party)
Syntax
LPTSTR GetErrorString(LONG lErrorNumber)
Arguments
LONG lErrorNumber Error number
Result
LPTSTR Error string
But when I am passing a value in Delphi7 like
GetErrorString(310);
I am declaring it in my unit-
Function GetErrorString(lErrorNumber : LongInt): String;StdCall;
implementation
Function GetErrorString;external 'Third-Party.DLL';
I am receiving blank string instead of actual Error String. I don't know the exact data type of LPTSTR.
Also tell me the proper steps to use it in my project.

LPTSTR is just a pointer to raw character data. Delphi's equivilent is either PAnsiChar or PWideChar, depending on whether the DLL was compiled for Ansi or Unicode. LPTSTR is always Ansi in Delphi 2007 and earlier (which includes Delphi 7) and always Unicode in Delphi 2009 and later, so you may need to account for that. If the DLL was compiled for Unicode, you would have to ue PWideChar instead of LPTSTR. As such, it is better to use PAnsiChar and PWideChar directly instead of LPTSTR to avoid mismatches between different environments (unless the DLL exports separate versions of the function for both types, like most Win32 API functions do).
Also, depending on the actual calling convention being used by the DLL, the function may be using cdecl or stdcall. In the absence of an explicit calling convention, most C/C++ compilers use cdecl, but they could just as easily be using stdcall and just not document it. So you need to find out, because it makes a BIG difference because cdecl and stdcall have different semantics for stack management and parameter passing.
So, with that said, the correct function declaration will be either:
function GetErrorString(lErrorNumber: Integer): PAnsiChar; cdecl; external 'filename.dll';
Or:
function GetErrorString(lErrorNumber: Integer): PWideChar; cdecl; external 'filename.dll';
Or:
function GetErrorString(lErrorNumber: Integer): PAnsiChar; stdcall; external 'filename.dll';
Or:
function GetErrorString(lErrorNumber: Integer): PWideChar; stdcall; external 'filename.dll';
You will have to do some research to find out whether the DLL is using Ansi or Unicode, and whether it is using cdecl or stdcall, if the documentation does not specifically state that information.

First, a Delphi string is refcounted, and thus something else than a pointer to char (LPTSTR). I suggest you avoid those traps as beginner, and go for straight pointers.
Second LPTSTR is a pointer to a one byte char (LPSTR), or a pointer to a two byte char (LPWSTR) depending on if UNICODE is defined.
So the correct solution is to make the function return pansichar or pwidechar, depending on how UNICODE was defined in your C++ program.
If you start passing character buffers between different languages, make sure they use the same allocator to (de)allocate them, or make sure that each module frees the allocations that it makes.

Related

Calling delphi procedure from C++

I have a DLL written in Delphi that I need to call from a C++ code. The signature of the procedure in Delphi is:
procedure GetDeviceName( No: integer;
Name: PChar;
Len: integer;
var Error: integer); stdcall;
From the code from the DLL I believed that I could call it with this block of code:
typedef void (__stdcall *GetDeviceNamePtr)(int, char*, int, int*);
GetDeviceNamePtr GetDeviceName = (GetDeviceNamePtr) GetProcAddress(m, "GetDeviceName");
char DeviceName[256];
int Error;
GetDeviceName(1, DeviceName, 256, &Error);
However, I've got an access violation. Should the last parameter of the procedure signature be a pointer to an integer or an integer ? I'm confused about the "var" keyword in the declaration of the procedure. I've checked the exported symbols of DLL and the procedure considered is properly exported. What's wrong in my procedure ?
Some more info regarding the environment:
It's a dll compiled with delphi 6.
ANSI only.
I'm debugging on the C++ side and the exception is right on the call to the function.
Nothing is returned from the function calling the DLL API.
From the fairly small amount of information you have provided, my initial thought is that you have declared the array DeviceName as a local variable so it's on the stack, and then overrun the stack giving you an AV when you try to return.
In modern versions of Delphi, Char is wchar_t and PChar is wchar_t* (so a Len of 256 might either mean 256 bytes or 128 characters).
var in the prototype indicates a parameter passed by reference, so I would expect int*.
Have you checked for variable sizes in the version of Delphi the DLL is written in and the calling application? If they think that Integer and int are different sizes (32bit vs. 64bit) your pointers will be garbage.
Are you able to step into the DLL in your debugger? If you can look at how the variables are being assigned when you enter the routine that should identify if you are passing what it expects.
And as a final thought - are you sure the Delphi DLL actually works? (I have been caught out by this before, and you could spend days on something that's not your problem!)

Calling c++ function from delphi

I wrote a DLL in C++ whose functions will be called from a Delphi application.
One of the functions in the DLL takes a Pointer to a buffer where an XML string should be written. But, when I write a string into the buffer, after returning from the function the application crashes with an "Access violation at address 0048B... in module ....exe. Write of address 3030D..." error.
The calling convention of the function declarations are the same, both in the DLL and the application.
I've made a simple application in Delphi to simulate the behavior of the application and it works fine. The biggest problem is that I don't have any information about the application internals: no sources, no documents, not even logs. Just function declarations and parameter descriptions.
Function declaration in delphi:
function functionName(var Buffer: Pointer; var BuffLen: Integer): Integer; stdcall;
Function Declaration in the DLL:
extern "C" int WINAPI functionName(char*, int*);
Does someone know how to solve this?
From my tests, I have a feeling that the problem is in the application, not in the DLL. However, I'm not completely sure about this. Are there any possible tests I can do at the DLL site to either solve the problem or locate the issue?
I'd really appreciate any help in this matter.
As a side note, the DLL is compiled with Visual Studio. Can this cause the problem?
The DLL function you showed is declared wrong in your Delphi code. var Buffer: Pointer is equivilent to void** in C, or void*& in C++, but certainly not to char* like the DLL function is expecting. Using a void**/void*& parameter would be useful if the DLL were allocating memory to return to the application, but from your description that is not the case.
Use this Delphi declaration instead:
function functionName(Buffer: PAnsiChar; var BuffLen: Integer): Integer; stdcall;
PAnsiChar in Delphi is equivalent to char* in C/C++.
You should read the following blog article about the gotchas to watch out for when converting C/C++ declarations to Delphi:
Rudy's Delphi Corner: Pitfalls of converting

Receive an array of string from a c++ DLL in Delphi 7

I am creating a DLL in C++, it would be used in a Delphi 7 project.
This question is related to this one, where I present two functions Validate and GetToken only that now they will be done in C++ and the array of strings GetToken produces would be sent back to Delphi.
The problems is I don't know how to create the function in the dll that will return the array of string in c++, and I don't know how it would be stored for further use in Delphi.
The declaration of the function is as follows:
function GetToken(Chain:string):Arrayofstring;
According to your code review, the Delphi code expects the function to have the following signature:
function GetToken(Chain: AnsiString): array of AnsiString;
You cannot write such a function in C++. C++ doesn't know what Delphi strings are, and it doesn't know what Delphi dynamic arrays are, either. Both types need to be allocated from Delphi's memory manager, which your C++ DLL won't have access to. Furthermore, C++ doesn't know how to use Delphi's register calling convention.
The DLL interface was designed poorly. DLLs should never use language-specific types unless it was the designer's intention to exclude all other languages. (And in this case, even later versions of the same language are excluded because the definition of AnsiString changed in Delphi 2009 to include more metadata that Delphi 7 won't handle properly.) The safest calling convention to choose is generally stdcall. It's what everything in the Windows API uses.
A better interface would use types that are common to all languages, and it would dictate the use of memory management that's accessible universally. There are several common ways to do that. For example:
The strings are returned as simple nul-terminated arrays of characters — PAnsiChar in Delphi; char* in C++. The DLL allocates buffers for the strings, and also allocates a buffer for the array of those strings. When the host application is finished using the array and the strings, it calls another function exported by the DLL wherein the DLL frees the memory it allocated. This is the model used by, for example, FormatMessage; when the host program is finished the with message string, it calls LocalFree.
type
PStringArray = ^TStringArray;
TStringArray = array[0..Pred(MaxInt) div SizeOf(PAnsiChar)] of PAnsiChar;
function GetToken(Char: PAnsiChar): PStringArray; stdcall;
procedure FreeStringArray(StringArray: PStringArray); stdcall;
char** __stdcall GetToken(char const* Chain);
void __stdcall FreeStringArray(char** StringArray);
Use COM to return a safearray of BStr objects. It's similar to the previous technique, but the memory management is defined by COM instead of by your DLL, so there's less stuff that needs to be defined by either party of the interface.
Pass a callback function to the DLL, so instead of returning an array of strings, the DLL just calls the function once for each string it identifies. Then you don't have to define what any array looks like, and the lifetime of each string can be just the lifetime of the callback call — if the host application wants a copy, it can do so. The new function signature would look something like this:
type
TTokenCallback = procedure(Token: PAnsiChar); stdcall;
procedure GetToken(Chain: PAnsiChar; ProcessToken: TTokenCallback); stdcall;
typedef void (__stdcall* TokenCallback)(char const* Token);
void __stdcall GetToken(char const* Chain, TokenCallback ProcessToken);
If you're not the one who designed the DLL interface, then you need to lean on the folks who did and get it changed to be more accessible to non-Delphi code. If you can't do that, then the final alternative is to write a DLL in Delphi that wraps your DLL to massage the parameters into something each side understands.

“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.
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.

Delphi PChar to C++ const char*

I am trying to use a C++ dll from a native program. I am following the virtual method scenario as explained here
Lets say my C++ function signature is of the form
int Setup(const char* szIp, const char* szPort);
And the corresponding delphi signature is
function Setup(ip, port: PChar):Integer: virtual; cdecl; abstract;
And somewhere from the delphi program i can call
pObj.Setup('192.168.1.100', '97777');
The control comes into the dll, but szIp and szPort formal parameters only receives the first character of the ip and port that I had passed from the delphi program.
I understand that it has to do with null terminating the string properly in delphi. So i had tried the following too.
var
pzIp, pzPort: PChar;
szIp, szPort: string;
begin
szIp := '192.168.1.2';
szPort := '9777';
//initilize memory for pchar vars
GetMem(pzIp, Length(szIp)+1);
GetMem(pzPort, Length(szPort)+1);
//null terminate the strings
pzIp[Length(szIp)+1] := #0;
pzPort[Length(szPort)+1] := #0;
//copy strings to pchar
StrPCopy(pzIp, szIp);
StrPCopy(pzPort, szPort);
end.
This a'int working either. When i Writeln pzIp and pzPort I get strange results.
Forgot to tell, all member functions from the C++ dll are compiled with __stdcall and exported properly
In Delphi 2010 (and Delphi 2009) the "char" type is actually a WIDEChar - that is, 16 bits wide. So when you call your C++ function, if that is expecting CHAR to be 8 bits wide (so called "ANSI", rather than UNICODE), then it is going to misinterpret the input parameter.
e.g. if you pass the string 'ABC'#0 (I'm showing the null terminator explicitly but this is just an implicit part of a string in Delphi and does not need to be added specifically) this passes a pointer to an 8 byte sequence, NOT 4 bytes!
But because the 3 characters in your string have only 8-bit code-point values (in Unicode terms, this means that what the C++ code "sees" is a string that looks like:
'A'#0'B'#0'C'#0#0#0
Which would explain why your C++ code seems only to be getting the first character of the string - it is seeing the #0 in the 2nd byte of that first character and assuming that it is the null terminator for the entire string.
You either need to modify your C++ code to correctly receive pointers to WideChar strings, OR modify the function signature in Delphi and convert your strings to ANSIString in the Delphi code before passing those to the C++ function:
Revised function signature:
function Setup(ip, port: PANSIChar):Integer: virtual; stdcall; abstract;
and the corresponding "Long hand" showing conversion of strings to ANSIString before calling the function - the compiler may take care of this for you but you might find it helpful to make it clear in your code rather than relying on "compiler magic":
var
sIPAddress: ANSIString;
sPort: ANSIString;
begin
sIPAddress := '192.168.1.100';
sPort := '97777';
pObj.Setup(sIPAddress, sPort);
// etc...
Is char the same size in both compilers? If you are using D2009/D2010, char is now 16-bits.
If I understand correctly your function prototype should be stdcall as well.
function Setup(ip, port: PChar):Integer: virtual; stdcall; abstract;
ps. Delphi strings are already null-terminated.