我想发送一条记录,现在只有一个字符串,但我会添加更多变量。这是我第一次使用记录,所以这可能是一个愚蠢的问题。但是,为什么这样做:
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()
它根本就没有?
我获取访问权限/ app冻结...
方案是:发送方是使用SetWindowsHookEx挂钩的DLL,接收端加载/调用SetWindowsHookEx的简单exe ...
答案 0 :(得分:8)
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;
话虽如此,在任何一种情况下,您都应该将自己的自定义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');
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');
最后,WPARAM
的{{1}}参数是WM_COPYDATA
,而不是HWND
。如果发件人没有自己的HINSTANCE
,则只需传递0.不要传递发件人的HWND
变量。