views:

157

answers:

2

i use to send a data on two separate process but it fails. it works only under same process... this is concept.

//-----------------------------------------------------------------------------------
MainApps
//-----------------------------------------------------------------------------------

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;

//-----------------------------------------------------------------------------------
SubApps
//-----------------------------------------------------------------------------------

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;
+12  A: 

You're right. Addresses only have meaning within a single process. The PMyRec value you create in the first process is just a garbage address in the target process.

To send an arbitrary block of memory to another process via a window message, you should use the wm_CopyData message. You give that message the address of the data and the size, and the OS takes care of copying it into the target process's address space.

Since your data includes a string, which is represented internally as a another pointer, it won't be enough to just copy the 12 bytes of your record. You'll need to allocate additional memory to hold the record and the string data in a single block of memory so wm_CopyData can copy it and the target process can read it.

Here's one way to do it, using a stream to collect the data into a single block of memory.

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;

We write the lengths of the strings in addition to the strings' characters so that the recipient knows how many characters belong to each one. The recipient's code will look like this:

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;

I've used the non-standard TReadOnlyMemoryStream since it makes everything easier. Here's a simple implementation for it:

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;
Rob Kennedy
thanx for the tips it works fine..
XBasic3000
+2  A: 

Hi everyone I found short answer!

const
  WM_EXCHANGE_INFO_DATA     = WM_User + 8891;

MainApps

type
  TMyData = record
    Name: String[250];
    Address: String[250];
    Age: Integer;
  end;

procedure TForm1.Button1Click(Sender: TObject);
var
  pData: TMyData;
  DataStruct: TCopyDataStruct;
begin
  pData.Name    := 'R Carillo';
  pData.Address := 'Lahug Cebu City';
  pData.Age     := 29;
  DataStruct.dwData := WM_EXCHANGE_INFO_DATA;//custom message
  DataStruct.cbData := SizeOf(TMyData);
  DataStruct.lpData := @pData;
  SendMessage(FRcvrHandle, WM_COPYDATA, 0, LParam(@DataStruct));
end;

SubApps

type
  TMyData = record
    Name: String[250];
    Address: String[250];
    Age: Integer;
  end;

procedure TfrmVocaManager.WMCopyData(var Msg: TMessage);
var
  pData: TMyData;
begin
  if (PCopyDataStruct(Msg.LParam)^.dwData = WM_EXCHANGE_INFO_DATA) then
  begin
    CopyMemory(@pData, PCopyDataStruct(Msg.LParam)^.lpData, PCopyDataStruct(Msg.LParam)^.cbData);
    Msg.Result := 1;
  end;
  showmessage('Name : '    +pData.Name +#13#10+
              'Address : ' +pData.Address +#13#10+
              'Age : '     +inttostr(pData.Age));
end;
XBasic3000
You've found a short answer because you simplifed your problem by altering your requirements in the question, by using short strings instead of strings. ;-)
Sertac Akyuz
Please don't conflate window messages with copy-data IDs. They're not the same things. There's no need to use a window message when all you need is some arbitrary number that neither program already recognizes as a copy-data ID. I'm talking about the value you store in `dwData`. Also, you don't need `CopyMemory`; simply assign `pData := PMyData(Message.CopyDataStruct.lpData)^`.
Rob Kennedy
XBasic3000