从 .Net 访问 COM 对象时发生访问冲突
如果这篇文章太长,我很抱歉,但如果有人至少能指出阅读粗体标题,并为我指出正确的方向,我会很高兴。 这个问题困扰了我几天,但在网上找不到答案。 这些是我迄今为止发现的事情。
1. “访问冲突”异常破坏了我的托管应用程序
我的 C# WinForms 应用程序有时会在 Windows 窗体中选择 TabPage 时因“访问冲突”异常(“尝试读取或写入受保护的内存”)而关闭选项卡控件。 从堆栈跟踪(围绕 Application.Run 进行 try/catch)中,我可以看到异常发生在 UnsafeNativeMethods 内部调用的 System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg) 处。 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 分钟的时间内引发它。)
我在多个 TabPage 中有多个 Chart 实例,这通常在我切换选项卡时发生。 我仍然不知道如何重现它,除了在它发生之前切换这些选项卡几分钟之外,所以我无法使用我们的源代码控制来可靠地找到没有这个问题的构建。 我通过托管 AxChart 包装类(派生自 AxHost)访问图表,该类是由 VS 设计器自动创建的。
4. 我的下一步应该是什么?
如果有人能指出我应该采取的下一步措施以找到实际原因,我将非常感激。 实验(删除并返回代码)并没有多大好处,因为我不知道如何重现它,因此每次迭代都需要花费大量时间才能说服自己错误仍然存在。
我发现人们经常建议“切换编译器优化”之类的建议,但由于异常不是确定性抛出的,我不想简单地重新排列一些字节并希望它永远不会返回。
I am sorry if the post is too long, but I would be happy if someone would at least point read the bolded titles, and point me in the right direction. I am having this problem for couple of days, but was unable to found the answer on the net. These are the things I have found out so far.
1. "Access violation" exception crushes my managed application
My C# WinForms app sometimes closes with an "Access violation" exception ("Attempted to read or write protected memory"), right in the moment when selecting a TabPage in a windows form TabControl. From the stack trace (try/catch around Application.Run) I can see that the exception happens at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
, called inside 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. The faulting module seems to be a COM object (ChartFX Client Server 6.2)
Using WinDbg (with SoS loaded), I caught it on the unmanaged side, inside ChartFX.ClientServer.Core.dll (that's a COM charting component we are using):
(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=??
[edit:]
I also wasn't able to get the unmamanged stack details from WinDbg (it said "Stack unwind info not available"):
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 is not easy to reproduce (although it can be provoked usually in less than 5 min.)
I have several Chart instances in several TabPages, and this usually happens while I am switching the tabs. I still don't know how to reproduce it, besides switching those tabs for several minutes before it happens, so I cannot use our source control to reliably find the build which didn't have this problem. I am accessing the charts through the managed AxChart wrapper class (derived from AxHost), which was created by VS designer automatically.
4. What should be my next step?
If someone could point me to the next step I should do to find the actual cause, I would be very grateful. Experimenting (removing and returning code) does not do much good, because I don't know how to reproduce it, so it would take large amounts of time on each iteration just to convince myself that the bug is still there.
I have found that people often suggest something like "switching compiler optimizations", but since the exception is not thrown deterministically, I don't want to simply rearrange some bytes and hope that it never returns.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
通过在代码中添加大量日志跟踪,我注意到在某些情况下图表的属性之一会变成 Double.NaN。 在我得到之后,应用程序总是在下一次图表重绘期间崩溃。 通过处理图表的 PrePaint 和 PostPaint 事件(幸运的是它有这些事件),我确认崩溃就发生在这两个事件之间。
特别是,只有当我在第一次绘制图表之前(自上次更新以来的第一次)设置图表的缩放时,才会发生这种情况。 我设法以不同的方式做到这一点,从那以后它就没有崩溃过。
我对这个“解决方案”不太满意,因为这显然是一些内部问题,在它实际崩溃之前无法精确检测到,而我可能只是这样隐藏它。 但我必须暂时保留它,因为否则我会浪费太多时间。
[更新]
成功复制错误
我做了一个快速测试应用程序,在实际绘制图表之前,我将某些图表的属性设置了两次,并且应用程序立即崩溃。 我已向 Software FX 报告了该错误,但没有得到答复。 这并不是我在使用此控件时遇到的第一个令人恼火的错误,但对于我们的下一个版本,我们将切换到他们的托管 (.Net) 版本,因此我们至少将使用 Reflector 来找出如何解决这些错误。
不管怎样谢谢大家!
By adding lots of log traces all over the code, I've managed to notice that in some cases one of the Chart's properties turns into Double.NaN. After I get that, app always crashed during next Chart repaint. By handling the Chart's PrePaint and PostPaint events (luckily it has these events), I confirmed that the crash happens right between these two events.
In particular, it only happens if I set the chart's zoom before it has been painted for the first time (first time since the last update). I managed to do it in a different way, and it hasn't crashed since.
I am not very satisfied with this "solution", since it is obviously some internal problem which cannot be precisely detected before it actually crashes, and I may be only hiding it this way. But I have to leave it as it is for now because I am losing too much time otherwise.
[Update]
Replicated the bug successfully
I did a quick test app, where I set some chart's properties twice, before it is actually painted, and application crashes immediately. I have reported the bug to Software FX, but got no answer. This is not the first irritating bug I am having with this control, but for our next release we're switching to their managed (.Net) version, so we will at least have Reflector to find out how to resolve those bugs.
Anyway thanks everyone!
它看起来很像线程安全问题。
我建议您首先阅读控件的文档,专门查找有关线程安全的内容。 COM 组件通常使用的线程模型与(琐碎的).NET 使用不同,并且通常不兼容。
It looks very much like a thread safety issue.
I would suggest you start by reading the control's documentation, looking specifically for mention of thread-safety. The threading model typically used by COM components is different - and often incompatible - with (trivial) .NET usage.
不久前有过其中一个。 我们的例子是 PInvoke 调用:OpenPrinter(string port);
我们的问题是托管代码发送了:“LPT1:”,但非托管代码声明了一个 byte[1024] 数组并从字符串的地址开始读取 1024 个字节。 这将读取已分配字符串(“LPT1:”)的范围之外的内容,并且偶尔会进入未分配给应用程序的内存,从而导致此间歇性的 AccessViolationException。
我们通过将调用更改为 OpenPrinter(int length, string port) 来修复此问题,以便非托管代码可以声明长度正确的字节数组。
这篇文章 by ctacke 还提供了一些关于尝试找出可能导致此问题的原因的文章。
Had one of these a while back. Our case in point was a PInvoke call: OpenPrinter(string port);
Our issue was that the managed code sent: "LPT1:" but the unmanaged code declared a byte[1024] array and read 1024 bytes onwards from the address of the string. This would read outside of the bounds of the allocated string ("LPT1:") and occasionally wander into memory that wasn't allocated for the application thereby causing this intermittent AccessViolationException.
We fixed this by changing the call to: OpenPrinter(int length, string port) so the unmanaged code could declare a byte array that was the correct length.
This post by ctacke also has some goodies on trying to work out what might be causing this issue.