标签:
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) { static TCHAR szAppName[] = TEXT("SysMets4"); HWND hwnd; MSG msg; WNDCLASS wndClass; //The window Class wndClass.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 name TEXT("Get System Metrics No. 4"), //Window caption WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL, //Window Style CW_USEDEFAULT, //initial x position CW_USEDEFAULT, //initial y position CW_USEDEFAULT, //initial x size CW_USEDEFAULT, //initial y size NULL, //parent window handle NULL, //window menu handle hInstance, //program instance handle NULL); //creation parameters ShowWindow(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 WndProc LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static int cxChar, cxCaps, cyChar, cxClient, cyClient, iMaxWidth; HDC hdc; int i, x, y, iVertPos, iHorzPos, iPaintBeg, iPaintEnd; PAINTSTRUCT ps; SCROLLINFO si; TCHAR szBuffer[10]; TEXTMETRIC tm; 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 size si.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 size si.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 information si.cbSize = sizeof(si); si.fMask = SIF_ALL; GetScrollInfo(hwnd, SB_VERT, &si); //Save the position for comparison later on iVertPos = 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 it if (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 information si.cbSize = sizeof(si); si.fMask = SIF_ALL; //Save the position for comparison later on GetScrollInfo(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 window if (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 position si.cbSize = sizeof(si);; si.fMask = SIF_POS; GetScrollInfo(hwnd, SB_VERT, &si); iVertPos = si.nPos; //Get horizontal scroll bar position GetScrollInfo(hwnd, SB_HORZ, &si); iHorzPos = si.nPos; //Find painting limits iPaintBeg = 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) { static TCHAR szAppName[] = TEXT("KeyView1"); HWND hwnd; MSG msg; WNDCLASS wndClass; //The window Class wndClass.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 name TEXT("Keyboard Message Viewer #1"), //Window caption WS_OVERLAPPEDWINDOW, //Window Style CW_USEDEFAULT, //initial x position CW_USEDEFAULT, //initial y position CW_USEDEFAULT, //initial x size CW_USEDEFAULT, //initial y size NULL, //parent window handle NULL, //window menu handle hInstance, //program instance handle NULL); //creation parameters ShowWindow(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 WndProc 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; static RECT rectScroll; 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) //get the message { case WM_CREATE: case WM_DISPLAYCHANGE: //Get maximum size of client area 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 = (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 rectangle rectScroll.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 array for (i = cLinesMax - 1; i > 0; i--) { 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); 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 (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) { static TCHAR szAppName[] = TEXT("StockFont"); HWND hwnd; MSG msg; WNDCLASS wndClass; //The window Class wndClass.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 name TEXT("Stock Fonts"), //Window caption WS_OVERLAPPEDWINDOW, //Window Style CW_USEDEFAULT, //initial x position CW_USEDEFAULT, //initial y position CW_USEDEFAULT, //initial x size CW_USEDEFAULT, //initial y size NULL, //parent window handle NULL, //window menu handle hInstance, //program instance handle NULL); //creation parameters ShowWindow(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 WndProc LRESULT 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 int iFont, cFonts = sizeof stockfont / sizeof stockfont[0]; HDC hdc; int i, x, y, cxGrid, cyGrid; PAINTSTRUCT ps; TCHAR szFaceName[LF_FACESIZE], szBuffer[LF_FACESIZE + 64]; TEXTMETRIC tm; 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 headings for (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)); } //Characters for(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) { static TCHAR szAppName[] = TEXT("KeyView2"); HWND hwnd; MSG msg; WNDCLASS wndClass; //The window Class wndClass.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 name TEXT("Keyboard Message Viewer #2"), //Window caption WS_OVERLAPPEDWINDOW, //Window Style CW_USEDEFAULT, //initial x position CW_USEDEFAULT, //initial y position CW_USEDEFAULT, //initial x size CW_USEDEFAULT, //initial y size NULL, //parent window handle NULL, //window menu handle hInstance, //program instance handle NULL); //creation parameters ShowWindow(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 WndProc LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static DWORD dwCharSet = DEFAULT_CHARSET; static int cxClientMax, cyClientMax, cxClient, cyClient, cxChar, cyChar; static int cLinesMax, cLines; static PMSG pmsg; static RECT rectScroll; 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) //get the message { case WM_INPUTLANGCHANGE: dwCharSet = wParam; // fall through case WM_CREATE: case WM_DISPLAYCHANGE: //Get maximum size of client area cxClientMax = GetSystemMetrics(SM_CXMAXIMIZED); cyClientMax = GetSystemMetrics(SM_CYMAXIMIZED); //Get character size for fixed-pitch font 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); //Allocate memory for display lines. if (pmsg) free(pmsg); cLinesMax = cyClientMax / cyChar; pmsg = (PMSG)malloc(cLinesMax * sizeof(MSG)); cLines = 0; //fall through case WM_SIZE: if (WM_SIZE == message) { cxClient = LOWORD(lParam); cyClient = HIWORD(lParam); } //Calculate scrolling rectangle rectScroll.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 array for (i = cLinesMax - 1; i > 0; i--) { 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); break; //i.e., call DefWindowProc so Sys messages work 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)); 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) { static TCHAR szAppName[] = TEXT("Typer"); HWND hwnd; MSG msg; WNDCLASS wndClass; //The window Class wndClass.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 name TEXT("Typing Program"), //Window caption WS_OVERLAPPEDWINDOW, //Window Style CW_USEDEFAULT, //initial x position CW_USEDEFAULT, //initial y position CW_USEDEFAULT, //initial x size CW_USEDEFAULT, //initial y size NULL, //parent window handle NULL, //window menu handle hInstance, //program instance handle NULL); //creation parameters ShowWindow(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 WndProc LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static DWORD dwCharSet = DEFAULT_CHARSET; static int cxChar, cyChar, cxClient, cyClient, cxBuffer, cyBuffer, xCaret, yCaret; static TCHAR * pBuffer = NULL; HDC hdc; int x, y, i; PAINTSTRUCT ps; TEXTMETRIC tm; switch (message) //get the message { case WM_INPUTLANGCHANGE: dwCharSet = wParam; // fall through case 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 through case WM_SIZE: if (WM_SIZE == message) { cxClient = LOWORD(lParam); cyClient = HIWORD(lParam); } //calculate window size in characters cxBuffer = max(1, cxClient / cxChar); cyBuffer = max(1, cyClient / cyChar); //allocate memory for buffer and clear it if (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 corner xCaret = 0; yCaret = 0; if (hwnd == GetFocus()) SetCaretPos(xCaret * cxChar, yCaret * cyChar); InvalidateRect(hwnd, NULL, TRUE); return 0; case WM_SETFOCUS: //create and show the caret CreateCaret(hwnd, NULL, cxChar, cyChar); SetCaretPos(xCaret * cxChar, yCaret * cyChar); ShowCaret(hwnd); return 0; case WM_KILLFOCUS: //hide and destroy the caret HideCaret(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'): //backspace if (xCaret > 0) { xCaret--; SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1); } break; case TEXT('\t'): //tab do { SendMessage(hwnd, WM_CHAR, TEXT(' '), 1); } while (xCaret % 4 != 0); break; case TEXT('\n'): //line feed if (++yCaret == cyBuffer) yCaret = 0; break; case TEXT('\r'): //carriage return xCaret = 0; if (++yCaret == cyBuffer) yCaret = 0; break; case TEXT('\x1B'): //escape for (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); }
标签:
原文地址:http://blog.csdn.net/sesiria/article/details/51730933