1

I have an app that doesn't show any forms most of the time, and I needed it to close by itself when a user was shutting down the system.

So I created a question, and the answer there, SystemEvents.SessionEnding, seemed to work.

At first.

Here is my Program.Main() method:

/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
    #region Pre-launch stuff
    // Are we upgrading from an older version? We need to grab our old settings!
    if (Properties.Settings.Default.UpgradeSettings)
    {
        Properties.Settings.Default.Upgrade();
        Properties.Settings.Default.UpgradeSettings = false;
        Properties.Settings.Default.Save();
    }

    // Visual styles
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);

    #if !DEBUG
    // Add the event handler for handling UI thread exceptions to the event.
    Application.ThreadException += new ThreadExceptionEventHandler(ErrorHandling.Application_ThreadException);

    // Set the unhandled exception mode to force all Windows Forms errors to go through our handler.
    Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);

    // Add the event handler for handling non-UI thread exceptions to the event. 
    AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(ErrorHandling.CurrentDomain_UnhandledException);
    #endif

    // Since we have no forms open we need to watch for the shutdown event; otherwise we're blocking it
    SystemEvents.SessionEnding += new SessionEndingEventHandler(SystemEvents_SessionEnding);

    // ...
    // ...
    // ...

    Application.Run();
}

The method SystemEvents_SessionEnding runs a method calling Exit(), which in turn runs:

    public static void Exit()
    {
        MessageBox.Show("test");

        try
        {
            IconHandler.SuperNotifyIcon.Dispose();
        }
        finally
        {
            Application.Exit();
        }
    }

And yet, my app blocks shutdown at times. And the message box that I added there doesn't show up.

I can't figure out how such a simple, simple path of execution could fail. But it does, and it's a major irritant for both me and my app's users.

Any ideas? Thoughts to throw around?


EDIT:

Per corvuscorax's answer, I've tried adding a form, but it's acted weird - at first, I had this in the form's code:

        ShowInTaskbar = false;
        WindowState = FormWindowState.Minimized;
        Shown += (sender, e) => Hide();

And modified the Program.Exit() method to close that form, which would run the disposal code as well in the FormClosing event.

It turned out disabling ShowInTaskbar stopped the form from receiving a HWND_BROADCAST message. I've commented out that line now, but even so, I found that shutdown blocking was occurring. Once I clicked a button I made to show the form and tried to shut down again, it completed smoothly.

Community
  • 1
  • 1
unrelativity
  • 3,670
  • 6
  • 38
  • 63

2 Answers2

1

Does your code have a message pump?

You need a message pump inside each user interface thread of your application so that any windows messages aimed at your application are dispatched and processed. Even if you never show a Form on the screen if the thread is a user interface thread when it will still be sent other system messages that would need to be dispatched and processed.

In a typical WinForms applications the message pump is inside this call...

Application.Run(new MyForm());

...which only exits once the MyForm instance is closed. In your case it seems you never make a call to the Application.Run method or anything similar. In which case your not handling windows messages. So what is your processing loop when you are not showing a Form?

Phil Wright
  • 22,580
  • 14
  • 83
  • 137
1

I would recommend against not having any forms at all.

Just do a normal WinForms application and set the main Form to be hidden and you get all the message processing working as always.

Addendum

As an added bonus of doing it this way, you also get a console for free while developing - by not hiding the main form, you can have an easy output view, simulate input, trigger actions with buttons, all sorts of useful stuff.

Addendum 2

Here's how I normally create invisible main forms. In the forms constructor, add this:

if (!Debugger.IsAttached)
{
    // Prevent the window from showing up in the task bar AND when Alt-tabbing
    ShowInTaskbar = false;
    FormBorderStyle = FormBorderStyle.FixedToolWindow; 

    // Move it off-screen
    StartPosition = FormStartPosition.Manual;
    Location = new Point(SystemInformation.VirtualScreen.Right+10, SystemInformation.VirtualScreen.Bottom+10);
    Size = new System.Drawing.Size(1, 1);
}

This will hide the window when not running from within the debugger.

corvuscorax
  • 5,850
  • 3
  • 30
  • 31
  • I've been told that a few times before, but I don't think I've been given a reason for why I should do so. So I guess I'll ask you; how come? – unrelativity Jan 03 '11 at 07:05
  • It's just a general thing, I suppose. In my experience, you always buy yourself extra trouble if you go against the way Frameworks are designed to be used. I've created quite a few tray apps over the years, and have never had any problems with the invisible Window approach. But I'm not saying that you can't get the other approach to work, just that it's probably easier to do it with a main form. – corvuscorax Jan 03 '11 at 07:18
  • You sold me on the addendum... even though I can't think of anything I want to implement for a debug window right now :P Well, I guess I'll give using a form a shot, and see what happens when I shut down. – unrelativity Jan 03 '11 at 09:22
  • So I've had a go at your answer, but I've ran into a bit of trouble. I've edited my question to add what's happening. – unrelativity Jan 16 '11 at 06:40
  • @a2h - I've added an example of how I normally create hidden main forms. – corvuscorax Jan 16 '11 at 13:09