2016-02-19 3 views
5

Я хочу отправить запись, которая прямо сейчас имеет только строку, но я добавлю больше переменных. Я впервые работаю с записями, так что это, возможно, глупый вопрос. Но, почему это работает:SendMessage (WM_COPYDATA) + Запись + 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() 

это просто не идет?

Я получаю нарушение прав доступа/приложение сублимационное ...

Сценария: отправка стороны является DLL зацепили с помощью SetWindowsHookEx, принимающей стороны простого ехе, который загружен/называется SetWindowsHookEx ...

ответ

8

String[255] является фиксированным 256-байтовым блоком памяти, где символьные данные хранятся непосредственно в этой памяти. Таким образом, безопасно проходить через все границы процесса без сериализации.

A 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 поле. Сам VCL использует внутри себя WM_COPYDATA, поэтому вы не хотите, чтобы эти сообщения были сбиты с толку, и наоборот. Вы можете использовать RegisterWindowMessage(), чтобы создать уникальный идентификатор, чтобы избежать конфликтов с идентификаторами, используемыми другими 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. Если у отправителя нет своего HWND, просто пройдите 0. Не передавайте переменную HInstance вашего отправителя.

+0

Отличный ответ! Еще раз спасибо, всегда с полными и информативными ответами. – LessStress