我正在C ++中加载delphi dll。当我使用带有char *作为缓冲区的函数(char *作为过程的参数给定)时,我只会得到垃圾数据。
当我有返回char *的函数时,一切都很好。

我是C ++的新手,我花了很多时间试图破解它。请帮忙。

一切在下面的代码中说明。我在那里放置了3个函数,以清楚地说明我的意思。

缓冲区有问题的示例函数是:
DLL_PingConnection(var avXml:PChar):Boolean; -返回true / false,因为参数需要缓冲区,并且函数在缓冲区中完成,所以应该有有效的xml(但只有垃圾桶)

#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函数将仅返回此值,而不返回良好的xml。



编辑:

最初,我使用了Netbeans + MinGW,但正如注释中所建议的那样,我使用了其他编译​​器:Borland Builder c ++ 6.0和Embarcadero RAD Studio XE3(C ++ Builder)。即使您使用了Remy Lebeau提到的所有调用约定类型,问题仍然保持不变。

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


我在c ++ builder下遇到了小问题。在这种环境下,我无法清理缓冲区:

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


尝试使用'char *&'也会使程序崩溃。

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


使用char **将导致类型与缓冲区不匹配。

编辑2:

根据David Heffernan的要求,我附上文档样本。重要部分翻译成英文。其余只是PIngConnection应该返回的xlm结构。那里的帮助不多-整个文档都是这样的。



PS:我在这里问过类似的问题:Trash characters when using buffers in c++-基于WxWidgets的代码(虽然WxWidgets会引起问题,但事实并非如此。也许有人会觉得WxWidgets代码很有用)。

编辑3:

我设法获得有关dll的更多信息。

Delphi版本是7。

确保调用类型为stdcall。 (DLL_PingConnection:函数(var avXml:PChar):布尔值; stdcall;)

这是在delphi中从此dll调用函数的方式:

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;

最佳答案

DLL_PingConnection(var avXml:PChar):Boolean;


这不是完整的声明。显然,它是一个function,因为它具有Boolean返回类型。但是它是否也声明了调用约定-stdcall(在C / C ++中为__stdcall)还是cdecl(在C / C ++中为__cdecl)?如果不是,则它使用的是Delphi的默认register约定(仅在Borland / CodeGear / Embarcadero C ++编译器中为__fastcall,但在任何其他C / C ++编译器中均没有)。现有的typedef使用的是C ++编译器的默认调用约定,通常为__cdecl。调用约定不匹配是使用DLL时最常见的问题,因为它导致调用堆栈的管理不当,从而影响参数的传递,访问和清除方式。

另外,DLL写入哪个版本的Delphi? PChar在Delphi 2007中为PAnsiChar(在C ++中为char*),但在Delphi 2009及更高版本中为PWideChar(在C ++中为wchar_t*)。由于数据是XML,因此可能会使用PAnsiChar / char*

同样,在Delphi声明中将PChar参数作为var传递,这与C中的指针和C ++中的引用相同。

您需要这些重要的信息,才能在C / C ++代码中使用此DLL函数。除非文档中明确说明了这些详细信息,否则DLL有一个显示实际声明的C / C ++ .h / .hpp文件,那么您最好的办法就是猜测,鉴于显示的不完整声明,可能会有多种变化。远:

(如果需要,可以用char*&替换char**):

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*&);


如果DLL函数使用的是cdeclstdcall,则可以,因为大多数C / C ++编译器都支持这些调用约定。但是,如果DLL函数改用register,并且没有使用Borland / CodeGear / Embarcadero C ++编译器,那么您就是SOL。您必须将DLL包装在另一个由Delphi编写的DLL中,该DLL导出使用更多可移植签名的包装函数。

07-24 09:47
查看更多