Диагностика по "превышению квоты" 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