2016-02-19 18 views
5

レコードを送信したいと思いますが、今は文字列しかありませんが、変数を追加します。レコードで作業するのは初めてですから、これは愚かな質問かもしれません。しかし、なぜこの作品:SendMessage(WM_COPYDATA)+レコード+文字列

type 
    TDataPipe = record 
    WindowTitle: String[255]; 
    end; 

var 
    Data: TDataPipe; 
    copyDataStruct : TCopyDataStruct; 
begin 
    Data.WindowTitle:= String(PChar(HookedMessage.lParam)); 
    copyDataStruct.dwData := 0; 
    copyDataStruct.cbData := SizeOf(Data); 
    copyDataStruct.lpData := @Data; 
    SendMessage(FindWindow('TForm1', nil), WM_COPYDATA, Integer(hInstance), Integer(@copyDataStruct));  
end; 

受信側:

type 
    TDataPipe = record 
    WindowTitle: String[255]; 
    end; 

procedure TForm1.WMCopyData(var Msg: TWMCopyData); 
var 
    sampleRecord : TDataPipe; 
begin 
    sampleRecord.WindowTitle:= TDataPipe(Msg.CopyDataStruct.lpData^).WindowTitle; 
    Memo1.Lines.Add(sampleRecord.WindowTitle); 
end; 
はなぜ記録上ならば、私が使用:私が使用

WindowTitle: String; //removed the fixed size 

と送信側の

Data.WindowTitle:= PChar(HookedMessage.lParam); //removed String() 

それは単に行きませんか?

私が取得、アクセス違反/アプリの凍結...

シナリオです:側を送信することは、SetWindowsHookExを使用して夢中DLL、受信側は、SetWindowsHookExと呼ばれるロードシンプルなexeファイル/です...

答えて

8

String[255]は固定された256バイトのメモリブロックで、文字データはそのメモリに直接格納されます。したがって、直列化を行わずにプロセスの境界を越えてそのまま渡すことは安全です。

Stringは、ダイナミックタイプです。メモリ内の他の場所に格納されている文字データへのポインタを含んでいます。したがって、プロセスの境界を超えてStringをそのまま渡すことはできません。渡すべき値はポインタ値です。これは受信プロセスにとって意味がありません。 Stringデータをフラット形式にシリアル化する必要があります。フラット形式は、受信プロセスに安全に渡すことができ、受信プロセスによって逆シリアル化できます。

送信側:

type 
    PDataPipe = ^TDataPipe; 
    TDataPipe = record 
    WindowTitleLen: Integer; 
    WindowTitleData: array[0..0] of Char; 
    //WindowTitleData: array[0..WindowTitleLen-1] of Char; 
    end; 

var 
    Wnd: HWND; 
    s: String; 
    Data: PDataPipe; 
    DataLen: Integer; 
    copyDataStruct : TCopyDataStruct; 
begin 
    Wnd := FindWindow('TForm1', nil); 
    if Wnd = 0 then Exit; 

    s := PChar(HookedMessage.lParam); 
    DataLen := SizeOf(Integer) + (SizeOf(Char) * Length(s)); 
    GetMem(Data, DataLen); 
    try 
    Data.WindowTitleLen := Length(s); 
    StrMove(Data.WindowTitleData, PChar(s), Length(s)); 

    copyDataStruct.dwData := ...; // see notes further below 
    copyDataStruct.cbData := DataLen; 
    copyDataStruct.lpData := Data; 
    SendMessage(Wnd, WM_COPYDATA, 0, LPARAM(@copyDataStruct));  
    finally 
    FreeMem(Data); 
    end; 
end; 

受信側:

言われていること
type 
    PDataPipe = ^TDataPipe; 
    TDataPipe = record 
    WindowTitleLen: Integer; 
    WindowTitleData: array[0..0] of Char; 
    //WindowTitleData: array[0..WindowTitleLen-1] of Char; 
    end; 

procedure TForm1.WMCopyData(var Msg: TWMCopyData); 
var 
    Data: PDataPipe; 
    s: string; 
begin 
    Data := PDataPipe(Msg.CopyDataStruct.lpData); 
    SetString(s, Data.WindowTitleData, Data.WindowTitleLen); 
    Memo1.Lines.Add(s); 
end; 

、どちらの状況では、あなたが本当にcopyDataStruct.dwDataフィールドに独自のカスタムID番号を割り当てるべきである。例えば。 VCL自体は内部でWM_COPYDATAを使用しています。そのため、あなたはそれらのメッセージをあなたのものと混同したくないでしょうし、その逆もありません。

var 
    dwMyCopyDataID: DWORD; 

... 

var 
    ... 
    copyDataStruct : TCopyDataStruct; 
begin 
    ... 
    copyDataStruct.dwData := dwMyCopyDataID; 
    ... 
end; 

... 

initialization 
    dwMyCopyDataID := RegisterWindowMessage('MyCopyDataID'); 

var 
    dwMyCopyDataID: DWORD; 

... 

procedure TForm1.WMCopyData(var Msg: TWMCopyData); 
var 
    ... 
begin 
    if Msg.CopyDataStruct.dwData = dwMyCopyDataID then 
    begin 
    ... 
    end else 
    inherited; 
end; 

... 

initialization 
    dwMyCopyDataID := RegisterWindowMessage('MyCopyDataID'); 

最後に、WM_COPYDATAWPARAMパラメータはHWND、ないHINSTANCEです:あなたは、他のWM_COPYDATAのユーザーによって使用されるIDとの競合を避けるために、一意のIDを作成するためにRegisterWindowMessage()を使用することができます。送信者に独自のHWNDがない場合は、0を渡すだけです。送信者のHInstance変数を渡さないでください。

+0

偉大な答え!いつも完全で有益な回答をありがとう。 – LessStress

関連する問題