从应用程序读取和更新 CAPS 或 NUM Lock 状态






4.21/5 (23投票s)
它描述了如何在 C# 应用程序中使用 WIN32 API 读取和更新切换键(NUM lock、CAPS lock 等)。

引言
几个月前,我为客户开发了一个桌面应用程序。在完成需求后,我正在增强 UI,特别是主窗体。在早期版本的 Microsoft Word(2007 年之前)中,有一些标签是可点击的,并且可以反映键盘的更改(详情如下),我想在我的应用程序中添加这些功能。
zip 文件中还包含了代码的 HTML 文档。选择 HTML 文件夹中的index.html进行查看。
切换键
几乎所有的键盘都有三个 LED 灯:NUM lock、CAPS lock 和 SCROLL lock。这些 LED 灯显示相应按键的状态。由于这些按键要么处于 ON 状态,要么处于 OFF 状态,它们的值在 1 和 0 之间切换,因此它们被称为切换键。INSERT 键也包含在切换键中,尽管它没有 LED 灯,但其值要么是 ON(插入模式)要么是 OFF(覆盖模式),因此也包含在此类别中。
在我的应用程序中,我想添加 NUM lock 和 CAPS lock 的状态。我在谷歌上搜索,找到了一个使用Microsoft.VisualBasic.Devices
的解决方案。这是一个非常简单的解决方案,但有一些限制。您只能读取按键的状态,没有设置按键状态的方法。(我不会在这里讨论这个,我可能会写一篇单独的文章来介绍它)。应用程序的截止日期非常临近,我无法按照计划实现这个额外功能。由于客户没有要求此功能,因此交付时只有只读选项。
目标
本文的目的是从应用程序中获取和设置 CAPS lock 和 NUM lock 等的状态。
背景
WIN32 API 提供了许多低级函数来访问系统硬件。由于安全级别很高,这些功能在 .NET Framework 中不直接可用。为了与硬件进行低级交互,我们必须使用一些特殊的语法在 .NET 中调用 WIN32 API 方法。
为此,必须包含System.Runtime.InteropServices
命名空间。
遵循 C 语言的风格,我们必须声明函数,以便我们可以在代码中使用该方法。
声明
[DllImport("APIname.dll")]
internal static extern returnType
MethodName (argument(s));
用法
您可以像使用任何其他本地方法一样使用此方法
int nvar = Win32APIMethodName();
使用的 WIN32 API 方法
使用 WIN32 API 的两个方法来实现所需的结果。
[DllImport("user32.dll")]
internal static extern short GetKeyState(int keyCode);
GetKeyState
方法通过键码参数返回指定按键的状态。如果关闭,返回值将是 0;如果开启,返回值将是 1。
参数
keyCode
:指定要检查的按键的虚拟键码。该代码必须在 1 到 254 之间的值。有关完整列表,请参阅虚拟键码。
请检查MSDN以获取完整描述。
[DllImport("user32.dll")]
static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, int dwExtraInfo);
此函数对于模拟具有焦点的窗口的按键很有用。
参数
bVk
:指定虚拟键码。该代码必须在 1 到 254 之间的值。有关完整列表,请参阅虚拟键码。
bScan
:指定按键的硬件扫描码。
dwFlags:
指定函数操作的各个方面。通常使用KEYEVENTF_EXTENDEDKEY
或KEYEVENTF_KEYUP
。如果指定,表示按键被释放。如果未指定,表示按键被按下。
dwExtraInfo
:指定与按键事件关联的附加值。
请检查MSDN以获取完整描述。
注意:在测试应用程序中,在需要的地方,我们将使用System.Keys
枚举,而不是虚拟键码。
创建测试应用程序
在 C# 中创建一个新的 Windows 应用程序项目。从控件框的菜单和工具栏部分,将状态栏添加到您的窗体。在状态栏中添加三个标签。分别将它们命名为lblINS、lblNUM 和lblCAPS。最好将它们的宽度设置为 40。
将这些标签的DoubleClickEnabled
属性设置为true
。
将这些标签的AutoSize
属性设置为false
。
为这些标签生成DoubleClick
事件来处理双击操作,例如:
private void lblNUM_DoubleClick(object sender, EventArgs e)
{
PressKeyboardButton(Keys.NumLock);
UpdateNUMLock();
}
窗体事件
将窗体的KeyPreview
属性设置为true
。这将有助于将按在子控件上的按键传递给父控件(窗体)(为了更好地理解此属性,请构建附加的测试应用程序,并将此属性设置为false
)。
在设计视图中,双击窗体以生成其Form_Load
事件,并调用UpdateKeys
方法。它将读取按键的当前状态并相应地更新窗体。
private void Form1_Load(object sender, EventArgs e)
{
// read the current status of the specified keys
UpdateKeys();
}
生成窗体的Form1_KeyUp
事件来处理按键事件(当按下一个键然后释放时会引发此事件)。
private void Form1_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyData == Keys.Insert)
{
UpdateInsert();
}
else if (e.KeyData == Keys.NumLock)
{
UpdateNUMLock();
}
else if (e.KeyData == Keys.CapsLock)
{
UpdateCAPSLock();
}
}
实用方法
PressKeyboardButton
此方法以键码作为参数,通过向操作系统发送一个按键按下消息,然后发送一个按键释放消息来模拟按键事件。
private void PressKeyboardButton(Keys keyCode)
{
const int KEYEVENTF_EXTENDEDKEY = 0x1;
const int KEYEVENTF_KEYUP = 0x2;
keybd_event((byte)keyCode, 0x45, KEYEVENTF_EXTENDEDKEY, 0);
keybd_event((byte)keyCode, 0x45, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
}
UpdateNUMLock
它使用GetKeyState
读取 NUM lock 的状态并相应地更新窗体。
在末尾调用了this.Refresh()
以通过发送重绘请求来确保窗体更新。
private void UpdateNUMLock()
{
bool NumLock = (GetKeyState((int)Keys.NumLock)) != 0;
if (NumLock)
{
lblNUM.Text = "NUM";
}
else
{
lblNUM.Text = String.Empty;
}
this.Refresh();
}
类似地,也可以实现 CAPS lock 和 INSERT 的其他方法。
结论
您的应用程序将能够反映切换键的状态。通过双击状态栏中的标签,您还可以从应用程序中更新这些键的状态。
尽管我读文章和博客已经很久了,现在我决定通过分享我的知识和经验来做出贡献。我很乐意收到您对此的反馈,如果您喜欢,请不要忘记评价此文章。
历史
- 2008/05/24 1.0.0.0 首次发布