首页 > 其他分享 >12.2 实现键盘模拟按键

12.2 实现键盘模拟按键

时间:2023-10-08 15:56:01浏览次数:46  
标签:KEYEVENTF 函数 键盘 12.2 窗体 按键 NULL 模拟

本节将向读者介绍如何使用键盘鼠标操控模拟技术,键盘鼠标操控模拟技术是一种非常实用的技术,可以自动化执行一些重复性的任务,提高工作效率,在Windows系统下,通过使用各种键盘鼠标控制函数实现动态捕捉和模拟特定功能的操作。

键盘鼠标的模拟是实现自动化的必备流程,通常我们可以使用keybd_event()实现对键盘的击键模拟,使用SetCursorPos()实现对鼠标的模拟,使用两者的配合读者可以很容易的实现对键盘鼠标的控制,本节将依次封装实现,模拟键盘鼠标控制功能,读者可根据自己的实际需求选用不同的函数片段。

12.2.1 模拟键盘按键

模拟按键的核心功能是通过调用keybd_event()函数实现的,如下是这段代码的完整实现,首先MySetKeyBig()函数该函数用于设置键盘状态是否为大小写,用户可以传入一个状态值来设置当前输入法大小写模式,MyAnalogKey()函数用于实现模拟键盘按键,该函数接收一个英文字符串,并自动实现击键操作,代码实现并不复杂,读者可自行测试功能。

#include <windows.h>
#include <iostream>

using namespace std;

// 设置键盘大小写状态 为TRUE则切换大写状态,否则切换小写状态
VOID MySetKeyBig(BOOL big = FALSE)
{
  // 判断键盘CapsLock键是否开启状态,开启状态则为大写,否则为小写
  if (GetKeyState(VK_CAPITAL))
  {
    // 如果当前键盘状态为大写,要求改小写,则模拟按键CapsLock切换状态
    if (!big)
    {
      keybd_event(VK_CAPITAL, NULL, KEYEVENTF_EXTENDEDKEY | 0, NULL);
      keybd_event(VK_CAPITAL, NULL, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, NULL);
    }

    std::cout << "[键盘状态] " << "切换大写" << std::endl;
  }
  else
  {
    // 如果当前键盘状态为小写,要求改大写,则模拟按键CapsLock切换状态
    if (big)
    {
      keybd_event(VK_CAPITAL, NULL, KEYEVENTF_EXTENDEDKEY | 0, NULL);
      keybd_event(VK_CAPITAL, NULL, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, NULL);
    }

    std::cout << "[键盘状态] " << "切换小写" << std::endl;
  }
}

// 模拟键盘按键
VOID MyAnalogKey(char* str)
{
  int iLen = 0;
  char* tmp = NULL;
  INPUT* keys = NULL;
  BOOL bOldState = FALSE;

  // 保存此操作前的键盘状态
  bOldState = (BOOL)GetKeyState(VK_CAPITAL);
  iLen = lstrlen(str);
  tmp = (char*)malloc(iLen);
  memmove(tmp, str, iLen);
  for (int i = 0; i < iLen; i++)
  {
    // 某些符号非直属键盘按键,这里只过滤转换两个,以后用到其它字符自行添加过滤
    if (tmp[i] == ' ')
    {
      // 产生一个击键消息步骤:按下->抬起
      keybd_event(VK_SPACE, NULL, KEYEVENTF_EXTENDEDKEY | 0, NULL);
      keybd_event(VK_SPACE, NULL, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, NULL);
    }
    else if (tmp[i] == ',')
    {
      keybd_event(VK_OEM_COMMA, NULL, KEYEVENTF_EXTENDEDKEY | 0, NULL);
      keybd_event(VK_OEM_COMMA, NULL, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, NULL);
    }
    else if (tmp[i] >= 'a' && tmp[i] <= 'z')
    {
      // 根据字符大小写切换键盘大小写状态
      MySetKeyBig(0);
      // keybd_event只识别大写
      keybd_event((BYTE)tmp[i] - 32, NULL, KEYEVENTF_EXTENDEDKEY | 0, NULL);
      keybd_event((BYTE)tmp[i] - 32, NULL, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, NULL);
    }
    else if ((tmp[i] >= 'A' && tmp[i] <= 'Z') || (tmp[i] >= '0' && tmp[i] <= '9'))
    {
      MySetKeyBig(1);
      keybd_event((BYTE)tmp[i], NULL, KEYEVENTF_EXTENDEDKEY | 0, NULL);
      keybd_event((BYTE)tmp[i], NULL, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, NULL);
    }
    else
    {
      keybd_event((BYTE)tmp[i] + 64, NULL, KEYEVENTF_EXTENDEDKEY | 0, NULL);
      keybd_event((BYTE)tmp[i] + 64, NULL, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, NULL);
    }
    Sleep(50);
  }
  // 恢复此操作之前的键盘状态
  MySetKeyBig(bOldState);
  free(tmp);
}

int main(int argc, char *argv[])
{
  Sleep(5000);
  MyAnalogKey((char*)"Love life , Love LyShark WelCome LyShark Cpp Home ...");

  system("pause");
  return 0;
}

读者可自行编译并运行上述代码片段,将光标移动到记事本中,等待五秒钟,则会依次敲击如下所示的键盘按键;

12.2.2 设置窗体最大化

如下代码实现了设置一个窗体置顶并将该窗体最大化显示的效果,该代码实现原理是通过使用EnumWindows函数传递一个回调函数,实现对特定窗体的枚举,当找到对应窗体句柄后则将该窗体句柄传递给global_hwnd全局句柄中,当获取到Google浏览器句柄之后则通过GetSystemMetrics函数得到当前全屏窗体的像素比,通过调用SetWindowPos可将一个窗体设置为置顶显示,最后可调用SendMessage函数向特定窗体句柄发送最大化消息,使其填充满整个屏幕,代码如下所示;

#include <iostream>
#include <windows.h>

using namespace std;

HWND global_hwnd = 0;

// 将字符串逆序
char * Reverse(char str[])
{
  int n = strlen(str);
  int i;
  char temp;
  for (i = 0; i < (n / 2); i++)
  {
    temp = str[i];
    str[i] = str[n - i - 1];
    str[n - i - 1] = temp;
  }
  return str;
}

// 窗体枚举回调函数
BOOL CALLBACK lpEnumFunc(HWND hwnd, LPARAM lParam)
{
  char WindowName[MAXBYTE] = { 0 };
  DWORD ThreadId, ProcessId = 0;

  GetWindowText(hwnd, WindowName, sizeof(WindowName));
  ThreadId = GetWindowThreadProcessId(hwnd, &ProcessId);
  printf("句柄: %-9d --> 线程ID: %-6d --> 进程ID: %-6d --> 名称: %s \n", hwnd, ThreadId, ProcessId, WindowName);

  // 首先逆序输出字符串,然后比较前13个字符
  if (strncmp(Reverse(WindowName), "emorhC elgooG", 13) == 0)
  {
    global_hwnd = hwnd;
  }
  return TRUE;
}

int main(int argc, char* argv[])
{
  // 枚举Google浏览器句柄
  EnumWindows(lpEnumFunc, 0);
  std::cout << "浏览器句柄: " << global_hwnd << std::endl;

  if (global_hwnd != 0)
  {
    // 获取系统屏幕宽度与高度 (像素)
    int cx = GetSystemMetrics(SM_CXSCREEN);
    int cy = GetSystemMetrics(SM_CYSCREEN);
    std::cout << "屏幕X: " << cx << " 屏幕Y: " << cy << std::endl;

    // 传入指定的HWND句柄
    HWND hForeWnd = GetForegroundWindow();
    DWORD dwCurID = GetCurrentThreadId();
    DWORD dwForeID = GetWindowThreadProcessId(hForeWnd, NULL);
    AttachThreadInput(dwCurID, dwForeID, TRUE);

    // 将该窗体呼出到顶层
    ShowWindow(global_hwnd, SW_SHOWNORMAL);
    SetWindowPos(global_hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
    SetWindowPos(global_hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
    SetForegroundWindow(global_hwnd);
    AttachThreadInput(dwCurID, dwForeID, FALSE);

    // 发送最大化消息
    SendMessage(global_hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0);    // 最大化
    // SendMessage(global_hwnd, WM_SYSCOMMAND, SC_MINIMIZE, 0); // 最小化
    // SendMessage(global_hwnd, WM_SYSCOMMAND, SC_CLOSE, 0);    // 关闭
  }

  system("pause");
  return 0;
}

读者可自行编译并运行上述程序,此时会将谷歌浏览器全屏并置顶显示,输出信息如下图所示;

12.2.3 读取与设置剪辑板

读取与设置剪辑版可用于对数据的拷贝与设置,调用setClipbar函数并传入一段字符串可实现将传入字符串拷贝到剪辑版的功能,使用getClipBoardValue则可实现读取剪辑版中的内容到程序内。

#include <iostream>
#include <windows.h>
#include <time.h>

// 将字符串写入到剪切板
BOOL setClipbar(const char* data)
{
  int contentSize = strlen(data) + 1;
  HGLOBAL hMemory; LPTSTR lpMemory;

  // 打开剪切板
  if (!OpenClipboard(NULL))
  {
    return FALSE;
  }
  // 清空剪切板
  if (!EmptyClipboard())
  {
    return FALSE;
  }
  // 对剪切板分配内存
  if (!(hMemory = GlobalAlloc(GMEM_MOVEABLE, contentSize)))
  {
    return FALSE;
  }
  // 锁定内存区域
  if (!(lpMemory = (LPTSTR)GlobalLock(hMemory)))
  {
    return FALSE;
  }

  // 复制数据到内存区域,解除内存锁定
  memcpy_s(lpMemory, contentSize, data, contentSize);
  GlobalUnlock(hMemory);

  // 设置剪切板数据
  if (!SetClipboardData(CF_TEXT, hMemory))
  {
    return FALSE;
  }
  
  // std::cout << "已复制: " << data << " 长度: " << contentSize << std::endl;
  return CloseClipboard();
}

// 获取剪切板内容
char* getClipBoardValue()
{
  // 初始化
  char* url, *pData;
  size_t length;

  // 打开剪切板
  if (!OpenClipboard(NULL))
  {
    return FALSE;
  }

  // 获取剪切板内的数据
  HANDLE hData = GetClipboardData(CF_TEXT);
  if (hData == NULL)
  {
    return FALSE;
  }

  // 获取数据长度
  length = GlobalSize(hData);
  url = (char*)malloc(length + 1);

  // 将数据转换为字符
  pData = (char*)GlobalLock(hData);
  strcpy_s(url, length, pData);

  // 清理工作
  url[length] = 0;
  GlobalUnlock(hData);
  CloseClipboard();

  // 返回结果并释放内存
  char* result = _strdup(url);
  free(url);
  return result;
}

int main(int argc, char *argv[])
{
  Sleep(5000);

  for (size_t i = 0; i < 10; i++)
  {
    // 填充字符串
    char MyData[256] = { 0 };
    sprintf(MyData, "hello lyshark --> %d", i);

    // 设置到剪辑版
    BOOL set_flag = setClipbar(MyData);
    if (set_flag == TRUE)
    {
      // std::cout << "[*] 已设置" << std::endl;

      // 获取剪辑版
      char *data = getClipBoardValue();
      std::cout << "[剪辑版数据] = " << data << std::endl;
    }
  }

  system("pause");
  return 0;
}

运行上述程序,依次实现填充字符串并设置到剪辑版,最后再通过getClipBoardValue函数从剪辑版内读出数据,如下图所示;

本文作者: 王瑞
本文链接: https://www.lyshark.com/post/95b1ad6c.html
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

标签:KEYEVENTF,函数,键盘,12.2,窗体,按键,NULL,模拟
From: https://www.cnblogs.com/LyShark/p/17749290.html

相关文章

  • ORA-12012 Error on auto execute of job "SYS"."ORA$AT_OS_OPT_SY_<NN> in 12.2 Dat
    1错误2023-10-08T13:11:12.127171+08:00Errorsinfile/oracle/diag/rdbms/arch/ARCH2/trace/ARCH2_j000_305066.trc:ORA-12012:erroronautoexecuteofjob"SYS"."ORA$AT_OS_OPT_SY_154038"ORA-20001:StatisticsAdvisor:Invalidtasknamefo......
  • dogtail键盘值
    dogtail键盘输入方法有的时候想使用键盘输入,但是不知道键盘按键在代码中对应哪个字符串。比如我想按一下tab键,那dogtail中使用keyCombo('tab'),结果报错了啊。先看一下源代码:tree.pydefkeyCombo(self,comboString):ifconfig.debugSearching:log......
  • huoji69键盘的映射值
    先用xev程序获取按键的keycode的值,剩下的就是自己修改即可。修改.Xmodmap文件,内容为:1!keycode9=asciitildegrave2keycode9=asciitildegrave3!keycode66=Escape4keycode118=F12Insert5keycode119=F11Prior6keycode112=F10Next7keycode117=Delet......
  • 使用Windows API中键盘、鼠标监控钩子
    本节将介绍如何使用WindowsAPI中的SetWindowsHookEx和RegisterHotKey函数来实现键盘鼠标的监控。这些函数可以用来设置全局钩子,通过对特定热键挂钩实现监控的效果,两者的区别在于SetWindowsHookEx函数可以对所有线程进行监控,包括其他进程中的线程,而RegisterHotKey函数只能对当前......
  • 矩阵键盘的基本操作
    矩阵键盘的基本操作1、矩阵键盘的扫描思想与独立按键不同的是,按键的两个引脚都分别连接的单片机的I/O端口,一个作为行信号,另外一个作为列信号。我们以4X4的矩阵键盘为例,试着探讨其工作方式和扫描思路。在上面的矩阵键盘中,要识别出黄色按键的按下状态,应该怎么做呢?对于矩阵键盘,......
  • 05-独立按键的基本操作与扩展应用
    05-独立按键的基本操作与扩展应用在写代码前需做如下,否则独立按键无响应:由电路图可知:独立按键默认为高电平,当按键按下时为低电平即S7(P30)、S6(P31)、S5(P32)、S4(P33)当按下按键时,P3口对应低电平,松开后对应高电平,判断是否一直按下此时需要使用while(1)死循环,由于是按下点......
  • win卸载输入法之后,在系统设置的键盘中还有这个输入法
    解决方法:删除对应的注册表进入计算机\HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\CTF\TIP里面有很多的{}包括的选择查看里面的有LanguageProfile的项,点击里面的第二层,若是里面有这个输入法的,将这个LanguageProfile删除即可。......
  • Linux系统中驱动之设备树添加按键驱动方法
    大家好,每日一个简单的驱动,日久方长,对Linux驱动就越来越熟悉,也越来容易学会写驱动程序。今日进行简单的按键驱动。一、Linux下按键驱动原理按键驱动和LED驱动原理上来讲基本都是一样的,都是操作GPIO,只不过一个是读取GPIO的高低电平,一个是从GPIO输出高低电平。本次实现按键输入......
  • python提取论文图片波形数据:pyautogui键盘移动鼠标,跨模块全局变量使用,cv2局部放大窗口
    最近写了一个python提取论文图片波形数据的脚本,代码如下。涉及新知识点:pyautogui键盘移动鼠标,跨模块全局变量使用,cv2局部放大窗口,matplotlib图片在pyQT5lable显示,坐标变换,多线程同时使用。搜索相关关键字去对应代码区看注释就可以了。gui窗口:1#-*-coding:utf-8-*-2......
  • 51单片机-独立按键控制电机
    #include"reg52.h"typedefunsignedintu16;typedefunsignedcharu8;sbitk1=P3^1;sbitk2=P3^0;sbitk3=P3^2;sbitk4=P3^3;sbitmotor=P1^0;voiddelay(u16i){while(i--);}voidmoto_run(){motor=......