I recently had a requirement where a user was able to perform an action externally to my application, and my application then had to detect this for processing.

I could of course just had a poller running away in the background to check, but as the requirement also needed user input, why not just wait until the user switched back to my application, then check and deal with accordingly?

A simple demonstration of application activation and deactivation
A simple demonstration of application activation and deactivation

The Activated and Deactivate events

Your standard Form object has an Activated event and a Deactivate event that (very logically!) are fired when your form is activated and deactivated.

Brilliant! Well, it would be - if your application is a single form application that doesn't show any window ever. Including message boxes. Still, you could do it that way, if your processing was absolute and always done there and then. In my case, I had to give the user a choice - if they said no, then I would simply ask them again later (ie the next time the application was activated). But if I stuck with the Activated event it would mean they would get prompted after displaying another dialog in my application... probably not the sort of behaviour you want to exhibit.

The WM_ACTIVATEAPP message

So how does Windows do it? It does it via the use of the WM_ACTIVATEAPP (MSDN reference). Windows sends this message when a window belonging to a different application than the active window is about to be activated. It's also good enough enough to send it to the window about to be deactivated too, so this one message can cover both activation and deactivation.

Overriding WndProc

Even after all these years of being a C# developer, I still marvel at just how easy this stuff is. In my VB6 days, I could (and did) do exactly the same thing, but it was difficult to implement and fun to debug.

To handle Windows Messages in C# we just need to override the WndProc method on your Form. Then we can check for the WM_ACTIVATEAPP message arriving and handle it accordingly.

csharp
const int WM_ACTIVATEAPP = 0x1C;

protected override void WndProc(ref Message m)
{
  if (m.Msg == WM_ACTIVATEAPP)
  {
    if (m.WParam != IntPtr.Zero)
    {
      // the application is getting activated
    }
    else
    {
      // the application is getting deactivated
    }
  }

  base.WndProc(ref m);
}

According to the MSDN docs, lParam contains the thread identifier, however we can safely ignore this. wParam (the WParam property on the Message struct) is the important bit. It's a simple true or false value (true for activation, false otherwise) and so we just need to check it isn't zero and we're good to go.

Add some events

You could just stick that override in your applications form and use it like that, but as always we should think a little bit about the future and promote some code re-use! In this case, I'm going to create a pair of events and add them (along with the override) to a base class, so next time I want this functionality the events are sitting there waiting.

csharp
public class BaseApplicationForm : Form
{
  public event EventHandler ApplicationActivated;

  public event EventHandler ApplicationDeactivated;

  protected virtual void OnApplicationActivated(EventArgs e)
  {
    EventHandler handler;

    handler = this.ApplicationActivated;

    if (handler != null)
      handler(this, e);
  }

  protected virtual void OnApplicationDeactivated(EventArgs e)
  {
    EventHandler handler;

    handler = this.ApplicationDeactivated;

    if (handler != null)
      handler(this, e);
  }

  protected override void WndProc(ref Message m)
  {
    if (m.Msg == NativeMethods.WM_ACTIVATEAPP)
    {
      if (m.WParam != IntPtr.Zero)
        this.OnApplicationActivated(EventArgs.Empty);
      else
        this.OnApplicationDeactivated(EventArgs.Empty);
    }

    base.WndProc(ref m);
  }
}

A simple demonstration

You can download the demonstration code from the end of this post, but here's a simple example of the code in use:

csharp
public partial class MainForm : BaseApplicationForm
{
  protected override void OnActivated(EventArgs e)
  {
    base.OnActivated(e);

    this.LogEvent("Activated");
  }

  protected override void OnDeactivate(EventArgs e)
  {
    base.OnDeactivate(e);

    this.LogEvent("Deactivate");
  }

  protected override void OnApplicationActivated(EventArgs e)
  {
    base.OnApplicationActivated(e);

    this.LogEvent("ApplicationActivated");
  }

  protected override void OnApplicationDeactivated(EventArgs e)
  {
    base.OnApplicationDeactivated(e);

    this.LogEvent("ApplicationDeactivated");
  }

  private void LogEvent(string text)
  {
    logTextBox.AppendText(string.Format("{0}\t{1}\n\n", DateTime.UtcNow.ToString("hh:mm:ss"), text));
  }
}

The Activated, Deactivate, ApplicationActivated and ApplicationDeactivated events are all being used (well, their overrides are) to log information. If you run the example, you will see that Activated and Deactivate are called whenever the form itself is processed, ie from opening a dialog or displaying a message box, whilst the ApplicationActivated and ApplicationDeactivated events are only called when I switch to another application.

Closing Thoughts

In the above example, the ApplicationActivated and ApplicationDeactivated events are raised as expected - including if you already have a modal dialog open in your application. Just something to keep in mind if you use this sort of functionality to prompt the user for an action. If your events are showing their own modal dialog, that's fine, but if they are trying to do something else, such as set focus to a control, or open a floating window - then you're likely to run into problems.

Update History

  • 2013-12-29 - First published
  • 2020-11-21 - Updated formatting

Like what you're reading? Perhaps you like to buy us a coffee?

Donate via Buy Me a Coffee

Donate via PayPal


Files


Comments

# Daniel

Brilliant sollution, was looking or something like this and it worked perfectly. Thank you!

Reply

# Mark

Very helpful solution. Thank you!

Reply