Windows 程序设计--(六)键盘

6.2 击键消息

当按下一个键时,Windows把WM_KEYDOWN或者WM_SYSKEYDOWN消息放入有输入焦点的窗口的消息队列;当您释放一个键时,Windows把WM_KEYUP或者WM_SYSKEYUP消息放入消息队列中。

 

 

键按下

键释放

非系统键

WM_KEYDOWN

WM_KEYUP

系统键

WM_SYSKEYDOWN

WM_SYSKEYUP

通常「down(按下)」和「up(放开)」消息是成对出现的。不过,如果您按住一个键使得自动重复功能生效,那么当该键最后被释放时,Windows会给窗口消息处理程序发送一系列WM_KEYDOWN(或者WM_SYSKEYDOWN)消息和一个WM_KEYUP(或者WM_SYSKEYUP)消息。像所有放入队列的消息一样,按键消息也有时间信息。通过呼叫GetMessageTime,您可以获得按下或者释放键的相对时间。

 

6.2.1 系统键击和非系统键击

一般来说和ALT组合的都是系统键击,其它的都是非系统键击。

#include <Windows.h>
#include <iostream>

using namespace std;

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
    static TCHAR szAppName[] = TEXT("WNDCLASS NAME");
    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("窗口创建失败!需要Windows NT!(传递消息为UNICODE)"), szAppName, MB_ICONERROR);

        return 0;
    }

    hwnd = CreateWindow(
        szAppName,
        TEXT("Hk_Mayfly"),
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        100,//宽度
        100,//高度
        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) {

    switch (message) {
    case WM_SYSKEYDOWN:
        MessageBox(hwnd, TEXT("系统键击按下!"), TEXT("test!"), NULL);
        return 0;
    case WM_KEYDOWN:
        MessageBox(hwnd, TEXT("非系统键击按下!"), TEXT("test!"), NULL);
        return 0;
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}

 

6.2.2 虚拟键代码

扫描码(通码)和断码的相关信息可以在 https://www.cnblogs.com/Mayfly-nymph/p/11228966.html 找到说明。

 

前四个虚拟键码中有三个指的是鼠标键:

 

十进制

十六进制

WINUSER.H标识符

必需?

IBM兼容键盘

1

01

VK_LBUTTON

 

鼠标左键

2

02

VK_RBUTTON

 

鼠标右键

3

03

VK_CANCEL

ˇ

Ctrl-Break

4

04

VK_MBUTTON

 

鼠标中键

我们不能在击键消息中检测到鼠标消息。

虚拟键代码

Symbolic Constant NameHexadecimal ValueMouse Or Keyboard Equivalent
VK_LBUTTON01Left mouse button
VK_RBUTTON02Right mouse button
VK_CANCEL03Control-break processing
VK_MBUTTON04Middle mouse button (three-button mouse)
VK_XBUTTON105Windows 2000: X1 mouse button
VK_XBUTTON206Windows 2000: X2 mouse button
07Undefined
VK_BACK08BACKSPACE key
VK_TAB09TAB key
0A0BReserved
VK_CLEAR0CCLEAR key
VK_RETURN0DENTER key
0E0FUndefined
VK_SHIFT10SHIFT key
VK_CONTROL11CTRL key
VK_MENU12ALT key
VK_PAUSE13PAUSE key
VK_CAPITAL14CAPS LOCK key
VK_KANA15IME Kana mode
VK_HANGUEL15IME Hanguel mode (maintained for compatibility; use VK_HANGUL)
VK_HANGUL15IME Hangul mode
16Undefined
VK_JUNJA17IME Junja mode
VK_FINAL18IME final mode
VK_HANJA19IME Hanja mode
VK_KANJI19IME Kanji mode
1AUndefined
VK_ESCAPE1BESC key
VK_CONVERT1CIME convert
VK_NONCONVERT1DIME nonconvert
VK_ACCEPT1EIME accept
VK_MODECHANGE1FIME mode change request
VK_SPACE20SPACEBAR
VK_PRIOR21PAGE UP key
VK_NEXT22PAGE DOWN key
VK_END23END key
VK_HOME24HOME key
VK_LEFT25LEFT ARROW key
VK_UP26UP ARROW key
VK_RIGHT27RIGHT ARROW key
VK_DOWN28DOWN ARROW key
VK_SELECT29SELECT key
VK_PRINT2APRINT key
VK_EXECUTE2BEXECUTE key
VK_SNAPSHOT2CPRINT SCREEN key
VK_INSERT2DINS key
VK_DELETE2EDEL key
VK_HELP2FHELP key
'0'300 key
'1'311 key
'2'322 key
'3'333 key
'4'344 key
'5'355 key
'6'366 key
'7'377 key
'8'388 key
'9'399 key
3A40Undefined
'A'41A key
'B'42B key
'C'43C key
'D'44D key
'E'45E key
'F'46F key
'G'47G key
'H'48H key
'I'49I key
'J'4AJ key
'K'4BK key
'L'4CL key
'M'4DM key
'N'4EN key
'O'4FO key
'P'50P key
'Q'51Q key
'R'52R key
'S'53S key
'T'54T key
'U'55U key
'V'56V key
'W'57W key
'X'58X key
'Y'59Y key
'Z'5AZ key
VK_LWIN5BLeft Windows key (Microsoft Natural Keyboard)
VK_RWIN5CRight Windows key (Microsoft Natural Keyboard)
VK_APPS5DApplications key (Microsoft Natural Keyboard)
5EReserved
VK_SLEEP5FComputer Sleep key
VK_NUMPAD060Numeric keypad 0 key
VK_NUMPAD161Numeric keypad 1 key
VK_NUMPAD262Numeric keypad 2 key
VK_NUMPAD363Numeric keypad 3 key
VK_NUMPAD464Numeric keypad 4 key
VK_NUMPAD565Numeric keypad 5 key
VK_NUMPAD666Numeric keypad 6 key
VK_NUMPAD767Numeric keypad 7 key
VK_NUMPAD868Numeric keypad 8 key
VK_NUMPAD969Numeric keypad 9 key
VK_MULTIPLY6AMultiply key
VK_ADD6BAdd key
VK_SEPARATOR6CSeparator key
VK_SUBTRACT6DSubtract key
VK_DECIMAL6EDecimal key
VK_DIVIDE6FDivide key
VK_F170F1 key
VK_F271F2 key
VK_F372F3 key
VK_F473F4 key
VK_F574F5 key
VK_F675F6 key
VK_F776F7 key
VK_F877F8 key
VK_F978F9 key
VK_F1079F10 key
VK_F117AF11 key
VK_F127BF12 key
VK_F137CF13 key
VK_F147DF14 key
VK_F157EF15 key
VK_F167FF16 key
VK_F1780HF17 key
VK_F1881HF18 key
VK_F1982HF19 key
VK_F2083HF20 key
VK_F2184HF21 key
VK_F2285HF22 key
VK_F2386HF23 key
VK_F2487HF24 key
888FUnassigned
VK_NUMLOCK90NUM LOCK key
VK_SCROLL91SCROLL LOCK key
 9296OEM specific
979FUnassigned
VK_LSHIFTA0Left SHIFT key
VK_RSHIFTA1Right SHIFT key
VK_LCONTROLA2Left CONTROL key
VK_RCONTROLA3Right CONTROL key
VK_LMENUA4Left MENU key
VK_RMENUA5Right MENU key
VK_BROWSER_BACKA6Windows 2000: Browser Back key
VK_BROWSER_FORWARDA7Windows 2000: Browser Forward key
VK_BROWSER_REFRESHA8Windows 2000: Browser Refresh key
VK_BROWSER_STOPA9Windows 2000: Browser Stop key
VK_BROWSER_SEARCHAAWindows 2000: Browser Search key
VK_BROWSER_FAVORITESABWindows 2000: Browser Favorites key
VK_BROWSER_HOMEACWindows 2000: Browser Launch and Home key
VK_VOLUME_MUTEADWindows 2000: Volume Mute key
VK_VOLUME_DOWNAEWindows 2000: Volume Down key
VK_VOLUME_UPAFWindows 2000: Volume Up key
VK_MEDIA_NEXT_TRACKB0Windows 2000: Next Track key
VK_MEDIA_PREV_TRACKB1Windows 2000: Previous Track key
VK_MEDIA_STOPB2Windows 2000: Stop Media key
VK_MEDIA_PLAY_PAUSEB3Windows 2000: Play/Pause Media key
VK_LAUNCH_MAILB4Windows 2000: Launch Mail key
VK_LAUNCH_MEDIA_SELECTB5Windows 2000: Select Media key
VK_LAUNCH_APP1B6Windows 2000: Launch Application 1 key
VK_LAUNCH_APP2B7Windows 2000: Launch Application 2 key
B8-B9Reserved
VK_OEM_1BAWindows 2000: for the US standard keyboard, the ';:' key
VK_OEM_PLUSBBWindows 2000: for any country/region, the '+' key
VK_OEM_COMMABCWindows 2000: for any country/region, the ',' key
VK_OEM_MINUSBDWindows 2000: for any country/region, the '-' key
VK_OEM_PERIODBEWindows 2000: for any country/region, the '.' key
VK_OEM_2BFWindows 2000: for the US standard keyboard, the '/?' key
VK_OEM_3C0Windows 2000: for the US standard keyboard, the '`~' key
C1D7Reserved
D8DAUnassigned
VK_OEM_4DBWindows 2000: for the US standard keyboard, the '[{' key
VK_OEM_5DCWindows 2000: for the US standard keyboard, the '\|' key
VK_OEM_6DDWindows 2000: for the US standard keyboard, the ']}' key
VK_OEM_7DEWindows 2000: for the US standard keyboard, the 'single-quote/double-quote' key
VK_OEM_8DF 
E0Reserved
 E1OEM specific
VK_OEM_102E2Windows 2000: either the '<>' key or the '\|' key on the RT 102-key keyboard
 E3E4OEM specific
VK_PROCESSKEYE5Windows 95, Windows NT 4.0, and Windows 2000: IME PROCESS key
 E6OEM specific
VK_PACKETE7Windows 2000: Used to pass Unicode characters as if they were keystrokes. The VK_PACKET key is the low word of a 32-bit Virtual Key value used for non-keyboard input methods. The Unicode character is the high word.
E8Unassigned
 E9F5OEM specific
VK_ATTNF6Attn key
VK_CRSELF7CrSel key
VK_EXSELF8ExSel key
VK_EREOFF9Erase EOF key
VK_PLAYFAPlay key
VK_ZOOMFBZoom key
VK_NONAMEFCReserved for future use
VK_PA1FDPA1 key
VK_OEM_CLEARFEClear key

 

6.2.4 转移状态

是否按下了位移键(Shift、Ctrl和Alt)或开关键(Caps Lock、Num Lock和Scroll Lock)。通过呼叫GetKeyState函数,您就能获得此信息。例如:

iState = GetKeyState (VK_SHIFT) ;

 

如果按下了Shift,则iState值为负(即设定了最高位置位)。如果Caps Lock键打开,则从

iState = GetKeyState (VK_CAPITAL) ;

 

6.3 字符消息

6.3.1 四类字符消息

 

字符

死字符

非系统字符

WM_CHAR

WM_DEADCHAR

系统字符

WM_SYSCHAR

WM_SYSDEADCHAR

WM_CHAR和WM_DEADCHAR消息是从WM_KEYDOWN得到的;而WM_SYSCHAR和WM_SYSDEADCHAR消息是从WM_SYSKEYDOWN消息得到的(我将简要地讨论一下什么是死字符)。

 

6.3.2 消息顺序

按键

字符代码

产生方法

ANSI C控制字符

Backspace

0x08

Ctrl-H

\b

Tab

0x09

Ctrl-I

\t

Ctrl-Enter

0x0A

Ctrl-J

\n

Enter

0x0D

Ctrl-M

\r

Esc

0x1B

Ctrl-[

 
#include <Windows.h>
#include <iostream>

using namespace std;

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
    static TCHAR szAppName[] = TEXT("WNDCLASS NAME");
    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("窗口创建失败!需要Windows NT!(传递消息为UNICODE)"), szAppName, MB_ICONERROR);

        return 0;
    }

    hwnd = CreateWindow(
        szAppName,
        TEXT("Hk_Mayfly"),
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        100,//宽度
        100,//高度
        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) {

    switch (message) {
    case WM_CHAR:
        switch (wParam) {
        case 'A':
            MessageBox(hwnd, TEXT("A键被按下!"), TEXT("TEST!"), NULL);
            break;
        case 0x61:
            MessageBox(hwnd, TEXT("a键被按下!"), TEXT("TEST!"), NULL);
            break;
        case '\n':
            MessageBox(hwnd, TEXT("回车键被按下!"), TEXT("TEST!"), NULL);
            break;
        case '\r':
            MessageBox(hwnd, TEXT("Enter--换行键被按下!"), TEXT("TEST!"), NULL);
            break;
        }
        
        return 0;
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}

 

 

6.3.4 死字符消息

Windows程序经常忽略WM_DEADCHAR和WM_SYSDEADCHAR消息。

定义:在某些非U.S.英语键盘上,有些键用于给字母加上音调。因为它们本身不产生字符,所以称之为「死键」。

工作方式:当使用者按下这个死键时,窗口消息处理程序接收到一个wParam等于音调本身的ASCII或者Unicode代码的WM_DEADCHAR消息。当使用者再按下可以带有此音调的字母键(例如A键)时,窗口消息处理程序会接收到WM_CHAR消息,其中wParam等于带有音调的字母「a」的ANSI代码。

 

6.5 插入符号(不是光标)

当您往程序中输入文字时,通常有一个底线、竖条或者方框来指示输入的下一个字符将出现在屏幕上的位置。这个标志通常称为「光标」,但是在Windows下写程序,您必须改变这个习惯。在Windows中,它称为「插入符号」。「光标」是指表示鼠标位置的那个位图图像。

 

 

键盘操作实例--贪吃球球

整体思路:

  • 在客户区绘制球球和食物
  • 捕获键盘消息,根据键盘操作改变球球的坐标(位置+大小)
  • 当食物与球球接触,食物消息,更新球球大小,随机产生另一个食物
#include <Windows.h>
#include <iostream>
#include <time.h>
#include <tchar.h>

#define random(x) (rand()%x)

using namespace std;

bool Flag = TRUE;

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
    static TCHAR szAppName[] = TEXT("WNDCLASS NAME");
    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("窗口创建失败!需要Windows NT!(传递消息为UNICODE)"), szAppName, MB_ICONERROR);

        return 0;
    }

    hwnd = CreateWindow(
        szAppName,
        TEXT("Hk_Mayfly"),
        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) {
    HDC hdc;
    PAINTSTRUCT ps;
    int i = 0;
    static int cxClient, cyClient, cxFood, cyFood, Count, cyChar;
    static POINT apt[2];
    srand((int)time(NULL));
    static HBRUSH hBrush;
    TCHAR szBuffer[100], szBuffer_A[100];
    TEXTMETRIC tm;

    switch (message) {
    case WM_CREATE:
        hdc = GetDC(hwnd);
        GetTextMetrics(hdc, &tm);
        cyChar = tm.tmHeight + tm.tmExternalLeading;
        hBrush = CreateSolidBrush(RGB(255, 0, 0));
        ReleaseDC(hwnd, hdc);
        return 0;
    case WM_SIZE:
        /*
        初始化球球位置为客户区正中心
        */
        if (Flag) {
            cxClient = LOWORD(lParam);
            cyClient = HIWORD(lParam);

            apt[0].x = cxClient / 2 - 50;
            apt[0].y = cyClient / 2 - 50;
            apt[1].x = cxClient / 2 + 50;
            apt[1].y = cyClient / 2 + 50;

            Flag = FALSE;
        }
        /*
        食物地址随机
        */
        cxFood = random(cxClient);
        cyFood = random(cyClient);
        return 0;
    case WM_KEYDOWN:
        /*
        按键控制球球的移动方向
        */
        switch (wParam) {
        case VK_UP:
            apt[0].y -= 5;
            apt[1].y -= 5;
            break;
        case VK_DOWN:
            apt[0].y += 5;
            apt[1].y += 5;
            break;
        case VK_LEFT:
            apt[0].x -= 5;
            apt[1].x -= 5;
            break;
        case VK_RIGHT:
            apt[0].x += 5;
            apt[1].x += 5;
            break;
        case VK_ESCAPE:
            wsprintf(szBuffer, TEXT("Congratulation!!! You Get %d"), Count);
            MessageBox(hwnd, szBuffer, TEXT("Grade"), 0x4L | 0x40L);
            exit(0);
        }
        /*
        客户区无效化
        */
        InvalidateRect(hwnd, NULL, TRUE);
        return 0;
    case WM_PAINT:
        /*
        判断球球是否吃到食物
        */
        if (apt[0].x <= cxFood - 5 && apt[1].x >= cxFood + 5 && apt[0].y <= cyFood - 5 && apt[1].y >= cyFood + 5) {
            /*
            生成新的食物,且食物不能在球球内
            */
            cxFood = random(cxClient);
            cyFood = random(cyClient);
            while (apt[0].x <= cxFood - 5 && apt[1].x >= cxFood + 5 && apt[0].y <= cyFood - 5 && apt[1].y >= cyFood + 5) {
                cxFood = random(cxClient);
                cyFood = random(cyClient);
            }
            /*
            球球长大了
            */
            apt[0].x -= 4;
            apt[1].x += 4;
            apt[0].y -= 4;
            apt[1].y += 4;
            ++Count;
        }
        hdc = BeginPaint(hwnd, &ps);
        SelectObject(hdc, GetStockObject(GRAY_BRUSH));
        //绘制球球
        Ellipse(hdc, apt[0].x, apt[0].y, apt[1].x, apt[1].y);
        SelectObject(hdc, hBrush);
        //绘制食物
        Ellipse(hdc, cxFood - 5, cyFood - 5, cxFood + 5, cyFood + 5);

        TextOut(hdc, cxClient - 150, 30 + cyChar * i++, szBuffer_A, wsprintf(szBuffer_A, TEXT("当前分数:%d"), Count));
        TextOut(hdc, cxClient - 150, 30 + cyChar * i++, szBuffer_A, wsprintf(szBuffer_A, TEXT("向上移动:↑")));
        TextOut(hdc, cxClient - 150, 30 + cyChar * i++, szBuffer_A, wsprintf(szBuffer_A, TEXT("向下移动:↓")));
        TextOut(hdc, cxClient - 150, 30 + cyChar * i++, szBuffer_A, wsprintf(szBuffer_A, TEXT("向左移动:←")));
        TextOut(hdc, cxClient - 150, 30 + cyChar * i++, szBuffer_A, wsprintf(szBuffer_A, TEXT("向右移动:→")));
        TextOut(hdc, cxClient - 150, 30 + cyChar * i++, szBuffer_A, wsprintf(szBuffer_A, TEXT("退出:ESC")));
        EndPaint(hwnd, &ps);
        return 0;
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}

 

破解贪吃球球

有兴趣可以看下:点击进入

转载于:https://www.cnblogs.com/Mayfly-nymph/p/11438354.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值