@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查询客户端rect时,我曾经得到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(2012年7月11.0.50706.0 QRELRC)来获得这种奇怪的行为,并且没有一种答案能够解决这个问题。在做出任何假设之前,请务必阅读它们并测试我的代码,因为这些代码已经在很多方面进行了测试,并且有微妙的差异,最终没有提供任何改进。
答案 0 :(得分:6)
CreateWindow
函数是Windows API的一部分,Visual Studio不提供它,Visual Studio版本不会影响它。
最可能的解释是VS2012中的默认项目设置添加了一个清单,该清单将应用程序标记为Aero-aware或DPI-aware,并且该清单的存在会更改WinAPI函数的行为。您可以编辑或删除清单以获取旧行为。
Windows正在检查PE标头中的“最小操作系统版本”字段也是可能的,但不太可能。较新版本的链接器通常不支持将旧版本写入此字段,但editbin
工具可用于在链接后更改它。
答案 1 :(得分:6)
正如其他人所说,AdjustWindowRect或AdjustWindowRectEx是要走的路。如果设置了WS_VSCROLL或WS_HSCROLL,则需要进行特殊处理。在我的util库中,我也对WS_EX_STATICEDGE进行了特殊处理,但是不要问我从哪里获得这些信息(至少它不在调整版DevowRect [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;
}
答案 2 :(得分:3)
尝试使用AdjustWindowRectEx
并确保样式和扩展样式与完全匹配窗口将使用的最终样式。例如如果您在CreateWindow
之后的某个时间设置了一些其他样式,即使由某个库自动完成,您还必须为AdjustWindowRectEx
设置相应的样式。您可能希望在创建后检索样式,以查看Windows或某些库是否添加了样式。
如果你使用滚动条,你也应该考虑它。