2016-02-19 12 views
5

Voglio inviare un record, che al momento ha solo una stringa, ma aggiungerò altre variabili. È la prima volta che lavoro con i dischi, quindi questa è forse una domanda stupida. Ma, perché questo funziona: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; 

lato ricevente:

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; 

Perché se sul disco, io uso:

WindowTitle: String; //removed the fixed size 

e sul lato di invio che uso:

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

semplicemente non va?

ottengo le violazioni di accesso/app freeze ...

Lo scenario è: l'invio di lato è una DLL utilizzando agganciato SetWindowsHookEx, ricevendo lato un semplice exe che ha caricato/chiamato SetWindowsHookEx ...

risposta

8

A String[255] è un blocco di memoria fisso a 256 byte, in cui i dati dei caratteri sono memorizzati direttamente in quella memoria. Come tale, è sicuro passare così com'è attraverso i confini del processo senza serializzazione.

A String, d'altra parte, è un tipo dinamico. Contiene solo un puntatore ai dati dei caratteri che vengono memorizzati altrove nella memoria. Pertanto, non è possibile passare uno String così come è al di là dei limiti del processo, tutto ciò che si passa è il valore del puntatore, che non ha alcun significato per il processo di ricezione. È necessario serializzare i dati String in un formato flat che può essere passato in modo sicuro e deserializzato dal processo di ricezione. Per esempio:

lato invio:

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; 

lato ricevente:

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; 

Detto questo, in entrambi i casi, si dovrebbe davvero essere assegnando il proprio numero ID personalizzato al campo copyDataStruct.dwData. La VCL stessa utilizza internamente lo WM_COPYDATA, quindi non vuoi che quei messaggi vengano confusi con i tuoi e viceversa. È possibile utilizzare RegisterWindowMessage() per creare un ID univoco per evitare conflitti con gli ID utilizzati da altri utenti 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'); 

Infine, il parametro di WPARAMWM_COPYDATA è un HWND, non un HINSTANCE. Se il mittente non ha il proprio numero HWND, basta passare 0. Non passare la variabile HInstance del mittente.

+0

Ottima risposta! Grazie ancora, sempre con risposte complete e informative. – LessStress

Problemi correlati