Communication between C++  program and Delphi DLL

Aboud Abdou
Aboud Abdou used Ask the Experts™
on
Hello;

I have a c++ program which launch a Delphi DLL where there are informations i need in C++.

So I need to return data from Delphi to C++.

I make some tries, and the last succefull try was to pass a c++ pointer to function and use the function in the delphi side;

Until Now, everything is OK;

the problem occurs when i try to send string from Delphi to C++; I receive non comprehensible string; ( chinese letters + arabic letters + mathmatical symbols ).

after several tries; I noticed that in Delphi, the problem occurs if there is a UnicodeString or String in the program even if it is not in the string sent to C++.

If I have only PWideChar in the program, that works; if there is String or UnicodeString, AnsiString ..., (even if it is not concatenated to PWideChar), the received string is unreadable.

I hope find a solution for this problem. I will put some code in commentaires.
Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®

Author

Commented:
Code C++
#include <iostream>
#include <thread>
#include <string>
#include <windows.h>
//-----------------------------------------------------------------
using namespace std;
//-----------------------------------------------------------------
#define NombreDeMainLoop 100
#define SLEEP_TIME 100
//-----------------------------------------------------------------
void CallFunc(wchar_t* str)
{
wstring ws(str);
string st(ws.begin(), ws.end());
cout << "I have been called " << st << endl;
}
//-----------------------------------------------------------------
void __stdcall call()
{
HINSTANCE hDllDeTest = LoadLibrary(L"DllDeTest.dll");
if (!hDllDeTest) {
std::cout << "could not load the dynamic library" << std::endl;
return;
}
typedef void(*Func) (wchar_t*);
typedef void (WINAPI* MyFunc)(Func);
MyFunc CallDelphi = (MyFunc)GetProcAddress(hDllDeTest, "call");
if (!CallFunc) {
std::cout << "could not locate the function" << std::endl;
return;
}
CallDelphi(&CallFunc);
}
//-----------------------------------------------------------------
int main()
{
thread T(call);
for (size_t i = 1; i <= NombreDeMainLoop; i++)
{
std::cout << "......................................." << endl;
std::this_thread::sleep_for(std::chrono::milliseconds(SLEEP_TIME));
}
T.join();
}

Author

Commented:
Code Delphi which works

 library DllDeTest;
uses
 Winapi.Windows;
{$R *.res}
Const
 MAX_LOOP = 100;
 SLEEP_TIME = 100;
type
 TFunc = procedure( infos: PWideChar);
procedure call( cppFunc : TFunc); stdcall;
var
 I: Integer;
 widechar: PWideChar;
begin
 for I := 1 to MAX_LOOP do
 begin
 widechar := PWideChar('ABCD');
 cppFunc( widechar );
 sleep(SLEEP_TIME);
 end;
end;
exports
 call;
begin
end.

Author

Commented:
Code Delphi which does not work
 library DllDeTest;
uses
 Winapi.Windows;
{$R *.res}
Const
 MAX_LOOP = 100;
 SLEEP_TIME = 100;
type
 TFunc = procedure( infos: PWideChar);
procedure call( cppFunc : TFunc); stdcall;
var
 I: Integer;
 widechar: PWideChar;
str:String;
begin
 for I := 1 to MAX_LOOP do
 begin
 
 str = IntToStr(I);
 widechar := PWideChar(str);
 cppFunc( widechar );
 sleep(SLEEP_TIME);
 end;
end;
exports
 call;
begin
end.
Learn Ruby Fundamentals

This course will introduce you to Ruby, as well as teach you about classes, methods, variables, data structures, loops, enumerable methods, and finishing touches.

Top Expert 2016

Commented:
widechar := PWideChar(str);

this statement is wrong because str is an AnsiString and holds an array of ansi characters. if you cast this string to a PWideChar (pointer to wide characters), it is not a conversion but each pair of single-byte characters in the ansi string would be looked on as a wide character, what is not you wanted. moreover a wide char string must be terminated by a double-zero wide character what is only the case by accident if you run your code.

to come out of this you should use StringToWideChar function like that:

var
  wideChars   : array[0..11] of WideChar;
  myString    : String;

begin
  // Set up our string
  myString := 'Hello World';

  // Copy to a WideChar format in our array
  StringToWideChar(myString, wideChars, 12);

Open in new window


the wideChars array savely could be passed to the cpp callback function. be aware that your wide char array must be big enough to take the terminating double-zero  char. it would be best to create a really big array which could take all possible return strings.

Sara
Kent OlsenData Warehouse / Database Architect

Commented:
Hi Abdou,

What C++ compiler are you using?  If it's Borland, things should work very cleanly as much of the C++ library, particularly the forms management, is written in Delphi.

Author

Commented:
Hi Kent; No It is NOT Borland; It Is MSVC

Author

Commented:
Hi Sara; Thanks a lot of your time; i will test this solution and i hope that will work;
I have a question related to your solution;

You fixed  the length of the WideChar array to 12; what if i don't know previously the length of my string

thanks in advance
Top Expert 2016
Commented:
if you know the maximum possible use an array which is twice as big as the maximum. it i stack memory and really doesn't matter .

var
  wideChars   : array[0..4095] of WideChar;  // 8k buffer should be enough
  myString    : String;
  len: Integer;

begin
  // Set up our string
  myString := 'Hello World';
  len:= Length(MyString)

  // Copy to a WideChar format in our array
  if  len < 4096 then
      StringToWideChar(myString, wideChars, len);
  end;

Open in new window


another way is to provide the buffer from c++ by passing a wchar_t pointer  (== PWideChar) and the size of the buffer as arguments. then you would not need a callback function at all but simply copy to the passed buffer if length is not too much. the delphi function then would return the copied length to Result what is the return value.

procedure call(wideChars : PWideChar, sizWideChars : Integer) : Integer;
begin
  // Set up our string
  myString := 'Hello World';
  len:= Length(MyString);

  // Copy to a WideChar format in our array
  if  len < 4096 then
      StringToWideChar(myString, wideChars, len);
  end;
  Result := len;
end;
 [/code]

at the c++ side it would be like
typedef int (WINAPI* MyFunc)(wchar_t *, int);
MyFunc CallDelphi = (MyFunc)GetProcAddress(hDllDeTest, "call");
if (!CallFunc) {
std::cout << "could not locate the function" << std::endl;
return;
}
wchar_t wideChars[4096] = { 0 };  // all characters are zero
int len = CallDelphi(wideChars, 4096);
....

Open in new window


Sara

Author

Commented:
Thanks Sara; Yes I already thought about the second solution but i find it not clean enough;

Your first solution look clean but I have always the probleme in the side of C++; I receive always non comprehensible string;

the signature of the function in C++ side is one of the two next signature:

void      CallFunc(wchar_t* str);
void      CallFunc(wchar_t wchars[]);


Any Hint!

Author

Commented:
The returned value in C++ Side
Err1.png
Top Expert 2016
Commented:
it looks as if the pointer passed from delphi to c++ is an address of a different address room. hence, the results in c++ are some garbage as they point to some different memory space. or, the address room is ok but because the delphi function already has been terminated the memory now contains garbage.



about the second solution but i find it not clean enough
it is much cleaner to provide a buffer from sender as to get it back by callback. the best advantage is that the call can be made synchronously and not asynchronously by means of threading and polling. if using a callback you have to make sure that your main thread was available when being called. but in your code the main thread is sleeping. hence the call can be performed only after sleep and its much likely that the delphi function already has returned and local memory is garbage.

i would recommend to check wether the second solution works. if so, the pointers passed from c++ successfully can be used in delphi function to copy data. if not, one way out is to pass a handle to global memory from c++ . this handles allows to get a valid pointer to global memory by calling GlobalLock. this works both in delphi and c++. you easily will find code samples.

c++: 
const int OK = 1;
HGLOBAL h = GlobalAlloc(GMEM_FIXED, 4096);
// pass the h as unsigned integer to delphi
int result = CallDelphi(h);
if (result == OK)
{
     wchar_t  * pwc = (wchar_t *)GlobalLock(h);
     if (pwc)
     {
          std::wsstring wstr;
          std::wcout << wstr << L"\n";
     }
}   
GlobalUnlock(h);
GlobalFree(h);

Open in new window



Sara

Author

Commented:
Thank you, for your help.

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial