0

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

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

我的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;
}

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

感谢帮助。

4

3 回答 3

2

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

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

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

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

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

要查看 C++ 函数发生了什么变化,您的 Delphi 代码应该MyWideString在调用 C++ 函数后输出记录字段的内容。

于 2012-07-31T04:31:39.927 回答
1

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

于 2012-07-31T03:59:06.210 回答
1

您对字符串字段的管理不善。 PWideChar并且PChar “指向 WideString 的指针”和“指向 AnsiString 的指针”不同。Delphi 有PWideString( WideString*) 和PAnsiString( AnsiString*) 类型用于此目的。您还应该使用 Delphi 的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;
}

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

于 2012-07-31T04:35:32.843 回答