Диагностика по "превышению квоты" Win32Exception

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

У меня только что был один из тех опытов. Я думаю, что вам будет достаточно перечислить угловые данные в виде списка элементов, чтобы вы поняли, что у нас здесь:

  • Win2008 Server
  • 64-битная среда
  • Приложение WPF, используемое несколькими клиентами одновременно
  • Приложение - это средство запуска, которое открывает другие приложения с помощью Process.Start()
  • Иногда мы получаем исключение, указанное ниже
System.ComponentModel.Win32Exception (0x80004005): Not enough quota is
available to process this command
at MS.Win32.UnsafeNativeMethods.PostMessage(HandleRef hwnd,
   WindowMessage msg, IntPtr   wparam, IntPtr lparam)
at System.Windows.Interop.HwndTarget.UpdateWindowSettings(Boolean
   enableRenderTarget, Nullable`1 channelSet)
at System.Windows.Interop.HwndTarget.UpdateWindowPos(IntPtr lParam)
at System.Windows.Interop.HwndTarget.HandleMessage(WindowMessage msg,
   IntPtr wparam, IntPtr lparam)
at System.Windows.Interop.HwndSource.HwndTargetFilterMessage(IntPtr hwnd,
   Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, 
   Boolean& handled)
at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate
   callback, Object args, Int32 numArgs)
at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object
   source, Delegate method, Object args, Int32 numArgs, Delegate
   catchHandler)

РЕДАКТИРОВАТЬ #1 После некоторого осмотра, здесь более подробно:

  • Запуск - это двухэтапный процесс, программа запуска запускает промежуточное окно с помощью Process.WaitForExit()

  • Из промежуточного окна другие процессы могут быть запущены таким же образом (Process.WaitForExit).

  • При открытом только промежуточном окне и отсутствии взаимодействия с пользователем количество дескрипторов процесса запуска увеличивается с течением времени. Максимальное увеличение, которое мы видели здесь, составляет 400 -> 6000 ручек.

Факты, добавленные в Edit, действительно заставляют меня задуматься, не может ли быть где-нибудь утечка дескриптора в фреймворке Я пытаюсь выделить проблему и проверить, могу ли я воспроизвести ее с нуля. Тем временем, любой намек, идея, поддержка или даже шоколад с радостью приняты!

РЕДАКТИРОВАТЬ № 2: В попытке сделать процесс реагировать на PostMessage()мы удалили Thread.WaitForExit, Вместо этого мы добавили обработчик для события Exited процесса и отправили модуль запуска в цикл, как показано ниже:

       while (foo == true)
        {
            System.Threading.Thread.Sleep(1000);
        }

Наборы Exited-Handler foo ложно и больше ничего не делает. Тем не менее, количество Ручек увеличивается (от 400 до 800 за полчаса).

РЕДАКТИРОВАТЬ #3 Вот что-то интересное, наконец.

       while (foo == true)
        {
            System.Threading.Thread.Sleep(1000);
            GC.Collect();
        }

Это сохраняет его таким, каким он должен быть, несколько ручек, все изящно. Теперь это заставляет меня задуматься, что здесь не так... Я снова поговорю с ответственным разработчиком, чтобы проверить, что еще делает программа запуска. До сих пор я слышал, что он читает несколько значений конфигурации, используя XmlDocument.Load (), который не является IDisposable - затрудняет утечку...

2 ответа

Решение

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

Я знаю, что этому вопросу шесть лет, но у нас только что случилась та же самая проблема, и я использовал это для вдохновения. Что мне не понравилось, так это идея спать нить и петлю.

Вместо этого я создал окно WPF. Сделали его прозрачным, шириной в один пиксель и добавили к нему открытое свойство типа Process.

Затем, вместо вызова.WaitForExit, я написал Shared Sub (извините, я использую терминологию VB.NET), которая выполняет следующее

Public Shared Sub DoWaitForProcessToExit(ByVal poProc As Process, ByVal oOwner As Window)
    Dim oWFE As WaitForProcessToExit
    poProc.EnableRaisingEvents = True
    oWFE = New WaitForProcessToExit
    oWFE.oProc = poProc
    If Not oOwner Is Nothing Then
        oWFE.Owner = oOwner
    End If
    oWFE.ShowDialog()
    oWFE = Nothing
End Sub

ПРОСТО, если случается что-то безумное, и процесс уже завершен к моменту активации этого диалога:

Private Sub WaitForProcessToExit_Activated(sender As Object, e As EventArgs) Handles Me.Activated

    Try
        If oProc.HasExited Then
            Try
                RemoveHandler oProc.Exited, AddressOf oProc_Exited
            Catch ex As Exception

            End Try
            'whatever happened .... it seems to have gone too quick for this to invoke the _Exited event
            Me.Close()
        End If
    Catch
        Try
            Try
                RemoveHandler oProc.Exited, AddressOf oProc_Exited
            Catch ex As Exception

            End Try
            Me.Close()
        Catch

        End Try
    End Try
End Sub

Теперь мне нужно сделать это только тогда, когда диалог загружен:

Private Sub WaitForProcessToExit_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded

    AddHandler oProc.Exited, AddressOf oProc_Exited

End Sub

А затем просто ответьте на событие Exited. Нет спит, нет петель.

Private Sub oProc_Exited(sender As Object, e As EventArgs)

    'This event is raised by the exiting process, which is in a different thread, so, invoke my own
    'close method from my own Dispatcher
    Windows.Application.Current.Dispatcher.Invoke(Sub() CloseMe(), Windows.Threading.DispatcherPriority.Render)

End Sub

Private Sub CloseMe()

    Me.Close()

End Sub
Другие вопросы по тегам