Getting trash data in char* while using it as buff

2019-09-02 04:58发布

问题:

I'm loading a delphi dll in c++. When I use functions with char* as buffers (char* given as parameter to the procedure) I get only trash data. When I have functions that return char* all is fine.

I'm new to c++ and I spend a lot of time trying to crack this. Please help.

Everything is explained in code below. I have put there 3 functions to show exacly what I mean.

Example function that has problem with buffer is: DLL_PingConnection(var avXml:PChar):Boolean; - it returns true/false, as parameter it takes buffer and the function is done in buffer there should be valid xml (but there is only trash)

#include <windows.h> //this will load delphi dll
#include <iostream>
#include <fstream>
#include <stdlib.h>
#include <string.h>

using namespace std;

// ------------------------------------------------ pointers on functions inside Delphi DLL (32 bits)
typedef bool(*TYPE_DLL_SetLicense)(char*, char*); //initialize dll stuff - I load licence from a file into char* - everything works fine
typedef bool(*TYPE_DLL_PingConnection)(char*); //the char* is buffer - I give empty char* as parameter and I should get correct xml with serwer data - I GET ONLY TRASH :(
typedef char*(*TYPE_DLL_ERR_DESCRIPTION)(void); //this function does not use buffer it returns char* - everything works fine

//so as you see problem is with buffers and function like this: DLL_PingConnection(buffer)

int main()
{

// ------------------------------------------------ Loading the library  
    HINSTANCE hGetProcIDDLL = LoadLibrary("C:\\full_path\\SOMEDLL.dll");

    //checking the library
    if (hGetProcIDDLL == NULL) {std::cout << "Could NOT load the dynamic library" << std::endl;return EXIT_FAILURE;}
    else{std::cout << "dynamic library loaded" << std::endl;}

// ------------------------------------------------ START: resolving functions adresses

    TYPE_DLL_SetLicense DLL_SetLicense = (TYPE_DLL_SetLicense)GetProcAddress(hGetProcIDDLL, "DLL_SetLicense");
    if (!DLL_SetLicense) {std::cout << "Could NOT locate the function: DLL_SetLicense" << std::endl;return EXIT_FAILURE;}
    else{std::cout << "Function DLL_SetLicense located" << std::endl;}

    TYPE_DLL_PingConnection DLL_PingConnection = (TYPE_DLL_PingConnection)GetProcAddress(hGetProcIDDLL, "DLL_PingConnection");
    if (!DLL_PingConnection) {std::cout << "Could NOT locate the function: DLL_PingConnection" << std::endl;return EXIT_FAILURE;}
    else{std::cout << "Function DLL_PingConnection located" << std::endl;}

    TYPE_DLL_ERR_DESCRIPTION DLL_ERR_DESCRIPTION = (TYPE_DLL_ERR_DESCRIPTION)GetProcAddress(hGetProcIDDLL, "DLL_ERR_DESCRIPTION");
    if (!DLL_ERR_DESCRIPTION) {std::cout << "Could NOT locate the function: DLL_ERR_DESCRIPTION" << std::endl;return EXIT_FAILURE;}
    else{std::cout << "Function DLL_ERR_DESCRIPTION located" << std::endl;}        


std::cout << "\n\nInitialization over. \n\n" << std::endl;  


// ------------------------------------------------ START: calling functions from delphi dll       

//DLL_SetLicence - this function take buffer as parameter, but dont return anything into the buffer. All works fine.

    //start - we read licence from file
    char buffer_licence[1242];
    memset(buffer_licence,0,sizeof(buffer_licence));

//I read content of buffer_licence usinf ifstream from the file here (but I don't put the code, to keep sample minimal)

    //we set licence with dll function
    bool is_licence = DLL_SetLicense(buffer_licence,(char*)"");

    //the output
    if (is_licence == TRUE)
      std::cout << "Licence has been set\n";
    else
      std::cout << "Licence has been NOT set\n";




//DLL_PingConnection - it takes empty buffer as parameter, it should save xml into buffer but it saves only trash.

    //we try to save ping to the file - buffer
    char buffor_ping_xml[2000];
    memset(buffor_ping_xml,0,sizeof(buffor_ping_xml));

    //this should gieve proper xml, but it returns only trash.... please help
    bool is_ping = DLL_PingConnection(buffor_ping_xml);

    if(is_ping)
    {

        std::cout << "DLL_PingConnection True\n"; //function returned true, so it worked correct.

        std::cout << buffor_ping_xml; //but in the buffer is trash that I show on the screen. I also tried to put buffor_ping_xml info the file (diferent ways) but always result was trash just like on screen.

    }
    else
    {
        std::cout << "DLL_PingConnection False: \n";
    }           



//DLL_ERR_DESCRIPTION - if will automaticly return error description if there is any error to report. No buffer, no problems.

        std::cout << buffor_ping_xml; //the data on screet is fine, so is in file and everywhere else.



    return EXIT_SUCCESS;
}

PingConnection function will return only this instead of good xml.

EDIT:

Oroginally I used Netbeans + MinGW, but as suggested in comments I have used alternative compilers: Borland builder c++ 6.0, and Embarcadero RAD Studio XE3 (C++ Builder). The problems stayed the same even thou I used all calling convention types Remy Lebeau mentioned.

typedef bool(*TYPE_DLL_PingConnection)(char*); //standard calling convention default for compiler - returns trash
typedef bool(__cdecl *TYPE_DLL_PingConnection)(char*); //returns trash also
typedef bool(__stdcall *TYPE_DLL_PingConnection)(char*); //doesnt write anything to the buffer
typedef bool(__fastcall *TYPE_DLL_PingConnection)(char*); //returns trash

I have encountered small problem under c++ builder. I can't clean buffer under this enviroment:

memset(buffer,0,sizeof(buffer)); // will crash the program under c++ builder

Trying to use 'char *&' will crash the program also.

typedef bool(__cdecl *TYPE_DLL_PingConnection)(char*&);
OR
typedef bool(__stdcall *TYPE_DLL_PingConnection)(char*&);
OR
typedef bool(__fastcall *TYPE_DLL_PingConnection)(char*&);

char * buffer;
bool is_ping = DLL_PingConnection(buffer);

Using char ** will cause type mismatch with buffer.

EDIT2:

As requested by David Heffernan I attach sample of documentation. Important parts are trasnated to english. Rest is just structure of xlm that PIngConnection should return. Not much of help there - entire documentation is like this.

PS: I asked similar question here: Trash characters when using buffers in c++ - code based on WxWidgets (I though WxWidgets creates the problem, but it doesn't. Maybe someone will find WxWidgets code usefull thou).

EDIT 3:

I managed to get some more information about dll.

Delphi version is 7.

For sure calling type is stdcall. ( DLL_PingConnection: function(var avXml: PChar): Boolean; stdcall; )

This is how a function from this dll is called in delphi:

lPointer := nil;  //pointer
lOSOZPointer := nil; //pointer
lpXML := nil; //pChar

lpXML:=StringToPChar(lXML);
lPointer := lpXML;

lWynik:=OSOZ_GetServerDataTime(lpXML);
if lWynik then
begin
  lOSOZPointer := lpXML;
  //akcja na wyniku
end;

if lPointer <> nil then begin
  Freemem(lPointer);
end;
if lOSOZPointer <> nil then begin
  OSOZ_FreeMem(lOSOZPointer);
end;

回答1:

DLL_PingConnection(var avXml:PChar):Boolean;

This is not a full declaration. Obviously, it is a function since it has a Boolean return type. But does it also declare a calling convention as well - stdcall (__stdcall in C/C++) or cdecl (__cdecl in C/C++)? If not, then it is using Delphi's default register convention instead (which is __fastcall in Borland/CodeGear/Embarcadero C++ compilers only, but has no equivalent in any other C/C++ compiler). Your existing typedefs are using your C++ compiler's default calling convention, which is usually __cdecl. Calling convention mismatches are the most common problem with using DLLs, as it causes mismanagement of the call stack, which affects how parameters are passed, accessed, and cleaned up.

Also, what version of Delphi was the DLL written in? PChar is PAnsiChar (char* in C++) in Delphi 2007, but is PWideChar (wchar_t* in C++) in Delphi 2009 and later. Chances are, since the data is XML, then PAnsiChar/char* is likely being used.

Also, the PChar parameter is being passed as a var in the Delphi declaration, which is the same as a pointer in C and a reference in C++.

You need these important pieces of information in order to use this DLL function in C/C++ code. Unless the documentation explictly states these details, or the DLL has a C/C++ .h/.hpp file showing the actual declaration, then the best you can do is guess, and there are several variations possible given the incomplete declaration you have shown so far:

(char*& can be replaced with char** if needed):

typedef bool (__cdecl *TYPE_DLL_PingConnection)(char*&);

typedef bool (__stdcall *TYPE_DLL_PingConnection)(char*&);

typedef bool (__fastcall *TYPE_DLL_PingConnection)(char*&);

typedef bool (__cdecl *TYPE_DLL_PingConnection)(wchar_t*&);

typedef bool (__stdcall *TYPE_DLL_PingConnection)(wchar_t*&);

typedef bool (__fastcall *TYPE_DLL_PingConnection)(wchar_t*&);

If the DLL functions are using cdecl or stdcall, then you are OK, as most C/C++ compilers support those calling conventions. However, if the DLL functions are using register instead, and if you are not using a Borland/CodeGear/Embarcadero C++ compiler, then you are SOL. You would have to wrap the DLL inside another Delphi-written DLL that exports wrapper functions that use more portable signatures.