@EDIT:我发现这似乎是Windows 8 RC的一个问题,因为我尝试了Windows 7和VS 2012,经典和Aero视图,它工作正常。 感谢@Werner Henze和@Ven Boigt的反馈
编辑2:原来这是一个在Windows中的错误,由于正在testing,它被修正在较新的版本,所以我不必担心这个了。 无论如何感谢您的反馈。
我曾经做过以下操作来创build一个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。
最重要的是,正在创build的窗口的实际大小是804 * 629,我没有看到任何理由,因为可resize的框架(来自WS_THICKFRAME)明显大于每边2像素。
我认为这是Windows 8的Aero行为的一个问题,但后来我意识到这只是发生在我的VS2012构build,而不是我的VS2008构build。 如果我在Aero或经典风格上运行它,这种行为只会适用于VS2012版本。 为什么? 有什么我可以改变我的VS2012项目configuration来解决这个可怕的行为?
我尝试改变项目configuration的“DPI Awareness”设置,但是这没有什么区别。 我也在相同的configuration页面中删除了清单的使用,在结果窗口中仍然没有看到任何改变。
这是我的testing代码:
#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; }
在回答之前请仔细观察代码,因为您最终可能会回答我已经尝试过的事情(我发布这个问题是因为我已经浪费了所有的select)。
我正在使用Windows 8 RC(MS Windows 6.2.8400)和VS2012 RC(2012年7月11.0.50706.0 QRELRC)来获得这种奇怪的行为,没有哪一种答案能够解决这个问题。 在做出任何假设之前,请务必阅读它们并testing我的代码,因为此代码已经以多种方式进行了testing,但最终提供了无法改进的细微差异。
正如其他人所说AdjustWindowRect或AdjustWindowRectEx是要走的路。 如果设置了WS_VSCROLL或WS_HSCROLL,则需要进行特殊处理。 在我的util库中,我也对WS_EX_STATICEDGE有特殊的处理,但是不要问我从哪里得到这些信息(至少它不在AdjustWindowRect [Ex]的MSDN文档中)。
你最近测试的错误是你调用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; }
CreateWindow
函数是Windows API的一部分,它不是由Visual Studio提供的,Visual Studio版本也不会影响它。
最可能的解释是,VS2012中的默认项目设置添加了一个清单,该清单将应用程序标记为Aero-aware或DPI-aware,并且该清单的存在会改变WinAPI函数的行为。 您可以编辑或删除清单来获取旧的行为。
Windows正在检查PE头中的“最小操作系统版本”字段也是可能的,但不太可能。 较新版本的链接器通常不支持将旧版本写入此字段,但可以使用editbin
工具将其更改为链接后。
尝试使用AdjustWindowRectEx
并确保样式和扩展样式完全匹配窗口将使用的最终样式。 例如,如果在CreateWindow
之后的某个时间设置了一些额外的样式,即使某些库自动完成,也必须为AdjustWindowRectEx
设置相应的样式。 您可能想要在创建后检索样式,以查看Windows或某个库是否添加样式。
如果你使用滚动条,你也应该考虑这个因素。