书上的原来的程序是这样
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT("KeyView1");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass))
{
MessageBox(NULL, TEXT("This program requires Windows NT!"),
szAppName, MB_ICONERROR);
return 0;
}
hwnd = CreateWindow(szAppName, TEXT("Keyboard Message Viewer #1"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
static int cxClientMax, cyClientMax, cxClient, cyClient, cxChar, cyChar;
static int cLinesMax, cLines;
static PMSG pmsg;//PMSG就是MSG的指针形式
static RECT rectScroll;
static RECT rectTest;
static TCHAR szTop[] = TEXT("Message Key Char ")
TEXT("Repeat Scan Ext ALT Prev Tran");
static TCHAR szUnd[] = TEXT("_______ ___ ____ ")
TEXT("______ ____ ___ ___ ____ ____");
static TCHAR* szFormat[2] = {
TEXT("%-13s %3d %-15s%c%6u %4d %3s %3s %4s %4s"),//虚拟按键输出格式
TEXT("%-13s 0x%04X%1s%c %6u %4d %3s %3s %4s %4s") };//字元代码输出格式
static TCHAR* szYes = TEXT("Yes");
static TCHAR* szNo = TEXT("No");
static TCHAR* szDown = TEXT("Down");
static TCHAR* szUp = TEXT("Up");
static TCHAR* szMessage[] = {
TEXT("WM_KEYDOWN"), TEXT("WM_KEYUP"),
TEXT("WM_CHAR"), TEXT("WM_DEADCHAR"),
TEXT("WM_SYSKEYDOWN"), TEXT("WM_SYSKEYUP"),
TEXT("WM_SYSCHAR"), TEXT("WM_SYSDEADCHAR") };
HDC hdc;
int i, iType;
PAINTSTRUCT ps;
TCHAR szBuffer[128], szKeyName[32];
TEXTMETRIC tm;
switch (message)
{
case WM_CREATE:
case WM_DISPLAYCHANGE://显示器的分辨率改变后响应该消息
//获得客户区的最大宽度和最大高度,这里是最大化的时候的客户区的最大宽度和最大高度
cxClientMax = GetSystemMetrics(SM_CXMAXIMIZED);
cyClientMax = GetSystemMetrics(SM_CYMAXIMIZED);
//Get character size for fixed-pitch font
hdc = GetDC(hwnd);
SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));//设置等宽字体
GetTextMetrics(hdc, &tm);
cxChar = tm.tmAveCharWidth;
cyChar = tm.tmHeight;
ReleaseDC(hwnd, hdc);
//Allocate memory for display lines
if (pmsg)
free(pmsg);
cLinesMax = cyClientMax / cyChar; //计算最多要接收多少个消息
pmsg = malloc(cLinesMax * sizeof(MSG)); //pmsg存放这些消息
cLines = 0;
//fall through
case WM_SIZE:
if (message == WM_SIZE) //如果程序触发的是WM_SIZE消息,就获取客户区的宽度,高度,
{ //如果程序是从WM_DISPLAYCHANGE运行到这里的,if就不需要执行
cxClient = LOWORD(lParam);
cyClient = HIWORD(lParam);
}
//Calculate scrolling rectangle
rectScroll.left = 0;
rectScroll.right = cxClient;
rectScroll.top = cyChar;
rectScroll.bottom = cyChar * (cyClient / cyChar); ///
rectTest.left= 0;
rectTest.top = cyChar;
rectTest.right = cxClient / 2;
rectTest.bottom = cyClient / 2;
InvalidateRect(hwnd, NULL, TRUE);
return 0;
case WM_KEYDOWN:
case WM_KEYUP:
case WM_CHAR:
case WM_DEADCHAR:
case WM_SYSKEYDOWN:
case WM_SYSKEYUP:
case WM_SYSCHAR:
case WM_SYSDEADCHAR:
//Rearrange storage array
for (int i = cLinesMax - 1; i > 0; i--) //接收到消息后每个消息后移,保证位置越靠前的消息是最新的
{ //若i>=cLinesMax 则这些消息都不会被显示
pmsg[i] = pmsg[i - 1];
}
//Store new message
pmsg[0].hwnd = hwnd;
pmsg[0].message = message;
pmsg[0].wParam = wParam;
pmsg[0].lParam = lParam;
cLines = min(cLines + 1, cLinesMax);
//Scroll up the display
ScrollWindow(hwnd, 0, -cyChar, &rectScroll, &rectScroll);
//UpdateWindow(hwnd);
//ScrollWindow(hwnd, 0, -cyChar, NULL, NULL);
break;//i.e.,call DefWindowProc so Sys messages work
case WM_PAINT:
{
hdc = BeginPaint(hwnd, &ps);
SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));
SetBkMode(hdc, TRANSPARENT); ///将背景模式设置为透明的,这样画下划线的时候,空白的部分就不会用背景色,这里是白色来填充
TextOut(hdc, 0, 0, szTop, lstrlen(szTop));
TextOut(hdc, 0, 0, szUnd, lstrlen(szUnd));
for (int i = 0; i < min(cLines, cyClient / cyChar - 1); i++)
{
iType = pmsg[i].message == WM_CHAR ||
pmsg[i].message == WM_SYSCHAR ||
pmsg[i].message == WM_DEADCHAR ||
pmsg[i].message == WM_SYSDEADCHAR;
GetKeyNameText(pmsg[i].lParam, szKeyName, sizeof(szKeyName) / sizeof(TCHAR));
TextOut(hdc, 0, (cyClient / cyChar - 1 - i) * cyChar, szBuffer, //从下往上显示
wsprintf(szBuffer, szFormat[iType],
szMessage[pmsg[i].message - WM_KEYFIRST],
pmsg[i].wParam,
(PTSTR)(iType ? TEXT(" ") : szKeyName),
(TCHAR)(iType ? pmsg[i].wParam : ' '),
LOWORD(pmsg[i].lParam),
HIWORD(pmsg[i].lParam) & 0xFF,
0x01000000 & pmsg[i].lParam ? szYes : szNo,
0x20000000 & pmsg[i].lParam ? szYes : szNo,
0x40000000 & pmsg[i].lParam ? szDown : szUp,
0x80000000 & pmsg[i].lParam ? szUp : szDown));
}
/*
if (cLines == 0)
{
for (int i = 0; i < min(cLines, cyClient / cyChar - 1); i++)
{
iType = pmsg[i].message == WM_CHAR ||
pmsg[i].message == WM_SYSCHAR ||
pmsg[i].message == WM_DEADCHAR ||
pmsg[i].message == WM_SYSDEADCHAR;
GetKeyNameText(pmsg[i].lParam, szKeyName, sizeof(szKeyName) / sizeof(TCHAR));
TextOut(hdc, 0, (cyClient / cyChar - 1 - i) * cyChar, szBuffer, //从下往上显示
wsprintf(szBuffer, szFormat[iType],
szMessage[pmsg[i].message - WM_KEYFIRST],
pmsg[i].wParam,
(PTSTR)(iType ? TEXT(" ") : szKeyName),
(TCHAR)(iType ? pmsg[i].wParam : ' '),
LOWORD(pmsg[i].lParam),
HIWORD(pmsg[i].lParam) & 0xFF,
0x01000000 & pmsg[i].lParam ? szYes : szNo,
0x20000000 & pmsg[i].lParam ? szYes : szNo,
0x40000000 & pmsg[i].lParam ? szDown : szUp,
0x80000000 & pmsg[i].lParam ? szUp : szDown));
}
}
else
{
int temp = pmsg[0].message;
iType = pmsg[0].message == WM_CHAR ||
pmsg[0].message == WM_SYSCHAR ||
pmsg[0].message == WM_DEADCHAR ||
pmsg[0].message == WM_SYSDEADCHAR;
GetKeyNameText(pmsg[0].lParam, szKeyName, sizeof(szKeyName) / sizeof(TCHAR));
TextOut(hdc, 0, (cyClient / cyChar - 1 - 0) * cyChar, szBuffer, //从下往上显示
wsprintf(szBuffer, szFormat[iType],
szMessage[pmsg[0].message - WM_KEYFIRST],
pmsg[0].wParam,
(PTSTR)(iType ? TEXT(" ") : szKeyName),
(TCHAR)(iType ? pmsg[0].wParam : ' '),
LOWORD(pmsg[0].lParam),
HIWORD(pmsg[0].lParam) & 0xFF,
0x01000000 & pmsg[0].lParam ? szYes : szNo,
0x20000000 & pmsg[0].lParam ? szYes : szNo,
0x40000000 & pmsg[0].lParam ? szDown : szUp,
0x80000000 & pmsg[0].lParam ? szUp : szDown));
}
*/
EndPaint(hwnd, &ps);
return 0;
}
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
我的问题是既然我按下一个按键产生多个消息,又由于窗口过程每次只处理一个消息那是不是绘制的时候就好多都是重复绘制的,因为无效区域里面只要绘制一个消息就可以了,若不考虑其他需要重新绘制的情况,可以改成了注释中的写法,但是不行出现了以下结果。
可以看到本应该出现的WM_KEYDOWN消息没有出现,经过一番查找之后,使用了VS2019的SPY++才找到原因,
如图所示实际上在按下一个按键的时候,例如‘A’,虽然发送了三个消息,但是却只发送了两次WM_PAINT,一次是在WM_KEYDOWN和WM_CHAR发出之后,另一次是在WM_KEYUP发出之后,这样的话,就能解释了。改动之后的程序由于只输出最新的那个消息,在第一个WM_PAINT消息接收到之后由于WM_CHAR是最新的,所以只输出了WM_CHAR,而不输出WM_KEYDOWN,同时也可以看到ScrollWindow确实向上滚动了一行。这样的结果在原书的程序中也可以得到体现,当输入一个按键后,例如’A’,若仔细看的话 可以发现KEYDOWN和CHAR消息是同时出现的,KEYUP消息后出现。有趣的是若"同时"按下两个按键会出现以下现象:
若用SPY++可以看到:
但是还有一个问题:WM_PAINT消息是哪来的?
回到之前的滚动条程序中若删除ScrollWindow函数之后的UpdateWindow函数,程序仍能正常滚动
删除UpdateWindow前的消息响应
消息UpdateWindow后的消息响应
可以看到基本没有差别***(或许是由于按下方向键和WM_PAINT消息之间没有其他消息)***但这样足以说明ScrollWindow消息会发送WM_PAINT消息
版权声明:本文为qq_45992231原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。