0

I have a Visual Studio solution that contains multiple WPF projects. The main project launches a WPF application that acts as the parent/gateway to the other projects. After navigating the menus on the main application window, there is a button that launches another application that displays MS Access DB information. The code to launch the secondary application from the main application is as follows (with some names/paths changed for simplicity):

private void OpenDatabase(object sender, RoutedEventArgs e)
        {
            ShowInTaskbar = false; // prevents parent app from showing in taskbar
            Process DBAPP = new Process();
            DBAPP.StartInfo.FileName = "path\to\DBAPP.exe";
            DBAPP.Start();
            DBAPP.WaitForExit();

            ShowInTaskbar = true; // parent app now shows in taskbar
        } 

The secondary, database application has ShutdownMode="OnExplicitShutdown", then within the mainWindow.cs ( not the App.xaml(.cs) ) I have a handler for the close button as follows:

private void CloseMainMenu(object sender, RoutedEventArgs e)
        {
            Close();
            Application.Current.Shutdown();
        }

When I hit close on the secondary application I return to the first chunk of code and I see an error on the line ShowInTaskbar = true; that says "System.ComponentModel.Win32Exception: 'Not enough quota is available to process this command'"

I only see this error when I have the secondary application running for more than around 3-5 minutes - possibly the database connection or the windows themselves using up resources; if I quickly open the secondary application and close it immediately - returning to the main application - I do not see this error.

  • Are you calling OpenDatabase from a non-UI thread? Doesn't WaitForExit block? – Rob Goodwin Mar 20 '23 at 14:10
  • @RobGoodwin I call OpenDatabase() on a button click from a the main application GUI. I beleive WaitForExit blocks. Which I would want since I don't want the user interacting with the main application until the secondary application is closed. – UnsanitizedInput Mar 20 '23 at 14:11
  • I feel like that could have something to do with you issue, but that is just a guess. Blocking the UI thread is undesirable IMO. I would disable the pieces you do not want messed with or the whole app window for that manner. – Rob Goodwin Mar 20 '23 at 14:19
  • @RobGoodwin Ok that makes sense, but if I disable the app window I would need to know when the secondary application is closed. I was using WatiForExit() as that signal. I do not want to reference the main application from within the secondary application and do not know what sort of signal to use besides WiatForExit() from within the main applicaiton. – UnsanitizedInput Mar 20 '23 at 14:27
  • WaitForExit is ok, just don't do it on the ui thread so that the ui can respond to events and such – Rob Goodwin Mar 20 '23 at 15:41
  • @RobGoodwin would you mind elaborating what you mean by don't do it on the ui thread? The secondary application is able to respond to events but the main application window is not - which is how I want it. Is there a better way to achieve this? Also by not changing the value for ShowInTaskbar I am no longer seeing the not enough quota error but I am not convinced that was the issue. – UnsanitizedInput Mar 20 '23 at 15:51
  • When making a blocking call, spawn off a task/thread to perform that work so that the main UI thread can still service requests. Does that make sense? I can write up a small sample if that does not – Rob Goodwin Mar 21 '23 at 13:07

1 Answers1

0

From reading other posts on SO (here and here plus countless others) I discovered, that upon returning from the spawned process to the caller process, there is a message queue that needs to be cleared. Directly returning after a blocking call to Process.WaitForExit() may not allow the message queue to be cleared - especially if a significant amount of time has passed - before the caller process (tries to) resumes execution. In order to resolve this, the Process.WaitForExit() call can be put into its own thread; seen here and in my code example below.

Essentially what is being done is that after starting the spawned process, a new thread is started whose only job is to handle the return from Process.WaitForExit() and then die off. If we have a loop that is constantly checking for the thread to be alive every 500ms, there is enough buffer between the thread dying and the caller process attempting to resume execution for the message queue to be cleared; hence no more error.

I was initially seeing the error after about 3-5 minutes but have not seen it with the spawned process running for over 30 minutes and handling many database operations. I would consider this resolved.

private void OpenDatabase(object sender, RoutedEventArgs e)
        {
            Process DBAPP = new Process();
            DBAPP.StartInfo.FileName = "path\to\DBAPP.exe";
            DBAPP.Start();
            Thread DBThread = new Thread(() => waitForProcessExit(DBAPP));
            DBThread.Start();
            while (DBThread.IsAlive) Thread.Sleep(500);
        } 

public static bool waitForProcessExit(Process spawnedProc)
        {
            spawnedProc.WaitForExit();
            return true;
        }
Jen
  • 1,964
  • 9
  • 33
  • 59