Delphi应用程序与偶尔崩溃的程序通信 - 供应商指责我的Delphi应用程序

4
我编写了一个使用COM与第三方程序通信的Delphi DLL。一些用户报告称第三方程序偶尔会崩溃。其他以完全相同方式使用软件的用户从未遇到过崩溃问题。当此崩溃发生时,第三方程序似乎在我的DLL应用程序中变得不可用。
供应商声称这是Delphi DLL编码方式的问题,尽管他们没有看到源代码,无法确定DLL导致崩溃的具体原因,但他们知道这是“某个问题”。
除了我认为第三方程序不应因我的DLL中的微小问题而崩溃之外,让我们假设我的DLL确实需要修复一些东西。
如何确定我的应用程序可能会引起这种情况?有没有人有使用COM与这样超敏感程序通信的经验?有没有一些常见的事情需要查找,可能会导致第三方程序崩溃?
6个回答

4
  1. 让客户满意。
  2. 不要假设问题不是由你的dll引起的,即使“其他以相同方式使用软件的人从未遇到崩溃”,但在不同的数据情况下,它可能会表现出不同的行为...
  3. 建议您设置日志记录到一个“特殊”的诊断版本的文本文件中。
  4. 记录所有内容,包括参数、异常和您执行的步骤。甚至可以记录每个函数的开始和结束以及每一行代码。

以下是示例:

Loaded DLL
Started MyFunction1 with parameters: 1,4,hello
   1
   2
   ...
   500
Ended MyFunction1

为了实现这个功能,我会设置几个函数(在它们自己的单元中):

// opens a text file (fixed name), and appends to it.
function InitializeLog; 

// closes the file
function CloseLog;      

//add a log line.
function Log(message:string='', startNewFunction:boolean:False); 

你可以这样调用它:
function MyFunction1(Integer,Integer,String);
begin
  try
    Log('Loaded DLL');

    //use inttostr and do some string concats to get the params
    Log('Started MyFunction1 with parameters: 1,4,hello',true); 

    //Then every other line:
    Log; 
    //this would increment a global variable FuncLine:Integer
    //and write it to the file.    

  except
    On E:Exception (Log('***'+E.Message));
  end;
end;

像这样的代码应该有一个{$DEFINE}来启用这些日志功能,以便启用/禁用诊断日志记录。

这也可能很有用。


4

请查看报告58409的Quality Central。

它涉及FPU掩码和Dll。

简单来说,FPU掩码的设置决定了如何处理浮点异常。

例如,如果您有一个由其他人编写的Application_A,它加载了Dll_A(也由其他人编写)和您编写的Dll_B,并且您的Dll更改了FPU掩码,则此更改对Application_A和Dll_A同样有效。

让我们举个例子: 您已安装WinZIP、SubVersion等,这些软件在Windows文件资源管理器中注册了附加功能(右键单击弹出菜单),然后您从应用程序.exe内部调用TOpenDialog,那么这些附加功能可能会破坏您的FPU设置。

希望这能帮到您。 (额外提示:使用Sysinternal查看应用程序加载的dll)


Samuel - 有没有什么方法可以告诉我这种情况在我的情况下是否发生? - Dave
2
保存和恢复控制字,使得应用程序始终看到与以前相同的值。如果这样可以解决问题,那么它就是原因。 - Loren Pechtel

3

你是否考虑过使用MadExcept?如果在接口方法中捕获错误,可以记录调用堆栈或向用户显示一个对话框,并将标准的EOleSysError返回给调用exe。

类似这样:

  except
    on e: Exception do
    begin
      MadExcept.HandleException();

      raise EOleSysError.Create('InitializeObject Failed',
        ErrorNumberToHResult(1 + CODE_BASE), 1);
    end;

如果应用程序挂起但没有抛出异常,您可以使用MadExcept实用程序madTraceProcess查看正在发生的情况。这将让您为运行中的应用程序生成调用堆栈。您的dll不会在主线程上,但您将能够看到您的调用堆栈。这是一个很好的方法来判断当挂起发生时您的dll是否真正做了任何事情。
我有一个与不使用MadExcept的exe交互的COM dll,这种方法对我很有效。

我正在DLL中运行EurekaLog,但是当第三方应用程序崩溃时它没有引发异常。据我所知,当它崩溃时根本没有通知EurekaLog。 - Dave

2

我不知道madExcept等工具是如何工作的,但我经常使用jclDebug.pas + JclHookExcept.pas(来自JEDI JCL库)。它会创建一个Windows API钩子,因此可以捕获所有(!)异常,即使您像这样做:

try
  raise exception.create('test');
except
  //eat exception
end;

通常情况下,你不会看到这个异常,因为它被“吃掉”了...但是使用钩子,你可以得到所有的异常。例如:我曾经在Midas.dll中遇到过一个“灾难性故障”,通过钩子,我看到了在此异常之前dll中出现了一个“数据库连接丢失”的错误,所以我知道发生了什么。(顺便说一下:JclHookExcept.pas = 钩子,jclDebug.pas = 堆栈跟踪)。
我现在发现JclHookExcept.pas还有一个“JclHookExceptionsInModule”过程,所以你可以强制(?)钩住特定库中的所有异常...
一些演示代码:
procedure AnyExceptionNotify(ExceptObj: TObject; ExceptAddr: Pointer; OSException: Boolean);
begin
  //log exception
end;

initialization
  // Start Exception tracking
  JclStartExceptionTracking;
  JclTrackExceptionsFromLibraries;
  JclStackTrackingOptions := [stStack, stRawMode, stAllModules];
  // Assign notification procedure for hooked RaiseException API call. This
  // allows being notified of any exception
  JclAddExceptNotifier(AnyExceptionNotify);

2
如果你在使用他们公布的接口时,程序出现崩溃,那么很有可能存在问题。不过,要证明这一点又是另外一回事了。
你能否创建一个小型的 Delphi 应用程序并复现该问题,然后将其提供给供应商?通过看到可重现的故障,可以帮助说服供应商需要进行修复。至少,这可以帮助确定他们认为你正在做什么“错误”的事情,并告诉你如何做到“正确”。
你还可以尝试使用 C# 或甚至 VBScript 复制失败情况。

我非常赞同。我经历过很多次这种情况,最快的解决方案总是创建一个新的可重复的测试程序并将其与完整源代码一起提供给供应商。如果可能的话,请记录与API的所有交互(传递了哪些参数以及返回了什么)。 - skamradt
4
供应商表示Dave的DLL有问题。 供应商应该制作测试程序,而不是Dave,因为供应商实际上已经看到了问题。 如果没有测试程序,那么至少提供一份步骤清单以演示问题。 - Rob Kennedy
1
我认为供应商没有看到这个问题。我以为这是戴夫的客户的问题。无论如何,我同意供应商应该加强努力,至少帮助隔离问题。不幸的是,他们似乎不是很热心,所以这就落到了戴夫身上,他需要说服他们存在问题或者找到一个解决方法。谁知道,在复制问题时,他可能会发现自己犯了错误。底线是:戴夫需要关注他的客户。 - Bruce McGee
1
是的,供应商在这里并没有提供太多帮助。如果我发现错误,我很乐意承认。第三方应用程序无法处理我的DLL的“多线程”性质的可能性有多大?它是否可能根本没有处理它的能力? - Dave
2
是的,这是可能的。我会直接问供应商他们的应用程序是否支持并发。无论如何,在尝试重现问题时,这可能是一个很好的起点。 - Bruce McGee

2

如果我正确理解了情况,不幸的是,如果您的DLL没有崩溃而被调用的第三方程序停止响应,那么您在您这一侧无法做太多事情。 崩溃是发生在他们的代码中,但只有通过您的DLL调用才会触发。 调试日志应该真正在他们的应用程序中完成。
不过,您可以记录您的DLL对这个第三方程序的所有调用,包括参数和一些上下文信息。
然后,在崩溃之前查看最后一条跟踪可能会给您一些信息...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接