键盘钩子原理----开发按键发音程序

2023-10-29 21:40

本文主要是介绍键盘钩子原理----开发按键发音程序,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、前言

一日,看见我妈正在用电脑练习打字,频频低头看键盘,我想:要是键盘能发音的话,不就可以方便她养成"盲打"的好习惯吗?光想不做可不行,开始行动(您可千万别急着去拿工具箱啊^_^)...
按键能发音,其关键就是让程序能够知道当前键盘上是哪个键被按下,并播放相应的声音,自己的程序当然不在话下,那么其它程序当前按下哪个键如何得知呢?利用键盘钩子便可以很好地解决。

二、挂钩(HOOK)的基本原理

WINDOWS调用挂接的回调函数时首先会调用位于函数链首的函数,我们只要将自己的回调函数置于链首,该回调函数就会首先被调用。那么如何将我们自己的回调函数置于函数链的链首呢?函数SetWindowsHookEx()实现的就是该功能。我们首先来看一下SetWindowsHookEx函数的原型:

复制代码
HHOOK SetWindowsHookEx(int idHook,       HOOKPROC lpfn,     HINSTANCE hMod,    DWORD dwThreadId  
); 
复制代码

第一个参数:指定钩子的类型,有WH_MOUSE、WH_KEYBOARD等十多种(具体参见MSDN)

第二个参数:标识钩子函数的入口地址

第三个参数:钩子函数所在模块的句柄;

第四个参数:钩子相关函数的ID用以指定想让钩子去钩哪个线程,为0时则拦截整个系统的消息。

另外需要注意的是为了捕获所有事件,挂钩函数应该放在动态链接库DLL中。

三、具体实现

理论的话就不多说了,运行VC++6.0,新建一个MFC AppWizard(dll)工程,命名为Hook,使用默认的创建DLL类型的选项,也就是使用共享MFC DLL,点击完成后开始编写代码:

(1)在Hook.h中定义全局函数

BOOL installhook(); //钩子安装函数
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam);//挂钩函数

(2)在Hook.cpp文件的#endif下添加定义全局变量Hook的代码:

static HHOOK hkb=NULL;
HINSTANCE hins; //钩子函数所在模块的句柄

(3)添加核心代码

BOOL installhook()
{hkb=SetWindowsHookEx(WH_KEYBOARD,(HOOKPROC)KeyboardProc,hins,0);return TRUE;
}

第一个参数指定钩子的类型,因为我们只用到键盘操作所以设定为WH_KEYBOARD;第二个参数将钩子函数的入口地址指定为KeyboardProc,当钩子钩到任何消息后便调用这个函数,即当不管系统的哪个窗口有键盘输入马上会引起KeyboardProc的动作;第三个参数是钩子函数所在模块的句柄;最后一个参数是钩子相关函数的ID用以指定想让钩子去钩哪个线程,为0时则拦截整个系统的消息;

现在,就开始定义当键盘上的键按下时程序要做什么了~

KeyboardProc动作:

复制代码
LRESULT CALLBACK KeyboardProc(int nCode,WPARAM wParam,LPARAM lParam)
{ if(((DWORD)lParam&0x40000000) && (HC_ACTION==nCode)){switch(wParam) //键盘按键标识
        {case ''1'':sndPlaySound("1.wav",SND_ASYNC);break; //当数字键1被按下case ''2'':sndPlaySound("2.wav",SND_ASYNC);break;case ''3'':sndPlaySound("3.wav",SND_ASYNC);break;case ''4'':sndPlaySound("4.wav",SND_ASYNC);break;....case ''A'':sndPlaySound("a.wav",SND_ASYNC);break; //当字母键A被按下case ''B'':sndPlaySound("b.wav",SND_ASYNC);break;case ''C'':sndPlaySound("c.wav",SND_ASYNC);break;case ''D'':sndPlaySound("d.wav",SND_ASYNC);break;....}}LRESULT RetVal = CallNextHookEx( hkb, nCode, wParam, lParam ); return RetVal;
}
复制代码

上面的代码中我们用播放声音做为按键被按下后的动作,API函数sndPlaySound的第一个参数定义的声音文件的绝对路径(比如要播放C盘下的a.wav,就定义成"C:\\a.wav");第二参数定义播放模式,SND_ASYNC模式可以及时地释放正在播放的声音文件,立刻停止当前声音的播放转去播放新的声音,这样在我们连续击键时就不会有阻塞感了.为了执行sndPlaySound函数,必须在Hook.cpp的文件头加上:

#include "mmsystem.h"

并且点击VC++菜单上的“工程”-“设置”进入Link属性页,在L对象/库模块下输入:winmm.lib后确定即可.

(4)添加输出标识

在Hook.def的末尾添加

installhook
KeyboardProc

短短的四步,键盘钩子的制作算是完成了,编译生成后的DLL文件就可以自由的用别的程序来调用了.

在程序中如何调用DLL呢?那就简单了.再用VC++6.0新建一个MFC AppWizard(exe)工程,命名为KeySound,点击"确定"后选择程序类型为对话框,直接点击确定即可.

在KeySoundDlg.cpp文件中的OnInitDialog()初始化函数的CDialog::OnInitDialog();下面添加:

//阻止程序反复驻留内存,也为了防止有两个程序同时读取DLL而发生错误.

复制代码
CreateMutex(NULL, FALSE, "KeySound");
if(GetLastError()==ERROR_ALREADY_EXISTS)OnOK();//读取DLL
static HINSTANCE hinstDLL; 
typedef BOOL (CALLBACK *inshook)(); 
inshook instkbhook;
if(hinstDLL=LoadLibrary((LPCTSTR)"Hook.dll"))
{instkbhook=(inshook)GetProcAddress(hinstDLL,"installhook");  instkbhook();
}
else
{MessageBox("当前目录找不到Hook.dll文件,程序初始化失败");OnOK();
}
复制代码

将编译生成后的KeySound.exe和Hook.dll放在同一目录下,定义好声音文件,运行KeySound.exe后打开记事本或写字板,体验一下系统为您即时快速地朗读您按下的每一个键的快感吧^-^

有一点必须说明,标准键盘有101个键,您想让多少键发声音,就必须在上面的KeyboardProc动作里定义多少个键,常用的10个数字键和26个英文字母不会给您带来太大的困难,只要相应的''A''对应A键,''1''对应1键就可以,但如果您希望能让更多的键都有各种特色音乐的话,很可能会遇到一些键盘编码上的麻烦,比如ESC键就不能简单的用''ESC''来搞定了,得用VK_ESCAPE,又比如Alt键得用VK_MENU来定义,没有个键盘编码表的话会令人相当头疼,这里我介绍一种让程序来告诉您键盘按键名称的方法:

为一个工程添加PreTranslateMessage映射,添加如下代码:

复制代码
char KeyName[50];
ZeroMemory(KeyName,50);
if(pMsg -> message == WM_KEYDOWN)
{ GetKeyNameText(pMsg->lParam,KeyName,50);MessageBox(KeyName);
} 
复制代码

那么当程序窗口显示在面前时按下某个键,就会弹出一个消息显示该键的名称,然后用''''包起来就可以了,比如逗号句号,就是'',''和''.'',简单吧:)

到此就全部完成了按键发音程序的编写,通过改变声音文件的名称而不用改动程序本身就可以达到更换按键声音的目的了,只是有个遗憾,声音文件在硬盘中的位置不能变更,从C盘换移动D盘程序就不能播放了,怎么样才能灵活的读取声音文件呢?可以用API函数GetModuleFileName来得到程序所在的目录,具体实现方法如下:

(1)在Hook.h的public:下面添加:

BOOL InitInstance(); //初始化函数

(2)在Hook.cpp的#endif下添加定义全局变量的代码:

char szBuf[256];
char *p;
CString msg;

(3)在Hook.cpp中适当位置添加:

复制代码
BOOL CHookApp::InitInstance ()
{ hins=AfxGetInstanceHandle();GetModuleFileName(AfxGetInstanceHandle( ),szBuf,sizeof(szBuf));p = szBuf;while(strchr(p,''\\'')) { p = strchr(p,''\\''); p++; }*p = ''\0''; msg=szBuf;return TRUE; 
}
复制代码

(4)新建一个文件夹并命名为Sound;

(5)改变声音文件物理位置定义方式

case ''1'':sndPlaySound(msg+"sound\\1.wav",SND_ASYNC);break; 

msg是得到程序当前所在目录,加上后面的代码就是指播放当前目录下的Sound目录里的1.wav文件,这样就将声音文件的绝对路径改成了灵活的相对路径.您只要把KeySound.exe,Hook.dll和Sound文件夹放在同一个文件夹下,以后只要搬动整个文件夹就能实现声音文件的任意移动了。

调试时需要注意:将Hook.dll、Sound目录放在KeySound.exe的执行目录下。假如编译链接的时候出现unresolved external symbol __imp__sndPlaySoundA@8 这样的信息,请在Project Settings中加入Winmm.lib 。

 

 

出处:https://www.cnblogs.com/MaxWoods/p/3916583.html

转载于:https://www.cnblogs.com/mq0036/p/10412543.html

这篇关于键盘钩子原理----开发按键发音程序的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/303527

相关文章

Android开发中gradle下载缓慢的问题级解决方法

《Android开发中gradle下载缓慢的问题级解决方法》本文介绍了解决Android开发中Gradle下载缓慢问题的几种方法,本文给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧... 目录一、网络环境优化二、Gradle版本与配置优化三、其他优化措施针对android开发中Gradle下载缓慢的问

使用Go语言开发一个命令行文件管理工具

《使用Go语言开发一个命令行文件管理工具》这篇文章主要为大家详细介绍了如何使用Go语言开发一款命令行文件管理工具,支持批量重命名,删除,创建,移动文件,需要的小伙伴可以了解下... 目录一、工具功能一览二、核心代码解析1. 主程序结构2. 批量重命名3. 批量删除4. 创建文件/目录5. 批量移动三、如何安

将java程序打包成可执行文件的实现方式

《将java程序打包成可执行文件的实现方式》本文介绍了将Java程序打包成可执行文件的三种方法:手动打包(将编译后的代码及JRE运行环境一起打包),使用第三方打包工具(如Launch4j)和JDK自带... 目录1.问题提出2.如何将Java程序打包成可执行文件2.1将编译后的代码及jre运行环境一起打包2

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

《Android悬浮窗开发示例((动态权限请求|前台服务和通知|悬浮窗创建)》本文介绍了Android悬浮窗的实现效果,包括动态权限请求、前台服务和通知的使用,悬浮窗权限需要动态申请并引导... 目录一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后

在不同系统间迁移Python程序的方法与教程

《在不同系统间迁移Python程序的方法与教程》本文介绍了几种将Windows上编写的Python程序迁移到Linux服务器上的方法,包括使用虚拟环境和依赖冻结、容器化技术(如Docker)、使用An... 目录使用虚拟环境和依赖冻结1. 创建虚拟环境2. 冻结依赖使用容器化技术(如 docker)1. 创

基于Python开发PPTX压缩工具

《基于Python开发PPTX压缩工具》在日常办公中,PPT文件往往因为图片过大而导致文件体积过大,不便于传输和存储,所以本文将使用Python开发一个PPTX压缩工具,需要的可以了解下... 目录引言全部代码环境准备代码结构代码实现运行结果引言在日常办公中,PPT文件往往因为图片过大而导致文件体积过大,

MySQL中的MVCC底层原理解读

《MySQL中的MVCC底层原理解读》本文详细介绍了MySQL中的多版本并发控制(MVCC)机制,包括版本链、ReadView以及在不同事务隔离级别下MVCC的工作原理,通过一个具体的示例演示了在可重... 目录简介ReadView版本链演示过程总结简介MVCC(Multi-Version Concurr

使用DeepSeek API 结合VSCode提升开发效率

《使用DeepSeekAPI结合VSCode提升开发效率》:本文主要介绍DeepSeekAPI与VisualStudioCode(VSCode)结合使用,以提升软件开发效率,具有一定的参考价值... 目录引言准备工作安装必要的 VSCode 扩展配置 DeepSeek API1. 创建 API 请求文件2.

基于Python开发电脑定时关机工具

《基于Python开发电脑定时关机工具》这篇文章主要为大家详细介绍了如何基于Python开发一个电脑定时关机工具,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. 简介2. 运行效果3. 相关源码1. 简介这个程序就像一个“忠实的管家”,帮你按时关掉电脑,而且全程不需要你多做

Redis主从/哨兵机制原理分析

《Redis主从/哨兵机制原理分析》本文介绍了Redis的主从复制和哨兵机制,主从复制实现了数据的热备份和负载均衡,而哨兵机制可以监控Redis集群,实现自动故障转移,哨兵机制通过监控、下线、选举和故... 目录一、主从复制1.1 什么是主从复制1.2 主从复制的作用1.3 主从复制原理1.3.1 全量复制