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

时间:2012-07-31 03:14:21

标签: c++ c delphi memory-management dll

我之前问了一个关于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;
}

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

感谢您的帮助。

3 个答案:

答案 0 :(得分:2)

这可能不是导致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字段的内容。

答案 1 :(得分:1)

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

答案 2 :(得分:1)

您对字符串字段管理不善。 PWideCharPChar 不是与“指向WideString的指针”和“指向AnsiString的指针”相同的东西。 Delphi为此目的有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是用不同版本的Delphi / C编写的话++ Builder由于RTL差异,内存管理器差异,有效负载布局差异等等,WideString可以传递,因为它的内存和布局由操作系统控制,而不是RTL。