Reputation: 2951
When I re-throw a EUpdateError
exception in the TDatasetProvider.OnUpdateError
event, it is not recognized as EUpdateError
exception in the catch block. It's only recognized as base Excption
.
try
...
//calls the TDatasetPorvider.OnUpdateError event.
myClientDataSet.ApplyUpdates(0);
...
except
on ex: EUpdateError do
begin
//never goes here
//Evaluate ex.ErrorCode
end;
on ex: Exception do
begin
//always goes here
//the expression (ex is EUpdateError) returns false;
end;
end;
Hiere is the corresponding .OnUpdateError
implementaion:
procedure MyDataModule.MyDatasetProviderOnUpdateError(..;E: EUpdateError;...);
beign
//Here, the expression (E is EUpdateException) returns true;
raise E;
end;
The exception is re-thrown, but as it seems the EUpdateError
is transformed into a plain base Execption.
Does anybody know, why the class type get lost?
I would need that type in order to check the .ErrorCode
to know what went wrong and to prepare the proper user message.
Upvotes: 0
Views: 619
Reputation: 37211
Unfortunately, the "old-style" DataSnap server exceptions are marshalled to the client as text only (E.Message) so the exception class name and instance data are lost in the process. See SConnect
unit, TDataBlockInterpreter.InterpretData
method (the except block).
EDIT: Here's a very simplistic example to give you an idea (not tested at all):
// new methods
function TDataBlockInterpreter.ReadException(const Data: IDataBlock): Exception;
var
Flags: TVarFlags;
AClassName, AMessage, AContext: string;
ErrorCode, PreviousError: Integer;
OriginalException: Exception;
begin
AClassName := ReadVariant(Flags, Data);
AMessage := ReadVariant(Flags, Data);
if AClassName = 'EUpdateError' then
begin
AContext := ReadVariant(Flags, Data);
ErrorCode := ReadVariant(Flags, Data);
PreviousError := ReadVariant(Flags, Data);
OriginalException := ReadException(Data);
Result := EUpdateError.Create(AMessage, AContext, ErrorCode, PreviousError, OriginalException);
end
// else if AClassName = ... then ...
else
Result := Exception.Create(AMessage);
end;
procedure TDataBlockInterpreter.WriteException(E: Exception; const Data: IDataBlock);
begin
WriteVariant(E.ClassName, Data);
WriteVariant(E.Message, Data);
if E is EUpdateError then
begin
WriteVariant(EUpdateError(E).Context, Data);
WriteVariant(EUpdateError(E).ErrorCode, Data);
WriteVariant(EUpdateError(E).PreviousError, Data);
WriteException(EUpdateError(E).OriginalException, Data);
end;
end;
// modified methods
procedure TDataBlockInterpreter.DoException(const Data: IDataBlock);
begin
raise ReadException(Data);
end;
procedure TDataBlockInterpreter.InterpretData(const Data: IDataBlock);
var
Action: Integer;
begin
Action := Data.Signature;
if (Action and asMask) = asError then DoException(Data);
try
case (Action and asMask) of
asInvoke: DoInvoke(Data);
asGetID: DoGetIDsOfNames(Data);
asCreateObject: DoCreateObject(Data);
asFreeObject: DoFreeObject(Data);
asGetServers: DoGetServerList(Data);
asGetAppServers: DoGetAppServerList(Data);
else
if not DoCustomAction(Action and asMask, Data) then
raise EInterpreterError.CreateResFmt(@SInvalidAction, [Action and asMask]);
end;
except
on E: Exception do
begin
Data.Clear;
Data.Signature := ResultSig or asError;
WriteException(E, Data);
FSendDataBlock.Send(Data, False);
end;
end;
end;
Upvotes: 1