“Quota Exceeded”Win32Exception的诊断

时间:2012-04-10 10:25:49

标签: .net wpf performance winapi win32exception

大多数时候,作为.Net开发人员工作,我们可以自由地在我们的高级抽象世界中捣乱,但有时候现实会让你在私密部分踢你,并告诉你找一个真的理解。

我刚刚有过这样的经历之一。我认为只需将角落数据列为项目列表就可以了解我们的内容:

  • Win2008服务器
  • 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 经过一番检查,这里有更多细节:

  • 启动是一个两步过程,Launcher启动一个中间件 使用Process.WaitForExit()

  • 的窗口
  • 从中间窗口,可以以相同的方式启动更多进程(Process.WaitForExit)。

  • 只有中间窗口打开且没有用户交互,启动程序进程的句柄数量会随着时间的推移而增加。我们在这里看到的最大增幅是400 - > 6000把手。

编辑中添加的事实确实让我想知道框架中是否存在句柄泄漏。我正在尝试隔离问题并检查是否可以从头开始重现。与此同时,很高兴接受任何提示,想法,支持甚至巧克力!

编辑#2 :为了使流程对PostMessage()做出响应,我们删除了Thread.WaitForExit。相反,我们为Process的Exited事件添加了一个Handler,并将Launcher发送到如下循环:

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

退出处理程序将foo设置为false,不执行任何其他操作。 Handles的数量仍在增加(半小时内400到800)。

编辑#3 终于有了一些有趣的东西。

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

这使它保持原样,几乎没有把手,都很漂亮。现在这让我想知道这里有什么问题......我会再次与负责人讨论,以检查发射器的其他功能。到目前为止,我听说它使用XmlDocument.Load()读取了一些配置值,这不是IDisposable - 这使得在这里产生任何泄漏很难......

2 个答案:

答案 0 :(得分:5)

错误告诉您窗口的消息队列在向其发布消息时达到了其最大容量。这意味着拥有该窗口的线程没有足够快地处理消息,如果有的话。

答案 1 :(得分:1)

我知道这个问题已有六年历史了,但是我们遇到了同样的问题,我以此为灵感。我不喜欢的是休眠线程和循环的想法。

相反,我创建了一个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