Sep 26, 2007

How To Detect if Another Application is Running in Full Screen Mode

Recently I wrote a system tray application in C# that displayed reminders for people at regular intervals. What I wanted to avoid was displaying reminders when another application was running in full screen mode.  For example if PowerPoint was showing a presentation or there was a full screen video running the last thing I wanted was an annoying message to come up and bug people.

A quick search on Google showed that there isn't a lot of useful information on how to tell if another program is running in full screen, so it took me a little while to figure it out, but In the end it worked out to be a pretty simple check.

The steps involved are as follows

  1. Get the window handle for the current application.  I assume that if someone is running a full screen application, it's going to be the active application.
  2. Get the size of the display on which it is being shown.  Using the primary display is not appropriate if multiple displays are involved.
  3. Compare the size of the application and the size of the display.  If they match, it's in full screen mode.

There is a catch, however.  If the user is navigating the programs menu, using ALT+TAB, showing the desktop or they have just closed an application and nothing else will have focus.  In these situations the current application is going to be either the desktop or the windows shell (progman) and both of those windows are full screen windows.  If that's the case we still want to show messages.

OK, so let's break down the code.

First up, we need to declare a few methods so that we can query the operating system for window handles and information:

using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}

class MyClass
{
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
private static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll")]
private static extern IntPtr GetShellWindow();
[DllImport("user32.dll", SetLastError = true)]
private static extern int GetWindowRect(IntPtr hwnd, out RECT rc);

GetForegroundWindow returns a handle for the currently active window, GetDesktopWindow returns a handle for the desktop and GetShellWindow returns the handle for the windows shell.


GetWindowRect returns the size of a window specified by a particular handle.  Note that I've created a specific RECT class for this call.  This is because the .NET Rectangle class has a different structure to the RECT class used by the GetWindowRect method.


Now when we run this program the window handles for the Shell and the Desktop aren't going to change (unless something nasty happens to the shell of course!) so we can just call the GetDesktopWindow and GetShellWindow methods during application startup:

    private IntPtr desktopHandle; //Window handle for the desktop
private IntPtr shellHandle; //Window handle for the shell
//Get the handles for the desktop and shell now.
desktopHandle = GetDesktopWindow();
shellHandle = GetShellWindow();

Now whenever we want to show the message window we just need to check if any full screen applications are running as follows:

    //Detect if the current app is running in full screen
bool runningFullScreen = false;
RECT appBounds;
Rectangle screenBounds;
IntPtr hWnd;

//get the dimensions of the active window
hWnd = GetForegroundWindow();
if (hWnd!=null && !hWnd.Equals(IntPtr.Zero))
{
//Check we haven't picked up the desktop or the shell
if (!(hWnd.Equals(desktopHandle) || hWnd.Equals(shellHandle)))
{
GetWindowRect(hWnd, out appBounds);
//determine if window is fullscreen
screenBounds = Screen.FromHandle(hWnd).Bounds;
if ((appBounds.Bottom - appBounds.Top) == screenBounds.Height && (appBounds.Right - appBounds.Left) == screenBounds.Width)
{
runningFullScreen = true;
}
}
}

So, first we get the handle (hWnd) of the current foreground window using GetForegroundWindow().  It's possible that this method can return null, so we'll check it just in case.


We then check if the handle we retrieved is the handle for either the shell or the desktop.  if it is, then we skip the other checks.


Next we call GetWindowRect and to get the top-left and bottom-right corners of the window.  We also determine what display the application is running on and get the full size of the display.  The full size is inclusive of any taskbars, sidebars or other windows that chew up the normal screen real estate.

Then it's just a simple check to see if the dimensions are the same, set a flag, and we're done.

10 comments:

  1. With this code is posible to change another window size?

    Can you help me with one example?

    Thank you in advance.

    ReplyDelete
  2. With this code is posible to change another application window size?

    Could you put a sample?

    Thank you very much in advance

    ReplyDelete
  3. I need to write an application that need to monitor if system changes from full screen mode to normal mode and vice versa. Besides checking size of foreground window periodically, is there a more efficient way to do this.

    Thank you in advance.

    ReplyDelete
  4. Great Post!.
    But Screen doesnt exist in WPF.
    What class Do I have to use?.
    Thank you.

    ReplyDelete
  5. Thank you very much. Checking for desktop and shell window is important. But it will not work with full-screen console application. Anybody knows workaround ?

    ReplyDelete
  6. can you convert this to .NET Visual Basic 2010?

    ReplyDelete
  7. It returns true on "Show Desktop" (ALT+D) command when no any app in fullscreen. In my case it's bad as i developing docked to windows desktop toolbar and i need to detect fullscreen applications (e.g. remote desktop connection, teamviewer and etc..) and only then to hide my toolbar app - elsewhere it cover some fullscreen app area. With this logic i get true when no any active app, just show desktop pressed :)

    ReplyDelete
  8. Thanks a lot for the post it really helped me a lot

    ReplyDelete