AccessViolationException 在使用 COM 互操作的 .NET 应用程序中随机发生

Posted

技术标签:

【中文标题】AccessViolationException 在使用 COM 互操作的 .NET 应用程序中随机发生【英文标题】:AccessViolationException occurs randomly in .NET application using COM interop 【发布时间】:2011-10-27 04:04:11 【问题描述】:

最近,我们使用结构化编程技术重写了一个工作原型库以实现可维护性。它使用应用程序的基于 COM 的 API 从第三方应用程序 (TPA) 访问数据。 API 使用 Win32 消息传递 (WM_COPYDATA) 与 TPA 进行通信。

新库代码:

    使用 C# 和 Visual Studio 2010 SP1 编写。 面向 .NET Framework 4(原型中为 3.5)。 使用匿名方法和新的自定义泛型类来分解和简化原始代码,这两者都不使用(除了一些 .NET Framework 实用程序类型,如 HashSet)。 使用非嵌入式互操作类型 (Embed Interop Types=False) 和免注册 COM (Isolated=True) 引用基于 COM 的 API。 使用 Marshal.ReleaseComObject 对基于 COM 的 API 返回的对象进行确定性内存管理。 仅在单线程单元中运行的工作线程上使用基于 COM 的 API(基于 COM 的 API 是单元线程的)。 在调用基于 COM 的 API 时使用早期绑定。

我们的测试应用程序在基于 COM 的 API 中随机崩溃并出现 AccessViolationException。堆栈跟踪显示,这发生在对 UnsafeNativeMethods.DispatchMessageW 的调用(参见下面的示例)或基于 COM 的 API 中的几个不同属性中的任何一个中。测试器应用程序是一个简单的 WinForms 应用程序,带有菜单、状态栏和 RichTextBox。

这种行为在 Windows XP 和 Windows 7 中很常见,无论是否启用了工作线程和 UI 线程之间的线程间通信,无论我们是否将 ReleaseComObject 替换为 FinalReleaseComObject。当 TPA 非常繁忙时,它似乎更频繁地崩溃,例如。正在启动时。

原型库是使用VS 2008开发的。将项目转换为VS 2010后,原型应用程序仍然没有崩溃。

System.AccessViolationException 未处理 Message=Attempted to 读或写受保护的内存。这通常表明其他 内存已损坏。源 = System.Windows.Forms StackTrace: 在 System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(味精和味精) 在 System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 原因, Int32 pvLoopData) 在 System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 原因,ApplicationContext 上下文) 在 System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 原因,ApplicationContext 上下文) 在 System.Windows.Forms.Application.Run(窗体 mainForm) 在 D:\TestApps\Indexer\IndexerTester\Program.cs:line 17 中的 IndexerTester.Program.Main() 处

你见过类似的问题吗?

您能否就找出原因和/或潜在的解决方法提供任何建议?

【问题讨论】:

调用堆栈显示一个调用从工作线程封送到主线程。仅当您在该 STA 线程上创建 COM 对象时,您的 STA 才能工作。但这不太可能是问题所在,本机代码很少需要 C# 程序的帮助来轰炸 AV。你看不到任何东西,因为你没有启用非托管调试。 谢谢,我不知道这个调用堆栈代表线程间封送处理。不幸的是,我对非托管调试没有经验。我不确定我是否可以使用它,因为我无权访问 COM 源代码或其 PDB 文件。你能推荐一些资源来学习如何做到这一点吗? 如果没有这些资源,您将无能为力。编写一个单元测试来重现崩溃。并将其发送给代码的所有者,让他帮助您。 再次感谢。您关于线程间封送处理的观点帮助我意识到,无论工作线程是否将其状态/进度发送到 UI 线程,都会发生这种情况。我猜想 COM 对象负责这种封送处理。当我删除工作线程时,错误消失了;所以COM对象和工作线程之间存在冲突。 COM 对象使用 Win32 消息传递;我将尝试恢复工作线程,添加一个消息泵供 COM 对象使用。 更新:我们向 COM 对象的供应商发送了错误报告。供应商确认其代码中存在错误(非线程安全静态变量)并发布了修复程序。 【参考方案1】:

这有点晚了,但可能会对某人有所帮助。

我们向 COM 对象的供应商发送了错误报告:使用两个实例时会发生 AccessViolationException,每个实例位于不同的线程上。供应商确认其代码中存在错误(非线程安全静态变量)并发布了修复程序。

我们注意到,当第三方应用程序 (TPA) 和我们的测试应用程序(从 TPA 读取数据)都很忙时,崩溃会更加频繁。

至关重要的是,我们发现在繁重的工作量下,使用原型应用程序也会发生崩溃;我们错误地认为我们的重写引入了一个错误。这一发现说服我们向供应商报告。

【讨论】:

以上是关于AccessViolationException 在使用 COM 互操作的 .NET 应用程序中随机发生的主要内容,如果未能解决你的问题,请参考以下文章

SQLite 的 System.AccessViolationException

字符串太长时出现 AccessViolationException (C++)

为啥访问非托管内存会导致 System.AccessViolationException? [复制]

OpenCurrentDatabase 生成 System.AccessViolationException

RabbitMQ C# 驱动程序导致 System.AccessViolationException

OleDbConnection.Open() 引发的 AccessViolationException