SendMessage(WM_COPYDATA)+ Record + String

Les*_*ess 5 delphi

我想发送一条记录,现在只有一个字符串,但我会添加更多的变量.这是我第一次使用记录,所以这可能是一个愚蠢的问题.但是,为什么这样做:

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;
Run Code Online (Sandbox Code Playgroud)

接收方:

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;
Run Code Online (Sandbox Code Playgroud)

为什么如果在记录中,我使用:

WindowTitle: String; //removed the fixed size
Run Code Online (Sandbox Code Playgroud)

在发送方面我使用:

Data.WindowTitle:= PChar(HookedMessage.lParam); //removed String()
Run Code Online (Sandbox Code Playgroud)

它根本不去?

我得到访问冲突/ app冻结...

场景是:发送端是一个使用SetWindowsHookEx挂钩的DLL,接收端加载/调用SetWindowsHookEx的简单exe ...

Rem*_*eau 9

A 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;
Run Code Online (Sandbox Code Playgroud)

接收方:

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;
Run Code Online (Sandbox Code Playgroud)

话虽这么说,在任何一种情况下,你真的应该将自己的自定义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');
Run Code Online (Sandbox Code Playgroud)

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');
Run Code Online (Sandbox Code Playgroud)

最后,WPARAM参数WM_COPYDATA是a HWND,而不是HINSTANCE.如果发件人没有自己的HWND,只需传递0.不要传递发件人的HInstance变量.