String 如何在应用程序之间发送包含字符串的记录

String 如何在应用程序之间发送包含字符串的记录,string,delphi-xe2,record,wm-copydata,String,Delphi Xe2,Record,Wm Copydata,因此,我有一个类,它使用WM_COPYDATA允许应用程序进行通信 type TMyRec = record Name: string[255]; // I want just string Age: integer; Birthday: TDateTime; end; function TAppCommunication.SendRecord(const ARecordType: ShortString; const ARecordToSend: Pointe

因此,我有一个类,它使用WM_COPYDATA允许应用程序进行通信

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

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;

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,
    WPARAM(FLocalReceiverForm.Handle), LPARAM(@ADataToSend));

  Result := _SendResponse <> 0;
end;
接收器应用程序:

procedure TReceiverMainForm.OnAppMessageReceived(const ASender
  : TPair<HWND, string>; const AReceivedData: TCopyDataStruct;
  var AResult: integer);
var
  _MyRec: TMyRec;
  _RecType: ShortString;
  _RecData: Pointer;
begin
  ...
  else
  begin
    if (AReceivedData.dwData) = Ord(TCopyDataType.cdtRecord) then
    begin
    _RecType := PShortString(AReceivedData.lpData)^;
      _RecData := PByte(AReceivedData.lpData)+1+Length(_RecType);
      if (_RecType = TTypeInfo(System.TypeInfo(TMyRec)^).Name) then
      begin
        _MyRec := TMyRec(_RecData^);
        ShowMessage(_MyRec.Name + ', Age: ' + IntToStr(_MyRec.Age) + ', birthday: ' +
          DateToStr(_MyRec.Birthday));
      end;
    end;
    AResult := -1;
  end;
end;
发件人:

_AppCommunication.SendRecord(@_Rec, System.TypeInfo(TMyRec));
短字符串的固定大小为256字节(最大1字节长度+最多255个AnsiChars),因此很容易嵌入到记录中并按原样发送

另一方面,字符串是指向为字符数组动态分配的内存的指针。因此,来回序列化需要做更多的工作

为了满足您的要求,您不能简单地将ShortString替换为String,而不同时更改两者之间的所有其他内容来解释这种差异

您已经有了发送可变长度字符串的基本框架在发送数据之前发送长度,因此您可以在此基础上进行扩展以处理字符串值,例如:

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

  TStreamHelper = class helper for TStream
  public
    function ReadInteger: Integer;
    function ReadDouble: Double;
    function ReadString: String;
    ...
    procedure WriteInteger(Value: Integer);
    procedure WriteDouble(Strm: Value: Double);
    procedure WriteString(const Value: String);
  end;

function TStreamHelper.ReadInteger: Integer;
begin
  Self.ReadBuffer(Result, SizeOf(Integer));
end;

function TStreamHelper.ReadDouble: Double;
begin
  Self.ReadBuffer(Result, SizeOf(Double));
end;

function TStreamHelper.ReadString: String;
var
  _Bytes: TBytes;
  _Len: Integer;
begin
  _Len := ReadInteger;
  SetLength(_Bytes, _Len);
  Self.ReadBuffer(PByte(_Bytes)^, _Len);
  Result := TEncoding.UTF8.GetString(_Bytes);
end;

...

procedure TStreamHelper.WriteInteger(Value: Integer);
begin
  Self.WriteBuffer(Value, SizeOf(Value));
end;

procedure TStreamHelper.WriteDouble(Value: Double);
begin
  Self.WriteBuffer(Value, SizeOf(Value));
end;

procedure TStreamHelper.WriteString(const Value: String);
var
  _Bytes: TBytes;
  _Len: Integer;
begin
  _Bytes := TEncoding.UTF8.GetBytes(Value);
  _Len := Length(_Bytes);
  WriteInteger(_Len);
  Self.WriteBuffer(PByte(_Bytes)^, _Len);
end;

如果_RecType=TTypeInfoSystem.TypeInfoTMyRec^.Name,那么这完全是过火了。如我前面的回答所示,只需使用if _RecType='TMyRec',然后就可以了。这只是为了防止以后有人重命名记录。相信我,这样的事情会发生,只有客户才会发现它发生了。你不能在不破坏协议的情况下重命名通过通信发送的记录SendRecord应该可以处理任何类型的记录,但我似乎必须对此进行研究。也许是序列化?@EdijsKolesnikovičs是的,这是序列化的基础。将结构化/动态数据转换为平面格式进行传输,然后再将其转换回来。一个函数很难处理多种类型,尤其是在处理动态数据时。但有办法做到这一点,即使是专用库,基于RTTI的方法使用扩展RTTI,而不是传统RTTI,只要您能够承受复杂性和开销。就个人而言,我更喜欢简单的方法
_AppCommunication.SendRecord(@_Rec, System.TypeInfo(TMyRec));
type
  TMyRec = record
    Name: string;
    Age: integer;
    Birthday: TDateTime;
  end;

  TStreamHelper = class helper for TStream
  public
    function ReadInteger: Integer;
    function ReadDouble: Double;
    function ReadString: String;
    ...
    procedure WriteInteger(Value: Integer);
    procedure WriteDouble(Strm: Value: Double);
    procedure WriteString(const Value: String);
  end;

function TStreamHelper.ReadInteger: Integer;
begin
  Self.ReadBuffer(Result, SizeOf(Integer));
end;

function TStreamHelper.ReadDouble: Double;
begin
  Self.ReadBuffer(Result, SizeOf(Double));
end;

function TStreamHelper.ReadString: String;
var
  _Bytes: TBytes;
  _Len: Integer;
begin
  _Len := ReadInteger;
  SetLength(_Bytes, _Len);
  Self.ReadBuffer(PByte(_Bytes)^, _Len);
  Result := TEncoding.UTF8.GetString(_Bytes);
end;

...

procedure TStreamHelper.WriteInteger(Value: Integer);
begin
  Self.WriteBuffer(Value, SizeOf(Value));
end;

procedure TStreamHelper.WriteDouble(Value: Double);
begin
  Self.WriteBuffer(Value, SizeOf(Value));
end;

procedure TStreamHelper.WriteString(const Value: String);
var
  _Bytes: TBytes;
  _Len: Integer;
begin
  _Bytes := TEncoding.UTF8.GetBytes(Value);
  _Len := Length(_Bytes);
  WriteInteger(_Len);
  Self.WriteBuffer(PByte(_Bytes)^, _Len);
end;
function TAppCommunication.SendRecord(const ARecord: TMyRec): Boolean;
var
  _Stream: TMemoryStream;
begin
  _Stream := TMemoryStream.Create;
  try
    _Stream.WriteString('TMyRec');
    _Stream.WriteString(ARecord.Name);
    _Stream.WriteInteger(ARecord.Age);
    _Stream.WriteDouble(ARecord.Birthday);
    _Stream.Position := 0;
    Result := SendStreamData(_Stream, TCopyDataType.cdtRecord);
  finally
    FreeAndNil(_Stream);
  end;
end;

// more overloads of SendRecord()
// for other kinds of records as needed... 
procedure TSenderMainForm.BitBtn1Click(Sender: TObject);
var
  ...
  _Rec: TMyRec;
begin
  ...
  _Rec.Name := 'Edijs';
  _Rec.Age := 29;
  _Rec.Birthday := EncodeDate(1988, 10, 06);
  _AppCommunication.SendRecord(_Rec);
  ...
end;
type
  TReadOnlyMemoryStream = class(TCustomMemoryStream)
  public
    constructor Create(APtr: Pointer; ASize: NativeInt);
    function Write(const Buffer; Count: Longint): Longint; override;
  end;

constructor TReadOnlyMemoryStream.Create(APtr: Pointer; ASize: NativeInt);
begin
  inherited Create;
  SetPointer(APtr, ASize);
end;

function TReadOnlyMemoryStream.Write(const Buffer; Count: Longint): Longint;
begin
  Result := 0;
end;

procedure TReceiverMainForm.OnAppMessageReceived(const ASender : TPair<HWND, string>; const AReceivedData: TCopyDataStruct; var AResult: integer);
var
  ... 
  _Stream: TReadOnlyMemoryStream;
  _MyRec: TMyRec;
  _RecType: String;
begin
  ...
  else
  begin
    if (AReceivedData.dwData = Ord(TCopyDataType.cdtRecord)) then
    begin
      _Stream := TReadOnlyMemoryStream(AReceivedData.lpData, AReceivedData.cbData);
      try
        _RecType := _Stream.ReadString;
        if (_RecType = 'TMyRec') then
        begin
          _MyRec.Name := _Stream.ReadString;
          _MyRec.Age := _Stream.ReadInteger;
          _MyRec.Birthday := _Stream.ReadDouble;
          ShowMessage(_MyRec.Name + ', Age: ' + IntToStr(_MyRec.Age) + ', birthday: ' + DateToStr(_MyRec.Birthday));
        end;
      finally
        _Stream.Free;
      end;
    end;
    AResult := -1;
  end;
end;