2016-02-19 1 views
5

Je veux envoyer un enregistrement, pour l'instant il ne contient qu'une chaîne, mais j'ajouterai plus de variables. C'est la première fois que je travaille avec des disques, alors c'est peut-être une question idiote. Mais, pourquoi cela fonctionne:SendMessage (WM_COPYDATA) + Enregistrement + Chaîne

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; 

côté réception:

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; 

Pourquoi si au dossier, j'utilise:

WindowTitle: String; //removed the fixed size 

et sur le côté d'envoyer je l'utilise:

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

il ne va tout simplement pas?

Je reçois des violations d'accès/gel app ...

Le scénario est le suivant: côté émetteur est une DLL accroché à l'aide SetWindowsHookEx, côté réception d'un simple exe qui a chargé/appelé SetWindowsHookEx ...

Répondre

8

A String[255] est un bloc de mémoire fixe de 256 octets, où les données de caractères sont stockées directement dans cette mémoire. En tant que tel, il est sûr de passer tel quel à travers les limites de processus sans sérialisation. Un String, par contre, est un type dynamique. Il contient juste un pointeur vers des données de caractères qui sont stockées ailleurs dans la mémoire. En tant que tel, vous ne pouvez pas passer un String tel quel à travers les limites de processus, tout ce que vous transmettez est la valeur du pointeur, ce qui n'a aucune signification pour le processus de réception. Vous devez sérialiser les données String dans un format plat qui peut être transmis en toute sécurité et désérialisé par le processus de réception. Par exemple:

côté Envoi:

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; 

côté réception:

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; 

Cela dit, dans les deux cas, vous devriez vraiment assignez votre propre numéro d'identification personnalisé au champ copyDataStruct.dwData. La VCL elle-même utilise WM_COPYDATA en interne, donc vous ne voulez pas que ces messages soient confondus avec les vôtres, et vice versa. Vous pouvez utiliser RegisterWindowMessage() pour créer un identifiant unique pour éviter les conflits avec les ID utilisés par d'autres utilisateurs 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'); 

Enfin, le paramètre WPARAM de WM_COPYDATA est un HWND, pas HINSTANCE. Si l'expéditeur n'a pas son propre HWND, transmettez simplement 0. Ne passez pas la variable HInstance de votre expéditeur.

+0

Bonne réponse! Merci encore, toujours avec des réponses complètes et informatives. – LessStress