2017-10-16 139 views
-1

所以,我有一個類使用WM_COPYDATA來允許應用程序進行通信。如何讀取其他進程內存

type 
    TMyRec = record 
    Name: string[255]; 
    Age: integer; 
    Birthday: TDateTime; 
    end; 

function TAppCommunication.SendRecord(const ARecordToSend: Pointer; const ARecordType: PTypeInfo): Boolean; 
var 
    _Stream: TMemoryStream; 
begin 
    _Stream := TMemoryStream.Create; 
    try 
    _Stream.Write(NativeInt(ARecordType), SizeOf(TTypeInfo)); 
    _Stream.Write(NativeInt(ARecordToSend), SizeOf(ARecordToSend)); 
    _Stream.Position := 0; 

    Result := SendStreamData(_Stream, TCopyDataType.cdtRecord) 
    finally 
    FreeAndNil(_Stream); 
    end; 
end; 

function TAppCommunication.SendStreamData(const AStream: TMemoryStream; 
    const ADataType: TCopyDataType): Boolean; 
var 
    _CopyDataStruct: TCopyDataStruct; 
begin 
    Result := False; 

    if AStream.Size = 0 then 
    Exit; 

    _CopyDataStruct.dwData := integer(ADataType); 
    _CopyDataStruct.cbData := AStream.Size; 
    _CopyDataStruct.lpData := AStream.Memory; 

    Result := SendData(_CopyDataStruct); 
end; 

function TAppCommunication.SendData(const ADataToSend: TCopyDataStruct) 
    : Boolean; 
var 
    _SendResponse: integer; 
    _ReceiverHandle: THandle; 
begin 
    Result := False; 

    _ReceiverHandle := GetRemoteReceiverHandle; 
    if (_ReceiverHandle = 0) then 
    Exit; 

    _SendResponse := SendMessage(_ReceiverHandle, WM_COPYDATA, 
    integer(FLocalReceiverForm.Handle), integer(@ADataToSend)); 

    Result := _SendResponse <> 0; 
end; 

發送應用程序:

procedure TSenderMainForm.BitBtn1Click(Sender: TObject); 
var 
    _AppCommunication: TAppCommunication; 
    _ms: TMemoryStream; 
    _Rec: TMyRec; 
    _Record: TAttrData; 
begin 
    _AppCommunication := TAppCommunication.Create('LocalReceiverName', OnAppMessageReceived); 
    _ms := TMemoryStream.Create; 
    try 
    _AppCommunication.SetRemoteReceiverName('LocalReceiverNameServer'); 
    _AppCommunication.SendString('ąčęėįšųūž123'); 
    _AppCommunication.SendInteger(998); 
    _AppCommunication.SendDouble(0.95); 

    _Rec.Name := 'Edijs'; 
    _Rec.Age := 29; 
    _Rec.Birthday := EncodeDate(1988, 10, 06); 
    _Record.Len := 1988; 
    //_ms.Write(_Rec, SizeOf(TMyRec)); 
    //_AppCommunication.SendStreamData(_ms, TCopyDataType.cdtRecord); 
    _AppCommunication.SendRecord(@_rec, System.TypeInfo(TMyRec)); 
    //_AppCommunication.SendRecord(@_Record, System.TypeInfo(TAttrData)); 
    finally 
    FreeAndNil(_ms); 
    FreeAndNil(_AppCommunication); 
    end; 
end; 

接收應用程序:

procedure TReceiverMainForm.OnAppMessageReceived(const ASender 
    : TPair<HWND, string>; const AReceivedData: TCopyDataStruct; 
    var AResult: integer); 
var 
    Info: PTypeInfo; 
    Data: PTypeData; 
    KindName: String; 
    SubName: String; 
    _TypeInfo: TTypeInfo; 
    _MyRec: TMyRec; 
begin 
.... 
    else 
    begin 
    memLog.Lines.Add('Unknown data received.'); 

    if (AReceivedData.dwData) = integer(TCopyDataType.cdtRecord) then 
    begin 
     memLog.Lines.Add('Record received.'); 

     // This one works fine if "_Stream.Write(NativeInt(ARecordType), SizeOf(TTypeInfo));" 
     // is commented out 
     //_MyRec := GetProcessMyRec(ASender.Key, pointer(NativeUint(AReceivedData.lpData^)), SizeOf(TMyRec)); 

     _TypeInfo := GetProcessTypeInfo(ASender.Key, 
     Pointer(AReceivedData.lpData^), SizeOf(TTypeInfo)); 

     Info := System.TypeInfo(TMyRec); 
     if (_TypeInfo.Name = Info^.Name) and (_TypeInfo.Kind = Info^.Kind) then 
     begin 
     // _MyRec := GetProcessMyRec(ASender.Key, Pointer(AReceivedData.lpData^), SizeOf(TMyRec)); works 
     _MyRec := GetProcessMyRec(ASender.Key, pointer(NativeInt(AReceivedData.lpData^) + 
      SizeOf(TTypeInfo)), SizeOf(TMyRec)); 

     ShowMessage(_MyRec.Name + ', Age: ' + IntToStr(_MyRec.Age) + ', birthday: ' + 
      DateToStr(_MyRec.Birthday)); 
     end; 
    end; 
    AResult := -1; 
    end; 
end; 

的問題是,如果我發送所屬類別和記錄在一起,我不能在讀第二個。如果我單獨發送,我可以讀取TypInfo或記錄。我應該修正什麼才能使其工作?

回答

1

您不能跨越進程邊界使用指針,更不用說指向RTTI的指針了。你不應該發送指針TMyRec(肯定不是指針到它的RTTI)。您需要發送的實際TMyRec自身的副本(你有註釋,以確切做到這一點的代碼),例如:

type 
    PMyRec = ^TMyRec; 
    TMyRec = packed record 
    Name: string[255]; 
    Age: integer; 
    Birthday: TDateTime; 
    end; 

function TAppCommunication.SendRecord(const ARecordToSend: Pointer; ARecordSize: Integer): Boolean; 
var 
    _Stream: TMemoryStream; 
begin 
    _Stream := TMemoryStream.Create; 
    try 
    _Stream.WriteBuffer(ARecordToSend^, ARecordSize); 
    _Stream.Position := 0; 
    Result := SendStreamData(_Stream, TCopyDataType.cdtRecord); 
    finally 
    FreeAndNil(_Stream); 
    end; 
end; 

... 

// need to cast to WPARAM and LPARAM, not Integer... 
_SendResponse := SendMessage(_ReceiverHandle, WM_COPYDATA, WPARAM(FLocalReceiverForm.Handle), LPARAM(@ADataToSend)); 

... 

var 
    _Rec: TMyRec; 

_Rec.Name := 'Edijs'; 
_Rec.Age := 29; 
_Rec.Birthday := EncodeDate(1988, 10, 06); 
_AppCommunication.SendRecord(@_Rec, SizeOf(_Rec)); 

procedure TReceiverMainForm.OnAppMessageReceived(const ASender : TPair<HWND, string>; const AReceivedData: TCopyDataStruct; var AResult: integer); 
var 
    _MyRec: PMyRec; 
begin 
    .... 
    else 
    begin 
    if AReceivedData.dwData = Ord(TCopyDataType.cdtRecord) then 
    begin 
     memLog.Lines.Add('Record received.'); 
     _MyRec := PMyRec(AReceivedData.lpData); 
     // Use _MyRec^ data as needed... 
     ShowMessage(_MyRec.Name + ', Age: ' + IntToStr(_MyRec.Age) + ', birthday: ' + DateToStr(_MyRec.Birthday)); 
    end else 
     memLog.Lines.Add('Unknown data received.'); 
    AResult := -1; 
    end; 
end; 

如果您需要在相同的cdtRecord ID下發送多種類型的記錄,那麼您需要發送實際的記錄類型名稱(而不是其RTTI) E中的記錄數據,例如:

function TAppCommunication.SendRecord(const ARecordType: ShortString; const ARecordToSend: Pointer; ARecordSize: Integer): Boolean; 
var 
    _Stream: TMemoryStream; 
begin 
    _Stream := TMemoryStream.Create; 
    try 
    _Stream.WriteBuffer(@ARecordType, 1+Length(ARecordType)); 
    _Stream.WriteBuffer(ARecordToSend^, ARecordSize); 
    _Stream.Position := 0; 
    Result := SendStreamData(_Stream, TCopyDataType.cdtRecord); 
    finally 
    FreeAndNil(_Stream); 
    end; 
end; 

var 
    _Rec: TMyRec; 

_Rec.Name := 'Edijs'; 
_Rec.Age := 29; 
_Rec.Birthday := EncodeDate(1988, 10, 06); 
_AppCommunication.SendRecord('TMyRec', @_Rec, SizeOf(_Rec)); 

procedure TReceiverMainForm.OnAppMessageReceived(const ASender : TPair<HWND, string>; const AReceivedData: TCopyDataStruct; var AResult: integer); 
var 
    _RecType: ShortString; 
    _RecData: Pointer; 
    _MyRec: PMyRec; 
begin 
    .... 
    else 
    begin 
    if AReceivedData.dwData = Ord(TCopyDataType.cdtRecord) then 
    begin 
     memLog.Lines.Add('Record received.'); 
     _RecType := PShortString(AReceivedData.lpData)^; 
     _RecData := PByte(AReceivedData.lpData)+1+Length(_RecType); 
     if (_RetType = 'TMyRec') then 
     begin 
     _MyRec := PMyRec(_RecData); 
     // Use _MyRec^ data as needed... 
     ShowMessage(_MyRec.Name + ', Age: ' + IntToStr(_MyRec.Age) + ', birthday: ' + DateToStr(_MyRec.Birthday)); 
     end 
     else 
     ... 
    end else 
     memLog.Lines.Add('Unknown data received.'); 
    AResult := -1; 
    end; 
end; 

否則,你需要使用一個更復雜的序列化機制來識別更廣義的方式爲你記錄類型和領域。

相關問題