delphi 发送消息(WM_COPYDATA)+记录+字符串

yqlxgs2m  于 2023-02-15  发布在  其他
关注(0)|答案(2)|浏览(343)

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

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,接收端是加载/调用SetWindowsHookEx的简单exe ...

vbopmzt1

vbopmzt11#

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;

也就是说,在任何一种情况下,您都应该为copyDataStruct.dwData字段分配自己的自定义ID号。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变量。

carvr3hs

carvr3hs2#

准备:

procedure TMainForm.CreateParams(var Params: TCreateParams);
begin
 inherited;
 StrCopy(Params.WinClassName, PChar(SingleInstClassName)); // Copies a null-terminated string. StrCopy is designed to copy up to 255 characters from the source buffer into the destination buffer. If the source buffer contains more than 255 characters, the procedure will copy only the first 255 characters.
end;

发送人:

procedure TAppData.ResurectInstance(Arg: string);
VAR
   Window: HWND;
   DataToSend: TCopyDataStruct;
begin
  Arg:= Trim(Arg);

  { Prepare the data you want to send }
  DataToSend.dwData := CopyDataID;  // CopyDataID = Unique ID for my apps
  DataToSend.cbData := Length(Arg) * SizeOf(Char);
  DataToSend.lpData := PChar(Arg);

  { We should never use PostMessage() with the WM_COPYDATA message because the data that is passed to the receiving application is only valid during the call. Finally, be aware that the call to SendMessage will not return until the message is processed.}
  Window:= WinApi.Windows.FindWindow(PWideChar(SingleInstClassName), NIL);    // This is a copy of cmWindow.FindTopWindowByClass
  SendMessage(Window, WM_COPYDATA, 0, LPARAM(@DataToSend));
end;

接收者:

procedure TMainForm.WMCopyData(var Msg: TWMCopyData);
VAR
   FileName: string;
begin
 { Receives filename from another instance of this program }
 if  (Msg.CopyDataStruct.dwData = AppData.CopyDataID) { Only react on this specific message }
 AND (Msg.CopyDataStruct.cbData > 0)                  { Do I receive an empty string? }
 then
  begin
    SetString(FileName, PChar(Msg.CopyDataStruct.lpData), Msg.CopyDataStruct.cbData div SizeOf(Char));
    msg.Result:= 2006;                                { Send something back as positive answer }

    AppData.Restore;  

   ...
  end
 else
   inherited;
end;

相关问题