我之前问过一个关于 delphi 和 C/C++ DLL 的问题。
我现在有另一个关于记录/结构的问题。DLL 应该能够从 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;
}
由于某些原因,它崩溃等。我做错了什么?
感谢您的帮助。
这可能不是C++代码分配给TheWideString
指针时崩溃的原因,但我确实看到了期望的问题......
我注意到您将 Delphi AWideString
变量指向的字符串数据的地址放入记录的MyWideString
字段中。 将记录传递给 C++ 函数,该函数将新的指针值分配给记录的TheWideString/MyWideString
字段。当执行返回到 Delphi 代码时,您将输出 AWideString
变量的内容。
您的注释表明您希望 AWideString 变量的内容由 C++ 函数更改,但事实并非如此。
C++函数更改结构中的字段。它对字段先前指向的内存位置没有任何作用。 AWideString 指向的数据不会受到 C++ 函数的影响。
如果C++代码将数据复制到字段中包含的地址中,则它将覆盖AWideString
指向的字符串数据。由于AWideString
是 Delphi 托管字符串,并且 C++ 函数将向该字符串内存区域复制的数据多于原始字符串为其分配的空间,因此在 C++ 函数中复制数据将写入 Delphi 分配的字符串缓冲区的末尾,并可能损坏 Delphi 堆。一段时间后可能会发生崩溃。因此,您只将指针分配给字段而不复制数据是一件好事!;>
要查看 C++ 函数更改了哪些内容,Delphi 代码应在调用 C++ 函数后输出记录的MyWideString
字段的内容。
您对字符串字段的管理不善。 PWideChar
和 PChar
与">指向 WideString 的指针"和"指向 AnsiString 的指针"不是一回事。 德尔福有PWideString
(WideString*
(和PAnsiString
(AnsiString*
(类型。 你也应该使用德尔福的PInteger
(int*
(和PBoolean
(bool*
(类型而不是Pointer
(void*
(。 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++中的对齐方式。