Diagnosis on "Quota Exceeded" Win32Exception
Asked Answered
H

2

7

Most of the time working as a .Net developer gives us the freedom to mess around in our high-level-of-abstraction world, but sometimes reality kicks you in the private parts and tells you to find a man who really understands.

I've just had one of those experiences. I think it'll suffice to list the corner data as an item list for you to understand what we have here:

  • Win2008 Server
  • 64Bit Environment
  • WPF Application used by multiple Clients simultaneously
  • Application is a launcher, that opens other applications using Process.Start()
  • Occasionally we get the exception listed below
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)

EDIT #1 After some inspection, here's more detail:

  • Launching is a 2-Step-Process, the Launcher launches an intermediate window using Process.WaitForExit()

  • From the intermediate window, further processes can be started in the same way (Process.WaitForExit).

  • With only the intermediate window open and no user interaction, the launcher process's number of handles increases over time. The maximum increase we've seen here is 400 --> 6000 handles.

The facts added in the Edit really make me wonder whether there might be a handle leak in the framework somewhere. I'm trying to isolate the problem and check whether I can reproduce it from scratch. In the meantime, any kind of hint, idea, support or even chocolate is gladly accepted!

EDIT #2 : In an attempt to make the process responsive to PostMessage(), we removed the Thread.WaitForExit. Instead, we added a Handler for the Process's Exited event and sent the Launcher into a loop like the following:

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

The Exited-Handler sets foo to false and does nothing else. Still, the number of Handles rises (400 to 800 in half an hour).

EDIT #3 Here comes something interesting, at last.

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

This keeps it the way it's supposed to be, few handles, all nifty. Now that makes me wonder what's wrong here...I'll talk to the developer in charge again to check back what else the launcher does. So far, I've heard that it reads a few config values using XmlDocument.Load(), which is not an IDisposable - makes it kind of hard to produce any leakage here...

Haruspicy answered 10/4, 2012 at 10:25 Comment(0)
N
7

The error is telling you that a window's message queue reached its max capacity when posting a message to it. That means the thread that owns that window is not processing messages fast enough, if at all.

Noam answered 11/4, 2012 at 21:5 Comment(1)
Hm. This could go back to Thread.WaitForExit(), which leaves the thread unresponsive until the child thread exits...Thanks, I'll try using the event based way of handling process exit and post back here with feedback.Haruspicy
C
2

I know this question is six years old, but we have just had the same issue happen to us, and I used this for inspiration. What I didn't like though is the idea of sleeping the thread and a loop.

Instead, I created a WPF window. Made it transparent and one pixel high and wide, and added a public property of type Process to it.

Then, instead of calling .WaitForExit, I wrote a Shared Sub (sorry, I'm using VB.NET terminology) that does the following

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

JUST in case something mad happens and the process has already exited by the time this dialog is activated:

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

Now I only have to do this when the dialog is loaded:

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

    AddHandler oProc.Exited, AddressOf oProc_Exited

End Sub

And then just respond to the Exited event. No Sleeps, no loops.

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
Chainman answered 6/7, 2018 at 19:59 Comment(1)
Please note that this literally just replaces the .WaitForExit call. The calling code should already be taking care of Closing and Disposing the Process object, and Garbage Collection, if necessary.Chainman

© 2022 - 2024 — McMap. All rights reserved.