Нарушение прав доступа при доступе к COM-объекту из.Net
Прошу прощения, если сообщение слишком длинное, но я был бы рад, если бы кто-нибудь хотя бы указал на полужирные заголовки и указал мне правильное направление. У меня есть эта проблема в течение нескольких дней, но не смог найти ответ в сети. Это то, что я узнал до сих пор.
1. Исключение "нарушение прав доступа" уничтожает мое управляемое приложение
Мое приложение C# WinForms иногда закрывается с исключением "Нарушение прав доступа" ("Попытка чтения или записи в защищенную память"), прямо в момент выбора TabPage в форме окна TabControl. Из трассировки стека (попробуй / поймай вокруг Application.Run) я вижу, что исключение происходит при System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
называется внутри UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32 dwComponentID, Int32 reason, Int32 pvLoopData)
,
- Сообщение: попытка чтения или записи в защищенную память. Это часто указывает на то, что другая память повреждена. - Трассировка стека: в System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg) в System.Windows.Forms.Application.ComponentManager .System.Windows.Forms.UnsafeNativeMethods .IMsoComponentManager.FPushMessageLoopDjectDopImpDataIp32Imp32ImpDataDupDupDupDupIllPlayPlayPlayDirect_Imp32ImpLoopDirect_Imp32ImpLoopDirect_Imp32ImpDataDirect_Imp32ImpLoopDirect_Imp32ImpToopLoopDirect_Imp32ImpLoopDirectUp) в System.Windows.Forms.Application.ThreadContext .RunMessageLoopInner(причина Int32, контекст ApplicationContext) в System.Windows.Forms.Application.ThreadContext .RunMessageLoop(причина Int32, контекст ApplicationContext) в System.Windows.Forms.Application.Run(ApplicationContext context) в MyApp.Program.Main ()
2. Кажется, что неисправный модуль является COM-объектом (ChartFX Client Server 6.2)
Используя WinDbg (с загруженным SoS), я обнаружил его на неуправляемой стороне, внутри ChartFX.ClientServer.Core.dll (это компонент построения диаграмм COM, который мы используем):
(ca84.c98c): нарушение прав доступа - код c0000005 (первый шанс) Исключения первого шанса сообщаются перед обработкой любого исключения. Это исключение можно ожидать и обработать. 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,byt ptr [eax + 4Dh] ds: 0023: 0000004d =??
[edit:] Я также не смог получить информацию о неповрежденном стеке из WinDbg (там говорилось: "Информация о размотке стека недоступна"):
0: 000> кП ChildEBP RetAddr ВНИМАНИЕ: информация о размотке стека недоступна. Следующие кадры могут быть неправильными. 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 ПОЛЬЗОВАТЕЛЬ32! 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. Воспроизвести ошибку нелегко (хотя ее можно спровоцировать менее чем за 5 минут)
У меня есть несколько экземпляров Chart на нескольких вкладках, и это обычно происходит во время переключения вкладок. Я до сих пор не знаю, как воспроизвести его, кроме переключения этих вкладок на несколько минут, прежде чем это произойдет, поэтому я не могу использовать наш контроль версий, чтобы надежно найти сборку, в которой не было этой проблемы. Я получаю доступ к диаграммам через управляемый класс-оболочку AxChart (производный от AxHost), который был автоматически создан дизайнером VS.
4. Каким должен быть мой следующий шаг?
Если бы кто-то мог указать мне на следующий шаг, который я должен сделать, чтобы найти истинную причину, я был бы очень благодарен. Экспериментирование (удаление и возврат кода) не приносит пользы, потому что я не знаю, как его воспроизвести, поэтому на каждую итерацию уйдет много времени, чтобы убедить себя, что ошибка все еще существует.
Я обнаружил, что люди часто предлагают что-то вроде "переключения оптимизации компилятора", но, поскольку исключение не выдается детерминистически, я не хочу просто переставлять некоторые байты и надеюсь, что оно никогда не вернется.
Заранее большое спасибо!
С наилучшими пожеланиями, Groo
3 ответа
Добавив множество следов журналов по всему коду, мне удалось заметить, что в некоторых случаях одно из свойств диаграммы превращается в Double.NaN. После этого приложение всегда зависало при следующей перерисовке диаграммы. Обрабатывая события Chart PrePaint и PostPaint (к счастью, у них есть эти события), я подтвердил, что сбой происходит прямо между этими двумя событиями.
В частности, это происходит только в том случае, если я установил масштаб графика до того, как он был нарисован в первый раз (впервые после последнего обновления). Мне удалось сделать это по-другому, и с тех пор он не разбился.
Я не очень доволен этим "решением", так как это, очевидно, какая-то внутренняя проблема, которая не может быть точно обнаружена до ее фактического сбоя, и я, возможно, только скрываю ее таким образом. Но я должен оставить все как есть, потому что иначе я теряю слишком много времени.
[Обновить]
Реплицированная ошибка успешно
Я выполнил быстрое тестовое приложение, в котором я дважды устанавливал свойства диаграммы, прежде чем она на самом деле была нарисована, и приложение сразу зависало. Я сообщил об ошибке в Software FX, но не получил ответа. Это не первая раздражающая ошибка, с которой я сталкиваюсь с этим элементом управления, но в следующем выпуске мы переключаемся на их управляемую (.Net) версию, поэтому, по крайней мере, у нас будет Reflector, чтобы выяснить, как устранить эти ошибки.
В любом случае спасибо всем!
Это очень похоже на проблему безопасности потоков.
Я бы посоветовал вам начать с прочтения документации по элементу управления, обращая особое внимание на упоминание безопасности потоков. Модель потоков, обычно используемая COM-компонентами, отличается - и часто несовместима - с (тривиальным) использованием.NET.
Был один из них некоторое время назад. Нашим примером был вызов PInvoke: OpenPrinter(string port);
Наша проблема заключалась в том, что управляемый код отправил: "LPT1:", но неуправляемый код объявил массив байтов [1024] и прочитал 1024 байта и далее с адреса строки. Это будет считывать за пределами выделенной строки ("LPT1:") и иногда попадать в память, которая не была выделена для приложения, вызывая это прерывистое исключение AccessViolationException.
Мы исправили это, изменив вызов на: OpenPrinter(int length, string port), чтобы неуправляемый код мог объявить байтовый массив, который был правильной длины.
В этом посте ctacke также есть некоторые полезные сведения о том, что может быть причиной этой проблемы.