3

@EDIT:我发现这似乎是 Windows 8 RC 的问题,因为我尝试使用 Windows 7 和 VS 2012,包括经典视图和 Aero 视图,它工作正常。感谢@Werner Henze 和@Ven Boigt 的反馈

编辑 2:原来这是 Windows 中的一个错误,因为它是测试版,它在较新的版本中得到了修复,所以我不必再担心这个了。无论如何感谢您的反馈。

我曾经执行以下操作来创建一个 800*600 客户区的窗口:

dwStyle = WS_OVERLAPPED | WS_THICKFRAME | WS_BORDER | WS_DLGFRAME | WS_SYSMENU | WS_MINIMIZEBOX;
hWindowHandle = CreateWindow(   L"CGFramework", wszWndCaption, 
                            pWindowData->dwStyle, pWindowData->nPositionX, pWindowData->nPositionY, 
                            800 + GetSystemMetrics( SM_CXSIZEFRAME )*2, 
                            600 + GetSystemMetrics( SM_CYSIZEFRAME ) *2
                            + GetSystemMetrics( SM_CYCAPTION ),
                            0, 0, hInstance, 0
                        ); 

然后当用GetClientRect查询客户端矩形时,我以前得到800*600,但现在我将我的Visual Studio 2008项目升级到VS2012,现在GetClientRect()函数返回792*592。

最重要的是,正在创建的窗口的实际大小是 804*629,我认为这是没有原因的,因为可调整大小的框架(来自 WS_THICKFRAME)明显大于每边 2 个像素。

我认为这是 Windows 8 的 Aero 行为问题,但后来我意识到这仅发生在我的 VS2012 版本中,而不是我的 VS2008 版本中。如果我在 Aero 或经典风格上运行它没有区别,该行为仅适用于 VS2012 构建。为什么?我可以对我的 VS2012 项目配置进行更改以解决这种可怕的行为吗?

我尝试更改项目配置的“DPI Awareness”设置,但这没有任何区别。我还在同一个配置页面中删除了清单的使用,但在结果窗口中仍然看不到任何变化。

这是我的测试代码:

#include <Windows.h>
#include <cstdio>

LRESULT CALLBACK MainWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
    switch( uMsg )
    {
    // WM_DESTROY is sent when the window is being destroyed.
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    default:
        return DefWindowProc( hWnd, uMsg, wParam, lParam );
    }
}

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, INT nShowCmd )
{

    WNDCLASS wc;
    wc.style                = NULL; // CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc          = MainWndProc; 
    wc.cbClsExtra           = 0;
    wc.cbWndExtra           = 0;
    wc.hInstance            = hInstance;
    wc.hIcon                = LoadIcon(0, IDI_APPLICATION);
    wc.hCursor              = LoadCursor(0, IDC_ARROW);
    wc.hbrBackground        = CreateSolidBrush(GetSysColor(COLOR_3DFACE));
    wc.lpszMenuName         = 0;
    wc.lpszClassName        = L"CGFramework";
    DWORD dwStyle   = WS_OVERLAPPED | WS_THICKFRAME | WS_BORDER | 
                                WS_DLGFRAME | WS_SYSMENU | WS_MINIMIZEBOX; // WS_DLGFRAME | 
    if( !RegisterClass(&wc) )
    {
        MessageBox(0, L"RegisterClass FAILED", 0, 0);
        return E_FAIL;
    }

    RECT r;
    //r.left        = 100;
    //r.top     = 100;
    //r.right       = 800;
    //r.bottom  = 600;
    ////-----------------------------
    r.left      = 100;
    r.top       = 100;
    r.right     = 900;
    r.bottom    = 700;
    ////-----------------------------
    //r.left        = 100;
    //r.top     = 100;
    //r.right       = 800+GetSystemMetrics( SM_CXFRAME )*2;
    //r.bottom  = 600+GetSystemMetrics( SM_CYFRAME )*2+GetSystemMetrics( SM_CYCAPTION );

    BOOL result = AdjustWindowRect( &r, dwStyle, FALSE );

    HWND hWindowHandle = CreateWindow( L"CGFramework", L"testWindow", dwStyle,
                          r.left, r.top, r.right-r.left, r.bottom-r.top, 
                          // r.left, r.top, r.right, r.bottom, 
                          0, 0, hInstance, 0 );



    if( 0 == hWindowHandle )
    {
        MessageBox(0, L"CreateWindow FAILED", 0, 0);
        UnregisterClass( wc.lpszClassName, hInstance );
        return 0;
    }

    char buffer[512]; // for outing test message
    GetClientRect( hWindowHandle, &r );
    sprintf( &buffer[0], "left=%i, top=%i, right=%i, bottom=%i", r.left, r.top, r.right, r.bottom );
    MessageBoxA(0, buffer, 0, 0); // print rect values before ShowWindow

    ShowWindow( hWindowHandle, SW_SHOW );

    GetClientRect( hWindowHandle, &r );
    sprintf( &buffer[0], "left=%i, top=%i, right=%i, bottom=%i", r.left, r.top, r.right, r.bottom );
    MessageBoxA(0, buffer, 0, 0);  // print rect values after ShowWindow

    // main window loop
    MSG msg;
    ZeroMemory( &msg, sizeof( MSG ) );
    while( msg.message != WM_QUIT )
    {
        while( PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) )
        {
            TranslateMessage( &msg );
            DispatchMessage( &msg );
        }

        if( GetAsyncKeyState( VK_ESCAPE ) )
            DestroyWindow( hWindowHandle );
    }
    UnregisterClass( wc.lpszClassName, hInstance );
    return 0;
}

请在回答之前仔细查看代码,因为您最终可能会回答我已经尝试过的问题(我发布这个问题是因为我已经浪费了所有的选择)。

我正在使用 Windows 8 RC(MS windows 6.2.8400)和 VS2012 RC(11.0.50706.0 QRELRC 2012 年 7 月)来获得这种奇怪的行为,并且没有任何一个好的答案能够解决这个问题。在做出任何假设之前,请务必阅读它们并测试我的代码,因为这段代码已经在许多方面进行了测试,但存在细微的差异,最终没有提供任何改进。

4

3 回答 3

6

CreateWindow函数是 Windows API 的一部分,它不是由 Visual Studio 提供的,Visual Studio 版本不影响它。

最可能的解释是 VS2012 中的默认项目设置添加了一个清单,将应用程序标记为 Aero-aware 或 DPI-aware,并且该清单的存在改变了 WinAPI 函数的行为。您可以编辑或删除清单以获取旧行为。

Windows 也有可能(但不太可能)正在检查 PE 标头中的“最低操作系统版本”字段。较新版本的链接器通常不支持将旧版本写入此字段,但该editbin工具可用于在链接后更改它。

于 2012-08-06T13:45:37.603 回答
6

正如其他人所说AdjustWindowRect,或者AdjustWindowRectEx是要走的路。如果设置了WS_VSCROLLWS_HSCROLL,则需要进行特殊处理。在我的 util 库中WS_EX_STATICEDGE,我也对AdjustWindowRect[Ex].

您最新测试中的错误是您调用CreateWindow错误的方式,它需要宽度和高度,而不是右和下。这应该按要求工作:

dwStyle = WS_OVERLAPPED | WS_THICKFRAME | WS_BORDER | WS_DLGFRAME | WS_SYSMENU | WS_MINIMIZEBOX; 
RECT r; 
r.left      = 100; 
r.top       = 100; 
r.right     = 900; 
r.bottom    = 700; 
BOOL result = AdjustWindowRect( &r, dwStyle, FALSE ); 
hWindowHandle = CreateWindow( L"CGFramework", wszWndCaption, pWindowData->dwStyle,
                              pWindowData->nPositionX, pWindowData->nPositionY,  
                              r.left, r.top, r.right-r.left, r.bottom-r.top, 
                              0, 0, hInstance, 0 );

仅供参考,这里是完整的示例:

#include "stdafx.h"

#include <Windows.h> 
#include <cstdio> 
#include <cassert>

LRESULT CALLBACK MainWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) 
{ 
    switch( uMsg ) 
    { 
    // WM_DESTROY is sent when the window is being destroyed. 
    case WM_DESTROY: 
        PostQuitMessage(0); 
        return 0; 
    default: 
        return DefWindowProc( hWnd, uMsg, wParam, lParam ); 
    } 
} 

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, INT nShowCmd ) 
{ 

    WNDCLASS wc; 
    wc.style                = NULL; // CS_HREDRAW | CS_VREDRAW; 
    wc.lpfnWndProc          = MainWndProc;  
    wc.cbClsExtra           = 0; 
    wc.cbWndExtra           = 0; 
    wc.hInstance            = hInstance; 
    wc.hIcon                = LoadIcon(0, IDI_APPLICATION); 
    wc.hCursor              = LoadCursor(0, IDC_ARROW); 
    wc.hbrBackground        = CreateSolidBrush(GetSysColor(COLOR_3DFACE)); 
    wc.lpszMenuName         = 0; 
    wc.lpszClassName        = L"CGFramework"; 
    DWORD dwStyle   = WS_OVERLAPPED | WS_THICKFRAME | WS_BORDER |  
                                WS_DLGFRAME | WS_SYSMENU | WS_MINIMIZEBOX; 
    if( !RegisterClass(&wc) ) 
    { 
        MessageBox(0, L"RegisterClass FAILED", 0, 0); 
        return E_FAIL; 
    } 

    RECT r; 
    //r.left        = 100; 
    //r.top     = 100; 
    //r.right       = 800; 
    //r.bottom  = 600; 
    ////----------------------------- 
    r.left      = 100; 
    r.top       = 100; 
    r.right     = 900; 
    r.bottom    = 700; 
    ////----------------------------- 
    //r.left        = 100; 
    //r.top     = 100; 
    //r.right       = 800+GetSystemMetrics( SM_CXSIZEFRAME )*2; 
    //r.bottom  = 600+GetSystemMetrics( SM_CYSIZEFRAME )*2+GetSystemMetrics( SM_CYCAPTION ); 

    BOOL result = AdjustWindowRect( &r, dwStyle, FALSE ); 
    assert(result);
    char buffer[512]; 
    sprintf( &buffer[0], "adjust left=%i, top=%i, right=%i, bottom=%i", r.left, r.top, r.right, r.bottom ); 
    MessageBoxA(0, buffer, 0, 0); 

    HWND hWindowHandle = CreateWindow(  L"CGFramework", L"Window Test", dwStyle, r.left, r.top, r.right-r.left, r.bottom-r.top, 0, 0, hInstance, 0 );  

    if( 0 == hWindowHandle ) 
    { 
        MessageBox(0, L"CreateWindow FAILED", 0, 0); 
        UnregisterClass( wc.lpszClassName, hInstance ); 
        return 0; 
    } 

    assert(GetWindowRect(hWindowHandle, &r));
    sprintf( &buffer[0], "wnd left=%i, top=%i, right=%i, bottom=%i", r.left, r.top, r.right, r.bottom ); 
    MessageBoxA(0, buffer, 0, 0); 

    GetClientRect( hWindowHandle, &r ); 
    sprintf( &buffer[0], "style=%08x/%08x, exstyle=%08x, left=%i, top=%i, right=%i, bottom=%i", GetWindowLong(hWindowHandle, GWL_STYLE), dwStyle, GetWindowLong(hWindowHandle, GWL_EXSTYLE), r.left, r.top, r.right, r.bottom ); 
    MessageBoxA(0, buffer, 0, 0); 

    ShowWindow( hWindowHandle, SW_SHOW ); 

    GetClientRect( hWindowHandle, &r ); 
    sprintf( &buffer[0], "left=%i, top=%i, right=%i, bottom=%i", r.left, r.top, r.right, r.bottom ); 
    MessageBoxA(0, buffer, 0, 0); 

    MSG msg; 
    ZeroMemory( &msg, sizeof( MSG ) ); 
    while( msg.message != WM_QUIT ) 
    { 
        while( PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) ) 
        { 
            TranslateMessage( &msg ); 
            DispatchMessage( &msg ); 
        } 

        if( GetAsyncKeyState( VK_ESCAPE ) ) 
            DestroyWindow( hWindowHandle ); 
    } 

    UnregisterClass( wc.lpszClassName, hInstance ); 
    return 0; 
} 
于 2012-08-13T11:47:56.540 回答
3

尝试使用AdjustWindowRectEx并确保样式和扩展样式与窗口将使用的最终样式完全匹配。例如,如果您在一段时间后设置了一些附加样式CreateWindow,即使某些库自动完成,您也必须为AdjustWindowRectEx. 您可能希望在创建后检索样式以查看 Windows 或某些库是否添加了样式。

如果你使用滚动条,你也应该考虑到这一点。

于 2012-08-13T14:08:54.533 回答