以下内容摘自MoveWindow()
文档的备注部分:
如果bRepaint参数为TRUE,则系统发送WM_PAINT 移动窗口后立即向窗口过程发送消息 (也就是说,MoveWindow函数调用UpdateWindow函数)。
所以我假设当我将MoveWindow()
设置为bRepaint
时调用TRUE
时,将立即调用窗口过程并传递WM_PAINT
消息,但这就是我的测试显示:
MoveWindow()
时,将调用窗口过程
立即,但是传递了WM_ERASEBKGND
消息而不是消息
WM_PAINT
消息。WM_PAINT
消息。我是否错误地解释了文档?
注意:我说的是在父窗口对象上调用MoveWindow()
方法。
修改
这是我的测试代码:
/* Left mouse click on the window to call MoveWindow() */
#include <Windows.h>
HWND hEdit;
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch(message)
{
case WM_LBUTTONDOWN:
MoveWindow(hWnd, 200, 200, 700, 700, TRUE);
// Do not go back to message loop immediately
Sleep(3000);
break;
case WM_ERASEBKGND:
{
SendMessage(hEdit, WM_CHAR, (WPARAM)'e', 0);
}
break;
case WM_PAINT:
{
SendMessage(hEdit, WM_CHAR, (WPARAM)'p', 0);
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
}
break;
case WM_CLOSE:
DestroyWindow(hWnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
wc.lpszMenuName = NULL;
wc.lpszClassName = "WinClass";
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
RegisterClassEx(&wc);
HWND hWnd = CreateWindowEx(0, "WinClass", "", WS_OVERLAPPEDWINDOW, 261, 172, 594, 384, NULL, NULL, hInstance, NULL);
hEdit = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", "", WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL, 0, 0, 400, 21, hWnd, NULL, hInstance, NULL);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
MSG msg;
while(GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
答案 0 :(得分:6)
我是否错误地解释了文档?
基本上,是的。您发现了关于winapi函数的MSDN文档的一些事实,这一点非常非常重要。它不是一本教程。它假定您对winapi如何工作有基本的了解,这是您从阅读Petzold&#34;编程Windows&#34;中获得的知识。书。
那本书可以教你Windows绘画周期总是包含WM_ERASEBKGND。因此,首先绘制背景,然后使用WM_PAINT在其上绘制任何内容。
在MSDN文档中跳过此类实现细节的几个原因。首先,它有很多,包括一切只是让你很难通过文章。接下来,实际为WM_ERASEBKGND编写消息处理程序是很常见的。您通常只是将其传递给DefWindowProc()。使用您选择的WNDCLASSEX.hbrBackground,99%的时间足以完成工作。请注意你的窗户是如何搞砸的,因为这是你没做的。既然你写了一个消息处理程序,现在你的工作就是处理它。容易做到,只需自己调用DefWindowProc()。
最后,MSDN文档省略了细节,因为将它们固定下来使得很难改进Windows的工作方式。您可以从测试程序中看到另一个实现细节。通常,使用bPaint = TRUE调用MoveWindow 不根本不需要绘制任何内容。首次单击后,通过使用标题栏拖动窗口来轻松查看窗口。注意如何再次单击使窗口跳回,但是既没有WM_ERASEBKGND也没有WM_PAINT。
这是一项优化工作,使Windows更好用。并没有在MSDN文章中提到过。如果窗口没有移出屏幕并且后退并且窗口的大小没有改变那么它可以采用快捷方式。它只是将视频帧缓冲区中的像素从旧位置复制到新位置。比让应用程序重新绘制所有内容更有效。如果您在启用Aero的情况下运行,那么它甚至会更加优化,它根本不必复制像素。
最后但同样重要的是,虽然编写这样的代码来反向工程Windows是非常有教育意义和推荐的,但是你不必这样做。使用Spy++ utility更容易。
答案 1 :(得分:2)
可能发生的情况是MoveWindow
使用WM_ERASEBKGND
发送SendMessage
(会立即调用WndProc
回调并等待其处理),但WM_PAINT
来自PostMessage
(它只是将消息放入队列中,因此在睡眠后,当调用DispatchMessage
时将对其进行处理。)
我不知道这只是一个测试,或者你在使用阻止消息队列的MoveWindow
后真正处理了一些事情。如果是这样,那么你应该考虑将这项工作转移到另一个线程!
希望它有所帮助。
答案 2 :(得分:1)
我是否错误地解释了文档?
是和否。
不 - 文档很清楚。
是的 - 就像Hans Passant所说,你不能依赖MSDN的这些细节。
有许多WinAPI函数具有&#34;陷阱&#34;,未记录的行为或预期的环境状态(包括计时)等。可能是它的行为与某些版本的Windows中指定的行为相同。在某些情况下,也许它仍然存在。
在实践中,MS会测试很多应用程序,看看它们在做出这样的改变后是否有效。在这种情况下,由于您通常处理WM_PAINT&#34;当它发生时#34;并且只进行绘制时,很容易看出在大多数应用程序中这种更改不会影响最终用户结果。
因此,请始终将MSDN视为&#34;一般说明&#34;。使用您自己的测试和其他来源来获取实际的行为详细信息。很高兴您使用与Windows相关的API,如果您使用的是较少使用的API,那么您的情况会更糟(我在USB / HID相关API方面遭受了很多苦难)。
答案 3 :(得分:1)
我不想覆盖rony提供的测试代码,所以我决定发布我的替代方案,我认为更适合调试Windows Message,这是查看调试消息的唯一要求外部工具DebugView。如果有人愿意,这当然可以用列表框代替。
#include <Windows.h>
/*
get DebugView from here https://download.sysinternals.com/files/DebugView.zip
*/
/*___________________________________________________________________________________
*/
void __cdecl DebugPrint(TCHAR *fmt,...){
va_list args = NULL;
va_start(args, fmt);
static TCHAR _buff[512]="";
TCHAR buff[512];
wvsprintf(buff,fmt,args);
va_end(args);
if(lstrcmp(buff,_buff))
OutputDebugString(buff);
lstrcpy(_buff,buff);
return ;
}
/*___________________________________________________________________________________
*/
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch(message)
{
case WM_RBUTTONDOWN:
MoveWindow(hWnd, 200, 200, 700, 700, TRUE);
break;
case WM_MOVE:
DebugPrint("WM_MOVE");
break;
case WM_SIZE:
DebugPrint("WM_SIZE");
break;
case WM_ERASEBKGND:
DebugPrint("WM_ERASEBKGND");
break;
case WM_PAINT:
DebugPrint("WM_PAINT");
if(1){
PAINTSTRUCT ps;
TCHAR buff[]="Right mouse click on the window to call MoveWindow()";
HFONT hf=(HFONT)GetStockObject(DEFAULT_GUI_FONT);
HDC hdc = BeginPaint(hWnd, &ps);
hf=SelectObject(hdc,hf);
TextOut(hdc,8,12,buff, sizeof(buff)-sizeof(TCHAR));
hf=SelectObject(hdc,hf);
EndPaint(hWnd, &ps);
}else{
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
/*___________________________________________________________________________________
*/
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc;
ZeroMemory(&wc,sizeof(wc));
wc.cbSize = sizeof(WNDCLASSEX);
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
wc.lpszClassName = "WinClass";
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
RegisterClassEx(&wc);
HWND hWnd = CreateWindowEx(0, "WinClass", "",WS_OVERLAPPEDWINDOW|WS_VISIBLE,
261, 172, 594, 384, NULL, NULL, hInstance, NULL);
MSG msg;
while(GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}