47_想让程序模拟一波,用户击键消息,应该怎么做呢?

想让程序模拟一波,用户击键消息,应该怎么做呢?

有两种方法可以很方便地实现模拟击键消息。一是使用
SendMessage / PostMessage函数发送WM_KEYDOWN等消息。SendMessage和PostMessage函数的用法相同,不同的是PostMessage函数将消息发送到线程的消息队列并立即返回,不会等待窗口过程把消息处理完毕。我们已经用过SendMessage函数,接下来练习一下PostMessage函数。

二是使用keybd_event模拟击键事件。keybd_event函数的原型定义如下∶

voID WINAPl keybd_event(
_In_ BYTE bVk, //虚拟键码
_In_ BYTE bScan, //按键的扫描码,可以设置为0
_In_ DWORD dwFlags,//标志位
_In_ ULONG_PTR dwExtraInfo);//与击键相关的附加的32位值,可以设置为0

keybd_event函数合成一次击键事件,并不关心由谁来处理它。系统捕捉到击键事件后会转换为键盘消息WM_KEYDOWN或WM_KEYUP分发给当前系统中拥有键盘焦点的应用程序。

比如哦说,我的笔记本只有84个键盘,没有Home和End按键。我想使用数字按键1代替Home按键的效果,数字按键2代替End按键的效果。打开Chapter4\SystemMetrics4项目,添加对WM_CHAR消息的处理∶

#include <Windows.h>
#include <tchar.h>
#include "Metrics.h"

const int NUMLINES = sizeof(METRICS) / sizeof(METRICS[0]);
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    WNDCLASSEX wndclass;                            // RegisterClassEx函数用的WNDCLASSEX结构
    TCHAR szClassName[] = TEXT("MyWindow");         // RegisterClassEx函数注册的窗口类的名称
    TCHAR szAppName[] = TEXT("GetSystemMetrics");   // 窗口标题
    HWND hwnd;                                      // CreateWindowEx函数创建的窗口的句柄
    MSG msg;                                        // 消息循环所用的消息结构体

    wndclass.cbSize = sizeof(WNDCLASSEX);
    wndclass.style = CS_HREDRAW | CS_VREDRAW;
    wndclass.lpfnWndProc = WindowProc;
    wndclass.cbClsExtra = 0;
    wndclass.cbWndExtra = 0;
    wndclass.hInstance = hInstance;
    wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
    wndclass.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1);
    wndclass.lpszMenuName = NULL;
    wndclass.lpszClassName = szClassName;
    wndclass.hIconSm = NULL;
    RegisterClassEx(&wndclass);

    hwnd = CreateWindowEx(0, szClassName, szAppName, WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL,
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    while (GetMessage(&msg, NULL, 0, 0) != 0)
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return msg.wParam;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    HDC hdc;
    PAINTSTRUCT ps;
    TEXTMETRIC tm;
    SCROLLINFO si;
    HFONT hFont, hFontOld;
    static int s_iCol1, s_iCol2, s_iCol3, s_iHeight;// 第1~3列字符串的最大宽度,字符串高度
    static int s_cxClient, s_cyClient;              // 客户区宽度、高度
    static int s_cxChar;                            // 平均字符宽度,用于水平滚动条滚动单位
    int iVertPos, iHorzPos;                         // 垂直、水平滚动条的当前位置
    SIZE size = { 0 };
    int x, y;
    TCHAR szBuf[10];
    int nPaintBeg, nPaintEnd;                       // 无效区域
    RECT rect;

    switch (uMsg)
    {
    case WM_CHAR:
        switch (wParam)
        {
        case '1':
            PostMessage(hwnd, WM_KEYDOWN, VK_HOME, 0);
            break;
        case '2':
            keybd_event(VK_END,0,0,0);
            keybd_event(VK_END,0,KEYEVENTF_KEYUP,0);

        default:
            break;
        }
    case WM_CREATE:
        hdc = GetDC(hwnd);
        hFont = CreateFont(12, 0, 0, 0, 0, 0, 0, 0, GB2312_CHARSET, 0, 0, 0, 0, TEXT("宋体"));
        hFontOld = (HFONT)SelectObject(hdc, hFont);
        for (int i = 0; i < NUMLINES; i++)
        {
            GetTextExtentPoint32(hdc, METRICS[i].m_pLabel, _tcslen(METRICS[i].m_pLabel), &size);
            if (size.cx > s_iCol1)
                s_iCol1 = size.cx;
            GetTextExtentPoint32(hdc, METRICS[i].m_pDesc, _tcslen(METRICS[i].m_pDesc), &size);
            if (size.cx > s_iCol2)
                s_iCol2 = size.cx;
            GetTextExtentPoint32(hdc, szBuf,
                wsprintf(szBuf, TEXT("%d"), GetSystemMetrics(METRICS[i].m_nIndex)), &size);
            if (size.cx > s_iCol3)
                s_iCol3 = size.cx;
        }

        s_iHeight = size.cy + 2;
        GetTextMetrics(hdc, &tm);
        s_cxChar = tm.tmAveCharWidth;

        //让窗口大小和渲染文件内容匹配
        GetClientRect(hwnd, &rect);
        rect.right = s_iCol1 + s_iCol2 + s_iCol3 + GetSystemMetrics(SM_CXVSCROLL);
        AdjustWindowRectEx(&rect, GetWindowLongPtr(hwnd, GWL_STYLE),
            GetMenu(hwnd) != NULL, GetWindowLongPtr(hwnd, GWL_EXSTYLE));
        MoveWindow(hwnd, 0, 0, rect.right - rect.left, rect.bottom - rect.top, TRUE);
        SetWindowPos(hwnd, NULL, 0, 0, rect.right - rect.left, rect.bottom - rect.top,
            SWP_NOZORDER | SWP_NOMOVE);

        SelectObject(hdc, hFontOld);
        DeleteObject(hFont);
        ReleaseDC(hwnd, hdc);
        return 0;

    case WM_SIZE:
        // 客户区宽度、高度
        s_cxClient = LOWORD(lParam);
        s_cyClient = HIWORD(lParam);
        // 设置垂直滚动条的范围和页面大小
        si.cbSize = sizeof(SCROLLINFO);
        si.fMask = SIF_RANGE | SIF_PAGE;
        si.nMin = 0;
        si.nMax = NUMLINES - 1;
        si.nPage = s_cyClient / s_iHeight;
        SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
        // 设置水平滚动条的范围和页面大小
        si.cbSize = sizeof(SCROLLINFO);
        si.fMask = SIF_RANGE | SIF_PAGE;
        si.nMin = 0;
        si.nMax = (s_iCol1 + s_iCol2 + s_iCol3) / s_cxChar - 1;
        si.nPage = s_cxClient / s_cxChar;
        SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);
        return 0;

    case WM_VSCROLL:
        si.cbSize = sizeof(SCROLLINFO);
        si.fMask = SIF_ALL;
        GetScrollInfo(hwnd, SB_VERT, &si);
        iVertPos = si.nPos;
        switch (LOWORD(wParam))
        {
        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_THUMBTRACK:
            si.nPos = si.nTrackPos;
            break;
        case SB_TOP:
            si.nPos = 0;
            break;
        case SB_BOTTOM:
            si.nPos = NUMLINES - 1;
            break;
        }
        // 设置位置,然后获取位置,如果si.nPos越界,Windows不会设置
        si.cbSize = sizeof(SCROLLINFO);
        si.fMask = SIF_POS;
        SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
        GetScrollInfo(hwnd, SB_VERT, &si);

        // 如果Windows更新了滚动条位置,我们更新客户区
        if (iVertPos != si.nPos)
        {
            ScrollWindow(hwnd, 0, s_iHeight * (iVertPos - si.nPos), NULL, NULL);
            UpdateWindow(hwnd);
        }
        return 0;

    case WM_HSCROLL:
        si.cbSize = sizeof(SCROLLINFO);
        si.fMask = SIF_ALL;
        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_THUMBTRACK:
            si.nPos = si.nTrackPos;
            break;
        }
        // 设置位置,然后获取位置,如果si.nPos越界,Windows不会设置
        si.cbSize = sizeof(SCROLLINFO);
        si.fMask = SIF_POS;
        SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);
        GetScrollInfo(hwnd, SB_HORZ, &si);
        // 如果Windows更新了滚动条位置,我们更新客户区
        if (iHorzPos != si.nPos)
        {
            ScrollWindow(hwnd, s_cxChar * (iHorzPos - si.nPos), 0, NULL, NULL);
            UpdateWindow(hwnd);
        }
        return 0;

    case WM_KEYDOWN:
        switch (wParam)
        {
        case VK_UP:     // 向上箭头键
            SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, 0);
            break;
        case VK_DOWN:   // 向下箭头键
            SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, 0);
            break;
        case VK_PRIOR:  // PageUp键
            SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
            break;
        case VK_NEXT:   // PageDown键
            SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
            break;
        case VK_HOME:   // Home键(或者Fn + PageUp键)
            SendMessage(hwnd, WM_VSCROLL, SB_TOP, 0);
            break;
        case VK_END:    // End键(或者Fn + PageDown键)
            SendMessage(hwnd, WM_VSCROLL, SB_BOTTOM, 0);
            break;

        case VK_LEFT:   // 左箭头键
            SendMessage(hwnd, WM_HSCROLL, SB_LINELEFT, 0);
            break;
        case VK_RIGHT:  // 右箭头键
            SendMessage(hwnd, WM_HSCROLL, SB_LINERIGHT, 0);
            break;
        }
        return 0;

    case WM_PAINT:
        hdc = BeginPaint(hwnd, &ps);
        // 获取垂直滚动条、水平滚动条位置
        si.cbSize = sizeof(SCROLLINFO);
        si.fMask = SIF_POS | SIF_PAGE;
        GetScrollInfo(hwnd, SB_VERT, &si);
        iVertPos = si.nPos;
        si.cbSize = sizeof(SCROLLINFO);
        si.fMask = SIF_POS;
        GetScrollInfo(hwnd, SB_HORZ, &si);
        iHorzPos = si.nPos;

        SetBkMode(hdc, TRANSPARENT);
        hFont = CreateFont(12, 0, 0, 0, 0, 0, 0, 0, GB2312_CHARSET, 0, 0, 0, 0, TEXT("宋体"));
        hFontOld = (HFONT)SelectObject(hdc, hFont);

        // 获取无效区域
        nPaintBeg = max(0, iVertPos + ps.rcPaint.top / s_iHeight);
        nPaintEnd = min(NUMLINES - 1, iVertPos + ps.rcPaint.bottom / s_iHeight);
        for (int i = nPaintBeg; i <= nPaintEnd; i++)
        {
            x = s_cxChar * (-iHorzPos);
            y = s_iHeight * (i - iVertPos);
            TextOut(hdc, x,                     y, METRICS[i].m_pLabel, _tcslen(METRICS[i].m_pLabel));
            TextOut(hdc, x + s_iCol1,           y, METRICS[i].m_pDesc,  _tcslen(METRICS[i].m_pDesc));
            TextOut(hdc, x + s_iCol1 + s_iCol2, y, szBuf,
                wsprintf(szBuf, TEXT("%d"), GetSystemMetrics(METRICS[i].m_nIndex)));
        }

        SelectObject(hdc, hFontOld);
        DeleteObject(hFont);
        EndPaint(hwnd, &ps);
        return 0;

    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    

    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

使用keybd_event模拟击键消息,需要注意的是对于每一个按键必须成对使用,第一次表示按下,第二次带有KEYEVENTF_KEYUP标志表示释放按键。例如,如果需要模拟Win +R打开系统的"运行"程序,可以按如下方式使用keybd_event函数︰

            keybd_event(VK_LWIN,0,0,0);
            keybd_event('R', 0, 0, 0);
            keybd_event('R',0, KEYEVENTF_KEYUP,0);
            keybd_event(VK_LWIN,0, KEYEVENTF_KEYUP,0);

有时候,在按键按下和抬起之间可能需要暂停一会,例如模拟Alt + Tab组合键︰

    keybd_event(VK_MENU,0,0,0);
    keybd_event(VK_TAB,0,0,0);
    Sleep(3000);
    keybd_event(VK_TAB,0,KEYEVENTF_KEYUP,0);
    keybd_event(VK_MENU,0, KEYEVENTF_KEYUP,0);

Sleep函数可以暂停程序的执行︰

voID WINAPI Sleep(_In_DWORD dwMilliseconds); //暂停执行的时间间隔,以毫秒为单位

微软建议使用SendInput函数代替keybd_event函数。SendInput函数既可以模拟键盘输入也可以模拟鼠标输入

UINT WINAPl SendInput(
_In_ UINT nInputs, //plnputs数组中的结构数
_In_ LPINPUT plnputs,// INPUT结构的数组,每个结构表示一个键盘或鼠标输入事件
_In_ int cbSize); // INPUT结构的大小,以字节为单位

参数pInputs是一个INPUT结构的数组,每个结构表示一个键盘或鼠标输入事件。INPUT结构在WinUser.h头文件中定义如下∶

typedef struct tagINPUT {
    DWORD	type; //输入事件的类型
    union
    {
    MOUSEINPUT mi; // MOUSEINPUT结构
    KEYBDINPUT ki;  // KEYBDINPUT结构
    HARDWAREINPUT hi; // HARDWAREINPUT结构
    };
}INPUT,*PINPUT,FAR*LPINPUT;
  • 字段type指定输入事件的类型,该字段可以是下表所示的值之一。
宏常量含义
INPUT_MOUSE该事件是鼠标事件,使用mi结构
INPUT_KEYBOARD该事件是键盘事件,使用ki结构
INPUT_HARDWARE该事件是硬件事件,使用hi结构
  • mi ki hi都是结构体,分别指定鼠标、键盘和硬件事件的具体信息。硬件事件指的是键盘或鼠标以外的输入设备生成的消息。这几个结构比较简单,如果需要使用SendInput函数请自行查阅MSDN
  • 8
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值