从.Net访问COM对象时的访问冲突

时间:2009-03-15 16:58:51

标签: .net com access-violation chartfx

如果帖子太长,我很抱歉,但如果有人至少会看到粗体标题,我会很高兴,并指出我正确的方向。我有几天这个问题,但无法在网上找到答案。这些是我到目前为止发现的事情。

1。 “访问冲突”异常会破坏我的托管应用程序

我的C#WinForms应用程序有时会以“访问冲突”异常(“尝试读取或写入受保护的内存”)关闭,就在Windows窗体TabControl中选择TabPage时。从堆栈跟踪(尝试/捕获Application.Run)我可以看到异常发生在System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg),在UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32 dwComponentID, Int32 reason, Int32 pvLoopData)内调用。

-- Message: Attempted to read or write protected memory.
   This is often an indication that other memory is corrupt.
-- Stack trace:
   at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
   at System.Windows.Forms.Application.ComponentManager
      .System.Windows.Forms.UnsafeNativeMethods
      .IMsoComponentManager.FPushMessageLoop
      (Int32 dwComponentID, Int32 reason, Int32 pvLoopData)
   at System.Windows.Forms.Application.ThreadContext
      .RunMessageLoopInner(Int32 reason, ApplicationContext context)
   at System.Windows.Forms.Application.ThreadContext
      .RunMessageLoop(Int32 reason, ApplicationContext context)
   at System.Windows.Forms.Application.Run(ApplicationContext context)
   at MyApp.Program.Main()

2。错误模块似乎是一个COM对象(ChartFX Client Server 6.2)

使用WinDbg(加载了SoS),我在非托管端,ChartFX.ClientServer.Core.dll(我们正在使用的COM图表组件)中捕获它:

(ca84.c98c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=06e67c38 ecx=06e67c38 edx=000018c6 esi=06e7df30 edi=317a9e80
eip=31666110 esp=0015e040 ebp=0015e08c iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010246
ChartFX_ClientServer_Core!Ordinal5507+0x97b7:
31666110 8a404d          mov     al,byte ptr [eax+4Dh]      ds:0023:0000004d=??

[编辑:] 我也无法从WinDbg中获取未处理的堆栈详细信息(它说“堆栈展开信息不可用”):

0:000> kP
ChildEBP RetAddr  
WARNING: Stack unwind information not available. Following frames may be wrong.
0015e08c 3166288b ChartFX_ClientServer_Core!Ordinal5507+0x97b7
0015e394 3165a921 ChartFX_ClientServer_Core!Ordinal5507+0x5f32
0015e480 31678685 ChartFX_ClientServer_Core!Ordinal5496+0x26a
0015e568 3167bef4 ChartFX_ClientServer_Core!Ordinal5492+0x975
0015e668 316a356b ChartFX_ClientServer_Core!Ordinal5492+0x41e4
0015e77c 31709496 ChartFX_ClientServer_Core!Ordinal443+0x5745
0015e7d0 31707f70 ChartFX_ClientServer_Core!Ordinal2584+0x3cdc
0015e7f8 3170817d ChartFX_ClientServer_Core!Ordinal2584+0x27b6
0015e81c 3162fd76 ChartFX_ClientServer_Core!Ordinal2584+0x29c3
0015e86c 7719f8d2 ChartFX_ClientServer_Core!Ordinal899+0x6b6
0015e898 7719f794 USER32!GetMessageW+0x93
0015e910 771a06f6 USER32!GetWindowLongW+0x115
0015e940 771a069c USER32!CallWindowProcW+0x75
0015e960 747fcef4 USER32!CallWindowProcW+0x1b
0015e97c 747fd073 comctl32!Ordinal377+0x5c
0015e9e0 747fd027 comctl32!DefSubclassProc+0x92
0015ea04 747fd4e6 comctl32!DefSubclassProc+0x46
0015ea20 747fd073 comctl32!DefSubclassProc+0x505
0015ea84 747fd118 comctl32!DefSubclassProc+0x92
0015eae4 7719f8d2 comctl32!DefSubclassProc+0x137

第3。 Bug不容易重现(虽然它通常可以在不到5分钟内激发。)

我在几个TabPages中有几个Chart实例,这通常在我切换选项卡时发生。我仍然不知道如何重现它,除了在它们发生之前将这些标签切换几分钟,所以我不能使用我们的源代码控制来可靠地找到没有这个问题的构建。我通过管理的AxChart包装类(派生自AxHost)访问图表,该类由VS设计器自动创建。

4。我的下一步应该是什么?

如果有人能指出我下一步我应该找到真正的原因,我将非常感激。试验(删除和返回代码)没有多大帮助,因为我不知道如何重现它,因此每次迭代需要花费大量时间才能让自己相信错误仍然存​​在。

我发现人们经常建议像“切换编译器优化”这样的东西,但由于异常没有被确定性地抛出,我不想简单地重新安排一些字节并希望它永远不会返回。

提前多多感谢!

祝你好运, Groo

3 个答案:

答案 0 :(得分:2)

通过在代码中添加大量日志跟踪,我注意到在某些情况下,Chart的某个属性变为Double.NaN。在我得到它之后,应用程序总是在下一次图表重绘期间崩溃。通过处理Chart的PrePaint和PostPaint事件(幸运的是它有这些事件),我确认崩溃发生在这两个事件之间。

特别是,只有在我第一次绘制图表(自上次更新后第一次)之前设置图表的缩放时才会发生这种情况。我设法以不同的方式做到了,并且从那以后它没有崩溃。

我对这个“解决方案”不是很满意,因为它显然是一些内部问题,在它实际崩溃之前无法准确检测到,我可能只是这样隐藏它。但我现在必须保留现状,因为否则我会失去太多时间。

<强> [更新]

成功复制错误

我做了一个快速测试应用程序,在实际绘制之前,我设置了一些图表属性两次,并且应用程序立即崩溃。我已向Software FX报告了该错误,但没有得到答案。这不是我对这个控件的第一个恼人的错误,但是对于我们的下一个版本,我们将切换到他们的托管(.Net)版本,所以我们至少会让Reflector找到如何解决这些错误。

无论如何,谢谢大家!

答案 1 :(得分:1)

它看起来非常像线程安全问题 我建议你从阅读控件的文档开始,专门提到线程安全性。 COM组件通常使用的线程模型与(微不足道的).NET使用不同 - 并且通常是不兼容的。

答案 2 :(得分:0)

有一次这样做了。我们的例子是PInvoke调用:OpenPrinter(字符串端口);

我们的问题是托管代码发送:“LPT1:”但是非托管代码声明了一个byte [1024]数组,并从字符串的地址开始向前读取1024个字节。这将读取分配的字符串(“LPT1:”)的边界之外,偶尔会进入未分配给应用程序的内存,从而导致此间歇性AccessViolationException。

我们通过将调用更改为:OpenPrinter(int length,string port)来解决此问题,因此非托管代码可以声明一个正确长度的字节数组。

ctacke的{p> This post也有一些好处,试图弄清楚可能导致这个问题的原因。