2

クライアントに文字列 (tkString) を送信すると、TValue を再構築しようとすると、クライアントから (WMClientRecv 内で) アクセス違反が通知されます。なぜですか? -_-" 他の型付けされたデータではうまく機能します...

たとえば、整数 (tkEnum)

これがサーバーコードです(呼び出しデータをクライアントに送信します)

procedure TServerClass.InvokeMethod(const InvokableMethod: TInvokeableMethod; const MethodArg: TValue);
var
  InvokeRec : packed record
    Method: Array[0..19] of Char;
    ArgRawSize: Integer;
    ArgTypeInf: TTypeInfo;
    ArgRawData: Array[0..255] of Byte; 
  end;
begin
  // copy method name to record
  lstrcpy(InvokeRec.Method, PChar(InvokableMethod));

  // copy arg array to record
  InvokeRec.ArgRawSize := MethodArg.DataSize;

  if (MethodArg.DataSize <> 0) then
  begin
    MethodArg.ExtractRawData(PByte(@InvokeRec.ArgRawData[0]));

    InvokeRec.ArgTypeInf := MethodArg.TypeInfo^;
  end; 

  // send record
  ServerSocket.Socket.Connections[idxSocket].SendBuf(PByte(@InvokeRec)^, SizeOf(InvokeRec));
end;

クライアントは次のように読み取ります。

procedure TClientClass.ClientSocketRead(Sender: TObject; Socket: TCustomWinSocket);
var
  Buffer: PByte;
  BufLen: Integer;

  InvokeRec: TInvokeRec;
begin
  BufLen := Socket.ReceiveLength;

  Memo1.Lines.Append(Format('%d: Received %d bytes', [Memo1.Lines.Count, BufLen]));

  // dump keep-alive from recv buffer
  if (BufLen = 64) then
  begin
    GetMem(Buffer, BufLen);
    try
      Socket.ReceiveBuf(Buffer^, BufLen);
    finally
      FreeMem(Buffer, BufLen);
    end;
    Exit;
  end;

  Socket.ReceiveBuf(PByte(@InvokeRec)^, BufLen);
  SendMessage(Handle, WM_ClientRecv, 0, LPARAM(@InvokeRec));
end;

WMClientRecv のルーチン:

procedure TClientClass.WMClientRecv(var Msg: TMessage);
var
  // we send to server
  ReadRec: TSharedRec;

  // we recv from server
  InvokeRecPtr: PInvokeRec;

  Value: TValue;
begin
  InvokeRecPtr := PInvokeRec(Msg.LParam);

  case InvokeRecPtr.ArgRawSize of
  0:
    Value.Empty;
  else
    TValue.Make(PByte(@InvokeRecPtr.ArgRawData)^, PTypeInfo(@InvokeRecPtr.ArgTypeInf), Value);
  end;

  InvokableSubclass.ExecMethod(InvokeRecPtr.Method, Value);
end;

そしてExceMethod:

procedure TInvokableSubclass.ExecMethod(const vmName: string; const Arg0: TValue);
var
  LContext: TRttiContext;
begin
  //
  FSendMode := TextMode;

  //
  if (Arg0.DataSize = 0) then
  begin
    LContext.GetType(TInvokableSubclass).GetMethod(vmName).Invoke(Self, []);
    Exit;
  end;

  LContext.GetType(TInvokableSubclass).GetMethod(vmName).Invoke(Self, Arg0);
end;
4

2 に答える 2

4

タイプ の変数をシリアライズしようとしていますTTypeInfo。その宣言を見てください:

TTypeInfo = record
  Kind: TTypeKind;
  Name: ShortString;
 {TypeData: TTypeData}
end;

コードは最初の 2 つのフィールドをシリアル化しますが、実装の詳細の一部である隠しフィールドをシリアル化できません。これはかなり特殊なタイプです。TTypeInfoある変数を別の変数に単純に割り当てることはできません。変数を渡すことを意図していPTypeInfoます。

実際、型情報を送信するコードを考えてみましょう。

InvokeRec.ArgTypeInf := MethodArg.TypeInfo^;

このコードは に代入しているため、既に壊れています。これはTTypeInfo、上で述べたように、単純な代入では実行できません。

これは、PTypeInfoそれを所有するプロセスのアドレス空間でのみ意味があるためです。GetTypeData()また、返された を簡単に呼び出してシリアル化することはできませんPTypeData。これは複雑な構造であり、シリアライズが困難なポインターも含まれています。

最も簡単な方法は、独自の型情報シリアル化メカニズムを展開することだと思います。おそらく、型の名前を送信し、受信者にその名前を使用して検索させるだけで十分です。

于 2012-11-13T14:16:22.763 に答える
0

他の質問にコメントしたばかりのように、TTypeInfo可変長であり、他のデータへのポインターが含まれているため、そのまま送信して受信側で意味のあるものにすることはできません。TValue.TypeInfo.Name文字列を送信する必要があります。その後TRttiContext.FindType(Name).Handle、受信側で を使用して、適切なTTypeInfoを に渡すことができますTValue.Make()

于 2012-11-13T19:17:43.397 に答える