Reputation:
When i send to client a string (tkString), client tells me (inside WMClientRecv) access violation when trying to rebuild TValue, why ? -_-" with other typed data it works well...
for example, Integer (tkEnum)
here's server code (sends invoke data to client)
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;
Client reads it as:
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;
Routine of 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;
And 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;
Upvotes: 2
Views: 1529
Reputation: 596176
As I just commented on your other question, TTypeInfo
is variable length and contains pointers to other data, so you can't transmit it as-is and have it be meaningful on the receiving end. You will have to transmit the TValue.TypeInfo.Name
string and then you can use TRttiContext.FindType(Name).Handle
on the receiving end to get the appropriate TTypeInfo
to pass to TValue.Make()
.
Upvotes: 0
Reputation: 612954
You are trying to serialize a variable of type TTypeInfo
. Take a look at its declaration:
TTypeInfo = record
Kind: TTypeKind;
Name: ShortString;
{TypeData: TTypeData}
end;
Your code will serialize the first two fields, but will fail to serialize the hidden field that is part of the implementation detail. This is a rather special type. You cannot simply assign one TTypeInfo
variable to another. You are meant to pass around PTypeInfo
variables.
In fact, consider the code that sends the type information:
InvokeRec.ArgTypeInf := MethodArg.TypeInfo^;
This code is already broken because it is assigning to a TTypeInfo
, which as I stated above cannot be done with simple assignment.
Now, you obviously cannot trivially serialize a PTypeInfo
because that only has meaning in the address space of the process that owns it. And you cannot readily call GetTypeData()
and serialize the PTypeData
that is returned. That's a complex structure which also contains hard to serialize pointers.
I suspect the simplest approach will be to roll your own type information serialization mechanism. Perhaps it's enough to send the name of the type and then get the receiver to look it up using that name.
Upvotes: 4