相关函数预览:

SHORT WINAPI GetKeyState(_In_intnVirtKey);

SHORT WINAPI GetAsyncKeyState( _In_int vKey);

LRESULT WINAPI SendMessageW( _In_ HWND hWnd, _In_ UINT Msg, _Pre_maybenull_ _Post_valid_ WPARAM wParam,  _Pre_maybenull_ _Post_valid_LPARAM lParam);

#define SendMessage  SendMessageW

BOOL WINAPI CreateCaret(_In_HWND hWnd,_In_opt_HBITMAP hBitmap, _In_ int nWidth,_In_int nHeight);

BOOL WINAPI SetCaretPos(_In_int X,  _In_int Y);

BOOL WINAPI ShowCaret( _In_opt_HWND hWnd);

BOOL WINAPI HideCaret(_In_opt_HWND hWnd);

BOOL WINAPI DestroyCaret( VOID);

Windows提供了八种消息来表示键盘事件,其中击键消息有4种,字符消息有4种。窗口过程通过捕获WM_SETFOCUS和WM_KILLFOCUS消息来确定自己的窗口是否具有输入点。

Windows击键消息:

  键按下 键释放
非系统击键 WM_KEYDOWN WM_KEYUP
系统击键 WM_SYSKEYDOWN WM_SYSKEYUP

对于这四类击键消息,wParam是虚拟键代码,lParam则被分为六个字段。虚拟键代码再WINUSER.H头文件中,第一个为VK_LBUTTON,0x41~0x5A为A~Z, 0x30~0x39为主键盘上的0~9.

32位的lParam的15~00位为16位的重复计数,即当程序无法及时处理某一个键被持续按下的情况时,会将重复次数保存在这16位。

23~16位为8位的OEM扫描码

24位为扩展键标记,当击键来自于IBM加强型键盘的附加键时,该位为1。

29位为内容代码,如果击键的同时按下了alt键,则该位为1,收到WM_SYSKEYUP和WM_SYSKEYDOWN消息时,此位始终为1,收到WM_KEYUP和WM_KEYDOWN消息时,此位始终为0。有特殊情况,即当窗口最小化时,所有的击键都将产生WM_SYSKEYUP和WM_SYSKEYDOWN消息。

30位为键的先前状态,如果键先前处于释放状态,则该位为0,否则为1.WM_KEYUP和WM_SYSKEYUP的消息此字段总为1,而WM_SYSKEYDOWN和WM_KEYDOWN消息可能为0或1。

31位为转换状态,如果键正在按下,则此位为0,否则为1。

31 30 29 28~25 24 23~16 15~00

查询键的状态:

这里有两个函数

iState = GetKeyState(VK_SHIFT);//查询Shift键的状态

iState = GetAsyncKeyState(VK_SHIFT);

两个函数的区别在于前者是重消息队列中得到键盘消息,后者是直接侦测键盘的硬件中断(此处参考点击打开链接)。

如果我们用键盘的上下左右方向键来控制滚动条,可以在WM_KEYDOWN消息中使用SendMessage函数向窗口发送WM_VSCROLL消息或者WM_HSCROLL消息,然后交给滚动条的相关处理方法处理。通常情况下我们只需要处理WM_KEYDOWN消息,因为系统击键往往会由 DefWindowProc(hWnd, message, wParam, lParam);来处理。

Windows还有四种字符消息:

  字符 死字符
非系统字符 WM_CHAR WM_DEADCHAR
系统字符 WM_SYSCHAR WM_SYSDEADCHAR

字符消息是由TranslateMessage函数捕获到WM_KEYDOW消息后,发送WM_CHAR到消息队列后,由窗口过程捕获并处理。当我们按下A键然后释放时,窗口过程会依次收到WM_KEYDOWN, WM_CHAR, WM_KEYUP三条消息。如果我们一直按下A键然后再释放时,窗口过程会依次收到WM_KEYDOWN,WM_CHAR, WM_KEYDOWN,WM_CHAR, WM_KEYDOWN,WM_CHAR, WM_KEYDOWN,WM_CHAR,WM_KEYUP消息。

许多击键消息我们可以当作字符消息处理,特别是在文本编辑器中,这种方式可以让程序更清晰,这些击键可以由ctrl组合键的方式输入。

击键 字符码 产生方法 ANSIC转义码
空格 0x08 ctrl+H \b
Tab 0x09 ctrl+I \t
crtl+回车 0x0a ctrl+J \n
回车 0x0d ctrl+M \r
ESC 0x1b ctrl+[  

插入符号:

插入符号就是我们在输入文本时闪烁的 | 。由以下五个函数来管理插入符号

CreateCatet(hWnd, nullptr, x, y);//创建插入符

SetCaretPos(x, y);//设置插入符的位置

ShowCaret(hWnd);//显示插入符

HideCaret(hWnd);//隐藏插入符

DestroyCaret();//销毁插入符

当键盘布局发生改变时,窗口过程将收到WM_INPUTLANGCHANGE消息,其wParam为字符集代码,即CreateFont函数的第9个参数,所以在该消息中应当及时改变字体的设置,详见参考程序。

注:参考程序是在滚动条基础上做了一些改动,添加了WM_INPUTLANGCHANGE, WM_SETFOCUS, WM_KILLFOCUS, 重写了WM_KEYDOWN, WM_CHAR消息,改动了WM_CREATE, WM_SIZE, WM_PAINT消息的代码。

参考程序:

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{static int cxChar, cyChar, cxCaps, cxClient, cyClient, iMaxWidth, iMaxLine;//以下内容为在滚动条基础上增添的内容static int cxBuffer, cyBuffer, xCaret, yCaret;static TCHAR* pBuffer = nullptr;static DWORD dwCharSet = DEFAULT_CHARSET;//ENDHDC hdc;int i, x, y, iVertpos, iPaintBeg, iPaintEnd;SCROLLINFO si;TCHAR szBuffer[10];TEXTMETRIC tm;switch (message){case WM_INPUTLANGCHANGE:dwCharSet = wParam;break;case WM_CREATE://较滚动条有所变化{hdc = GetDC(hWnd);SelectObject(hdc, CreateFont(0, 0, 0, 0, 0, 0, 0, 0, dwCharSet, 0, 0, 0, FIXED_PITCH, nullptr));GetTextMetrics(hdc, &tm);cxChar = tm.tmAveCharWidth;cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2;//如果为等宽字体,则低位为0,如果为变宽字体,地位为1,大写字母的宽度为小写字母宽度的3/2cyChar = tm.tmHeight + tm.tmExternalLeading;DeleteObject(SelectObject(hdc, GetStockObject(SYSTEM_FONT)));ReleaseDC(hWnd, hdc);}break;case WM_SIZE:{cxClient = LOWORD(lParam);//获取客户区尺寸cyClient = HIWORD(lParam);iMaxLine = cyClient / cyChar;si.cbSize = sizeof(si);si.fMask = SIF_RANGE | SIF_PAGE;si.nMin = 0;si.nMax = iMaxLine;si.nPage = iMaxLine;SetScrollInfo(hWnd, SB_VERT, &si, true);iMaxWidth = cxClient / cxCaps;//每行输出的字数//以下内容为在滚动条基础上新增内容cxBuffer = max(1, iMaxWidth);cyBuffer = max(1, iMaxLine);if (pBuffer)free(pBuffer);pBuffer = (TCHAR*)malloc(cxBuffer * cyBuffer * sizeof(TCHAR));for(y = 0 ; y < cyBuffer ; ++y)for (x = 0; x < cxBuffer; ++x){pBuffer[x + y * cxBuffer] = ' ';}xCaret = yCaret = 0;if (hWnd == GetFocus()){SetCaretPos(xCaret * cxChar, yCaret * cyChar);}InvalidateRect(hWnd, nullptr, true);//END}break;case WM_SETFOCUS:CreateCaret(hWnd, nullptr, 1, cyChar);SetCaretPos(xCaret * cxChar, yCaret * cyChar);ShowCaret(hWnd);break;case WM_KILLFOCUS:HideCaret(hWnd);DestroyCaret();break;case WM_VSCROLL:{si.cbSize = sizeof(si);si.fMask = SIF_ALL;GetScrollInfo(hWnd, SB_VERT, &si);iVertpos = si.nPos;switch (wParam & 0xffff){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;si.nPos = (si.nPos < 0 ? 0 : si.nPos);break;case SB_PAGEDOWN:si.nPos += si.nPage;si.nPos = (si.nPos > si.nMax ? si.nMax : si.nPos);break;case SB_THUMBTRACK:si.nPos = si.nTrackPos;break;default:break;}si.fMask = SIF_POS;SetScrollInfo(hWnd, SB_VERT, &si, true);GetScrollInfo(hWnd, SB_VERT, &si);if (si.nPos != iVertpos){ScrollWindow(hWnd, 0, cyChar * (iVertpos - si.nPos), nullptr, nullptr);UpdateWindow(hWnd);}}break;case WM_CHAR:{for (i = 0; i < (int)LOWORD(lParam); ++i){switch (wParam){case '\b':if (xCaret > 0){xCaret--;SendMessage(hWnd, WM_KEYDOWN, VK_DELETE, 1);}break;case '\t':do{SendMessage(hWnd, WM_CHAR, ' ', 1);} while (xCaret % 8 != 0);break;case '\n':if (++yCaret == cyBuffer)yCaret = 0;break;case '\r':xCaret = 0;if (++yCaret == cyBuffer)yCaret = 0;break;default:pBuffer[xCaret + yCaret * cxBuffer] = (TCHAR)wParam;HideCaret(hWnd);hdc = GetDC(hWnd);SelectObject(hdc, CreateFont(0, 0, 0, 0, 0, 0, 0, 0, dwCharSet, 0, 0, 0, FIXED_PITCH, nullptr));TextOut(hdc, xCaret * cxChar, yCaret * cyChar, &pBuffer[xCaret + yCaret * cxBuffer], 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);break;case WM_COMMAND:{int wmId = LOWORD(wParam);// 分析菜单选择: switch (wmId){case IDM_ABOUT:DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);break;case IDM_EXIT:DestroyWindow(hWnd);break;default:return DefWindowProc(hWnd, message, wParam, lParam);}}break;case WM_KEYDOWN:{switch (wParam){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:for (x = xCaret; x < cxBuffer - 1; ++x)pBuffer[x + yCaret * cxBuffer] = pBuffer[x + yCaret * cxBuffer + 1];pBuffer[(yCaret + 1) * cxBuffer - 1] = ' ';HideCaret(hWnd);hdc = GetDC(hWnd);SelectObject(hdc, CreateFont(0, 0, 0, 0, 0, 0, 0, 0, dwCharSet, 0, 0, 0, FIXED_PITCH, nullptr));TextOut(hdc, xCaret * cxChar, yCaret * cyChar, &pBuffer[xCaret + yCaret * cxBuffer], cxBuffer - xCaret);DeleteObject(SelectObject(hdc, GetStockObject(SYSTEM_FONT)));ReleaseDC(hWnd, hdc);ShowCaret(hWnd);break;}SetCaretPos(xCaret * cxChar, yCaret * cyChar);break;}case WM_PAINT:{PAINTSTRUCT ps;hdc = BeginPaint(hWnd, &ps);// TODO: 在此处添加使用 hdc 的任何绘图代码...si.cbSize = sizeof(si);si.fMask = SIF_POS;GetScrollInfo(hWnd, SB_VERT, &si);iVertpos = si.nPos;SelectObject(hdc, CreateFont(0, 0, 0, 0, 0, 0, 0, 0, dwCharSet, 0, 0, 0, FIXED_PITCH, nullptr));for(y = 0 ; y < cyBuffer ; ++y)TextOut(hdc, 0, y * cyChar, &pBuffer[y * cxBuffer], cxBuffer);DeleteObject(SelectObject(hdc, GetStockObject(SYSTEM_FONT)));EndPaint(hWnd, &ps);}break;case WM_DESTROY:PostQuitMessage(0);break;default:return DefWindowProc(hWnd, message, wParam, lParam);}return 0;
}

参考文献:《Windows程序设计(第5版 珍藏版)》

(学习记录)Win32开发之键盘相关推荐

  1. WinFrom、C# 学习记录五 开发一个鼠标自动点击小软件

    一.说明 经常会被问到需要点击软件的,主要都是玩游戏的盆友,但是也有其它用途的.所以简单弄了一个,打算每当有时间,有需求,就加一些小功能. 这里主要是要记录一下相关开发工作,也记录一些使用/更新的信息 ...

  2. Cocos2d游戏开发学习记录——4.开发《植物大战僵尸》

    文章目录 1.植物大战僵尸 2.开发前导 3.载入页面 4.菜单页面 & 预备战斗页面 5.植物准备页面 6.正式战斗页面 7.战斗逻辑 8.向日葵逻辑 9.进度条逻辑 10.音乐逻辑 11. ...

  3. [FPGA 学习记录] FPGA 开发环境的搭建

    FPGA 开发环境的搭建 文章目录 一.Quartus II 主体软件安装[^1][^2] 1.1 选择软件安装位置 1.2 找到安装包存放位置 1.3 运行安装程序 1.4 开始安装 1.5 安装位 ...

  4. 学习记录-windows,开发板互通上网

    windows网络配置 step1:以太网设置为自动获取IP. step2:无线网络(WLAN)允许网络共享,选择以太网.设置这个,就可以将WiFi连接的网络共享给本地连接. step3:查看以太网I ...

  5. WEB开发零基础到入门之HTML+CSS(学习记录)

    序言 之前一直想把自己的一个学习记录,以文章形式记录下来,方便于自己后期查找和温故.自己想了想准备建个人博客站来做记录,但是又想到了csdn的大家庭:所以就放弃了用个人站记录的想法,关于这里简单说明下 ...

  6. Android开发技术周报176学习记录

    Android开发技术周报176学习记录 教程 当 OkHttp 遇上 Http 2.0 http://fucknmb.com/2018/04/16/%E5%BD%93OkHttp%E9%81%87% ...

  7. android培训内容明细,记录Android开发学习

    记录Android开发学习 Menu菜单学习 1.掌握Android中菜单的创建. 2.掌握Intent信使组件. 创建菜单Menu 我们模仿微信菜单栏学习,创建一个于微信菜单栏相似的菜单 那么我们应 ...

  8. 微信小程序开发学习记录(一):直播功能

    微信小程序直播是最近上线的一个新功能,用了将近一天半的时间实现了用直播API去创建直播间,中间踩了很多坑(有的到现在也不是很明白为啥这样就可以 那样就不行),网上有关直播功能的参考文章也比较少,所以这 ...

  9. c# 弹出虚拟键盘关闭虚拟键盘(适用win10、win7)学习记录(转载)

    转载自 https://www.cnblogs.com/lizhijian/p/12725782.html c# 弹出虚拟键盘关闭虚拟键盘(适用win10.win7) using System; us ...

最新文章

  1. hexo定制个人博客matery主体打开公式渲染
  2. 如何使用ElasTest实现测试的可观察性
  3. 二叉树的建造、递归与非递归遍历
  4. [安卓基础] 006.打开另一个Activity
  5. 2009年4月计算机网络原理,全国2009年4月高等教育自学考试计算机网络原理
  6. 合并bin文件-----带boot发布版本比较好用的bat(便捷版)
  7. APP版本管理SDK项目实战-准备篇
  8. android 锁屏显示消息格式,通知!在锁屏上显示Android通知 | MOS86
  9. iOS 性能优化之内存优化
  10. 小米手机miui12系统下拉状态栏开关图标排序方法
  11. 网心科技CEO、迅雷联席CEO陈磊:开启共享经济云计算时代
  12. 初学python类必须要知道的三个基础知识(入门理解)
  13. VB、VBA、VBS的区别你搞清楚了吗?
  14. picpick文字竖排了怎么变成横排
  15. 如何微信多开(PC端微信多开)
  16. 苹果数据泄漏:内鬼频出,这是库克的错吗?
  17. VMware 安装Ubuntu系统后,启动一直黑屏
  18. FFMPEG学习----解码视频
  19. codeblocks linux使用教程,codeblocks教程
  20. VC6.0的工程设置解读Project--Settings - 查志强(转载)

热门文章

  1. springcloud(十一):服务网关 Spring Cloud GateWay 入门
  2. 关于PDF加密的算法【PDFium笔记】
  3. vue组件加载完成之后执行方法_vuejs页面加载完成后执行函数
  4. 计算机网络按照延伸距离划分为,计算机应用基础填空题
  5. flash p2p流媒体服务器系统软件,Flash P2P流媒体服务器系统
  6. color a dir/s_2级绘本(A)Lesson 25:Kipper扮演小丑
  7. 第三方SDK编写文档 - iOS
  8. 网店走出了困境,全因有skycc营销软件
  9. 微信小程序开发竟然这么简单?!
  10. 搭建gloo网关(基于envoy)的wasm实验环境(阿里云、本机)