Delphi PChar to C++ const char* - c++

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.

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!)

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.

C++ dll function calling in delphi7

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.

What's is the VB6 equivalent of a C++ char array?

I'm trying to call a DLL written in C++ from a VB6 application.
Here's the C++ example code for calling the DLL.
char firmware[32];
int maxUnits = InitPowerDevice(firmware);
However, when I try to call it from VB6 I get the error bad DLL calling convention.
Public Declare Function InitPowerDevice Lib "PwrDeviceDll.dll" (ByRef firmware() As Byte) As Long
Dim firmware(32) As Byte
InitPowerDevice(firmware)
Edit: The C++ Prototype:
Name: InitPowerDevice
Parameters: firmware: returns firmware version in ?.? format in a character string (major revision and minor revision)
Return: >0 if successful. Returns number of Power devices connected
CLASS_DECLSPEC int InitPowerDevice(char firmware[]);
Been a long time, but I think you also need to change your C function to be stdcall.
// In the C code when compiling to build the dll
CLASS_DECLSPEC int __stdcall InitPowerDevice(char firmware[]);
' VB Declaration
Public Declare Function InitPowerDevice Lib "PwrDeviceDll.dll" _
(ByVal firmware As String) As Long
' VB Call
Dim fmware as String
Dim r as Long
fmware = Space(32)
r = InitPowerDevice(fmware)
I don't think VB6 supports calling cdecl functions in any normal way - there may be hacks for doing it. May be you can write a wrapper dll which wraps the cdecl function with a stdcall function and just forwards the call.
These are some hacks - but I haven't tried it.
http://planet-source-code.com/vb/scripts/ShowCode.asp?txtCodeId=49776&lngWId=1
http://planet-source-code.com/vb/scripts/ShowCode.asp?txtCodeId=62014&lngWId=1
You need to pass a pointer to the beginning of the array contents, not a pointer to the SAFEARRAY.
Perhaps what you need is either:
Public Declare Function InitPowerDevice Lib "PwrDeviceDll.dll" ( _
ByRef firmware As Byte) As Long
Dim firmware(31) As Byte
InitPowerDevice firmware(0)
or
Public Declare Function InitPowerDevice CDecl Lib "PwrDeviceDll.dll" ( _
ByRef firmware As Byte) As Long
Dim firmware(31) As Byte
InitPowerDevice firmware(0)
The CDecl keyword only works in a VB6 program compiled to native code. It never works in the IDE or in p-code EXEs.
Since your error is "bad calling convention", you should try changing the calling convention. C code use __cdecl by default, and IIRC VB6 had a Cdecl keyword you could use with Declare Function.
Otherwise you can change the C code to use __stdcall, or create a type library (.tlb) with the type information and calling convention. That might be better than Declare Function because you use the C datatypes when defining the type library, but VB6 recognizes them just fine.
As far as the argument type is concerned, firmware() As Byte with ByVal (not ByRef) should be fine.

Pass BSTR from C++ DLL function to VB6 application

I have this code in my VB6 app:
Private Declare Function FileGetParentFolder Lib "Z-FileIO.dll" _
(ByVal path As String) As String
Output.AddItem FileGetParentFolder(FileText.Text)
Output is a list, FileText is a text field containing a file path. My C++ DLL contains this function:
extern "C" BSTR ZFILEIO_API FileGetParentFolder(Path p)
{
try {
return SysAllocString(boost::filesystem::path(p).parent_path().c_str());
} catch (...) {
return SysAllocString(L"");
}
}
where Path is typedef'd as LPCSTR. The argument comes into my DLL perfectly, but whatever I try to pass back, the VB6 app shows only garbage. I tried several different methods with SysAllocStringByteLength, casting the SysAllocString argument to LPCWSTR and other variants. Either, I only see the first letter of the string, or I see only Y's with dots, just not the real string. Does anyone know what the real method is for creating and passing valid BSTRs from C++ to VB6?
Hopefully this will point you in the right direction. From memory...
VB6 uses COM BSTRs (2-byte wide character strings) internally, but when communicating with external DLLs it uses single- or multi-byte strings. (Probably UTF-8, but I don't remember for sure.) Your Path typedef to LPCSTR is an ANSI string, and that's why you can receive it correctly. The return value you generate is a wide-character string, but VB is expecting an ANSI string. You'll need to use WideCharToMultiByte to convert your return value before returning it.
Seems a little odd that VB does this implicit conversion, but that's the way it is. (As far as I remember.)
If you insist on using the function signature then you have to prepare a custom typelib for VB6 that includes this
[dllname("Z-FileIO.dll")]
module ZFileIO
{
[entry("FileGetParentFolder")]
BSTR FileGetParentFolder ([in] LPWSTR path);
};
In Declares param-types As String are automagically converted to ANSI string, i.e. LPSTR. The only way to pass/receive a unicode string (LPWSTR or BSTR) is by using typelib API function declaration.
Other than that you can always use As Long params in the declaration and expect LPWSTRs but then the consumer will have to wrap strings in StrPtr on every call to the API function.