Question

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?

Was it helpful?

Solution

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.

OTHER TIPS

(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.

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top