使用WS_OVERLAPPED时,AdjustWindowRectEx()和GetWindowRect()返回错误的大小

14

在打开窗口之前,我想计算窗口的完整大小。我使用AdjustWindowRectEx()来实现这一点。对于一个客户端大小为640x480的窗口,我的代码如下:

    wrect.left = 0;
    wrect.top = 0;
    wrect.right = 640;
    wrect.bottom = 480;
    AdjustWindowRectEx(&wrect, WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX, FALSE, 0);

这将返回以下值:

    left: -3
    top: -22
    right: 643
    bottom: 483

然而,使用CreateWindowEx()打开窗口并传递参数

    wrect.right - wrect.left
    wrect.bottom - wrect.top

随着窗口大小的改变,窗口的物理尺寸实际上是656x515像素。然而,GetWindowRect()返回646x505,即与AdjustWindowRectEx()返回的相同尺寸,但正如我所说,当我在桌面上拍摄屏幕截图并使用绘画程序测量窗口的大小时,它的物理尺寸实际上是656x515像素。有人能解释一下吗?

客户端大小没问题,是640x480,但看起来边框大小计算错误,因为边框使用的像素比AdjustWindowRectEx()和GetWindowRect()计算的多。

我使用的是Windows 7。

编辑:

这个问题被downvote了,是因为问题的标题有误导吗?如MSDN autodocs中所述,WS_OVERLAPPED不受AdjustWindowRectEx()支持。那么有没有其他方法可以计算WS_OVERLAPPED窗口的尺寸?使用WS_OVERLAPPEDWINDOW不是一个解决方案,因为它设置了WS_THICKFRAME和WS_MAXIMIZEBOX,而我不想要这些。

现在有一些测试代码,展示了这个问题。您可以看到客户端大小很好,但窗口的物理大小比GetWindowRect()返回的要大。

#include <stdio.h>

#include <windows.h>

#define CLASSNAME "Test"

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{  
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    WNDCLASSEX wcx;
    RECT wrect;
    HWND hWnd;
    char tmpstr[256];

    memset(&wcx, 0, sizeof(WNDCLASSEX));

    wcx.cbSize = sizeof(WNDCLASSEX);
    wcx.style = CS_HREDRAW|CS_VREDRAW;  
    wcx.lpfnWndProc = WindowProc;
    wcx.hInstance = hInstance;
    wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcx.hbrBackground = GetStockObject(BLACK_BRUSH);  // important! otherwise a borderless window resize will not be drawn correctly        
    wcx.lpszClassName = CLASSNAME;

    RegisterClassEx(&wcx);

    wrect.left = 0;
    wrect.top = 0;
    wrect.right = 640;
    wrect.bottom = 480;     
    AdjustWindowRectEx(&wrect, WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX, FALSE, 0); 

    hWnd = CreateWindowEx(0, CLASSNAME, "Test", WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX|WS_OVERLAPPED, 0, 0, wrect.right - wrect.left, wrect.bottom - wrect.top, NULL, NULL, hInstance, NULL);
    ShowWindow(hWnd, SW_SHOWNORMAL);

    GetWindowRect(hWnd, &wrect);
    sprintf(tmpstr, "%d %d %d %d\n", wrect.left, wrect.top, wrect.right, wrect.bottom);

    AllocConsole();
    WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), tmpstr, strlen(tmpstr), NULL, NULL);

    GetClientRect(hWnd, &wrect);
    sprintf(tmpstr, "%d %d %d %d\n", wrect.left, wrect.top, wrect.right, wrect.bottom);
    WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), tmpstr, strlen(tmpstr), NULL, NULL);

    Sleep(10000);

    DestroyWindow(hWnd);        
    UnregisterClass(CLASSNAME, hInstance);          

    return 0;
}   

2
也许你可以编写一个完整的程序来演示这个问题。 - David Heffernan
窗口报告的大小不包括像投影和其他操作系统元素一样的 OS chrome。 - Jonathan Potter
是的,这就是我所假设的,但我根本没有考虑到阴影。AdjustWindowRectEx()返回的大小与实际大小相差太大了。如果你看一下上面代码打开的窗口的物理大小,你会发现左边窗口边框是8个像素,右边窗口边框也是8个像素,当然不包括阴影。总共16个像素。然而,AdjustWindowRectEx()在x轴上只给出6个像素,即每边3个像素。那么这就是一个完全无用的结果,因为它偏离了10个像素。 - Andreas
由于WS_OVERLAPPEDWINDOW已经解决了问题,因此使用“WS_OVERLAPPEDWINDOW&(〜WS_MAXIMIZEBOX)”删除WS_MAXIMIZEBOX - γηράσκω δ' αεί πολλά διδασκόμε
1
请查看我上面的注释。WS_OVERLAPPEDWINDOW 还会设置 WS_THICKFRAME,而我不需要它。窗口不应该有调整大小的边框。 - Andreas
显示剩余2条评论
2个回答

7

GetWindowRect API无法提供正确的大小:

由于兼容性要求,某些度量值以这样一种方式报告,以使其与启用Aero Glass(更准确地说,是“Windows Vista Aero”)时实际绘制在屏幕上的内容不一致。为了改变系统的外观,对于大多数应用程序而言,这种方法是必需的,但对于这不是问题的应用程序而言,则需要这种方法。然而,最近系统发生了变化,Vista RC1将会返回使用“winver = 6.0”链接的可执行文件从GetWindowRect()获取正确的渲染值。这允许新的和新链接的应用程序从GetWindowRect()获取“正确”的值。

GetWindowRect on non-resizable windows under Aero Glass

请改用DwmGetWindowAttribute来获取正确的大小:

DwmGetWindowAttribute(hWnd, DWMWA_EXTENDED_FRAME_BOUNDS, &wrect, sizeof(wrect));

谢谢,我确实将WINVER设置为0x500进行编译,这解释了错误的值。但是仍然存在问题:DwmGetWindowAttribute()需要一个窗口句柄。是否还有一种可以在没有窗口句柄的情况下计算大小的函数?我需要类似于AdjustWindowRectEx()的东西,它与Aero Glass兼容。 - Andreas

0
如果 AdjustWindowRect 不起作用,只需尝试创建窗口并在窗口创建后调整大小即可。
// Create the window with a nearly correct size
RECT rect;
rect.left = 0;
rect.top = 0;
rect.right = 640;
rect.bottom = 480;  
AdjustWindowRectEx(&rect, WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX, FALSE, 0); 

// try
hWnd = CreateWindowEx(0, CLASSNAME, "Test", WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX|WS_OVERLAPPED, 0, 0, rect.right - rect.left, rect.bottom - rect.top, NULL, NULL, hInstance, NULL);

// Get the size that is really required and adjust
RECT rectCreated;
GetWindowRect(hWnd, &rectCreated);
rect.right += rectCreated.right-rectcreated.left;
rect.bottom += rectCreated.bottom-rectcreated.top;

// Resize to let the window fir the inner client aea
SetWindowPos(hWnd,NULL,0,0,rect.right-rect.left,rect.bottom-rect.top,SWP_NOMOVE|SWP_NOZORDER);

// Show it.
ShowWindow(hWnd, SW_SHOWNORMAL);

代码未经测试。希望其中没有错误或语法错误。


2
但是GetWindowRect()也给出了错误的大小。请参见我上面的示例代码。GetWindowRect()返回与AdjustWindowRectEx()相同的大小,但它们都是错误的。 - Andreas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接