2

I wrote some code to determinate whether a certain window is inside the desktop bounds.
Somehow, it doesn't work. For every window, regardless whether it is inside or outside the desktop, false is returned. Something is terribly wrong here, but after staring at this piece of code for 3 hours now, I still don't know where the problem is. If I try to read a RECT struct from the pointer sent in the PMSG's WPARAM, I get a AccessViolationException. Why is this happening?

My code look like this, and always returns false:

static bool IsInBounds(HWND window)
{
    DEVMODE d;
    d.dmSize = sizeof(DEVMODE);
    BOOL b = EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &d);
    if(b == FALSE)
    {
        PostMessage(FindWindow(NULL, L"Window #1"), RegisterWindowMessage(L"FMSG"), (WPARAM)window, NULL);
    }
    RECT R;
    GetWindowRect(window, &R);
    POINT p = POINT();
    p.x = (LONG)d.dmPelsWidth;
    p.y = (LONG)d.dmPelsHeight;
    PostMessage(FindWindow(NULL, L"Window #1"), RegisterWindowMessage(L"PMSG"), (WPARAM)&R, d.dmPelsWidth);
    if(R.right < 0 || R.bottom < 0 || R.left > (LONG)d.dmPelsWidth || R.top > (LONG)d.dmPelsHeight)
    {
        return false;
    }
    return true;
}

EDIT: after calling IsInBounds, which returned an error number 1400 (Invalid window handle), but before returning from the hook callback, I called IsWindow(window), to see, if my handle is still valid. The truth is: it is in deed a valid handle! How can it be that GetWindowRect says its an invalid handle?

EDIT: I tried MonitorFromWindow as sujested, but it returned NULL, and calling GetLastError resulted in error no. 1400, which is already familiar to me. Looks like MonitorFromWindow implicitly calls GetWindowRect. I don't care about the size, but is there another way to get window coordinates from a handle?

alex
  • 1,228
  • 1
  • 16
  • 38
  • Reads a bit weird.. For instance if `EnumDisplayettings` fails, there's no point in getting pixel values from `DEVMODE`. The code, instead, posts some message and continues?? – Sertac Akyuz Jan 22 '11 at 14:49
  • Yeah, thats because the code is inside a c++ DLL, and posts a message to my C# app. The weird thing is, that it *Does not fail* – alex Jan 22 '11 at 15:08
  • What kind of window is it failing on? A specific type of window or everything? I have never tested this, but maybe message only windows don't have a size at all... – Anders Jan 22 '11 at 21:47
  • It fails for every window, I've tested it 1000 times. One time it worked for a maximized window. I don't know why... Everything seems weird. That handle is not invalid. Why does GetWindowRect fail with error number 1400? – alex Jan 22 '11 at 21:54

3 Answers3

4

PostMessage(FindWindow(NULL, L"Window #1"), RegisterWindowMessage(L"PMSG"), (WPARAM)&R, d.dmPelsWidth); will not work if "Window #1" is in another process, you can't just pass a RECT pointer cross process, if you need to pass something larger than a pointer cross process, use WM_COPYDATA. Even if it is not cross process, the fact that you are using PostMessage and not SendMessage means that the RECT is probably out of scope by the time the message is processed!

Using EnumDisplaySettings is a bit overkill, MonitorFromWindow with MONITOR_DEFAULTTONULL should be all you need.

Anders
  • 97,548
  • 12
  • 110
  • 164
  • I'm now checking the return value of GetWindowRect, and I'm always getting error# 1400, which means 'invalid handle'. `IsInBounds` is being called from a CBTProc, a CBT Hook callback. If `HCBT_DESTROYWND` is specified, the callback calls `IsInBounds` to check whether the window associated with the `window` handle, is inbound. for inbound windows, "1" should be returned, for outbound windows, "0". Does this mean, that the handle gets invalid **BEFORE** the window gets destroyed? – alex Jan 22 '11 at 18:12
  • @alex: Called how? Just a normal function call from inside the CBT hook callback, or PostMessage? The window should be valid when the hook is called. – Anders Jan 22 '11 at 18:19
  • a totally normal call like this: `case HCBT_DESTROYWND: if(IsInBounds((HWND)wParam))` – alex Jan 22 '11 at 18:23
3

(1) Straightforward

RECT rDesk = { 0 };
GetBystemParametersInfo(SPI_GETWORKAREA, 0, &rDesk, 0);
RECT rWnd;
GetWindowRect(wnd, &rWnd);
POINT pttl = { rWnd.left, rWnd.top);
POINT ptbr = { rWnd.right, rWnd.bottom);
if (PtInRect(&rDesk, pttl) && PtInRect(&rDesk, ptbr)) 
   // ....

SPI_GETWORKAREA queries the desktop area (as opposed to the screen, which includes the Taskbar etc.) Replace GetSystemParametersInfo with GetMonitorInfo to support multiple monitors.

(2) GetWindowRect
Does the target window belong to an elevated process, or a process running in a different security context?

(3) Other remarks
For RegisterMessage, don't use such non-unique names. After all, it works systemwide. I make a point of embedding a GUID in their name.

PostMessage has already been mentioned: it's asynchronous, and you can pass pointers cross-process.

I don't see why you need to pass your window handle to some other, unrelated window.

peterchen
  • 40,917
  • 20
  • 104
  • 186
  • (1): I'm not interessted in the height, only in the width of the screen, so I dont care about the taskbar. (2): GetWindowRect is called from a CBT Hook callback, only if HCBT_DESTROYWND is specified. `GetWindowRect` claims, that the handle is invalid, but when HCBT_ACTVATE is specified, I called `GetWindowRect` with the _same_ handle, without any problems. *after* calling GetWindowRect, and getting error 1400, I call `IsWindow` for the HWND, and it returns true! Thats really weird. Hope you can help me. – alex Jan 22 '11 at 21:52
  • I can't tell you why it behaves that way - especially since you supposedly *can* abort HCBT_DESTROYWND without the window being destroyed. – peterchen Jan 24 '11 at 22:28
  • Yes, the most wierd thing is that, if I write a hook to abort _every_ HCBT_DESTRYWND, a closed window is still visible in the taskbar, but not on the desktop anymore... so the handle **IS** valid, but there is no window behind it! If you dont believe me, try it yourself. maybe I did something wrong? – alex Jan 25 '11 at 19:56
2

PostMessage() is asynchronous and you're sending a pointer to a local variable of your function (RECT R). That local variable is most likely gone by the time the receiver gets around to processing the message.

Also, you ought to be checking the return value of GetWindowRect() to make sure it didn't fail.

Another thing, you should probably return if EnumDisplaySettings() has failed. The tests at line 16 will be nonsensical if DEVMODE d hasn't been filled in.

nobody
  • 19,814
  • 17
  • 56
  • 77
  • Yeah I know that, but I didn't return there, because I saw that `EnumDisplaySettings` isn't causing the problem. – alex Jan 22 '11 at 18:13