Windows 程序设计--击键消息和字符消息

键盘消息分为击键消息和字符消息。
具体的细节可以参看微软的官方文档说明:键盘输入概述
一、击键消息:指的是当前按下的是哪一个键。一般有以下4个击键消息WM_KEYDOWN、WM_SYSKEYDOWN、WM_KEYUP、WM_SYSKEYUP。其中又分为系统击键消息和非系统击键消息。但是我们常用的是非系统击键消息。当我们按下键盘上的一个键时就会产生一个WM_KEYDOWN消息,但是我们不知道当前按下的是哪一个按键,我们可以通过该消息的wParam参数知道当前按下的是哪一个按键。wParam参数所附带的信息一般是以VK_开头的键码值。
2.键码值:存储在击键消息WM_KEYDOWN、WM_SYSKEYDOWN、WM_KEYUP、WM_SYSKEYUP的wParam参数中,大多数都是以VK_开头的比如:VK_F1…

#define VK_F1             0x70
#define VK_F2             0x71
#define VK_F3             0x72

他们都定义在WINUSER.h头文件中。消息的另一个参数lParam包含了该消息的其他信息。此信息包括 重复计数、 扫描代码、 扩展键标志、 上下文代码、 以前的键状态标志和 转换状态标志。
比如我向某个窗口发送一个回车键消息,使用如下代码,表示我按下的是回车键。

SendMessage(hwnd,WM_KEYDOWN,VK_RETURN,0);

还有一个比较重要的就是转移状态了,它可以判断是否按下了转义键(Shift键,Ctrl键,Alt键或者切换键Caps Lock、Num Lock键、Scroll Lock键)。可以使用GetKeyState()函数检测这些键是否被按下,GetKeyState()函数的原型如下:

WINUSERAPI
SHORT
WINAPI
GetKeyState(
    _In_ int nVirtKey);

比如可以这样使用该函数:

if (GetKeyState(VK_MENU) & 0x8000))
{
    // ALT key is down.
}

说明:值0x8000包含用于测试键当前是否按下的位标志。
更多的说明,可以参考微软官方文档的描述键盘输入
虚拟键的键码值在WINUSER.h中定义,以下是一些常用的:

/*
 * Virtual Keys, Standard Set
 */
#define VK_LBUTTON        0x01
#define VK_RBUTTON        0x02
#define VK_CANCEL         0x03
#define VK_MBUTTON        0x04    /* NOT contiguous with L & RBUTTON */

#if(_WIN32_WINNT >= 0x0500)
#define VK_XBUTTON1       0x05    /* NOT contiguous with L & RBUTTON */
#define VK_XBUTTON2       0x06    /* NOT contiguous with L & RBUTTON */
#endif /* _WIN32_WINNT >= 0x0500 */
/*
 * 0x07 : reserved
 */
#define VK_BACK           0x08
#define VK_TAB            0x09

/*
 * 0x0A - 0x0B : reserved
 */

#define VK_CLEAR          0x0C
#define VK_RETURN         0x0D

/*
 * 0x0E - 0x0F : unassigned
 */

#define VK_SHIFT          0x10
#define VK_CONTROL        0x11
#define VK_MENU           0x12
#define VK_PAUSE          0x13
#define VK_CAPITAL        0x14

#define VK_KANA           0x15
#define VK_HANGEUL        0x15  /* old name - should be here for compatibility */
#define VK_HANGUL         0x15
/*
 * 0x16 : unassigned
 */
#define VK_JUNJA          0x17
#define VK_FINAL          0x18
#define VK_HANJA          0x19
#define VK_KANJI          0x19

/*
 * 0x1A : unassigned
 */

#define VK_ESCAPE         0x1B

#define VK_CONVERT        0x1C
#define VK_NONCONVERT     0x1D
#define VK_ACCEPT         0x1E
#define VK_MODECHANGE     0x1F

#define VK_SPACE          0x20
#define VK_PRIOR          0x21
#define VK_NEXT           0x22
#define VK_END            0x23
#define VK_HOME           0x24
#define VK_LEFT           0x25
#define VK_UP             0x26
#define VK_RIGHT          0x27
#define VK_DOWN           0x28
#define VK_SELECT         0x29
#define VK_PRINT          0x2A
#define VK_EXECUTE        0x2B
#define VK_SNAPSHOT       0x2C
#define VK_INSERT         0x2D
#define VK_DELETE         0x2E
#define VK_HELP           0x2F
/*
 * VK_0 - VK_9 are the same as ASCII '0' - '9' (0x30 - 0x39)
 * 0x3A - 0x40 : unassigned
 * VK_A - VK_Z are the same as ASCII 'A' - 'Z' (0x41 - 0x5A)
 */
#define VK_LWIN           0x5B
#define VK_RWIN           0x5C
#define VK_APPS           0x5D

/*
 * 0x5E : reserved
 */

#define VK_SLEEP          0x5F

#define VK_NUMPAD0        0x60
#define VK_NUMPAD1        0x61
#define VK_NUMPAD2        0x62
#define VK_NUMPAD3        0x63
#define VK_NUMPAD4        0x64
#define VK_NUMPAD5        0x65
#define VK_NUMPAD6        0x66
#define VK_NUMPAD7        0x67
#define VK_NUMPAD8        0x68
#define VK_NUMPAD9        0x69
#define VK_MULTIPLY       0x6A
#define VK_ADD            0x6B
#define VK_SEPARATOR      0x6C
#define VK_SUBTRACT       0x6D
#define VK_DECIMAL        0x6E
#define VK_DIVIDE         0x6F
#define VK_F1             0x70
#define VK_F2             0x71
#define VK_F3             0x72
#define VK_F4             0x73
#define VK_F5             0x74
#define VK_F6             0x75
#define VK_F7             0x76
#define VK_F8             0x77
#define VK_F9             0x78
#define VK_F10            0x79
#define VK_F11            0x7A
#define VK_F12            0x7B
#define VK_F13            0x7C
#define VK_F14            0x7D
#define VK_F15            0x7E
#define VK_F16            0x7F
#define VK_F17            0x80
#define VK_F18            0x81
#define VK_F19            0x82
#define VK_F20            0x83
#define VK_F21            0x84
#define VK_F22            0x85
#define VK_F23            0x86
#define VK_F24            0x87

更多的定义可以直接参考WINUSER.h。
二、字符消息:击键消息提供了大量的信息但是不包括字符消息。如果需要获取字符消息,则需要在线程的消息循环中调用TranslateMessage
如下所示:

int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hPrevInstance,
    LPSTR lpCmdLine, int nCmdShow)
{
    MSG msg;
    if (!InitApplication(hinstance))
        return FALSE;
    if (!InitInstance(hinstance, nCmdShow))
        return FALSE;
    BOOL fGotMessage;
    while ((fGotMessage = GetMessage(&msg, (HWND)NULL, 0, 0)) != 0 && fGotMessage != -1)
    {
        // 将虚拟密钥消息转换为字符消息。 
        // 字符消息将发布到调用线程的消息队列,下次线程调用 GetMessage 或 PeekMessage 函数时要读取。
        TranslateMessage(&msg);
        // 将消息调度到窗口过程。它通常用于调度 GetMessage 函数检索的消息。
        DispatchMessage(&msg);
    }
    return msg.wParam;
    UNREFERENCED_PARAMETER(lpCmdLine);
}

该函数的作用是把击键消息转化为字符消息。
字符消息也分为以下几类:
WM_CHAR、WM_DEADCHAR、WM_SYSCHAR、WM_SYSDEADCHAR、WM_UNICHAR。TranslateMessage()函数会将WM_KEYDOWN或者WM_SYSDOWN消息翻译成WM_CHAR或者WM_SYSCHAR消息。但是处理键盘输入的应用程序通常会忽略WM_CHAR、WM_UNICHAR消息以外的消息。WM_CHAR使用(16 位 Unicode 转换格式) 或 ANSI 字符集,而WM_UNICHAR使用UTF-32 (32 位 Unicode 转换格式) 。这与RegisterClass函数使用的版本有关。
2.典型的窗口过程函数会忽略除WM_CHAR以外的所有字符消息。当用户按下以下键时TranslateMessage()函数生成WM_CHAR消息。
①任意字符键
②Backspace(退格键)
③Enter(回车键)
④ESC键
⑤Shift+Enter(换行符)
⑥Tab键
当生成WM_CHAR消息时,通过wParam参数我们可以知道字符代码是什么。
以下是对于WM_CHAR消息的典型处理:

case WM_CHAR: 
     switch (wParam) 
      { 
       case 0x08:   
      // Process a backspace. 
       break; 
       case 0x0A: 
       // Process a linefeed.   
       break; 
       case 0x1B: 
       // Process an escape. 
       break; 
       case 0x09:   
       // Process a tab. 
       break; 
       case 0x0D: 
       // Process a carriage return. 
       break; 
       default: 
       // Process displayable characters. 
       break; 
     } 

下面给出具体的实例:
1.新建控制台应用程序:
我新建的工程名字是UseKeyboardInput.代码如下所示:

#include <iostream>
#include <afxwin.h>
#include <windows.h> 
#include <strsafe.h>
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int);
BOOL InitApplication(HINSTANCE);
BOOL InitInstance(HINSTANCE, int);

HINSTANCE hinst;

LRESULT CALLBACK WndProc(HWND hWnd, UINT message,
    WPARAM wParam, LPARAM lParam)
{
    HDC hDC;
    CRect rcClient;
    POINT ptClientUL;
    POINT ptClientLR;
    static POINTS ptBegin;
    static POINTS ptEnd;
    static POINTS ptPrevEnd;
    static BOOL bPrevLine = FALSE;
    
	switch (message)
	{
	case WM_KEYDOWN:
	{
		switch (wParam)
		{
		case VK_LEFT:
			MessageBox(NULL, _T("左方向键按下"), _T("提示"), MB_OK);
			break;
		case VK_RIGHT:
			MessageBox(NULL, _T("右方向键按下"), _T("提示"), MB_OK);
			break;
		case VK_UP:
			MessageBox(NULL, _T("Up 键按下"), _T("提示"), MB_OK);
			break;
		case VK_DOWN:
			MessageBox(NULL, _T("Down 键按下"), _T("提示"), MB_OK);
			break;
		case VK_HOME:
			MessageBox(NULL, _T("Home 键按下"), _T("提示"), MB_OK);
			break;
		case VK_END:
			MessageBox(NULL, _T("End 键按下"), _T("提示"), MB_OK);
			break;
		case VK_INSERT:
			MessageBox(NULL, _T("Insert 键按下"), _T("提示"), MB_OK);
			break;
		case VK_DELETE:
			MessageBox(NULL, _T("Delete 键按下"), _T("提示"), MB_OK);
			break;
		case VK_F2:
			MessageBox(NULL, _T("F2 键按下"), _T("提示"), MB_OK);
			break;
		}
	}
    case WM_CHAR:
    {
        switch (wParam)
        {
        case 0x08:
            MessageBox(NULL, _T("退格键按下"), _T("提示"), MB_OK);
            break;
        case 0x0A:
            MessageBox(NULL, _T("换行符(SHIFT + ENTER)被按下"), _T("提示"), MB_OK);
            break;
        case 0x1B:
            MessageBox(NULL, _T("ESC键按下"), _T("提示"), MB_OK);
            break;
        case 0x09:
            MessageBox(NULL, _T("Tab键按下"), _T("提示"), MB_OK);
            break;
        case 0x0D:
            MessageBox(NULL, _T("ENTER键(回车键)按下"), _T("提示"), MB_OK);
            break;
        default:
            break;
        }
        break;
    }
    case WM_SETFOCUS:
    {
        HWND hwnd;     // window handle 
        int x= 50 ;           // horizontal coordinate of cursor 
        int y= 50 ;           // vertical coordinate of cursor 
        int nWidth = 10;      // width of cursor 
        int nHeight =10 ;     // height of cursor 
        char* lpszChar;  // pointer to character 
         // Create a solid black caret. 
        CreateCaret(hWnd, (HBITMAP)NULL, nWidth, nHeight);
        // Adjust the caret position, in client coordinates. 
        SetCaretPos(x, y);
        // Display the caret. 
        ShowCaret(hWnd);
        break;
    }
    case WM_KILLFOCUS:
    {
        int x = 50;           // horizontal coordinate of cursor 
        int y = 50;          // vertical coordinate of cursor 

       wchar_t* lpszChar = NULL ;

        // Hide the caret. 
        HideCaret(NULL);

        // Draw the character on the screen. 
        HDC hDC;
        hDC = GetDC(hWnd);
        SelectObject(hDC,
            GetStockObject(SYSTEM_FIXED_FONT));

        TextOut(hDC, x, y,lpszChar, 1);

        ReleaseDC(hWnd, hDC);

        // Display the caret. 

        ShowCaret(hWnd);

        break;
    }

	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
		break;
	}
    return 0;
}

int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hPrevInstance,
    LPSTR lpCmdLine, int nCmdShow)
{
    MSG msg;

    if (!InitApplication(hinstance))
        return FALSE;

    if (!InitInstance(hinstance, nCmdShow))
        return FALSE;

    BOOL fGotMessage;
    while ((fGotMessage = GetMessage(&msg, (HWND)NULL, 0, 0)) != 0 && fGotMessage != -1)
    {
        // 将虚拟密钥消息转换为字符消息。 
        // 字符消息将发布到调用线程的消息队列,下次线程调用 GetMessage 或 PeekMessage 函数时要读取。
        TranslateMessage(&msg);
        // 将消息调度到窗口过程。它通常用于调度 GetMessage 函数检索的消息。
        DispatchMessage(&msg);
    }
    return msg.wParam;
    UNREFERENCED_PARAMETER(lpCmdLine);
}

BOOL InitApplication(HINSTANCE hinstance)
{
    WNDCLASSEX wcx;
    // Fill in the window class structure with parameters 
    // that describe the main window. 
    wcx.cbSize = sizeof(wcx);          // size of structure 
    wcx.style = CS_HREDRAW |
        CS_VREDRAW;                    // redraw if size changes 
    wcx.lpfnWndProc = (WNDPROC)WndProc;     // points to window procedure 
    wcx.cbClsExtra = 0;                // no extra class memory 
    wcx.cbWndExtra = 0;                // no extra window memory 
    wcx.hInstance = hinstance;         // handle to instance 
    wcx.hIcon = LoadIcon(NULL,
        IDI_APPLICATION);              // predefined app. icon 
    wcx.hCursor = LoadCursor(NULL,
        IDC_ARROW);                    // predefined arrow 
    wcx.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);                  // white background brush 
    wcx.lpszMenuName = (LPCWSTR)"MainMenu";    // name of menu resource 
    wcx.lpszClassName = (LPCWSTR)"MainWClass";  // name of window class 
    wcx.hIconSm = (HICON)LoadImage(hinstance, // small class icon 
        MAKEINTRESOURCE(5),
        IMAGE_ICON,
        GetSystemMetrics(SM_CXSMICON),
        GetSystemMetrics(SM_CYSMICON),
        LR_DEFAULTCOLOR);

    // Register the window class. 

    return RegisterClassEx(&wcx);
}

BOOL InitInstance(HINSTANCE hinstance, int nCmdShow)
{
    HWND hwnd;

    // Save the application-instance handle. 

    hinst = hinstance;

    // Create the main window. 

    hwnd = CreateWindow(
        (LPCWSTR)"MainWClass",        // name of window class 
        (LPCWSTR)"Sample",            // title-bar string 
        WS_OVERLAPPEDWINDOW, // top-level window 
        CW_USEDEFAULT,       // default horizontal position 
        CW_USEDEFAULT,       // default vertical position 
        CW_USEDEFAULT,       // default width 
        CW_USEDEFAULT,       // default height 
        (HWND)NULL,         // no owner window 
        (HMENU)NULL,        // use class menu 
        hinstance,           // handle to application instance 
        (LPVOID)NULL);      // no window-creation data 

    if (!hwnd)
        return FALSE;
    // Show the window and send a WM_PAINT message to the window 
    // procedure. 
    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);
    return TRUE;
}

该章节的详细细节,大家可以参考微软的官方文档键盘和鼠标输入
最终效果如下图所示:
在这里插入图片描述
欢迎大家一起交流。
参考:Windows程序设计–击键消息和字符消息
微软官方文档:键盘和鼠标输入

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值