【问题标题】:Use MapVirtualKeyA with Shift and Ctrl Alt将 MapVirtualKeyA 与 Shift 和 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

提前致谢。

【问题讨论】:

    标签: vba powerpoint


    【解决方案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】:

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

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

      当在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 是官方的 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 值!

      【讨论】:

        猜你喜欢
        • 2021-08-09
        • 1970-01-01
        • 1970-01-01
        • 2012-04-02
        • 2018-06-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-08-15
        相关资源
        最近更新 更多