4

私は2つの別々のプロセスでデータを送信していましたが、失敗しました。同じプロセスの下でのみ機能します...これがコンセプトです。

//------------------------------------------------ -----------------------------------
メインアプリ
//------------ -------------------------------------------------- ----------------------

Type
   PMyrec = ^TMyrec; 
   TMyrec = Record
   name : string;
   add : string;
   age : integer;
end;

:OnButtonSend
var aData : PMyrec;
begin
   new(aData);
   aData.Name := 'MyName';
   aData.Add := 'My Address';
   aData.Age : 18;
   SendMessage(FindWindow('SubApps'),WM_MyMessage,0,Integer(@aData));
end;

//------------------------------------------------ -----------------------------------
サブアプリ
//------------ -------------------------------------------------- ----------------------

Type
   PMyrec = ^TMyrec; 
   TMyrec = Record
   name : string;
   add : string;
   age : integer;
end;

:OnCaptureMessage

var
  aData : PMyrec;
begin
  aData := PMyrec(Msg.LParam);
  showmessage(aData^.Name);
end;
4

1 に答える 1

16

あなたが正しい。アドレスは、単一のプロセス内でのみ意味を持ちます。最初のプロセスで作成した PMyRec 値は、ターゲット プロセスのガベージ アドレスにすぎません。

ウィンドウメッセージを介して任意のメモリブロックを別のプロセスに送信するには、wm_CopyDataメッセージを使用する必要があります。そのメッセージにデータのアドレスとサイズを指定すると、OS がそれをターゲット プロセスのアドレス空間にコピーします。

データには別のポインターとして内部的に表される文字列が含まれているため、レコードの 12 バイトをコピーするだけでは十分ではありません。レコードと文字列データを 1 つのメモリ ブロックに保持するために、追加のメモリを割り当てる必要があります。これにより、レコードをwm_CopyDataコピーして、ターゲット プロセスがそれを読み取れるようになります。

これは、ストリームを使用してデータを単一のメモリ ブロックに収集する方法の 1 つです。

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;
于 2010-06-17T04:07:03.150 に答える