views:

102

answers:

2

Is it possible to detect if a window for a program outside mine is 1) completely visible, 2) partly hidden, or 3) completely hidden? I want to be able to tell my application not to do anything if a window (based on a retrieved handle) isn't visible. I don't care if the window has focus or not,what the z order is, or anything else, I'm just interested in how much of the window shows. If I need something else to get this, I'm fine, but is it possible? Thanks.

+3  A: 

Raymond Chen wrote an article about this a few years ago.

The gist of it is that you can use GetClipBox to tell you what kind of clipping region a window's device context has. A null region means the window is totally obscured, and a complex region means it's partially obscured. If it's a simple (rectangular) region, then visibility depends on whether the visible rectangle coincides with the bounds of the window.

A DC can only be used by one thread at a time. Therefore, you should not acquire the DC of a window for an application that isn't yours. Otherwise, you may encounter a situation where the other application — unaware of what you're doing — attempts to use its DC while you're still using it to inspect the clipping region. It should be perfectly safe to use it to make judgments about your own windows, though.

Rob Kennedy
Just curious: what's your source for those caveats and warnings about DC's and threads ? I can't find anything on MSDN about that, and the fact that GetDC() has a partner ReleaseDC() [which MSDN does say must be called by the same thread that called GetDC()] suggests that thread synchronisation is (or could/should be) handled in the GDI itself... i.e. if thread A tries to GetDC(hWnd) and thread B already has it, then thread A will wait until thread B releases it. This isn't documented, but is intuitively what I would expect. I should add that I haven't tested this theory.
Deltics
[continued]: not what I'd call "confirmation" but this question on experts-exchange does at least seem to support my theory: http://www.experts-exchange.com/Programming/Game/Game_Graphics/DirectX/Q_21986813.html
Deltics
[further continued:] GetDC() can return NULL, indicating a failure to obtain a DC, so in my theoretical thread A/B scenario, it's also possible that thread A might get a NULL DC, rather than being forced to wait for thread B (depending on how the GDI does actually behave in this area). I wonder how much code that calls GetDC() ever tests the result? The VCL [in]famously tests the result in TWinControl.GetDeviceContext() but fails to test for the REASON for any failure and just blindly (and, in the one time I actually encountered it, incorrectly) reports an EOutOfResources.
Deltics
Thanks Rob, the article you linked gives me exactly what I need.
Tom
+1  A: 

Here's the solution I used to determine if a form is actually visible (even only partially) to the user. You can easily adapt to your exact use case.

function IsMyFormCovered(const MyForm: TForm): Boolean;
var
   MyRect: TRect;
   MyRgn, TempRgn: HRGN;
   RType: Integer;
   hw: HWND;
begin
  MyRect := MyForm.BoundsRect;            // screen coordinates
  MyRgn := CreateRectRgnIndirect(MyRect); // MyForm not overlapped region
  hw := GetTopWindow(0);                  // currently examined topwindow
  RType := SIMPLEREGION;                  // MyRgn type

// From topmost window downto MyForm, build the not overlapped portion of MyForm
  while (hw<>0) and (hw <> MyForm.handle) and (RType <> NULLREGION) do
  begin
    // nothing to do if hidden window
    if IsWindowVisible(hw) then
    begin
      GetWindowRect(hw, MyRect);
      TempRgn := CreateRectRgnIndirect(MyRect);// currently examined window region
      RType := CombineRgn(MyRgn, MyRgn, TempRgn, RGN_DIFF); // diff intersect
      DeleteObject( TempRgn );
    end; {if}
    if RType <> NULLREGION then // there's a remaining portion
      hw := GetNextWindow(hw, GW_HWNDNEXT);
  end; {while}

  DeleteObject(MyRgn);
  Result := RType = NULLREGION;
end;

function IsMyFormVisible(const MyForm : TForm): Boolean;
begin
  Result:= MyForm.visible and
           isWindowVisible(MyForm.Handle) and
           not IsMyFormCovered(MyForm);
end;
François