当我向客户端发送一个字符串(tkString)时,客户端在尝试重建TValue时告诉我(WMClientRecv内部)访问冲突,为什么? -_-“与其他类型的数据一起使用效果很好......
例如,Integer(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;
答案 0 :(得分:4)
您正在尝试序列化TTypeInfo
类型的变量。看看它的声明:
TTypeInfo = record
Kind: TTypeKind;
Name: ShortString;
{TypeData: TTypeData}
end;
您的代码将序列化前两个字段,但无法序列化作为实现细节一部分的隐藏字段。这是一种相当特殊的类型。您不能简单地将一个TTypeInfo
变量分配给另一个变量。您打算传递PTypeInfo
个变量。
实际上,请考虑发送类型信息的代码:
InvokeRec.ArgTypeInf := MethodArg.TypeInfo^;
此代码已被破坏,因为它正在分配给TTypeInfo
,正如我上面所述,无法通过简单的赋值完成。
现在,您显然不能轻易地序列化PTypeInfo
,因为它只对拥有它的进程的地址空间有意义。并且您无法轻易调用GetTypeData()
并序列化返回的PTypeData
。这是一个复杂的结构,也包含难以序列化的指针。
我怀疑最简单的方法是推出自己的类型信息序列化机制。也许它足以发送该类型的名称,然后让接收者使用该名称查找它。
答案 1 :(得分:0)
正如我刚刚评论过您的other question,TTypeInfo
是可变长度并包含指向其他数据的指针,因此您无法按原样传输它并使其在接收端有意义。您必须传输TValue.TypeInfo.Name
字符串,然后才能在接收端使用TRttiContext.FindType(Name).Handle
,以便将相应的TTypeInfo
传递给TValue.Make()
。