Ты прав. Адреса имеют значение только в пределах одного процесса. Значение PMyRec, которое вы создаете в первом процессе, является просто адресом мусора в целевом процессе.
Чтобы отправить произвольный блок памяти другому процессу через оконное сообщение, вы должны использовать wm_CopyData
сообщение. Вы даете этому сообщению адрес данных и размер, а ОС позаботится о том, чтобы скопировать его в адресное пространство целевого процесса.
Поскольку ваши данные содержат строку, которая внутренне представлена как еще один указатель, будет недостаточно просто скопировать 12 байт вашей записи. Вам потребуется выделить дополнительную память для хранения записи и строковых данных в одном блоке памяти, чтобы wm_CopyData
мог ее скопировать, а целевой процесс мог ее прочитать.
Вот один из способов сделать это, используя поток для сбора данных в один блок памяти.
procedure SendRecord(Source, Target: HWnd; const Rec: TMyRec);
var
Buffer: TMemoryStream;
Len: Integer;
CopyData: TCopyDataStruct;
begin
Buffer := TMemoryStream.Create;
try
Len := Length(Rec.name);
Buffer.Write(Len, SizeOf(Len));
if Len > 0 then
Buffer.Write(Rec.name[1], Len * SizeOf(Char));
Len := Length(Rec.add);
Buffer.Write(Len, SizeOf(Len));
if Len > 0 then
Buffer.Write(Rec.add[1], Len * SizeOf(Char));
Buffer.Write(Rec.age, SizeOf(Rec.age));
CopyData.dwData := 0;
CopyData.cbData := Buffer.Size;
CopyData.lpData := Buffer.Memory;
SendMessage(Target, wm_CopyData, Source, LParam(@CopyData));
finally
Buffer.free;
end;
end;
Мы пишем длины строк в дополнение к символам строк, чтобы получатель знал, сколько символов принадлежит каждой из них. Код получателя будет выглядеть так:
procedure TBasicForm.WMCopyData(var Message: TWMCopyData);
var
Rec: TMyRec;
Len: Integer;
Buffer: TStream;
begin
Buffer := TReadOnlyMemoryStream.Create(
Message.CopyDataStruct.lpData, Message.CopyDataStruct.cbData);
try
if Message.CopyDataStruct.dwData = 0 then begin
Buffer.Read(Len, SizeOf(Len));
SetLength(Rec.name, Len);
if Len > 0 then
Buffer.Read(Rec.name[1], Len * SizeOf(Char));
Buffer.Read(Len, SizeOf(Len));
SetLength(Rec.add, Len);
if Len > 0 then
Buffer.Read(Rec.add[1], Len * SizeOf(Len));
Buffer.Read(Rec.age, SizeOf(Rec.age));
// TODO: Do stuff with Rec here.
Message.Result := 1;
end else
inherited;
finally
Buffer.Free;
end;
end;
Я использовал нестандартный TReadOnlyMemoryStream
, так как это все упрощает. Вот простая реализация для него:
type
TReadOnlyMemoryStream = class(TCustomMemoryStream)
public
constructor Create(Mem: Pointer; Size: LongInt);
function Write(const Buffer; Count: LongInt): LongInt; override;
end;
constructor TReadOnlyMemoryStream.Create;
begin
inherited Create;
SetPointer(Mem, Size);
end;
function TReadOnlyMemoryStream.Write;
begin
Result := 0;
end;
person
Rob Kennedy
schedule
17.06.2010