将 MapVirtualKeyA 与 Shift 和 Ctrl Alt 一起使用

Posted

技术标签:

【中文标题】将 MapVirtualKeyA 与 Shift 和 Ctrl Alt 一起使用【英文标题】:Use MapVirtualKeyA with Shift and Ctrl Alt 【发布时间】:2020-10-21 01:48:27 【问题描述】:

我找到了this page 用于将给定键码转换为相应字符的函数,具体取决于系统语言和键盘布局。我测试了它(在使用 VBA 的 PowerPoint 中)并且它可以工作,除了我不知道如何告诉函数 ShiftCtrl + Alt 被按下,因此该函数应该返回不同的结果。比如说:keycode 51 对应数字 3,当我将它传递给函数时,它返回3。但是如果用户按下 Shift 键怎么办?它应该返回#(或不同的字符,取决于键盘布局等)

所以,我知道如何检查是否正在按下 ShiftCtrl + Alt,但我不知道如何告诉函数正在按下按键。

我将以下代码放在一个模块中:

Public Declare PtrSafe Function MapVirtualKeyA Lib "user32" (ByVal uCode As Long, ByVal uMapType As Long) As Integer

然后在幻灯片的代码中我放了:

Sub test()

    MsgBox Chr(MapVirtualKeyA(vbKey3, 2)) ' always returns 3, even when pressing shift or ctrl + alt

End Sub

我想知道我必须在我的代码中更改什么,以便函数知道正在按下 ShiftCtrl + Alt

提前致谢。

【问题讨论】:

【参考方案1】:

这应该会有所帮助(但如果您使用的是 64 位版本的 Office,则需要对其进行修改以包含 64 位兼容声明)。

(In declarations)
Public Const VK_SHIFT = &H10
Public Const VK_CONTROL = &H11
Public Const VK_MENU = &H12
Private Declare Function GetAsyncKeyState Lib "user32" (ByVal vKey As Long) As Integer

Public Function GetAltState() As Boolean
    Call GetAsyncKeyState(VK_MENU)
    If GetAsyncKeyState(VK_MENU) <> 0 Then GetAltState = True
End Function

Public Function GetCtrlState() As Boolean
    Call GetAsyncKeyState(VK_CONTROL)
    If GetAsyncKeyState(VK_CONTROL) <> 0 Then GetCtrlState = True
End Function

Public Function GetShiftState() As Boolean
    Call GetAsyncKeyState(VK_SHIFT)
    If GetAsyncKeyState(VK_SHIFT) <> 0 Then GetShiftState = True
End Function

【讨论】:

感谢您的回答,但这不是我要求的,我已经知道该怎么做。正如我所说:“我知道如何检查是否按下了 Shift 或 Ctrl + Alt,但我不知道如何告诉函数正在按下按键”。所以我想将该信息(告诉是否按下 shift、ctrl 或/和 alt)传递给 MapVirtualKeyA 函数,以便它返回正确的字符。 也许将两者结合起来:检测 Shift 键等,将 MapVirtualKeyA 的返回值传递给返回相关字符的移位值的函数。 “到一个返回相关字符移位值的函数”——什么函数?这个函数需要知道系统的键盘布局并知道将它转换成什么字符。这是什么功能? 我怀疑你必须自己写。不幸的是,这是一份不错的工作。 必须有一个函数可以为我做到这一点。影响结果的键盘布局和其他因素数不胜数。【参考方案2】:

您可以使用ToAscii API。

Option Explicit

Private Declare PtrSafe Function ToAscii Lib "user32" (ByVal uVirtKey As Long, ByVal uScanCode As Long, lpbKeyState As Byte, lpChar As LongPtr, ByVal uFlags As Long) As Long
Private Declare PtrSafe Function MapVirtualKeyA Lib "user32" (ByVal uCode As Long, ByVal uMapType As Long) As Long

Sub Test()
    Debug.Print GetCharacterFromVK(uVirtKey:=vbKey3, shiftDown:=True)
End Sub

Function GetCharacterFromVK(uVirtKey As Long _
    , Optional shiftDown As Boolean = False _
    , Optional ctrlDown As Boolean = False _
    , Optional altDown As Boolean = False _
) As String
    'https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
    Const VK_SHIFT = &H10   'SHIFT key
    Const VK_CONTROL = &H11 'CTRL key
    Const VK_MENU = &H12    'ALT key
    '
    Dim keyState(0 To 255) As Byte
    Dim buffer As String: buffer = Space$(2)
    '
    'Set Key States by setting the high-order bit of the byte
    If shiftDown Then keyState(VK_SHIFT) = &H80
    If ctrlDown Then keyState(VK_CONTROL) = &H80
    If altDown Then keyState(VK_MENU) = &H80
    '
    'Populate buffer
    Select Case ToAscii(uVirtKey, MapVirtualKeyA(uVirtKey, 0), keyState(0), ByVal StrPtr(buffer), 0)
    Case 0
        'The specified virtual key has no translation for the current state of the keyboard
        GetCharacterFromVK = vbNullString
    Case 1
        'One character was copied to the buffer
        GetCharacterFromVK = Left$(buffer, 1)
    Case 2
        'Two characters were copied to the buffer
        GetCharacterFromVK = buffer
    End Select
End Function

【讨论】:

【参考方案3】:

要了解这个问题的答案,我们需要先了解键盘输入的工作原理。

微软对此有一个完整的部分:

https://docs.microsoft.com/en-gb/windows/win32/inputdev/about-keyboard-input

当在keyboard 上按下一个键时,会生成一个scan code。扫描码取决于设备。

下一步:

键盘设备驱动程序从键盘接收扫描码

并将它们变成虚拟键:

由系统定义的独立于设备的值,用于标识密钥的用途。

虚拟键代表键盘上的键,而不是它们可以代表什么字符。因此,例如 3 是一个虚拟键,但它是移位值(在我的例子中是 £),不是,并且取决于使用的键盘。

这些存储在设备相关的键盘布局中。 MapVirualKey 等基本函数调用使用默认键盘布局(即您现在使用的键盘布局)。如果你想使用不同的布局,你可以通过调用得到一个列表:

GetKeyboardLayoutList

因此,虚拟键和任何修饰符都会发送到键盘布局,这会返回适当的字符,然后将其呈现到屏幕等...

所以您的问题变成了“我们如何找到 myKey + shift 的 Unicode/ASCII 值?”。没有任何特别简单的答案,例如在我的计算机上 'shift 3 (£)' 的 ASCII 值为 163,但 'shift 4 ($)' 的 ASCII 值为 36!

这些示例使用 Excel,而不是 Powerpoint。

让我们先看看MapVirtualKey`。我没有使用一般的键盘布局。

在模块中,添加以下声明:

' https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-mapvirtualkeya
Public Declare PtrSafe Function MapVirtualKeyA Lib "user32" ( _
        ByVal uCode As Long, _
        ByVal uMapType As Long) As Integer

并将以下子程序添加到 Button1:

Sub Button1_Click()

Cells.Clear

' MapVirtualKeyA
Cells(1, 1) = "MapVirtualKeyA"
Cells(1, 1).Font.Bold = 1

char = "3"
Cells(2, 1) = "char: " & char
Cells(2, 2) = MapVirtualKeyA(Asc(char), 2)
Cells(2, 3) = Chr(MapVirtualKeyA(Asc(char), 2))

char = "£"
Cells(3, 1) = "char: " & char
Cells(3, 2) = MapVirtualKeyA(Asc(char), 2)
Cells(3, 3) = Chr(MapVirtualKeyA(Asc(char), 2)) ' doesn't exist

第一个调用正常,第二个调用不将£ 识别为虚拟键(不是),因此返回0

所以这对于确定 'shift 3' 应该返回什么没有多大用处。

我们可以使用VkKeyScan API 函数从£ 检索“shift”状态。

在模块中声明函数:

' https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-vkkeyscana
Public Declare Function VkKeyScan Lib "user32" Alias "VkKeyScanA" ( _
        ByVal cChar As Byte) As Integer

并将此代码添加到按钮:

'VkKeyScan
Cells(5, 1) = "VkKeyScan"
Cells(5, 1).Font.Bold = 1

char = "3"
keyScan = VkKeyScan(Asc(char))
shift = keyScan And &H100
ctrl = keyScan And &H200
vkKey = keyScan And &HFF

Cells(6, 1) = "char: " & char
Cells(6, 2) = "shift: " & shift
Cells(6, 3) = "ctrl: " & ctrl
Cells(6, 4) = keyScan
Cells(6, 5) = ChrW(keyScan)
Cells(6, 6) = vkKey
Cells(6, 7) = Chr(vkKey)

char = "£"
keyScan = VkKeyScan(Asc(char))
shift = keyScan And &H100
ctrl = keyScan And &H200
vkKey = keyScan And &HFF

Cells(7, 1) = "char: " & char
Cells(7, 2) = "shift: " & shift
Cells(7, 3) = "ctrl: " & ctrl
Cells(7, 4) = keyScan
Cells(7, 5) = ChrW(keyScan) ' wrong character
Cells(7, 6) = vkKey
Cells(7, 7) = Chr(vkKey)

这提供了更多信息,但仍然没有给我们答案。

这里有两种解决方案,两者几乎相同:

SendInput SendKeys

SendInput 是官方的 Win32API 做事方式,但涉及相当长的代码,并且被 VBA 的SendKeys 函数整齐地包裹起来。

SendKeys 将文本输出发送到当前活动的光标。

Cells(10, 1).Select
SendKeys ("3+3~")
While Cells(10, 1) = ""
DoEvents
Wend
Cells(10, 2) = Asc(Mid(Cells(10, 1), 1, 1))
Cells(10, 3) = Asc(Mid(Cells(10, 1), 2, 1))
End Sub

Cells(10, 1).Select 将光标放在我们想要的位置。

+用于模拟'shift',~模拟'enter'。

等待处理器赶上..,我们有 '£' 的 ASCII 值!

【讨论】:

以上是关于将 MapVirtualKeyA 与 Shift 和 Ctrl Alt 一起使用的主要内容,如果未能解决你的问题,请参考以下文章

#yyds干货盘点#shell进阶之shift与select相关技术

第三部分:批处理与变量

hpbit-shift作用

POJ 1063 Flip and Shift

在android中以编程方式关闭shift键

linux 终端操作与一些shall 命令