2016-02-19 39 views
5

我想發送一條記錄,現在只有一個字符串,但我會添加更多的變量。這是我第一次使用記錄,所以這可能是一個愚蠢的問題。但是,爲什麼這個工程:SendMessage(WM_COPYDATA)+ Record + String

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,接收端一個簡單的EXE,它加載/被叫和SetWindowsHookEx ...

回答

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; 

話雖這麼說,在這兩種情況下,你真的應該指定自己的自定義ID號到copyDataStruct.dwData領域。 VCL內部使用WM_COPYDATA,所以你不想讓這些消息與你的消息混淆,反之亦然。您可以使用RegisterWindowMessage()創建一個唯一的ID,以避免與其他WM_COPYDATA用戶使用的ID衝突:

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。如果發件人沒有自己的HWND,只需傳遞0.不要傳遞發件人的HInstance變量。

+0

很好的答案!再次感謝您,始終提供完整而翔實的答案。 – LessStress