【问题标题】:Getting trash data in char* while using it as buffer in function在 char* 中获取垃圾数据,同时将其用作函数中的缓冲区
【发布时间】:2013-08-04 19:26:06
【问题描述】:

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

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

下面的代码解释了一切。我已经放了 3 个函数来表达我的意思。

缓冲区有问题的示例函数是: DLL_PingConnection(var avXml:PChar):Boolean; - 它返回真/假,作为参数它需要缓冲区并且函数在缓冲区中完成应该有有效的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,但正如 cmets 中所建议的,我使用了替代编译器: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 ** 会导致类型与缓冲区不匹配。

EDIT2:

按照 David Heffernan 的要求,我附上了文档样本。重要部分被翻译成英文。 Rest 只是 PIngConnection 应该返回的 xlm 结构。那里没有太多帮助 - 整个文档都是这样的。

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

编辑 3:

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

Delphi 版本是 7。

确定调用类型是stdcall。 ( DLL_PingConnection: function(var avXml: PChar): Boolean; 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 是否返回分配的缓冲区?通常,写入调用方提供的缓冲区的函数也采用缓冲区大小参数。
  • Eric Brown:没有尺寸参数。只有 1 个参数:var avXml: PChar 和 reslt: boolean。函数返回 true,但在缓冲区内我只得到没有意义的东西。
  • simonc:你能告诉我你需要什么以及如何去做吗?抱歉,我是 C++ 新手。
  • 你有文件吗?!我们也看不出来吗?如果我们不能这样做是不公平的。当然这是一个骗局。谁在乎是否有任何 wxWidgets?问题出在界面边界上,并且是相同的。我敦促您在这里删除大量代码并显示一个最小的示例。并显示界面另一侧的文档。

标签: c++ arrays delphi dll char


【解决方案1】:

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* 很可能会被使用。

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

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

(如果需要,char*&amp; 可以替换为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 导出使用更多可移植签名的包装函数。

【讨论】:

  • __fastcall 与注册不同。还有一个 PChar 通过引用传递的问题。那是什么意思?是否也应该有一个释放功能?还是这个函数设计得不好,它仍然是调用者分配缓冲区。
  • 这是非常可靠的信息!谢谢。我很确定 dll 使用的是旧版本的 delphi,但我会与提供它的人确认这些信息(他们可能需要一些时间才能回复)。您可以举例说明如何在调用函数时使用 char*& 吗?我试过了,但我不能让它编译(使用 &)当我编译它时,我会尝试你描述的所有可能性。
  • 信息不是那么可靠。如果您的函数确实在 Delphi 代码中以这种方式声明,那么您不能从 C++ 编译器调用它,除非您碰巧使用 Emba 编译器。或者是64位!那是因为您无法匹配 Delphi register 调用约定。当 Remy 说 __fastcallregister 相同时,他完全错了。至于传递char*&amp;,首先要搞清楚语义。您需要知道谁负责分配缓冲区。是你的代码还是 DLL 这样做的?
  • 我所说的 __fastcall 适用于 C++Builder 的 __fastcall,而不是 Microsoft 的 __fastcall。 C++Builder 的__fastcall专门设计的以兼容Delphi 的register 调用约定。否则,C++Builder 代码根本无法与 Delphi RTL/VCL 库交互,因为 Delphi RTL/VCL 中的几乎所有内容都使用 register 调用约定(这就是为什么 C++Builder 编译器有一个单独的 @ 987654360@ Microsoft 兼容性的调用约定)。
  • 如果 OP 使用 C++ 编译器而不是 C++Builder,那么他将无法使用 Delphi 编写的 DLL,如果实际上使用的是 Borland 特定的 @987654361 @ 调用约定,而不是更便携的 cdeclstdcall 调用约定。
猜你喜欢
  • 2021-06-05
  • 1970-01-01
  • 2010-10-11
  • 2013-07-30
  • 1970-01-01
  • 2016-08-17
  • 2014-03-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多