2017-10-16 2 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에입니다.)

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; 

당신 경우 : 예를 들어, (당신이 정확한 그렇게 주석 코드가)에 실제TMyRec 자신의 복사본을 보낼 필요 동일한 cdtRecord ID로 여러 유형의 레코드를 보내야하는 경우 실제 레코드 유형 이름 (RTTI가 아님)을 보내야합니다. 전자 기록 데이터, 예를 들면 :

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; 

그렇지 않으면, 당신은 더 일반화 된 방식으로 레코드 유형과 필드를 식별하기 위해 더 정교한 직렬화 메커니즘을 사용해야합니다.

관련 문제