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;

您的代码将序列化前两个字段,但无法序列化作为实现细节的一部分的隐藏字段。这是一种比较特殊的类型。您不能简单地将一个TTypeInfo变量分配给另一个变量。你的意思是传递PTypeInfo变量。

事实上,考虑发送类型信息的代码:

InvokeRec.ArgTypeInf := MethodArg.TypeInfo^;

这段代码已经被破坏了,因为它正在分配给 a TTypeInfo,正如我上面所说的,这不能通过简单的分配来完成。

现在,您显然不能简单地序列化 a 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 回答