如何诊断COM可调用包装对象创建失败?

39

我正在使用CoCreateInstance创建一个COM对象(来自本地代码):

const 
   CLASS_GP2010: TGUID = "{DC55D96D-2D44-4697-9165-25D790DD8593}";

hr = CoCreateInstance(CLASS_GP2010, nil, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER, IUnknown, out unk);

实际上,我在使用Delphi,这意味着我调用了辅助函数:

CreateComObject(CLASS_GP2010);

大多数情况下,此函数都会成功。但是,在同一个可执行文件、同一个进程中,有时候,调用CoCreateInstance会失败,并显示以下错误信息:
Unspecified error (0x80004005 = E_FAIL)

再次调用该函数可能会成功,也可能失败。没有(明显的)规律。

这不是我的COM dll

如果这是我编写的一个普通的 COM dll,我会从DLL_ATTACH开始放置OutputDebugString,当有人尝试调用DllGetClassObject时,我会确认COM是否正确加载了我的DLL,并且它正在正确地请求实例化一个类。

不幸的是,这不是一个COM dll;它是一个.NET程序集dll。而COM子系统并不简单地“加载”我的dll。相反,COM被指示加载mscoree.dll

HKEY_CLASSES_ROOT
   CLSID
      {DC55D96D-2D44-4697-9165-25D790DD8593}
         InprocServer32
            @default = mscoree.dll

mscoree.dll 导出所需的 GetClassObject 函数。因此,mscoree.dll 返回 E_FAIL,而不是我。该故障从未在我的开发机器上发生过,但在客户机器上始终间歇性地失败。

如何启用 .NET 日志记录?

问题是,由于 mscoree.dll 返回 E_FAIL(而不是任何有用的信息):我该如何让它告诉我问题所在?

例如,似乎唯一遇到此故障的客户(除了大量使用 COM 对象之外)都在 Windows XP 上。也许他们正在经历 .NET 框架中已知的错误(版本 4 之前),其中您无法将不同版本的 .NET 运行时加载到同一进程中

这样做会引入 CLR 版本依赖性,可能与主机进程期望的 CLR 版本冲突

在使用COM包装器时,MSDN上的一篇文章中也提到了这种故障模式;您有选项指定clrVersion

如果已经加载了CLR的另一个版本,并且指定的版本可以在进程内并排加载,则会加载指定的版本;否则将使用加载的CLR。这可能会导致加载失败。

如果这是我的Windows XP上间歇性加载失败的原因,或者是.NET框架之前的旧版本,我该如何让mscoree.dll告诉我呢?

如果原因是其他什么,我怎样才能让.NET告诉我呢?


1
你可以在程序中加入一些诊断代码,以记录任何时候 mscoree.dll 被加载到进程中。你可以记录被加载的版本,然后在遇到故障时,检查进程生命周期内是否已经加载了不同的版本。如果是 CLR 版本冲突的问题,那么唯一可能的解释就是一个调皮的 shell 扩展使用了 .net,而不应该这样做。 - David Heffernan
@DavidHeffernan 我如何在运行时记录模块加载?我见过的唯一一种加载/卸载模块的方式是在 Delphi 的 事件日志 中。额外闲聊:我使用第一人称单数代词的大小写是有意的。 - Ian Boyd
+1;好问题。你应该解释一下HRESULT0x80004005E_FAIL(参见http://msdn.microsoft.com/en-us/library/windows/desktop/aa378137.aspx)。调试CCW和RCW的东西很难。这个.NET代码是你自己的吗?你能调试它吗?这可能是一个不错的开始:http://limbioliong.wordpress.com/2011/06/18/research-topic-hidden-ccw-per-com-client-app/ - Jeroen Wiert Pluimers
2
我会建议使用SysInternals的ProcMon来收集客户环境的日志。它是一个非常好的资源,可以收集组件本身没有提供的深度系统交互日志。 运行该工具确实会对性能造成一定影响,因此客户需要知道在问题重现后启动日志收集,然后停止并将跟踪信息发送给您。 - kb0
2个回答

1
您可以使用程序集绑定失败日志记录功能 - 需要启用该功能,然后您可以使用融合日志查看器查看结果。http://msdn.microsoft.com/en-us/library/e74a18c4(v=vs.110).aspx 不确定如何将其安装到客户机器上。
当我第一次阅读时,我的第一个想法是,考虑到它似乎在win xp机器上最常见,这是一个.NET版本/位问题,还是您的.NET DLL缺少这些机器上不存在的依赖项。

问题在于对象在一刻能够正常创建,然后在下一个调用中失败,接着又能正常工作。dll文件还在那里。 - Ian Boyd

0

至少,如果您在Visual Studio的调试器中运行它,您可能能够捕获第一次机会异常并获得一些见解。至少,您想知道什么样的错误导致E_FAIL。即使您没有调试符号,也应该能够做到这一点。

此外,即使您不能在同一个进程中加载多个.NET VM,但通过一些手动操作App.config和.dll清单,您可能仍然可以加载相同 .NET VM中的.dll,即使它们针对不同的.NET VM进行了编译。

最后,请检查Windows事件查看器下的应用程序事件,看看是否有任何记录。


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