最近在研究一个课题,如何能利用键盘的led灯通道进行有效通信,那么首先要做的就是尝试能否在不影响键盘的情况下控制LED灯(num lock ,caps lock ,scroll lock)的使用。

首先,如果并不是HID USB键盘,下面的C代码就可以解决:

http://www.rohitab.com/discuss/topic/32092-toggle-led-lights/?p=10051697#entry10051697

同样的C#版的在此:

http://www.aboutmycode.com/miscellaneous/faking-num-lock-caps-lock-and-scroll-lock-leds/

如果是USB键盘上面的代码是不奏效的,其中一个重要原因就是使用Windows API的Createfile是打不开USB键盘设备,无法得到他的句柄(通过GetLastError()得到2查看错误编码解释就知道了),这是因为Mouse和Keyboard这类HID类设备是被系统独占的。但是现在很多人都要使用USB设备的键盘,那怎么办呢?

这里介绍一款Autohotkey的软件,使用简单的脚本编写就能实现替代手工键盘操作的方式,他可以向游戏出大招一样制定一系列的操作,包括开机后你想干什么干什么用这个软件编写些脚本很容易实现,用这款脚本实现的LED灯控制在下面这篇文章中也得到了实现:

http://www.autohotkey.com/board/topic/9587-keyboard-led-control-capslocknumlockscrolllock-lights/

OK,到此结束……

/*----------------------------------------------------------------------------------------------------------------------------*/

显然,你不想为一个功能装上一个软件吧!所以我们就对C代码进行一个修改,让他能够完成这项工作。

这里就不得不用到一个内核级的WindowsAPI调用NtCreatefile()神器。

首先此函数在 Winternl.h 下有申明,里面记载着内核调用的各种原型,但它并没有导入到标准的库中,要使用他只能采用载入运行时动态链接库的方式,简称动态载入库。

在代码中写入:

    HINSTANCE hinstLib;
hinstLib=LoadLibrary(_T("NtDll.dll"));

在函数外定义好NtCreatefile的原型:(参数中的WINAPI漏了会出现外部命令解析错误)

typedef NTSTATUS (WINAPI *ntcf)(  _Out_     PHANDLE FileHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ POBJECT_ATTRIBUTES ObjectAttributes,
_Out_ PIO_STATUS_BLOCK IoStatusBlock,
_In_opt_ PLARGE_INTEGER AllocationSize,
_In_ ULONG FileAttributes,
_In_ ULONG ShareAccess,
_In_ ULONG CreateDisposition,
_In_ ULONG CreateOptions,
_In_ PVOID EaBuffer,
_In_ ULONG EaLength
);

重点是设置好设备注册时的路径:(这个在注册表里可以查到的)

wfilename->Buffer = _T("\\Device\\KeyboardClass2");

这里我的是KeyboardClass2,因电脑而已,0、1、2、3都尝试一下吧。(注:_T是获取Unicode的编码格式)

完事后要检查loadlibrary的成功性,获取函数入口地址并且执行:

if (hinstLib != NULL)
{
ProcAdd = (ntcf) GetProcAddress(hinstLib, "NtCreateFile"); // If the function address is valid, call the function.
if (NULL != ProcAdd)
{
fRunTimeLinkSuccess = TRUE;
(ProcAdd) ((PHANDLE)&hKeybd,(ACCESS_MASK)(+0x00000100+0x00000080+0x00100000),objattrib,io,,,,,(ULONG)(0x00000040+0x00000020),,);
}
// Free the DLL module.
fFreeResult = FreeLibrary(hinstLib);
}

这里的参数很复杂,也让我头疼了半天,主要是仔细看MSDN官方文档,尤其是其中的objattrib设置是关键:

InitializeObjectAttributes(objattrib,wfilename,OBJ_CASE_INSENSITIVE,NULL,NULL);

这是一个专门初始化这个数据结构的函数,一般情况下第三个参数就是使用OBJ_CASE_INSENSITIVE,别的一加进去就不行,即便你用的或,由于或实际上是“+”,也就是功能相加,如果其中的参数是带限制性的,出来的问题就是受限制更多。如果函数执行时出现IO方面的错误,通过上面的io参数查看对应码就能知道了。

若一切顺利下面的检查就能通过了:

    // If unable to call the DLL function, use an alternative.
if (! fRunTimeLinkSuccess)
{
printf("Message printed from executable\n");
return ;
}

当然,hKeybd句柄的返回值一定不能是NULL,必须保证有所返回才说明函数执行成功。

成功后就可以发参数操控了,这里先解释一下操控原理:

每当用户在键盘上按下一个按键的时候就会在系统中写入一个键盘输出报告,这个报告会通知所有键盘设备作出相应的改变,例如你在笔记本电脑把scroll lock灯点亮的,别的键盘的灯也会亮,实际上他只是在键盘输入报告中写入了一个字节:

后三位就是灯状态的记录,1表示亮,0表示不亮,所有键盘都会根据这个状态改变,所有我们要做的就是更改这个键盘报告这个字节中的后三位即可。

根据这个我们可以写三个宏定义,表示灯的情况:

#define KBD_CAPS 4
#define KBD_NUM 2
#define KBD_SCROLL 1

更改输出报告的状态,使用DeviceIoControl()神器,原型如下:

BOOL WINAPI DeviceIoControl(
_In_ HANDLE hDevice,
_In_ DWORD dwIoControlCode,
_In_opt_ LPVOID lpInBuffer,
_In_ DWORD nInBufferSize,
_Out_opt_ LPVOID lpOutBuffer,
_In_ DWORD nOutBufferSize,
_Out_opt_ LPDWORD lpBytesReturned,
_Inout_opt_ LPOVERLAPPED lpOverlapped
);

哪些参数是输入哪些是输出一目了然。

首先通过buffer获取灯状态:

DeviceIoControl(hKeybd, IOCTL_KEYBOARD_QUERY_INDICATORS, , , &buffer, sizeof(buffer), &retlen, );//buffer拿不到灯状态信息.

接着给buffer赋值:

buffer.wFlags ^= wFlags ;

这个异或操作实际上是为了关与开,如果原来是开的那就关了他,如果关着的就打开它:

DeviceIoControl(hKeybd, IOCTL_KEYBOARD_SET_INDICATORS, &buffer, sizeof(buffer),    , , &retlen, );

其中的宏需要有自定义:

// most of these are taken from ntkbdddk.h or w/e it's called
#define IOCTL_KEYBOARD_SET_INDICATORS CTL_CODE(FILE_DEVICE_KEYBOARD, 0x0002, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_KEYBOARD_QUERY_TYPEMATIC CTL_CODE(FILE_DEVICE_KEYBOARD, 0x0008, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_KEYBOARD_QUERY_INDICATORS CTL_CODE(FILE_DEVICE_KEYBOARD, 0x0010, METHOD_BUFFERED, FILE_ANY_ACCESS)

好了,做个测试,给buffer传对应参数,这个时候对应灯就成功点亮了。

这个技能可以做的事情很多,尤其是接近被废弃的scroll lock的灯,可以将他变成邮件通知,或者是各种需求的通知,这就根据需要而定了,如果要改成C#版本也非常容易,毕竟都是调用dll的api实现的,或许还能做成让他随音乐跳到,变成disco模式,随便玩吧。

让USB键盘的LED灯听你的!(不干扰使用)的更多相关文章

  1. 基于arduino UNO R3+ESP8266控制LED灯的开关(无USB转TTL工具实现)

    最近由于项目要求,需要开发物联网云平台,而本人对硬件和通信技术一窍不通,故而选择arduino这一简单单片机来实现学习掌握基础的硬件和通信技术. 下面就是本人通过查阅大佬资料做的一个整合版本的通过手机 ...

  2. 用LED灯和按键来模拟工业自动化设备的运动控制

    开场白: 前面三节讲了独立按键控制跑马灯的各种状态,这一节我们要做一个机械手控制程序,这个机械手可以左右移动,最左边有一个开关感应器,最右边也有一个开关感应器.它也可以上下移动,最下面有一个开关感应器 ...

  3. 我的 FPGA 学习历程(02)—— 实验:点亮 LED 灯

    关于 Quartus 的操作可以使用 Quartus 自带的帮助,帮助中带有全套的操作教程. 中文网络教程链接(链接至 altera中文官网,点击观看) Quartus II 软件设计系列:基础 Qu ...

  4. 51单片机学习笔记(郭天祥版)(1)——单片机基础和点亮LED灯

    关于单片机型号的介绍: STC89C52RC40C-PDIP 0721CV4336..... STC:STC公司 89:89系列 C:COMS 52(还有51,54,55,58,516,):2表示存储 ...

  5. USB键盘驱动分析

    简介 本文介绍USB驱动程序编写的流程,分析一个键盘的USB程序,基于linux-2.6.39 USB驱动概要 分层 主机层面的USB驱动的整体架构可以分成4层,自顶到下依次是 1.USB设备驱动:本 ...

  6. Arduino 翻译系列 - LED 灯闪烁

    原文地址 - https://www.arduino.cc/en/Tutorial/Blink 闪烁 这个例子展示了你能拿 Arduino / Genuino 板子来干的最简单的事:使开发板上的 LE ...

  7. 警惕USB键盘记录器

    最近媒体报道了一种新型的能记录账号.密码输入的“USB键盘记录器”,引发网友关注,该设备看上去和普通U盘没什么区别,将其插入电脑USB接口,然后把键盘线和它连接,该设备就能够自动记录用户在电脑上输入的 ...

  8. arduino 红外遥控器控制LED灯

    /* 日期:2016.9.1 功能:红外遥控器控制LED灯 开,关,闪烁,呼吸 元件: 跳线公公头 * 5 led 220欧电阻 红外接收管,红外遥控 接线: 红外灯面向自己从左到右分别接 IO3 , ...

  9. Beaglebone Black–GPIO 高低电平控制 LED 灯

    上一篇,运用 Linux 的 sysfs,控制本机上的 LED 灯,usr0 至 usr3,这次用 GPIO 控制外部的电路,点亮 LED 灯. 这次的全部材料: BBB 一台 购买 BBB 自带的 ...

随机推荐

  1. Python按行读文件对比

    1. 最基本的读文件方法: # File: readline-example-1.py   file = open("sample.txt")   while 1:     lin ...

  2. VirtualBox安装Ghost XP

    http://jingyan.baidu.com/album/5d368d1e1a88b73f60c05721.html?picindex=1

  3. mysql引擎互转问题

    // InnoDB转MyISAM ALTER TABLE `tablename` ENGINE = MYISAM // MyISAM转InnoDB alter table tablename type ...

  4. java之Comparator与Comparable

    转自:http://blog.csdn.net/zhangerqing 当需要排序的集合或数组不是单纯的数字型时,通常可以使用Comparator或Comparable,以简单的方式实现对象排序或自定 ...

  5. 一个使用C#的TPL Dataflow Library的例子:分析文本文件中词频

    博客搬到了fresky.github.io - Dawei XU,请各位看官挪步.最新的一篇是:一个使用C#的TPL Dataflow Library的例子:分析文本文件中词频.

  6. MySQL 使用while语句向数据表中批量插入数据

    1.创建一张数据表 mysql> create table test_while ( -> id int primary key) charset = utf8; Query OK, ro ...

  7. 【转载】ABAP-如何读取内表的字段名称

    原文地址:ABAP-如何读取内表的字段名称   *&---------------------------------------------------------------------* ...

  8. iOS APNS远程推送(史上最全步骤)

    /*****************************************1************************************************/ waterma ...

  9. OpenGL ES2学习笔记(9)-- 转换矩阵

    线性代数是计算机图形学的一块基石,本篇文章总结如何在Shader中使用矩阵来移动.缩放和旋转顶点. 代码和效果 把下面的代码复制到OpenGL Console里: import java.nio.By ...

  10. 使用like时left outer join和inner join的区别

    --select top 10000 * into #s from search set statistics time on set statistics io on select userId,c ...