views:

355

answers:

5

I want to determine if a certain window is visible to the user or hidden/occluded. In Windows XP I would use the GetClipBox() function and check for a NULLREGION or empty RECT return value. This worked perfectly fine, but on Windows Vista it does not work if another window is occluding the window. In this case, GetClipBox() is returning SIMPLEREGION with a non-empty RECT.

Does anybody know why this is not working on Vista or if there is another way to check if a window can be seen by the user?

+1  A: 

A somewhat messy way of doing it would be to get the position/size of the window you want to check, and then loop over all the windows higher than it in the Z-order and calculate what parts of your window they cover.

Herms
+2  A: 

In your install of Vista, is desktop window manager composition turned on? I would guess that it is, and that if you turn it off you'll get the expected XP behaviour. The issue is that under Vista with DWM composition, the way windows are handled has changed a lot, and I don't think there'll be a simple way to get at what you want.

DavidK
A: 

In your install of Vista, is desktop window manager composition turned on? I would guess that it is, and that if you turn it off you'll get the expected XP behaviour. The issue is that under Vista with DWM composition, the way windows are handled has changed a lot, and I don't think there'll be a simple way to get at what you want.

Yes, DWM is enabled on my machine. I tried the following to test your assumption:

  1. Right-clicked my application and selected "Properties"
  2. Clicked on the "Compatibility" tab
  3. Enabled the "Disable desktop composition" checkbox
  4. Clicked "Apply"

After this the GetClipBox() function works correctly. So your hunch is correct. I discovered that I can perform this programmatically by using the following function within my application:

DwmEnableComposition(DWM_EC_DISABLECOMPOSITION);

However, this disables transparency effects while my application is running, which is not desirable. I would prefer a less obtrusive solution.

Anyway, thanks for the advice!

flashk
A: 

Since you know what the output from GetClipRect is on both XP and Vista (with and without DWM enabled) you can create a function to determine this which takes different paths based on the OS. In psuedo-code:

function bool IsWindowVisible()
{
   bool isVisible = false;

   if (OSVersion == "XP")
   {
      // GetClipRect tests which run on XP and set isVisible
   }
   else if (OSVersion == "Vista")
   {
      if (DWMCompisitionEnabled == true)
      {
         // GetClipRect tests for Vista with DWM enabled and set isVisible
      }
      else
      {
         // GetClipRect tests for Vista with DWM disabled and set isVisible
      }
   }
   return isVisible;
}
Scott Dorman
+1  A: 

The simple reason GetClipBox() is not returning NULLREGION with DWM enabled is because you are not being clipped! The whole point of the DWM is each window (that isn't a child, like buttons or edit boxes) gets it's own buffer to draw to, so foreground windows can be moved around without having to fill in the windows behind them.

As a simple example, hover over your window entry in the task bar when it is in the background and see it being updated in the preview.

Also note that with glass edges, your window can be completely covered by other windows and still be visible! (You can't even test client area, because of extended glass, like Windows Media Player uses - resize it to as small as it will go and see it use glass for its entire area!) Of course, layered windows (from XP on) and custom window regions meant this could always be the case, but now it's the default.

Summary/TL;DR:

If you are doing heavy animation/fancy effects and want to reduce CPU usage when running under the DWM, probably the best you can do is detect when your application looses foreground and fallback to more CPU friendly updating (NOT no updating! If you get a WM_PAINT, and ignore it because you are in the background, you won't get one when you are activated!).

Simon Buchan
Thanks for the detailed explanation! As you guessed, my app is performing CPU intensive animations, so I try to reduce the CPU usage when my window is not visible. Checking if my app is the foreground window sounds like a reasonable work around, I will try it out.
flashk