Delphi 在TDatasetProvider.OnUpdateError中引发时无法识别EUpdateError异常。为什么?

Delphi 在TDatasetProvider.OnUpdateError中引发时无法识别EUpdateError异常。为什么?,delphi,delphi-2009,Delphi,Delphi 2009,当我在TDatasetProvider.OnUpdateError事件中重新抛出EUpdateError异常时,它在catch块中不会被识别为EUpdateError异常。它只被识别为基本exoption try ... //calls the TDatasetPorvider.OnUpdateError event. myClientDataSet.ApplyUpdates(0); ... except on ex: EUpdateError do begin //n

当我在
TDatasetProvider.OnUpdateError
事件中重新抛出
EUpdateError
异常时,它在catch块中不会被识别为
EUpdateError
异常。它只被识别为基本
exoption

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是相应的
.onUpdate错误
实现:

procedure MyDataModule.MyDatasetProviderOnUpdateError(..;E: EUpdateError;...);
beign
  //Here, the expression (E is EUpdateException) returns true;
  raise E;
end;
异常被重新抛出,但似乎
EUpdateError
被转换为普通的基本执行选项。
有人知道为什么这类人会迷路吗
我需要该类型来检查
.ErrorCode
,以了解出了什么问题并准备正确的用户消息。

不幸的是,“旧式”DataSnap服务器异常仅作为文本(例如消息)封送到客户端,因此异常类名和实例数据在此过程中丢失。请参阅
SConnect
单元,
tdatablockexplorer.interpretatdata
方法(块除外)

编辑:这里有一个非常简单的例子,给你一个想法(根本没有经过测试):


是否有方法将(sql/数据库)错误代码发送到try-except块,以便区分错误消息(以便对错误代码做出决定)?是的,但您必须修改并重新生成服务器和客户端可执行文件。您可以包含类名并使用一些实用程序函数来流式处理特定的异常实例数据,例如SQL错误代码。TDataSetProvider和TClientDataSet组件位于同一个DataModule(它是一个可执行文件)中。即使对于单个可执行文件,原理仍然是一样的:服务器代码(提供程序)“发送”异常(目前,仅发送其消息以及asError标志)因此,您必须修改该代码以包含异常类名和实例数据。客户端代码必须能够识别异常类,以便读取和解释实例数据。有没有简单的方法可以做到这一点?我不知道去哪里,怎么做。或者,您有简短的示例代码吗?
// 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;