65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.21/5 (23投票s)

2008年5月23日

GPL3

5分钟阅读

viewsIcon

132431

downloadIcon

4091

它描述了如何在 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_EXTENDEDKEYKEYEVENTF_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 首次发布
© . All rights reserved.