《Windows程序设计》读书笔六 键盘

来源:互联网 发布:常见数据库设计 编辑:程序博客网 时间:2024/05/20 18:03

6.1 键盘基础


6.1.1 忽略键盘

6.1.2 谁获得了焦点?

窗口过程通过WM_SETFOCUS和WM_KILLFOCUS消息来确定自己的窗口是否具有输入焦点


6.1.3 队列和同步

6.1.4 击键和字符

应用程序从windows接受的关于键盘事件的消息可分为击键和字符两种。

有些按键参数击键消息和字符消息

有些按键只产生击键消息


6.2 击键消息


6.2.1 系统键击和非系统键击

应用程序通常忽略WM_SYSKEYUP 和WM_SYSKEYDOWN消息,将他交给DefWindowProc来处理

如果你捕捉了也要再交给DefWindowProc处理

不与Alt组合会产生WM_KEYDOWN和WM_KEYUP消息。


6.2.2 虚拟键代码

虚拟键代码存储在WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWN,  WM_SYSKEYUP 的wParam中。

以VK开头,定义在winuser.h文件中





还有许多按键参考P179页


6.2.3 lparam信息


重复计数


OEM扫描码


扩展键标记


内容代码


键的先前状态


转换状态


6.2.4 转义状态

shift, Ctrl Alt 或者CapsLock NumLock Scroll Lock

iState = GetKeyState(VK_SHIFT);   //如果shift被按下 iState为负,高位置1


iState = GetKeyState(VK_CAPITAL); //如果CapsLock按键打开,低位置1.


也可以获得VK_LBUTTON, VK_RBUTTON, VK_MBUTTON得到鼠标按钮的状态。

GetKeyState 无法获得功能键的状态

可以使用

GetAsyncKeyState


6.2.5 使用击键消息

windows通常不产生字符的击键使用WM_KEYDOWN消息,尽管可以通过击键消息和转义状态信息把击键转换为字符。你会在非英语键盘上遇到问题


对于光标移动,功能键,Insert Delete  , WM_KEYDOWN 消息是最有用的。 但是Insert, Delete 经常被用作菜单快捷键。因为Windows会把菜单快捷键转换为菜单命令消息,所以应用程序不必自己处理这些击键。

总之大部分时间,你仅需要处理光标移动的WM_KEYDOWN 消息,有时候处理Insert Delete 的WM_KEYDOWN消息。当使用这些键时,可以通过GetKeyState函数来检查Shift和Ctrl犍的状态。


6.2.6 为SYSMETS加上键盘处理功能

system.h 代码

参见第四章

system.cpp 加入键盘控制的代码主要是把相应的键盘操作映射为相应的ScrollBar消息,简化代码。

#include <windows.h>#include "sysmets.h"LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure.int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow){staticTCHAR szAppName[] = TEXT("SysMets4");HWNDhwnd;MSGmsg;WNDCLASSwndClass;//The window ClasswndClass.style = CS_HREDRAW | CS_VREDRAW;wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class.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;//Register the Window Class to the Windows System. if (!RegisterClass(&wndClass)){MessageBox(NULL, TEXT("This program require Windows NT!"),szAppName, MB_ICONERROR);return 0;}//This function will generate an WM_CREATE message.hwnd = CreateWindow(szAppName,//Window class nameTEXT("Get System Metrics No. 4"),//Window captionWS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL,//Window StyleCW_USEDEFAULT,//initial x positionCW_USEDEFAULT,//initial y positionCW_USEDEFAULT,//initial x sizeCW_USEDEFAULT,//initial y sizeNULL,//parent window handleNULL,//window menu handlehInstance,//program instance handleNULL);//creation parametersShowWindow(hwnd, iCmdShow);UpdateWindow(hwnd);//This function will generate a WM_PAINT message./* The message loop for this program.if received the WM_QUIT message, the function will return 0.*/while (GetMessage(&msg, NULL, 0, 0)){TranslateMessage(&msg);DispatchMessage(&msg);}return msg.wParam;}//define the Window Procedure WndProcLRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){static intcxChar, cxCaps, cyChar, cxClient, cyClient, iMaxWidth;HDChdc;inti, x, y, iVertPos, iHorzPos, iPaintBeg, iPaintEnd;PAINTSTRUCTps;SCROLLINFOsi;TCHARszBuffer[10];TEXTMETRICtm;switch (message) //get the message{case WM_CREATE:hdc = GetDC(hwnd);GetTextMetrics(hdc, &tm);cxChar = tm.tmAveCharWidth;cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2)* cxChar / 2;cyChar = tm.tmHeight + tm.tmExternalLeading;ReleaseDC(hwnd, hdc);iMaxWidth = 40 * cxChar + 22 * cxCaps;return 0;case WM_SIZE:cxClient = LOWORD(lParam);cyClient = HIWORD(lParam);//Set vertical scroll bar range and page sizesi.cbSize = sizeof(si);si.fMask = SIF_RANGE | SIF_PAGE;si.nMin = 0;si.nMax = NUMLINES - 1;si.nPage = cyClient / cyChar;SetScrollInfo(hwnd, SB_VERT, &si, TRUE);//Set the horizontal scroll bar range and page sizesi.cbSize = sizeof(si);si.fMask = SIF_RANGE | SIF_PAGE;si.nMin = 0;si.nMax = 2 + iMaxWidth / cxChar;si.nPage = cxClient / cxChar;SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);return 0;case WM_VSCROLL://Get all the vertical scroll bar informationsi.cbSize = sizeof(si);si.fMask = SIF_ALL;GetScrollInfo(hwnd, SB_VERT, &si);//Save the position for comparison later oniVertPos = si.nPos;switch (LOWORD(wParam)){case SB_TOP:si.nPos = si.nMin;break;case SB_BOTTOM:si.nPos = si.nMax;break;case SB_LINEUP:si.nPos -= 1;break;case SB_LINEDOWN:si.nPos += 1;break;case SB_PAGEUP:si.nPos -= si.nPage;break;case SB_PAGEDOWN:si.nPos += si.nPage;break;case SB_THUMBPOSITION:si.nPos = si.nTrackPos;break;default:break;}//Set the position and then retrieve it. Due to adjustments//By Windows it may not be the same as the value set.si.fMask = SIF_POS;SetScrollInfo(hwnd, SB_VERT, &si, TRUE);GetScrollInfo(hwnd, SB_VERT, &si);// if the position has changed, scroll the window and update itif (si.nPos != iVertPos){ScrollWindow(hwnd, 0, cyChar*(iVertPos - si.nPos),NULL, NULL);UpdateWindow(hwnd); //instead of the invalidateRect() it will update the rect immediately.}return 0;case WM_HSCROLL:// Get all the vertical scroll bar informationsi.cbSize = sizeof(si);si.fMask = SIF_ALL;//Save the position for comparison later onGetScrollInfo(hwnd, SB_HORZ, &si);iHorzPos = si.nPos;switch (LOWORD(wParam)){case SB_LINELEFT:si.nPos -= 1;break;case SB_LINERIGHT:si.nPos += 1;break;case SB_PAGELEFT:si.nPos -= si.nPage;break;case SB_PAGERIGHT:si.nPos += si.nPage;break;case SB_THUMBPOSITION:si.nPos = si.nTrackPos;break;default:break;}//Set the Position and then retrieve it. Due to adjustments//by Windows it may not be the same as the value set.si.fMask = SIF_POS;SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);GetScrollInfo(hwnd, SB_HORZ, &si);//If the position has been changed, scroll the windowif (si.nPos != iHorzPos){ScrollWindow(hwnd, cxChar* (iHorzPos - si.nPos), 0,NULL, NULL);}return 0;case WM_KEYDOWN:switch(wParam){case VK_HOME:SendMessage(hwnd, WM_VSCROLL, SB_TOP, 0);break;case VK_END:SendMessage(hwnd, WM_VSCROLL, SB_BOTTOM, 0);break;case VK_PRIOR:SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);break;case VK_NEXT:SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);break;case VK_UP:SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, 0);break;case VK_DOWN:SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, 0);break;case VK_LEFT:SendMessage(hwnd, WM_HSCROLL, SB_PAGEUP, 0);break;case VK_RIGHT:SendMessage(hwnd, WM_HSCROLL, SB_PAGEDOWN, 0);break;}return 0;case WM_PAINT:hdc = BeginPaint(hwnd, &ps);//Get the vertical scroll bar positionsi.cbSize = sizeof(si);;si.fMask = SIF_POS;GetScrollInfo(hwnd, SB_VERT, &si);iVertPos = si.nPos;//Get horizontal scroll bar positionGetScrollInfo(hwnd, SB_HORZ, &si);iHorzPos = si.nPos;//Find painting limitsiPaintBeg = max(0, iVertPos + ps.rcPaint.top / cyChar);iPaintEnd = min(NUMLINES - 1,iVertPos + ps.rcPaint.bottom / cyChar);for (i = iPaintBeg; i <= iPaintEnd; ++i){x = cxChar * (1 - iHorzPos);y = cyChar * (i - iVertPos);TextOut(hdc, x, y,sysmetrics[i].szLabel,lstrlen(sysmetrics[i].szLabel));TextOut(hdc, x + 22 * cxCaps, y,sysmetrics[i].szDesc,lstrlen(sysmetrics[i].szDesc));SetTextAlign(hdc, TA_RIGHT | TA_TOP);TextOut(hdc, x + 22 * cxCaps + 40 * cxChar, y, szBuffer,wsprintf(szBuffer, TEXT("%5d"),GetSystemMetrics(sysmetrics[i].iIndex)));SetTextAlign(hdc, TA_LEFT | TA_TOP);}EndPaint(hwnd, &ps);return 0;case WM_DESTROY:PostQuitMessage(0);return 0;}return  DefWindowProc(hwnd, message, wParam, lParam);}
执行结果如下



6.3 字符消息

在消息循环中 TranslateMessage函数,负责把击键消息转换为字符消息。 WM_KEYDOWN   WM_SYSKEYDOWN.

6.3.1 四类字符消息



常用的是 WM_CHAR

wParam 参数是ANSI或者unicode 字符代码, 取决于注册窗口类使用RegisterClassA 或者RegisterClassW

通常用 (TCHAR) wParam


fUnicode = IsWindowUnicode(hwnd); //判断窗口函数注册是否为Unicode.


6.3.2 消息排序

输入字符a

WM_KEYDOWN

WM_CHAR

WM_KEYUP

输入大写字符A  shift+a

WM_KEYDOWN

WM_KEYDOWN

WM_CHAR

WM_KEYUP

WM_KEYUP


6.3.3 控制字符的处理

把TAB,回车,空格和ESC看做控制字符

6.3.4 死字符消息

6.4 键盘消息和字符集

6.4.1 KEYVIEW1 程序

#include <windows.h>LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure.int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow){staticTCHAR szAppName[] = TEXT("KeyView1");HWNDhwnd;MSGmsg;WNDCLASSwndClass;//The window ClasswndClass.style = CS_HREDRAW | CS_VREDRAW;wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class.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;//Register the Window Class to the Windows System. if (!RegisterClass(&wndClass)){MessageBox(NULL, TEXT("This program require Windows NT!"),szAppName, MB_ICONERROR);return 0;}//This function will generate an WM_CREATE message.hwnd = CreateWindow(szAppName,//Window class nameTEXT("Keyboard Message Viewer #1"),//Window captionWS_OVERLAPPEDWINDOW,//Window StyleCW_USEDEFAULT,//initial x positionCW_USEDEFAULT,//initial y positionCW_USEDEFAULT,//initial x sizeCW_USEDEFAULT,//initial y sizeNULL,//parent window handleNULL,//window menu handlehInstance,//program instance handleNULL);//creation parametersShowWindow(hwnd, iCmdShow);UpdateWindow(hwnd);//This function will generate a WM_PAINT message./* The message loop for this program.if received the WM_QUIT message, the function will return 0.*/while (GetMessage(&msg, NULL, 0, 0)){TranslateMessage(&msg);DispatchMessage(&msg);}return msg.wParam;}//define the Window Procedure WndProcLRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){static intcxClientMax, cyClientMax, cxClient, cyClient, cxChar, cyChar;static intcLinesMax, cLines;static PMSGpmsg;static RECTrectScroll;static TCHARszTop[] = TEXT("Message        Key       Char     ")  TEXT("Repeat Scan Ext ALT Prev Tran");static TCHARszUnd[] = 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")};HDChdc;inti, iType;PAINTSTRUCTps;TCHARszBuffer[128], szKeyName[32];TEXTMETRICtm;switch (message) //get the message{case WM_CREATE:case WM_DISPLAYCHANGE://Get maximum size of client areacxClientMax = GetSystemMetrics(SM_CXMAXIMIZED);cyClientMax = GetSystemMetrics(SM_CYMAXIMIZED);//Get character size for fixed-pitch fonthdc = 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 = (PMSG)malloc(cLinesMax * sizeof(MSG));cLines = 0;//fall through/*return 0;*/case WM_SIZE:if (WM_SIZE == message){cxClient = LOWORD(lParam);cyClient = HIWORD(lParam);}//Calculate scrolling rectanglerectScroll.left = 0;rectScroll.right = cxClient;rectScroll.top = cyChar;rectScroll.bottom = cyChar * (cyClient / cyChar);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 arrayfor (i = cLinesMax - 1; i > 0; i--){pmsg[i] = pmsg[i - 1];}//Store new messagepmsg[0].hwnd = hwnd;pmsg[0].message = message;pmsg[0].wParam = wParam;pmsg[0].lParam = lParam;cLines = min(cLines + 1, cLinesMax);//Scroll up the displayScrollWindow(hwnd, 0, -cyChar, &rectScroll, &rectScroll);break; //i.e., call DefWindowProc so Sys messages workcase 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 (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 : TEXT(' ')),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));}EndPaint(hwnd, &ps);return 0;case WM_DESTROY:PostQuitMessage(0);return 0;}return  DefWindowProc(hwnd, message, wParam, lParam);}

运行结果如下


6.4.3 字符集和字体


#include <windows.h>LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure.int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow){staticTCHAR szAppName[] = TEXT("StockFont");HWNDhwnd;MSGmsg;WNDCLASSwndClass;//The window ClasswndClass.style = CS_HREDRAW | CS_VREDRAW;wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class.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;//Register the Window Class to the Windows System. if (!RegisterClass(&wndClass)){MessageBox(NULL, TEXT("This program require Windows NT!"),szAppName, MB_ICONERROR);return 0;}//This function will generate an WM_CREATE message.hwnd = CreateWindow(szAppName,//Window class nameTEXT("Stock Fonts"),//Window captionWS_OVERLAPPEDWINDOW,//Window StyleCW_USEDEFAULT,//initial x positionCW_USEDEFAULT,//initial y positionCW_USEDEFAULT,//initial x sizeCW_USEDEFAULT,//initial y sizeNULL,//parent window handleNULL,//window menu handlehInstance,//program instance handleNULL);//creation parametersShowWindow(hwnd, iCmdShow);UpdateWindow(hwnd);//This function will generate a WM_PAINT message./* The message loop for this program.if received the WM_QUIT message, the function will return 0.*/while (GetMessage(&msg, NULL, 0, 0)){TranslateMessage(&msg);DispatchMessage(&msg);}return msg.wParam;}//define the Window Procedure WndProcLRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){static struct{int idStockFont;TCHAR * szStockFont;}stockfont[] = { OEM_FIXED_FONT,      TEXT("OEM_FIXED_FONT"),ANSI_FIXED_FONT,     TEXT("ANSI_FIXED_FONT"),ANSI_VAR_FONT,       TEXT("ANSI_VAR_FONT"),SYSTEM_FONT,         TEXT("SYSTEM_FONT"),DEVICE_DEFAULT_FONT, TEXT("DEVICE_DEFAULT_FONT"),SYSTEM_FIXED_FONT,   TEXT("SYSTEM_FIXED_FONT"),DEFAULT_GUI_FONT,    TEXT("DEFAULT_GUI_FONT") };static intiFont, cFonts = sizeof stockfont / sizeof stockfont[0];HDChdc;inti, x, y, cxGrid, cyGrid;PAINTSTRUCTps;TCHARszFaceName[LF_FACESIZE], szBuffer[LF_FACESIZE + 64];TEXTMETRICtm;switch (message) //get the message{case WM_CREATE:SetScrollRange(hwnd, SB_VERT, 0, cFonts - 1, TRUE);return 0;case WM_DISPLAYCHANGE:InvalidateRect(hwnd, NULL, TRUE);return 0;case WM_VSCROLL:switch (LOWORD(wParam)){case SB_TOP:iFont = 0;break;case SB_BOTTOM:iFont = cFonts - 1;break;case SB_LINEUP:case SB_PAGEUP:iFont -= 1;break;case SB_LINEDOWN:case SB_PAGEDOWN:iFont += 1;break;case SB_THUMBPOSITION:iFont = HIWORD(wParam);break;}iFont = max(0, min(cFonts - 1, iFont));SetScrollPos(hwnd, SB_VERT, iFont, TRUE);InvalidateRect(hwnd, NULL, TRUE);return 0;case WM_KEYDOWN:switch (wParam){case VK_HOME:SendMessage(hwnd, WM_VSCROLL, SB_TOP, 0);break;case VK_END:SendMessage(hwnd, WM_VSCROLL, SB_BOTTOM, 0);break;case VK_PRIOR:case VK_LEFT:case VK_UP:SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, 0);break;case VK_NEXT:case VK_RIGHT:case VK_DOWN:SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, 0);break;}return 0;case WM_PAINT:hdc = BeginPaint(hwnd, &ps);SelectObject(hdc, GetStockObject(stockfont[iFont].idStockFont));GetTextFace(hdc, LF_FACESIZE, szFaceName);GetTextMetrics(hdc, &tm);cxGrid = max(3 * tm.tmAveCharWidth, 2 * tm.tmMaxCharWidth);cyGrid = tm.tmHeight + 3;TextOut(hdc, 0, 0, szBuffer,wsprintf(szBuffer, TEXT("%s:Face Name = %s, CharSet = %i"),stockfont[iFont].szStockFont, szFaceName, tm.tmCharSet));for (i = 0; i < 17; ++i){MoveToEx(hdc, (i + 2) * cxGrid, 2 * cyGrid, NULL);LineTo(hdc, (i + 2) * cxGrid, 19 * cyGrid);MoveToEx(hdc, cxGrid, (i + 3) * cyGrid, NULL);LineTo(hdc, 18 * cxGrid, (i + 3) * cyGrid);}//vertical and horizontal headingsfor (i = 0; i < 16; ++i){TextOut(hdc, (2 * i + 5) * cxGrid / 2, 2 * cyGrid + 2, szBuffer,wsprintf(szBuffer, TEXT("%X-"), i));TextOut(hdc, 3 * cxGrid / 2 - tm.tmAveCharWidth, (i + 3) * cyGrid + 2, szBuffer,wsprintf(szBuffer, TEXT("-%X"), i));}//Charactersfor(y = 0; y < 16; y ++)for (x = 0; x < 16; x++){TextOut(hdc, (2 * x + 5) * cxGrid / 2,(y + 3) * cyGrid + 2, szBuffer,wsprintf(szBuffer, TEXT("%c"), 16 * x + y));}EndPaint(hwnd, &ps);return 0;case WM_DESTROY:PostQuitMessage(0);return 0;}return  DefWindowProc(hwnd, message, wParam, lParam);}
运行结果



6.4.4 Unicode解决方案

6.4.5 TrueType字体和大字体

keyview2 显示了如何在键盘布局改变时候相应的改变字体

#include <windows.h>LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure.int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow){staticTCHAR szAppName[] = TEXT("KeyView2");HWNDhwnd;MSGmsg;WNDCLASSwndClass;//The window ClasswndClass.style = CS_HREDRAW | CS_VREDRAW;wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class.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;//Register the Window Class to the Windows System. if (!RegisterClass(&wndClass)){MessageBox(NULL, TEXT("This program require Windows NT!"),szAppName, MB_ICONERROR);return 0;}//This function will generate an WM_CREATE message.hwnd = CreateWindow(szAppName,//Window class nameTEXT("Keyboard Message Viewer #2"),//Window captionWS_OVERLAPPEDWINDOW,//Window StyleCW_USEDEFAULT,//initial x positionCW_USEDEFAULT,//initial y positionCW_USEDEFAULT,//initial x sizeCW_USEDEFAULT,//initial y sizeNULL,//parent window handleNULL,//window menu handlehInstance,//program instance handleNULL);//creation parametersShowWindow(hwnd, iCmdShow);UpdateWindow(hwnd);//This function will generate a WM_PAINT message./* The message loop for this program.if received the WM_QUIT message, the function will return 0.*/while (GetMessage(&msg, NULL, 0, 0)){TranslateMessage(&msg);DispatchMessage(&msg);}return msg.wParam;}//define the Window Procedure WndProcLRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){static DWORDdwCharSet = DEFAULT_CHARSET;static intcxClientMax, cyClientMax, cxClient, cyClient, cxChar, cyChar;static intcLinesMax, cLines;static PMSGpmsg;static RECTrectScroll;static TCHARszTop[] = TEXT("Message        Key       Char     ")  TEXT("Repeat Scan Ext ALT Prev Tran");static TCHARszUnd[] = 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")};HDChdc;inti, iType;PAINTSTRUCTps;TCHARszBuffer[128], szKeyName[32];TEXTMETRICtm;switch (message) //get the message{case WM_INPUTLANGCHANGE:dwCharSet = wParam;// fall throughcase WM_CREATE:case WM_DISPLAYCHANGE://Get maximum size of client areacxClientMax = GetSystemMetrics(SM_CXMAXIMIZED);cyClientMax = GetSystemMetrics(SM_CYMAXIMIZED);//Get character size for fixed-pitch fonthdc = GetDC(hwnd);SelectObject(hdc, CreateFont(0, 0, 0, 0, 0, 0, 0, 0,dwCharSet, 0, 0, 0, FIXED_PITCH, NULL));GetTextMetrics(hdc, &tm);cxChar = tm.tmAveCharWidth;cyChar = tm.tmHeight;DeleteObject(SelectObject(hdc, GetStockObject(SYSTEM_FONT)));ReleaseDC(hwnd, hdc);//Allocate memory for display lines.if (pmsg)free(pmsg);cLinesMax = cyClientMax / cyChar;pmsg = (PMSG)malloc(cLinesMax * sizeof(MSG));cLines = 0;//fall throughcase WM_SIZE:if (WM_SIZE == message){cxClient = LOWORD(lParam);cyClient = HIWORD(lParam);}//Calculate scrolling rectanglerectScroll.left= 0;rectScroll.right= cxClient;rectScroll.top= cyChar;rectScroll.bottom= cyChar * (cyClient / cyChar);InvalidateRect(hwnd, NULL, TRUE);if (message == WM_INPUTLANGCHANGE)return 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 arrayfor (i = cLinesMax - 1; i > 0; i--){pmsg[i] = pmsg[i - 1];}//Store new messagepmsg[0].hwnd= hwnd;pmsg[0].message = message;pmsg[0].wParam= wParam;pmsg[0].lParam= lParam;cLines = min(cLines + 1, cLinesMax);//Scroll up the displayScrollWindow(hwnd, 0, -cyChar, &rectScroll, &rectScroll);break; //i.e., call DefWindowProc so Sys messages workcase WM_PAINT:hdc = BeginPaint(hwnd, &ps);SelectObject(hdc, CreateFont(0, 0, 0, 0, 0, 0, 0, 0,dwCharSet, 0, 0, 0, FIXED_PITCH, NULL));SetBkMode(hdc, TRANSPARENT);TextOut(hdc, 0, 0, szTop, lstrlen(szTop));TextOut(hdc, 0, 0, szUnd, lstrlen(szUnd));for (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 : TEXT(' ')),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));}DeleteObject(SelectObject(hdc, GetStockObject(SYSTEM_FONT)));EndPaint(hwnd, &ps);return 0;case WM_DESTROY:PostQuitMessage(0);return 0;}return  DefWindowProc(hwnd, message, wParam, lParam);}
运行结果如下



6.5 插入符号

输入字符时候提示当前插入位置的 ,caret

6.5.1 一些关于插入符号的函数


CreateCaret: 创建和窗口关了的插入符号

SetCaretPos  设置窗口内的插入符号的位置

ShowCaret  显示插入符号

HideCaret 隐藏插入符号

DestroyCaret 销毁插入符号

GetCaretPos 获得当前插入符号位置

GetCaretBlinkTime 获得插入符号闪烁时间

SetCaretBlinkTime 设置插入符号闪烁时间


在WM_SETFOCUS消息时调用CreateCaret函数

在WM_KILLFOCUS消息调用DestoryCaret函数


使用插入符号的一些规则: 创建的插入符号是隐藏的。在调用CreateCaret之后,窗口过程必须调用ShowCaret使其可见。另外,如果窗口过程处理的是一个非WM_PAINT消息,但是要在窗口内绘制某些东西时,它必须调用HideCaret隐藏插入符号。当它结束在窗口内的绘制之后,再调用ShowCaret来显示插入符号。HideCaret的效果是叠加的,

如果你调用了HideCaet很多次,那么你必须调用同样多次数的ShowCaret使插入符号可见。


6.5.2 TYPER 程序

一个简单的文字处理程序

#include <windows.h>#define BUFFER(x, y) *(pBuffer + y * cxBuffer + x)LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //window procedure.int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow){staticTCHAR szAppName[] = TEXT("Typer");HWNDhwnd;MSGmsg;WNDCLASSwndClass;//The window ClasswndClass.style = CS_HREDRAW | CS_VREDRAW;wndClass.lpfnWndProc = WndProc;// assign the window procedure to windows class.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;//Register the Window Class to the Windows System. if (!RegisterClass(&wndClass)){MessageBox(NULL, TEXT("This program require Windows NT!"),szAppName, MB_ICONERROR);return 0;}//This function will generate an WM_CREATE message.hwnd = CreateWindow(szAppName,//Window class nameTEXT("Typing Program"),//Window captionWS_OVERLAPPEDWINDOW,//Window StyleCW_USEDEFAULT,//initial x positionCW_USEDEFAULT,//initial y positionCW_USEDEFAULT,//initial x sizeCW_USEDEFAULT,//initial y sizeNULL,//parent window handleNULL,//window menu handlehInstance,//program instance handleNULL);//creation parametersShowWindow(hwnd, iCmdShow);UpdateWindow(hwnd);//This function will generate a WM_PAINT message./* The message loop for this program.if received the WM_QUIT message, the function will return 0.*/while (GetMessage(&msg, NULL, 0, 0)){TranslateMessage(&msg);DispatchMessage(&msg);}return msg.wParam;}//define the Window Procedure WndProcLRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){static DWORDdwCharSet = DEFAULT_CHARSET;static intcxChar, cyChar, cxClient, cyClient, cxBuffer, cyBuffer, xCaret, yCaret;static TCHAR *pBuffer = NULL;HDChdc;intx, y, i;PAINTSTRUCTps;TEXTMETRICtm;switch (message) //get the message{case WM_INPUTLANGCHANGE:dwCharSet = wParam;// fall throughcase WM_CREATE:hdc = GetDC(hwnd);SelectObject(hdc, CreateFont(0, 0, 0, 0, 0, 0, 0, 0,dwCharSet, 0, 0, 0, FIXED_PITCH, NULL));GetTextMetrics(hdc, &tm);cxChar = tm.tmAveCharWidth;cyChar = tm.tmHeight;DeleteObject(SelectObject(hdc, GetStockObject(SYSTEM_FONT)));ReleaseDC(hwnd, hdc);// fall throughcase WM_SIZE:if (WM_SIZE == message){cxClient = LOWORD(lParam);cyClient = HIWORD(lParam);}//calculate window size in characterscxBuffer = max(1, cxClient / cxChar);cyBuffer = max(1, cyClient / cyChar);//allocate memory for buffer and clear itif (pBuffer != NULL)free(pBuffer);pBuffer = (TCHAR *)malloc(cxBuffer * cyBuffer * sizeof(TCHAR));//Set all of the buffer to space ' 'for (y = 0; y < cyBuffer; y++)for (x = 0; x < cxBuffer; x++)BUFFER(x, y) = TEXT(' ');//set caret to upper left cornerxCaret = 0;yCaret = 0;if (hwnd == GetFocus())SetCaretPos(xCaret * cxChar, yCaret * cyChar);InvalidateRect(hwnd, NULL, TRUE);return 0;case WM_SETFOCUS://create and show the caretCreateCaret(hwnd, NULL, cxChar, cyChar);SetCaretPos(xCaret * cxChar, yCaret * cyChar);ShowCaret(hwnd);return 0;case WM_KILLFOCUS://hide and destroy the caretHideCaret(hwnd);DestroyCaret();return 0;case WM_KEYDOWN:switch (wParam){case VK_HOME:xCaret = 0;break;case VK_END:xCaret = cxBuffer - 1;break;case VK_PRIOR:yCaret = 0;break;case VK_NEXT:yCaret = cyBuffer - 1;break;case VK_LEFT:xCaret = max(xCaret - 1, 0);break;case VK_RIGHT:xCaret = min(xCaret + 1, cxBuffer - 1);break;case VK_UP:yCaret = max(yCaret - 1, 0);break;case VK_DOWN:yCaret = min(yCaret + 1, cyBuffer - 1);break;case VK_DELETE://delete the character in the buffer and redraw the character this line.for (x = xCaret; x < cxBuffer - 1; x++)BUFFER(x, yCaret) = BUFFER(x +1 , yCaret);BUFFER(cxBuffer - 1, yCaret) = TEXT(' ');HideCaret(hwnd);//redraw the character on the screen.hdc = GetDC(hwnd);SelectObject(hdc, CreateFont(0, 0, 0, 0, 0, 0, 0, 0,dwCharSet, 0, 0, 0, FIXED_PITCH, NULL));TextOut(hdc, xCaret * cxChar, yCaret * cyChar, &BUFFER(xCaret, yCaret),cxBuffer - xCaret); //redraw the text from the current Caret to the end of the line.DeleteObject(SelectObject(hdc, GetStockObject(SYSTEM_FONT)));ReleaseDC(hwnd, hdc);ShowCaret(hwnd);break;}SetCaretPos(xCaret * cxChar, yCaret * cyChar);return 0;case WM_CHAR://process for multiple input of the same characters.for (i = 0; i < (int)LOWORD(lParam); ++i){switch (wParam){case TEXT('\b')://backspaceif (xCaret > 0){xCaret--;SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);}break;case TEXT('\t')://tabdo {SendMessage(hwnd, WM_CHAR, TEXT(' '), 1); } while (xCaret % 4 != 0);break;case TEXT('\n')://line feedif (++yCaret == cyBuffer)yCaret = 0;break;case TEXT('\r')://carriage returnxCaret = 0;if (++yCaret == cyBuffer)yCaret = 0;break;case TEXT('\x1B')://escapefor (y = 0; y < cyBuffer; y++)for (x = 0; x < cxBuffer; x++)BUFFER(x, y) = TEXT(' ');xCaret = 0;yCaret = 0;InvalidateRect(hwnd, NULL, FALSE);break;default://character codes;BUFFER(xCaret, yCaret) = (TCHAR)wParam;HideCaret(hwnd);hdc = GetDC(hwnd);SelectObject(hdc, CreateFont(0, 0, 0, 0, 0, 0, 0, 0,dwCharSet, 0, 0, 0, FIXED_PITCH, NULL));TextOut(hdc, xCaret * cxChar, yCaret * cyChar,&BUFFER(xCaret, yCaret), 1);DeleteObject(SelectObject(hdc, GetStockObject(SYSTEM_FONT)));ReleaseDC(hwnd, hdc);ShowCaret(hwnd);if (++xCaret == cxBuffer){xCaret = 0;if (++yCaret == cyBuffer)yCaret = 0;}break;}}SetCaretPos(xCaret * cxChar, yCaret * cyChar);return 0;case WM_PAINT:hdc = BeginPaint(hwnd, &ps);SelectObject(hdc, CreateFont(0, 0, 0, 0, 0, 0, 0, 0,dwCharSet, 0, 0, 0, FIXED_PITCH, NULL));for (y = 0; y < cyBuffer; y++)TextOut(hdc, 0, y * cyChar, &BUFFER(0, y), cxBuffer);DeleteObject(SelectObject(hdc, GetStockObject(SYSTEM_FONT)));EndPaint(hwnd, &ps);return 0;case WM_DESTROY:PostQuitMessage(0);return 0;}return  DefWindowProc(hwnd, message, wParam, lParam);}

运行结果如下


0 0