Нарушение прав доступа при доступе к COM-объекту из .Net

Прошу прощения, если пост слишком длинный, но я был бы счастлив, если бы кто-нибудь хотя бы указал на прочтение выделенных жирным шрифтом заголовков и указал мне правильное направление. У меня уже несколько дней возникает эта проблема, но я не могу найти ответ в сети. Это то, что я выяснил до сих пор.

1. Исключение нарушения прав доступа приводит к сбою моего управляемого приложения

Приложение My C # WinForms иногда закрывается с исключением нарушения доступа (попытка чтения или записи в защищенную память) прямо в момент выбора TabPage в форме окна TabControl. Из трассировки стека (попробуйте / поймайте 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=??

[edit:] Мне также не удалось получить неподтвержденные данные стека из 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. Ошибка непросто воспроизвести (хотя обычно ее можно спровоцировать менее чем за 5 минут).

У меня есть несколько экземпляров Chart на нескольких страницах вкладок, и обычно это происходит, когда я переключаю вкладки. Я до сих пор не знаю, как его воспроизвести, кроме переключения этих вкладок за несколько минут до того, как это произойдет, поэтому я не могу использовать наш исходный элемент управления, чтобы надежно найти сборку, в которой не было этой проблемы. Я получаю доступ к диаграммам через управляемый класс-оболочку AxChart (производный от AxHost), который был автоматически создан дизайнером VS.

4. Что мне делать дальше?

Если бы кто-нибудь мог указать мне, какой следующий шаг я должен сделать, чтобы найти настоящую причину, я был бы очень благодарен. Эксперименты (удаление и возврат кода) не принесут много пользы, потому что я не знаю, как его воспроизвести, поэтому на каждой итерации потребуется много времени, чтобы убедить себя, что ошибка все еще существует.

Я обнаружил, что люди часто предлагают что-то вроде переключения оптимизаций компилятора, но поскольку исключение не генерируется детерминированно, я не хочу просто переставлять некоторые байты и надеяться, что оно никогда не вернется.


person Groo    schedule 15.03.2009    source источник
comment
Супер безумная идея, я знаю ... Вы когда-нибудь спрашивали людей ChartFX? Может, у них есть обертка .net? Я думаю, что код VS gen'd более надежен, чем все, что вы могли бы написать самостоятельно, поэтому я не уверен, что это может быть причиной ...   -  person    schedule 15.03.2009
comment
Можете ли вы также опубликовать вывод 'kn' из WinDbg? То, что у вас есть, хорошо, но недостаточно информации   -  person Ana Betts    schedule 15.03.2009
comment
У них действительно есть .Net-версия своей библиотеки, просто мы уже купили этот компонент и решили использовать его. Я все еще надеюсь, что это можно как-то исправить, если бы я нашел настоящую причину, стоящую за этим - надеюсь, что я сделал что-то не так, и это можно исправить :).   -  person Groo    schedule 15.03.2009
comment
@Paul Betts - Только что добавил, но "kP" .. У меня есть журнал .txt из WinDbg, поэтому у меня нет вывода "kn", я сделал "kP". Если вы думаете, что это поможет, я начну еще раз и выложу все обновленное. И почему он говорит, что нет информации о размотке стека?   -  person Groo    schedule 15.03.2009


Ответы (3)


Добавляя множество трассировок журнала по всему коду, мне удалось заметить, что в некоторых случаях одно из свойств Chart превращается в Double.NaN. После того, как я получил это, приложение всегда вылетало во время следующей перерисовки диаграммы. Обрабатывая события PrePaint и PostPaint Chart (к счастью, в нем есть эти события), я подтвердил, что сбой происходит прямо между этими двумя событиями.

В частности, это происходит только в том случае, если я устанавливаю масштаб диаграммы до того, как она будет отрисована в первый раз (впервые с момента последнего обновления). Мне удалось сделать это по-другому, и с тех пор он не ломался.

Я не очень доволен этим «решением», так как это, очевидно, какая-то внутренняя проблема, которую нельзя точно обнаружить до того, как она действительно выйдет из строя, и я могу просто скрывать ее таким образом. Но я должен оставить все как есть, иначе я теряю слишком много времени.

[Обновление]

Успешно воспроизведена ошибка

Я сделал быстрое тестовое приложение, где я дважды установил некоторые свойства диаграммы, прежде чем она будет фактически отрисована, и приложение сразу вылетит. Я сообщил об ошибке в Software FX, но не получил ответа. Это не первая раздражающая ошибка, с которой я сталкиваюсь с этим элементом управления, но в следующем выпуске мы переключаемся на их управляемую (.Net) версию, так что у нас, по крайней мере, будет Reflector, чтобы узнать, как исправить эти ошибки.

В любом случае спасибо всем!

person Groo    schedule 19.03.2009

Это очень похоже на проблему безопасности потоков.
Я бы посоветовал вам начать с чтения документации элемента управления, специально ища упоминания о безопасности потоков. Модель потоков, обычно используемая компонентами COM, отличается - и часто несовместима - с (тривиальным) использованием .NET.

person AviD    schedule 15.03.2009
comment
Я использую COM в однопоточной квартире через упомянутый RCW (AxChart). Любые вызовы из управляемого потока Gui должны автоматически отправляться в цикл сообщений STA (я считаю, что RCW должен делать именно это). Так что многопоточность здесь не должна быть проблемой. - person Groo; 16.03.2009
comment
Вы должны убедиться, что либо состояние вашего подразделения потока установлено в управляемой области (TrySetApartmentState), либо ваш Main размечен как [STAThread]. - person Garo Yeriazarian; 17.03.2009

Был один из них некоторое время назад. Нашим примером был вызов PInvoke: OpenPrinter (строковый порт);

Наша проблема заключалась в том, что управляемый код отправил: «LPT1:», но неуправляемый код объявил массив байтов [1024] и прочитал 1024 байта от адреса строки. Это будет читать за пределами выделенной строки («LPT1:») и иногда перемещаться в память, которая не была выделена для приложения, вызывая это прерывистое исключение AccessViolationException.

Мы исправили это, изменив вызов на: OpenPrinter (int length, string port), чтобы неуправляемый код мог объявить байтовый массив правильной длины.

Этот пост by ctacke также предлагает несколько полезных советов, чтобы выяснить, что может вызвать эту проблему.

person Quibblesome    schedule 15.03.2009
comment
Ну, моя самая большая проблема заключается в том, что исключение не возникает, когда я обращаюсь к объекту явно, но когда к нему обращаются из цикла сообщений приложения (может быть, перерисовка или что-то в этом роде?), И я предполагаю, что в этот момент память уже была повреждена как-то. - person Groo; 15.03.2009