Delphi和C / C ++ DLL Struct vs.Record

我之前问了一个关于delphi和C / C ++ DLL的问题。

我现在有另一个关于记录/结构的问题。 DLL应该能够动态地从MainAPP更改指针变量的VALUE。

我的delphi MAINAPP有以下记录:

type MyRec = record MyInteger : Pointer; MyWideString : pwidechar; MyString : pchar; MyBool : Pointer end; type TMyFunc = function ( p : pointer ): pointer; stdcall; procedure test; var MyFunction : TMyFunc; TheRecord : MyRec; AnInteger : Integer; AWideString : WideString; AString : String; ABool : Bool; begin AnInteger := 1234; AWideString := 'hello'; AString := 'hello2'; ABool := TRUE; TheRecord.MyInteger := @AnInteger; TheRecord.MyWideString := pwidechar(AWideString); TheRecord.AString := pchar(AString); TheRecord.ABool := @ABool; [...] @MyFunction := GetProcAddress... [...] MyFunction (@TheRecord); // now the DLL should be able to change the values dynamically. MessageBoxW (0, pwidechar(AWideString), '', 0); // Show the results how the DLL changed the String to... end; 

C / C ++代码(仅举例)

 typedef struct _TestStruct{ void *TheInteger; // Pointer to Integer wchar_t *TheWideString; // Pointer to WideString char *TheAnsiString; // Pointer to AnsiString bool *TheBool // Pointer to Bool }TestStruct; __declspec(dllexport) PVOID __stdcall MyExportedFunc (TestStruct *PTestStruct) { MessageBoxW(0 ,PTestStruct->TheWideString, L"Debug" , 0); // We read the value. PTestStruct->TheWideString = L"Let me change the value here."; return 0; } 

由于某些原因它崩溃等。我做错了什么?

感谢帮助。

您正在管理字符串字段。 PWideCharPChar与“指向WideString”和“指向AnsiString”的指针不同。 Delphi PWideString提供了PWideStringWideString* )和PAnsiStringAnsiString* )类型。 您还应该使用Delphi的PIntegerint* )和PBooleanbool* )类型而不是Pointervoid* )。 Delphi和C ++都是类型安全的语言。 尽可能远离无类型指针,您的代码将更好。

 type PMyRec = ^MyRec; MyRec = record MyInteger   : PInteger; MyWideString : PWideString; MyAnsiString : PAnsiString; MyBool    : PBoolean; end; TMyFunc = function ( p  : PMyRec ): Integer; stdcall; procedure test; var MyFunction  : TMyFunc; TheRecord  : MyRec; AnInteger  : Integer; AWideString : WideString; AAnsiString : AnsiString; ABool    : Bool; begin AnInteger         := 1234; AWideString        := 'hello'; AAnsiString        := 'hello2'; ABool           := TRUE; TheRecord.MyInteger    := @AnInteger; TheRecord.MyWideString  := @AWideString; TheRecord.MyAnsiString   := @AAnsiString; TheRecord.MyBool     := @ABool; [...] @MyFunction := GetProcAddress... [...] MyFunction  (@TheRecord); MessageBoxW (0, PWideChar(AWideString), '', 0); end; 

 typedef struct _MyRec { int   *MyInteger;   // Pointer to Integer WideString *MyWideString; // Pointer to WideString AnsiString *MyAnsiString; // Pointer to AnsiString bool   *MyBool;    // Pointer to Bool } MyRec, *PMyRec; __declspec(dllexport) int __stdcall MyExportedFunc (PMyRec PRec) { MessageBoxW(NULL, PRec->MyWideString->c_bstr(), L"Debug" , 0);  *(PRec->MyWideString) = L"Let me change the value here."; return 0; } 

话虽如此,在这样的DLL边界上操作AnsiString (和UnicodeString )值是非常危险的,特别是如果EXE和DLL由于RTL差异而在Delphi / C ++ Builder的不同版本中编写,内存管理器的差异但是,有效载荷布局的差异等等, WideString可以传递,因为它的内存和布局是由操作系统控制的,而不是RTL。

这可能不是C ++代码分配给TheWideString指针时崩溃的原因,但我确实看到了期望的问题……

我注意到您将Delphi AWideString变量指向的字符串数据的地址放入记录的MyWideString字段中。 您将记录传递给C ++函数,该函数将新指针值分配给记录的TheWideString/MyWideString字段。 当执行返回到Delphi代码时,输​​出AWideString变量的内容。

您的注释表明您希望通过C ++函数更改AWideString变量的内容,但这不会发生。

C ++函数更改结构中的字段。 它对该字段先前指向的内存位置没有任何作用。 AWideString指向的数据不受C ++函数的影响。

如果C ++代码将数据复制到字段中包含的地址中,那么它将覆盖AWideString指向的字符串数据。 由于AWideString是一个Delphi托管字符串,并且C ++函数将更多数据复制到该字符串内存区域而不是原始字符串分配空间,因此在C ++函数中复制数据将写入Delphi分配的字符串缓冲区的末尾并且可能腐蚀Delphi堆。 一段时间后可能会发生崩溃。 所以你只是将指针分配给字段而不是复制数据是一件好事! ;>

要查看C ++函数的更改,Delphi代码应在调用C ++函数后输出记录的MyWideString字段的内容。

同步结构中的字段顺序。 您可以使用错误的指针来破坏内存堆。 另外,检查Delphi和C ++中的对齐方式。