6

大多数时候,作为 .Net 开发人员,我们可以自由地在我们的高级抽象世界中胡闹,但有时现实会踢你的私处,告诉你找一个真正懂的人。

我刚刚有过这样的经历之一。我认为将角数据列为项目列表就足够了,以便您了解我们在这里拥有的内容:

  • Win2008服务器
  • 64位环境
  • 多个客户端同时使用的 WPF 应用程序
  • Application 是一个启动器,它使用 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 个句柄。

编辑中添加的事实真的让我想知道框架中的某个地方是否可能存在句柄泄漏。我正在尝试隔离问题并检查是否可以从头开始重现它。与此同时,任何形式的暗示、想法、支持甚至巧克力都被欣然接受!

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

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

Exited-Handler 设置foo为 false 并且不执行任何其他操作。尽管如此,Handles 的数量仍在增加(半小时内从 400 增加到 800)。

编辑#3 有趣的事情终于来了。

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

这使它保持了应有的方式,很少有把手,都很漂亮。现在这让我想知道这里出了什么问题......我将再次与负责的开发人员交谈,以检查启动器的其他功能。到目前为止,我听说它使用XmlDocument.Load()读取了一些配置值,这不是IDisposable- 使得这里很难产生任何泄漏......

4

2 回答 2

6

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

于 2012-04-11T21:05:56.490 回答
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
于 2018-07-06T19:59:01.817 回答