首页 星云 工具 资源 星选 资讯 热门工具
:

PDF转图片 完全免费 小红书视频下载 无水印 抖音视频下载 无水印 数字星空

C#模拟键盘输入、键状态和监听键盘消息

编程知识
2024年08月17日 23:58

模拟键盘输入

模拟键盘输入的功能需要依赖Windows函数实现,这个函数是SendInput,它是专门用来模拟键盘、鼠标等设备输入的函数。

另外和键盘输入相关的函数还有SendKeys,它是System.Windows.Forms. SendKeys,只能在WinFrom项目中使用,并且它的所有功能都可以由SendInput来实现。

另一个是keybd_event函数,这个函数依然是有用的,但是目前官方已经推荐使用SendInput替代它了。

SendInput的定义
[DllImport("user32.dll")]
static extern uint SendInput(int nInputs,INPUT[] pInputs,int cbSize);

INPUT对象中保存了输入内容,nInputs和cbSize代表pInputs的长度和INPUT结构的大小,这两个参数能帮助SendInput正确解析INPUT对象。返回值0表示失败,非零表示正确执行。

 

INPUT的定义
[StructLayout(LayoutKind.Sequential)]
struct KEYBDINPUT {
    public ushort wVk;
    public ushort wScan;
    public uint dwFlags;
    public uint time;
    public IntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
struct HARDWAREINPUT {
    public uint uMsg;
    public ushort wParamL;
    public ushort wParamH;
}
[StructLayout(LayoutKind.Sequential)]
struct MOUSEINPUT {
    public int dx;
    public int dy;
    public uint mouseData;
    public uint dwFlags;
    public uint time;
    public IntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Explicit)]
struct MOUSEKEYBDHARDWAREINPUT {
    [FieldOffset(0)]
    public HARDWAREINPUT hi;
    [FieldOffset(0)]
    public KEYBDINPUT ki;
    [FieldOffset(0)]
    public MOUSEINPUT mi;
}
[StructLayout(LayoutKind.Sequential)]
struct INPUT {
    public uint type;
    public MOUSEKEYBDHARDWAREINPUT mkhi;
}

INPUT结构中的type表示消息类型,值为1表示键盘消息。mkhi表示具体的消息内容,它可以模拟三类消息,其中键盘消息使用KEYBDINPUT表示,其它消息类型的结构不在这里介绍(虽然用不到MOUSEINPUT等结构,但是它们的定义不能省略,否则SendInput无法正确解析INPUT中的具体内容)。

⩥FieldOffset(0)将三个结构的起始都放在0位置,所以只能使用其中一个内容,因为一个INPUT也只能表示一个消息,这样设计可以节省空间。

KEYBDINPUT结构中的wVK表示虚拟键码 ,dwFlags的第一位bit默认0表示键盘按下事件,1表示键盘释放事件。

虚拟键码是一种能让Windows以与设备无关的方式处理键盘的技术,可以简单理解为:键盘上的每个键用一个数字来表示。

 

模拟A键
INPUT[] inputs = new INPUT[2];
inputs[0]=new INPUT {
    type=1,
    mkhi=new MOUSEKEYBDHARDWAREINPUT {
        ki=new KEYBDINPUT {
            wVk=0x41
        }
    }
};
inputs[1]=new INPUT {
    type=1,
    mkhi=new MOUSEKEYBDHARDWAREINPUT {
        ki=new KEYBDINPUT {
            wVk=0x41,
            dwFlags=2
        }
    }
};
SendInput(inputs.Length,inputs,Marshal.SizeOf(inputs[0]));

A键的虚拟键码是0x41。type=1表示这是键盘消息,dwFlags=2表示键盘释放事件。

这里INPUT数组模拟的就是使用物理键盘A键的过程。inputs[0]模拟A键按下,inputs[1]模拟A键释放。

 

模拟Ctrl+A
INPUT[] inputs = new INPUT[4];
inputs[0]=new INPUT {
    type=1,
    mkhi=new MOUSEKEYBDHARDWAREINPUT {
        ki=new KEYBDINPUT {
            wVk=0x11
        }
    }
};
inputs[1]=new INPUT {
    type=1,
    mkhi=new MOUSEKEYBDHARDWAREINPUT {
        ki=new KEYBDINPUT {
            wVk=0x41
        }
    }
};
inputs[2]=new INPUT {
    type=1,
    mkhi=new MOUSEKEYBDHARDWAREINPUT {
        ki=new KEYBDINPUT {
            wVk=0x41,
            dwFlags=2
        }
    }
};
inputs[3]=new INPUT {
    type=1,
    mkhi=new MOUSEKEYBDHARDWAREINPUT {
        ki=new KEYBDINPUT {
            wVk=0x11,
            dwFlags=2,
        }
    }
};
SendInput(inputs.Length,inputs,Marshal.SizeOf(inputs[0]));

0x11是Ctrl的虚拟键码,这里模拟了按下Ctrl键,按下A键,释放A键,释放Ctrl键的过程,实现了Ctrl+A的组合键效果。

 

SendInput除了能模拟击键消息外还可以在文本输入中模拟字符消息。

KEYBDINPUT结构的wScan表示字符内容,将dwFlags的第二位bit置1表示使用wScan属性而非wVK。

文本输入
string ntext = "你好";
INPUT[] inputs = new INPUT[ntext.Length*2];
for(int i = 0;i<ntext.Length;i++) {
    ushort ch = ntext[i];
    inputs[i*2]=new INPUT {
        type=1,
        mkhi=new MOUSEKEYBDHARDWAREINPUT {
            ki=new KEYBDINPUT {
                wScan=ch,
                dwFlags=4
            }
        }
    };
    inputs[i*2+1]=new INPUT {
        type=1,
        mkhi=new MOUSEKEYBDHARDWAREINPUT {
            ki=new KEYBDINPUT {
                wScan=ch,
                dwFlags=4|2
            }
        }
    };
}
SendInput(inputs.Length,inputs,Marshal.SizeOf(inputs[0]));

 

键状态

有时需要知道键盘按键的当前状态,可以使用GetKeyState函数。

GetKeyState的定义
[DllImport("user32.dll")]
static extern short GetKeyState(int VKey);

参数是键的虚拟码,对于开关键(Caps Look、Num Lock和Scroll Lock),返回值1表示开启状态。对于其它键返回负数表示按下状态。

CapsLock键状态
short iState = GetKeyState(0x14);

 

监听键盘消息

对于WinForm和WPF程序,要监听输入到本程序的键盘消息直接使用窗口的KeyDown和KeyUp事件即可。

对于其它键盘消息(即给本程序以外的键盘消息),需要使用钩子(hook)。

钩子是Windows系统消息处理机制中的一个节点,可以安装钩子来监听系统中的Windows消息。

Windows消息分很多种,对于特定的一类消息需要使用对应的特定类型的钩子,这里只介绍键盘消息的钩子。

钩子的安装需要调用系统SetWindowsHookEx方法。

 

SetWindowsHookEx的定义
[DllImport("user32.dll")]
static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hmod, int threadID);

idHook等于13表示全局键盘消息钩子,lpfn代表键盘消息处理程序,返回非IntPtr.Zero表示安装成功。

 

安装钩子
delegate int HookProc(int code,IntPtr wParam,IntPtr lParam);
static HookProc KeyboardProc;
static void InstallKeyboardHook() {
    KeyboardProc=KeyboardHookCallback;
    pKeyboardHook=SetWindowsHookEx(13,keyboardProc,IntPtr.Zero,0);
}

KeyboardHookCallback就是自定义的具体处理键盘消息的方法。

 

消息处理
[DllImport("user32.dll")]
static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
static int KeyboardHookCallback(int code,IntPtr wParam,IntPtr lParam) {
    if(code<0)
        return CallNextHookEx(IntPtr.Zero,code,wParam,lParam);
    int vkCode = Marshal.ReadInt32(lParam);
    System.Diagnostics.Debug.Write(vkCode+" ");
    long downup = (long)wParam;
    switch(downup) {
        case 256:
            System.Diagnostics.Debug.WriteLine("down");
            break;
        case 257:
            System.Diagnostics.Debug.WriteLine("up");
            break;
        case 260:
            System.Diagnostics.Debug.WriteLine("sys_down");
            break;
        case 261:
            System.Diagnostics.Debug.WriteLine("sys_up");
            break;
     }
     return CallNextHookEx(IntPtr.Zero,code,wParam,lParam);
}

从lParam中读取键的虚拟码(lParam其实是指向类似前文提到的KEYBDINPUT结构的指针),wParam表示击键事件的类型。CallNextHookEx将消息传递给下一个消息处理节点。

⩥使用前文提到的SendInput方法模拟键盘输入也能被钩子监听到。

⩥应避免在消息处理过程中进行耗时操作。

 

卸载钩子需要使用UnhookWindowsHookEx

UnhookWindowsHookEx的定义
[DllImport("user32.dll")]
static extern bool UnhookWindowsHookEx(IntPtr pHookHandle);

传入SetWindowsHookEx的返回值即可,返回true则卸载成功。

From:https://www.cnblogs.com/yxllxy/p/18361608
本文地址: http://shuzixingkong.net/article/1195
0评论
提交 加载更多评论
其他文章 二维偏序问题
偏序关系: 大概就是,满足自反性,反对称性,传递性。 将严格偏序关系建图,可以得到一个DAG(有向无环图) 二维偏序问题是:给定 \(n\) 个元素,每个元素有\(2\)个属性,定义某种偏序关系,对于所有 \(x_i\) ,求 \(x_j \prec x_i\) 的数量。 一种基本的操作方法是,某个
痞子衡嵌入式:英飞凌MirrorBit工艺NOR Flash的扇区架构设计
大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家讲的是英飞凌MirrorBit工艺NOR Flash的扇区架构设计。 NOR Flash 大家都很熟悉,其内部按组织从小到大分为 Page(128B/256B/512B)、Sector(4KB)、Block(32KB/64KB/128KB/25
痞子衡嵌入式:英飞凌MirrorBit工艺NOR Flash的扇区架构设计 痞子衡嵌入式:英飞凌MirrorBit工艺NOR Flash的扇区架构设计 痞子衡嵌入式:英飞凌MirrorBit工艺NOR Flash的扇区架构设计
Github Dorisoy网盘项目
相关github地址 https://github.com/dorisoy/Dorisoy.Pan?tab=readme-ov-file mysql8 sudo rpm -ivh mysql80-community-release-el7-5.noarch.rpm wget https://dev.
Github Dorisoy网盘项目 Github Dorisoy网盘项目 Github Dorisoy网盘项目
cloud compare PCA插件开发详细步骤(二)附代码
在上一节 https://blog.csdn.net/csy1021/article/details/141200135 我们已经完成了 具体开发前的准备工作,包括 各级 CMakelists.txt 的设置,相关内容的修改,并已成功编译 如需整个插件项目,编译后的 dll,或其他帮助,欢迎留言、私
cloud compare PCA插件开发详细步骤(二)附代码 cloud compare PCA插件开发详细步骤(二)附代码 cloud compare PCA插件开发详细步骤(二)附代码
C#实现国产Linux视频录制生成mp4(附源码,银河麒麟、统信UOS)
随着信创国产化浪潮的来临,在国产操作系统上的应用开发的需求越来越多。最近有个客户需要在银河麒麟和统信UOS上实现录制摄像头视频和麦克风声音,将它们录制成一个mp4文件。那么这样的功能要如何实现了?
C#实现国产Linux视频录制生成mp4(附源码,银河麒麟、统信UOS)
.NET中各种线程同步锁
编程编的久了,总会遇到多线程的情况,有些时候我们要几个线程合作完成某些功能,这时候可以定义一个全局对象,各个线程根据这个对象的状态来协同工作,这就是基本的线程同步。 ​支持多线程编程的语言一般都内置了一些类型和方法用于创建上述所说的全局对象也就是锁对象,它们的作用类似,使用场景有所不同。.Net中这
机器学习的数学基础--微积分
微积分运算在机器学习领域扮演着至关重要的角色,它不仅是许多基础算法和模型的核心,还深刻影响着模型的优化、性能评估以及新算法的开发。 掌握微积分,不仅让我们多会一种计算方式,也有助于理解各种机器学习算法和模型是如何寻找最优参数的。 1. 为什么需要微积分? 也许有些人会觉得微积分很难,这大概是因为我们
机器学习的数学基础--微积分 机器学习的数学基础--微积分 机器学习的数学基础--微积分
FFmpeg开发笔记(四十八)从0开始搭建直播系统的开源软件架构
​音视频技术的一个主要用途是直播,包括电视直播、电脑直播、手机直播等等,甚至在线课堂、在线问诊、安防监控等应用都属于直播系统的范畴。由于直播系统不仅涉及到音视频数据的编解码,还涉及到音视频数据的实时传输,因此直播领域采用的网络技术标准比较高,实现起来也比一般的WEB系统复杂。 一、直播系统的概念结构
FFmpeg开发笔记(四十八)从0开始搭建直播系统的开源软件架构 FFmpeg开发笔记(四十八)从0开始搭建直播系统的开源软件架构