2011-06-06 68 views
5

我正在一个有时会引发未处理的异常的DLL中工作。我正在使用madExcept来检测和调试错误的代码,但是当我最终部署我的DLL时,我想在DLL中包含我自己的全局异常处理程序来记录异常。如何在DLL中创建全局异常处理程序?

所以,问题是我怎么能在我的Delphi DLL设置一个全球性的异常处理程序?

回答

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; 

这真的是你能做的最好的,除非你使用COM。如果你正在编写一个COM DLL,使用safecall调用约定标记你的接口方法,编译器会默默地为你生成代码来处理异常传播。

+4

+1我认为您必须为您的DLL中的所有入口点执行此操作,因为异常应该不跨模块边界传播。拥有入口点例程的通用模板也有其他好处。例如,您可以将调用添加到例程,这些例程设置并恢复浮点控制字,当您的主机应用程序不是Delphi应用程序时,这经常会成为问题。 – 2011-06-06 06:51:55

10

究竟你的“全球异常处理”是什么意思?

视窗结构化异常处理(SEH),在32位,通过走在其中发生异常的线程的异常处理程序链查找处理程序。异常处理链是一个链接的记录列表,其头部位于FS:[0];记录通常在堆栈上分配,每当try时被推送,并在退出受保护块时弹出。每个异常记录都有一个回调例程; Windows在搜索阶段会调用此例程以查看异常的详细信息,以确定链的此“级别”是否将“处理”异常。然后,Windows再次通过异常链展开调用堆栈,使用不同的值调用每个回调,让它知道展开正在进行,直到它到达选择处理异常的处理程序。如果没有找到处理程序,则该进程将被终止,并且不会通知。通常情况下,这不会发生;操作系统会在堆栈底部(链中的最后一个元素)安装自己的最后机会处理程序,这通常会弹出熟悉的Windows“此程序遇到问题”对话框。但是如果事情变得非常糟糕,或者异常处理程序链被搞乱了,那么这个过程就会很难实现。因此,从这个对Windows异常处理的简要概述中,应该清楚的是没有单个“全局”处理程序,只有一个处理程序列表,每个线程一个列表(FS寄存器是线程上下文的一部分);因此,而“最后机会”处理程序是最早安装在堆栈中的处理程序。捕获DLL中发生的异常的最简单方法是立即在每个入口点安装异常处理程序。有关如何做到这一点的细节,请参阅梅森的答案(这与try/except);但请注意,如果您的DLL调用回其他地方(例如,通过回调例程),那么您可能会捕获对您而言并非“意味着”且不是由您的代码引起的异常。 (在DLL级别期望异常通过第三方代码传播的风格很糟糕,但可能发生。)

+0

大巴里!这让我更清楚为什么我有时看到“这个程序遇到了问题”,有时我却没有。我知道在这些情况下它很脆弱,不知道为什么。 – 2011-06-07 01:39:56

+3

@Warren Borland实际上拥有这项异常处理技术方面的专利,我认为EMBT现在拥有它。 MS为此付出了代价,但x64的处理方式有所不同,部分原因可能在于它获得了专利;另外,就我所知,GNU/LLVM /等等都没有优秀的SEH支持,部分原因是由于专利问题。不幸的是,真的。 – 2011-06-07 02:55:24