Delphi和C / C ++ DLL结构vs.Record(Delphi and C/C++ DL

2019-07-30 16:37发布

我以前问一个关于一个Delphi和C / C ++ DLL问题。

我现在已经大约记录/结构的另一个问题。 该DLL应能够动态地改变从MainAPP指针瓦尔的价值。

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

对于崩溃等等。我该怎么办错了某些原因?

感谢帮助。

Answer 1:

你是管理不善的字符串字段。 PWideCharPChar 是不一样的东西“指针WideString的”和“指针AnsiString类型”。 德尔福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;
}

随着中说,它可以是非常危险的操作AnsiString (和UnicodeString )值跨越这样一个DLL边界,特别是如果EXE和DLL被写入由于RTL差异,存储器管理器的差异不同版本的Delphi / C ++生成器的,有效载荷布局的差异等WideString是确定以绕过,但因为它的内存和布局由操作系统,而不是RTL控制。



Answer 2:

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

我注意到,你把字符串数据的地址德尔福AWideString变量指向到MyWideString的记录的字段。 传递记录到C ++函数,这一个新的指针值指定给TheWideString/MyWideString的记录的字段中。 当执行返回到Delphi代码,你输出的内容AWideString变量。

您的评论表明您期望的AWideString变量的内容由C ++函数被改变,但这不是会发生什么。

C ++函数改变了结构中的字段。 当然这并不能认为场先前指向的内存位置。 该AWideString点的数据,以将不被C ++函数的影响。

如果C ++代码复制的数据到包含在字段中的地址,那么它会覆盖在字符串数据AWideString点。 由于AWideString是一个Delphi管理的字符串,C ++函数将复制更多的数据比原来的字符串,字符串存储区已分配的空间,在C ++函数复制数据会写过去德尔福的最后分配的字符串缓冲区,可能腐败德尔福堆。 崩溃后可能会出现一段时间。 所以这是一个很好的,你只分配指针到外地,而不是复制数据! ;>

要查看C ++函数改变了什么,你的Delphi代码应输出的内容MyWideString记录的字段中调用C ++函数之后。



Answer 3:

同步字段才能在结构。 您可以使用错误的三分球打破内存堆。 此外,检查在两者Delphi和C ++对准。



文章来源: Delphi and C/C++ DLL Struct vs.Record