如何在DLL中创建全局异常处理程序?

Sal*_*dor 5 delphi delphi-xe

我正在使用DLL,有时会引发未处理的异常.我使用madExcept来检测和调试有缺陷的代码,但是当我最终部署我的DLL时,我希望在DLL中包含我自己的全局异常处理程序来记录异常.

所以问题是如何在我的Delphi DLL中设置全局异常处理程序?

Mas*_*ler 10

"全局异常处理程序"的概念在DLL中并不像VCL中存在的那样存在.要理解原因,请记住异常通过展开堆栈来传播,直到找到处理程序.VCL可以安装一个全局异常处理程序,因为在VCL应用程序中,发生的所有事情(不包括启动和关闭)都会TApplication.Run在调用堆栈中的某个位置,而这就是它放置异常处理程序的位置.由于你的DLL没有这样的单一中心点,你不能这样做.

你可以做的是在你的DLL中的某个地方设置一个"中央异常处理程序例程".它应该将一个Exception对象作为参数.然后对所有导出的例程执行类似的操作:

procedure MyExportedRoutine(param: integer);
begin
  try
    //do normal stuff
  except
    on E: Exception do
      CentralExeptionHandler(E);
  end;
end;
Run Code Online (Sandbox Code Playgroud)

除非你使用COM,否则这是你能做的最好的事情.如果您正在编写COM DLL,请使用safecall调用约定标记您的接口方法,编译器将为您静默生成负责异常传播的代码.

  • +1我认为你被迫为你的DLL中的所有入口点执行此操作,因为异常不应跨模块边界传播.拥有入口点例程的通用模板也有其他好处.例如,您可以添加对设置然后恢复浮点控制字的例程的调用,这在您的主机应用程序不是Delphi应用程序时通常会出现问题. (4认同)

Bar*_*lly 10

"全局异常处理程序"到底是什么意思?

Windows结构化异常处理(SEH)(32位)通过遍历发生异常的线程的异常处理程序链来查找处理程序.异常处理程序链是记录的链接列表,其中的头部位于FS:[0]; 记录通常在堆栈上分配,每次按下try并在退出受保护块时弹出.每个异常记录都有一个回调例程; Windows在其搜索阶段调用此例程以及异常的详细信息,以确定链的这个"级别"是否将"处理"异常.Windows然后通过再次遍历异常链将调用堆栈展开到该点,使用不同的值调用每个回调,让它知道展开正在进行,直到它到达选择处理异常的处理程序.如果没有找到处理程序,则该过程很难终止,无需通知.通常这不会发生; 操作系统在堆栈底部(链中的最后一个元素)安装自己的最后机会处理程序,这通常会弹出熟悉的Windows"此程序遇到问题"对话框.但是如果事情变得非常腐败,或者异常处理程序链已经被删除了,那么这个过程会很难实现.

因此,从这个简短的Windows异常处理概述中,应该清楚的是,没有单一的"全局"处理程序,只有一个处理程序列表,每个线程一个列表(FS寄存器是线程上下文的一部分); 并且"最后机会"处理程序是最早安装在堆栈中的处理程序.捕获DLL中发生的异常的最简单方法是在每个入口点立即安装异常处理程序.有关如何执行此操作的详细信息,请参阅Mason的答案(使用try/ except); 但要注意,如果你的DLL回调到其他地方(例如通过回调例程),那么你可能会捕获那些不是"意味着"的异常,而不是由你的代码引起的异常.(期望异常通过DLL级别的第三方代码进行传播是不好的方式,但它可能会发生.)

  • @Warren Borland实际上拥有这种异常处理技术的专利,我认为EMBT现在拥有它.MS为此付了钱,但x64处理的实现方式可能不同,部分原因是因为它获得了专利; 另外,据我所知,GNU/LLVM /等的SEH支持不是很好,部分原因在于专利权.不幸的是,真的. (3认同)