0

以前、デルファイと 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予想の問題が見られます...

AWideStringDelphi変数が指す文字列データのアドレスをMyWideStringレコードのフィールドに入力していることに気付きました。TheWideString/MyWideStringレコードを C++ 関数に渡します。これにより、レコードのフィールドに新しいポインター値が割り当てられます。実行が Delphi コードに戻るときに、AWideString変数の内容を出力します。

あなたのコメントは、AWideString 変数の内容が C++ 関数によって変更されることを期待していることを示していますが、それは起こりません。

C++ 関数は、構造体のフィールドを変更します。フィールドが以前に指していたメモリ位置には何もしません。AWideString が指すデータは、C++ 関数の影響を受けません。

C++ コードがフィールドに含まれるアドレスにデータをコピーすると、AWideStringポイントする文字列データが上書きされます。AWideStringは Delphi マネージド文字列であり、C++ 関数は、元の文字列が割り当てたスペースよりも多くのデータをその文字列メモリ領域にコピーするため、C++ 関数でデータをコピーすると、Delphi が割り当てた文字列バッファの末尾を超えて書き込み、おそらく破損します。Delphi ヒープ。しばらくするとクラッシュが発生する可能性があります。したがって、データをコピーするのではなく、ポインタをフィールドに割り当てるだけでよいのです! ;>

C++ 関数の変更内容を確認するには、Delphi コードでMyWideStringC++ 関数の呼び出し後にレコードのフィールドの内容を出力する必要があります。

于 2012-07-31T04:31:39.927 に答える
1

構造内のフィールドの順序を同期します。間違ったポインタを使用してメモリ ヒープを壊すことができます。また、Delphi と C++ の両方でアラインメントをチェックしてください。

于 2012-07-31T03:59:06.210 に答える
1

文字列フィールドの管理を誤っています。 「WideString へのポインタ」および「AnsiString へのポインタ」と同じものではPWideCharありPChar ません。その代わりに、 Delphi にはPWideString( WideString*) とPAnsiString( AnsiString*) の型があります。また、 ( ) の代わりにDelphi のPInteger( int*) およびPBoolean( ) 型を使用する必要があります。Delphi と C++ はどちらもタイプ セーフな言語です。可能であれば、型指定されていないポインターには近づかないでください。コードはその方が適しています。bool*Pointervoid*

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) することは非常に危険です。特に、RTL の違い、メモリ マネージャーの違い、ペイロードが原因で、EXE と DLL が異なるバージョンの Delphi/C++Builder で記述されている場合はなおさらです。ただし、メモリとレイアウトはRTLではなくOSによって制御されるため、レイアウトの違いなど WideStringは渡しても問題ありません。

于 2012-07-31T04:35:32.843 に答える