tags:

views:

497

answers:

3

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

+3  A: 

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.

utku_karatas
hmm, it didn't worked. The same result. But I could get rid of an access violation crash when the C++ function returned. So, i guess i am making progress :)
rptony
time to drop to assembly view :)
utku_karatas
making it stdcall had me resolved few other issues too, +1 for the tip
rptony
+4  A: 

Is char the same size in both compilers? If you are using D2009/D2010, char is now 16-bits.

IanH
yeah i am using D2010. And my C++ code is 32 bit. So the character size should match right ?
rptony
Not necessarily: BCB4 is a 32-bit C++ compiler, but a char is a byte. You could be null-terminating your string prematurely with the 0 high-byte for the first character.Try changing your test code above to declare szIP and szPort as AnsiString instead of string.
IanH
up vote for leading into the answer
rptony
+13  A: 

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...
Deltics
Thanks for the more detailed explanation: my answer was a quick thought which proved right, but I recommend this as the answer to be accepted.
IanH
Thanks @Deltics, that worked. A small change though, needed to be cast to make the delphi program compilepObj.Setup(PAnsiChar(sIPAddress), PAnsiChar(sPort));Special thanks to @IanH too
rptony