gpt4 book ai didi

c++ - Windows:检测右 alt 是否在当前布局中生成 Ctrl+Alt (AltGr)

转载 作者:行者123 更新时间:2023-12-02 10:38:21 27 4
gpt4 key购买 nike

Windows 中的某些键盘布局(例如 US-QWERTY)将右 Alt 视为常规 Alt 键,而其他(例如 US International)将其视为 AltGr,并在按下时同时生成 Ctrl 和 Alt。 Microsoft 键盘布局创建器提供了“将右 Alt 视为 Ctrl+Alt(也称为 AltGr)”选项来确定给定布局使用的模式。

Windows上有没有办法以编程方式确定当前事件的键盘布局以哪种方式处理正确的Alt?

Windows 10 中的屏幕键盘似乎将两者区分开来(根据布局将键标记为“Alt”或“AltGr”),但我不确定它是否通过公共(public) API、通过更深层次的 Hook 来确定操作系统,或者仅仅通过了解 Windows 附带的布局。

最佳答案

VkKeyScanExW function有以下 返回值 :

Type: SHORT

If the function succeeds, the low-order byte of the return value contains the virtual-key code and the high-order byte contains the shift state, which can be a combination of the following flag bits.

Return value  Description
1 Either SHIFT key is pressed.
2 Either CTRL key is pressed.
4 Either ALT key is pressed.
8 The Hankaku key is pressed
16 Reserved (defined by the keyboard layout driver).
32 Reserved (defined by the keyboard layout driver).

If the function finds no key that translates to the passed character code, both the low-order and high-order bytes contain –1.

… For keyboard layouts that use the right-hand ALT key as a shift key (for example, the French keyboard layout), the shift state is represented by the value 6, because the right-hand ALT key is converted internally into CTRL+ALT.



该函数将字符转换为相应的虚拟键代码和转换状态。该函数使用输入语言和由输入语言环境标识符标识的物理键盘布局来翻译字符。

不幸的是,我不知道反函数(使用输入语言和物理键盘布局将虚拟键代码和转换状态转换为相应的字符)。
函数 Get-KeyboardRightAlt在以下 PowerShell 脚本 58633725_RightAltFinder.ps1 中定义模拟特定移位状态的这种反函数 6 (对应于 RightAlt)并且对于任何已安装的输入区域设置标识符应用蛮力方法:检查来自 VkKeyScanExW 的输出对于每个可能的字符(Unicode 范围从 U+0020U+FFFD )。
Function Get-KeyboardRightAlt {
[cmdletbinding()]
Param (
[parameter(Mandatory=$False)] [int32]$HKL=0,
[parameter(Mandatory=$False)][switch]$All # returns all `RightAlt` codes
)
begin {
$InstalledInputLanguages = [System.Windows.Forms.InputLanguage]::InstalledInputLanguages
if ( $HKL -eq 0 ) {
$DefaultInputLanguage = [System.Windows.Forms.InputLanguage]::DefaultInputLanguage
$HKL = $DefaultInputLanguage.Handle
Write-Warning ( "input language {0:x8}: system default" -f $HKL )
} else {
if ( $HKL -notin $InstalledInputLanguages.Handle ) {
Write-Warning ( "input language {0:x8}: not installed!" -f $HKL )
}
}
$resultFound = $False
$result = [PSCustomObject]@{
VkKey = '{0:x4}' -f 0
Unicode = '{0:x4}' -f 0
Char = ''
RightAltKey = ''
}
}
Process {
Write-Verbose ( "input language {0:x8}: processed" -f $HKL )
for ( $i = 0x0020; # Space (1st "printable" character)
$i -le 0xFFFD; # Replacement Character
$i++ ) {
if ( $i -ge 0xD800 -and # <Non Private Use High Surrogate, First>
# DB7F # <Non Private Use High Surrogate, Last>
# DB80 # <Private Use High Surrogate, First>
# DBFF # <Private Use High Surrogate, Last>
# DC00 # <Low Surrogate, First>
# DFFF # <Low Surrogate, Last>
# E000 # <Private Use, First>
$i -le 0xF8FF # <Private Use, Last>
) { continue } # skip some codepoints
$VkKey = [Win32Functions.KeyboardScan]::VkKeyScanEx([char]$i, $HKL)
if ( ( $VkKey -ne -1 ) -and ( ($VkKey -band 0x0600) -eq 0x0600) ) {
$resultFound = $True
if ( $All.IsPresent ) {
$result.VkKey = '{0:x4}' -f $VkKey
$result.Unicode = '{0:x4}' -f $i
$result.Char = [char]$i
$result.RightAltKey = [Win32Functions.KeyboardScan]::
VKCodeToUnicodeHKL($VkKey % 0x100, $HKL)
$result
} else {
break
}
}
}
if ( -not ($All.IsPresent) ) { $resultFound }
}
} # Function Get-KeyboardRightAlt
# abbreviation HKL (Handle Keyboard Layout) = an input locale identifier

if ( $null -eq ( 'Win32Functions.KeyboardScan' -as [type]) ) {
Add-Type -Name KeyboardScan -Namespace Win32Functions -MemberDefinition @'

[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern short VkKeyScanEx(char ch, IntPtr dwhkl);

[DllImport("user32.dll")]
public static extern uint MapVirtualKeyEx(uint uCode, uint uMapType, IntPtr dwhkl);

[DllImport("user32.dll")]
static extern int GetKeyNameText(int lParam,
[Out] System.Text.StringBuilder lpString,
int nSize);

[DllImport("user32.dll")]
static extern int ToUnicodeEx(
uint wVirtKey, uint wScanCode, byte [] lpKeyState,
[Out, MarshalAs(UnmanagedType.LPWStr)] System.Text.StringBuilder pwszBuff,
int cchBuff, uint wFlags, IntPtr dwhkl);

[DllImport("user32.dll")]
public static extern bool GetKeyboardState(byte [] lpKeyState);

[DllImport("user32.dll")]
static extern uint MapVirtualKey(uint uCode, uint uMapType);

[DllImport("user32.dll")]
public static extern IntPtr GetKeyboardLayout(uint idThread);

public static string VKCodeToUnicodeHKL(uint VKCode, IntPtr HKL)
{
System.Text.StringBuilder sbString = new System.Text.StringBuilder();
byte[] bKeyState = new byte[255];
bool bKeyStateStatus = GetKeyboardState(bKeyState);
if (!bKeyStateStatus)
return "";
uint lScanCode = MapVirtualKey(VKCode,0);
uint xflags = 4; // If bit 2 is set, keyboard state is not changed (Windows 10, version 1607 and newer)

ToUnicodeEx(VKCode, lScanCode, bKeyState, sbString, (int)5, xflags, HKL);
return sbString.ToString();
}

// ↓ for the present: unsuccessful
public static string VKCodeToUnicodeHKLRightAlt(uint VKCode, IntPtr HKL)
{
System.Text.StringBuilder sbString = new System.Text.StringBuilder();
byte[] bKeyState = new byte[255];
// bool bKeyStateStatus = GetKeyboardState(bKeyState);
// if (!bKeyStateStatus)
// return "";
bKeyState[118] = 128; // LeftCtrl
bKeyState[120] = 128; // LeftAlt
bKeyState[121] = 128; // RightAlt
uint lScanCode = MapVirtualKey(VKCode,0);
uint xflags = 4; // If bit 2 is set, keyboard state is not changed (Windows 10, version 1607 and newer)

ToUnicodeEx(VKCode, lScanCode, bKeyState, sbString, (int)5, xflags, HKL);
return sbString.ToString();
// ↑ for the present: unsuccessful
}
'@
}

if ( -not ('System.Windows.Forms.InputLanguage' -as [type]) ) {
Add-Type -AssemblyName System.Windows.Forms
}

# EOF 58633725_RightAltFinder.ps1

输出 .该函数返回
  • bool 值(true 如果提供的键盘布局支持 RightAlt 功能,false 否则),或
  • (使用 -all 开关)包含完整 RightAlt+ 的对象?具有以下属性的列表:
  • VkKey       -(用于调试),
  • Unicode     - 结果字符的代码点,
  • Char        - 结果字符本身,
  • RightAltKey - 按住 RightAlt 时按下字符。

  • 当然,有时某些布局中的结果字符可能是死键,例如 Russian example 中的分音符号 ( ¨ )以下。
    . D:\PShell\SO\58633725_RightAltFinder.ps1 # activate the function

    Get-KeyboardRightAlt -HKL 0x04090405 # US keyboard

    False

    Get-KeyboardRightAlt -HKL 0xf0330419       # Russian - Mnemonic

    True

    Get-KeyboardRightAlt -HKL 0xf0330419 -all  # Russian - Mnemonic

    VkKey Unicode Char RightAltKey
    ----- ------- ---- -----------
    06c0 00a8 ¨ ъ
    06de 2019 ’ ь
    0638 20bd ₽ 8


    另一个问题是确定当前事件的键盘布局。以下 PowerShell 脚本声明 Get-KeyboardLayoutForPid可靠地获取任何进程的当前 Windows 键盘布局的函数(在 this my answer 中描述):
    if ( $null -eq ('Win32Functions.KeyboardLayout' -as [type]) ) {
    Add-Type -MemberDefinition @'
    [DllImport("user32.dll")]
    public static extern IntPtr GetKeyboardLayout(uint thread);
    '@ -Name KeyboardLayout -Namespace Win32Functions
    }

    Function Get-KeyboardLayoutForPid {
    [cmdletbinding()]
    Param (
    [parameter(Mandatory=$False, ValueFromPipeline=$False)]
    [int]$Id = $PID,
    # used formerly for debugging
    [parameter(Mandatory=$False, DontShow=$True)]
    [switch]$Raw
    )

    $InstalledInputLanguages = [System.Windows.Forms.InputLanguage]::InstalledInputLanguages
    $CurrentInputLanguage = [System.Windows.Forms.InputLanguage]::DefaultInputLanguage # CurrentInputLanguage
    $CurrentInputLanguageHKL = $CurrentInputLanguage.Handle # just an assumption
    ### Write-Verbose ('CurrentInputLanguage: {0}, 0x{1:X8} ({2}), {3}' -f $CurrentInputLanguage.Culture, ($CurrentInputLanguageHKL -band 0xffffffff), $CurrentInputLanguageHKL, $CurrentInputLanguage.LayoutName)

    Function GetRealLayoutName ( [IntPtr]$HKL ) {
    $regBase = 'Registry::' +
    'HKLM\SYSTEM\CurrentControlSet\Control\Keyboard Layouts'
    $LayoutHex = '{0:x8}' -f ($hkl -band 0xFFFFFFFF)
    if ( ($hkl -band 0xFFFFFFFF) -lt 0 ) {
    $auxKeyb = Get-ChildItem -Path $regBase |
    Where-Object {
    $_.Property -contains 'Layout Id' -and
    (Get-ItemPropertyValue -Path "Registry::$($_.Name)" `
    -Name 'Layout Id' `
    -ErrorAction SilentlyContinue
    ) -eq $LayoutHex.Substring(2,2).PadLeft(4,'0')
    } | Select-Object -ExpandProperty PSChildName
    } else {
    $auxKeyb = $LayoutHex.Substring(0,4).PadLeft(8,'0')
    }
    $KbdLayoutName = Get-ItemPropertyValue -Path (
    Join-Path -Path $regBase -ChildPath $auxKeyb
    ) -ErrorAction SilentlyContinue -Name 'Layout Text'
    $KbdLayoutName
    # Another option: grab localized string from 'Layout Display Name'
    } # Function GetRealLayoutName

    Function GetKbdLayoutForPid {
    Param (
    [parameter(Mandatory=$True, ValueFromPipeline=$False)]
    [int]$Id,
    [parameter(Mandatory=$False, DontShow=$True)]
    [string]$Parent = ''
    )
    $Processes = Get-Process -Id $Id
    $Weirds = @('powershell_ise','explorer') # not implemented yet

    $allLayouts = foreach ( $Proces in $Processes ) {
    $LayoutsExtra = [ordered]@{}
    $auxKLIDs = @( for ( $i=0; $i -lt $Proces.Threads.Count; $i++ ) {
    $thread = $Proces.Threads[$i]
    ## The return value is the input locale identifier for the thread:
    $LayoutInt = [Win32Functions.KeyboardLayout]::GetKeyboardLayout( $thread.Id )
    $LayoutsExtra[$LayoutInt] = $thread.Id
    if ($Raw.IsPresent) {
    $auxIndicator = if ($LayoutInt -notin ($CurrentInputLanguageHKL, [System.IntPtr]::Zero)) { '#' } else { '' }
    Write-Verbose ('thread {0,6} {1,8} {2,1} {3,8} {4,8}' -f $i,
    (('{0:x8}' -f ($LayoutInt -band 0xffffffff)) -replace '00000000'),
    $auxIndicator,
    ('{0:x}' -f $thread.Id),
    $thread.Id)
    }
    })
    Write-Verbose ('{0,6} ({1,6}) {2}: {3}' -f $Proces.Id, $Parent,
    $Proces.ProcessName, (($LayoutsExtra.Keys |
    Select-Object -Property @{ N='Handl';E={('{0:x8}' -f ($_ -band 0xffffffff))}} |
    Select-Object -ExpandProperty Handl) -join ', '))
    foreach ( $auxHandle in $LayoutsExtra.Keys ) {
    $InstalledInputLanguages | Where-Object { $_.Handle -eq $auxHandle }
    }
    $ConHost = Get-WmiObject Win32_Process -Filter "Name = 'conhost.exe'"
    $isConsoleApp = $ConHost | Where-Object { $_.ParentProcessId -eq $Proces.Id }
    if ( $null -ne $isConsoleApp ) {
    GetKbdLayoutForPid -Id ($isConsoleApp.ProcessId) -Parent ($Proces.Id -as [string])
    }
    }
    if ( $null -eq $allLayouts ) {
    # Write-Verbose ('{0,6} ({1,6}) {2}: {3}' -f $Proces.Id, $Parent, $Proces.ProcessName, '')
    } else {
    $allLayouts
    }
    } # GetKbdLayoutForPid

    $allLayoutsRaw = GetKbdLayoutForPid -Id $Id
    if ( $null -ne $allLayoutsRaw ) {
    if ( ([bool]$PSBoundParameters['Raw']) ) {
    $allLayoutsRaw
    } else {
    $retLayouts = $allLayoutsRaw |
    Sort-Object -Property Handle -Unique |
    Where-Object { $_.Handle -ne $CurrentInputLanguageHKL }
    if ( $null -eq $retLayouts ) { $retLayouts = $CurrentInputLanguage }
    $RealLayoutName = $retLayouts.Handle |
    ForEach-Object { GetRealLayoutName -HKL $_ }
    $ProcessAux = Get-Process -Id $Id
    $retLayouts | Add-Member -MemberType NoteProperty -Name 'ProcessId' -Value "$Id"
    $retLayouts | Add-Member -MemberType NoteProperty -Name 'ProcessName' -Value ($ProcessAux | Select-Object -ExpandProperty ProcessName )
    # $retLayouts | Add-Member -MemberType NoteProperty -Name 'WindowTitle' -Value ($ProcessAux | Select-Object -ExpandProperty MainWindowTitle )
    $retLayouts | Add-Member -MemberType NoteProperty -Name 'RealLayoutName' -Value ($RealLayoutName -join ';')
    $retLayouts
    }
    }
    <#

    .Synopsis
    Get the current Windows Keyboard Layout for a process.

    .Description
    Gets the current Windows Keyboard Layout for a process. Identify the process
    using -Id parameter.

    .Parameter Id
    A process Id, e.g.
    - Id property of System.Diagnostics.Process instance (Get-Process), or
    - ProcessId property (an instance of the Win32_Process WMI class), or
    - PID property from "TaskList.exe /FO CSV", …

    .Parameter Raw
    Parameter -Raw is used merely for debugging.

    .Example
    Get-KeyboardLayoutForPid

    This example shows output for the current process (-Id $PID).
    Note that properties RealLayoutName and LayoutName could differ (the latter is wrong; a bug in [System.Windows.Forms.InputLanguage] implementation?)

    ProcessId : 2528
    ProcessName : powershell
    RealLayoutName : United States-International
    Culture : cs-CZ
    Handle : -268368891
    LayoutName : US

    .Example
    . D:\PShell\tests\Get-KeyboardLayoutForPid.ps1 # activate the function

    Get-Process -Name * |
    ForEach-Object { Get-KeyboardLayoutForPid -Id $_.Id -Verbose }

    This example shows output for each currently running process, unfortunately
    even (likely unusable) info about utility/service processes.

    The output itself can be empty for most processes, but the verbose stream
    shows (hopefully worthwhile) info where current keboard layout is held.

    Note different placement of the current keboard layout ID:
    - console application (cmd, powershell, ubuntu): conhost
    - combined GUI/console app (powershell_ise) : the app itself
    - classic GUI apps (notepad, notepad++, …) : the app itself
    - advanced GUI apps (iexplore) : Id ≘ tab
    - "modern" GUI apps (MicrosoftEdge*) : Id ≟ tab (unclear)
    - combined GUI/service app (explorer) : indiscernible
    - etc… (this list is incomplete).

    For instance, iexplore.exe creates a separate process for each open window
    or tab, so their identifying and assigning input languages is an easy task.

    On the other side, explorer.exe creates the only process, regardless of
    open visible window(s), so they are indistinguishable by techniques used here…

    .Example
    gps -Name explorer | % { Get-KeyboardLayoutForPid -Id $_.Id } | ft -au

    This example shows where the function could fail in a language multifarious environment:

    ProcessId ProcessName RealLayoutName Culture Handle LayoutName
    --------- ----------- -------------- ------- ------ ----------
    5344 explorer Greek (220);US el-GR -266992632 Greek (220)
    5344 explorer Greek (220);US cs-CZ 67699717 US

    - scenario:
    open three different file explorer windows and set their input languages
    as follows (their order does not matter):
    - 1st window: let default input language (e.g. Czech, in my case),
    - 2nd window: set different input language (e.g. US English),
    - 3rd window: set different input language (e.g. Greek).
    - output:
    an array (and note that default input language window isn't listed).

    .Inputs
    No object can be piped to the function. Use -Id pameter instead,
    named or positional.

    .Outputs
    [System.Windows.Forms.InputLanguage] extended as follows:
    note the <…> placeholder

    Get-KeyboardLayoutForPid | Get-Member -MemberType Properties

    TypeName: System.Windows.Forms.InputLanguage

    Name MemberType Definition
    ---- ---------- ----------
    ProcessId NoteProperty string ProcessId=<…>
    ProcessName NoteProperty System.String ProcessName=powershell
    RealLayoutName NoteProperty string RealLayoutName=<…>
    Culture Property cultureinfo Culture {get;}
    Handle Property System.IntPtr Handle {get;}
    LayoutName Property string LayoutName {get;}

    .Notes
    To add the `Get-KeyboardLayoutForPid` function to the current scope,
    run the script using `.` dot sourcing operator, e.g. as
    . D:\PShell\tests\Get-KeyboardLayoutForPid.ps1

    Auhor: https://stackoverflow.com/users/3439404/josefz
    Created: 2019-11-24
    Revisions:

    .Link

    .Component
    P/Invoke

    <##>

    } # Function Get-KeyboardLayoutForPid

    if ( -not ('System.Windows.Forms.InputLanguage' -as [type]) ) {
    Add-Type -AssemblyName System.Windows.Forms
    }

    # EOF Get-KeyboardLayoutForPid.ps1

    关于c++ - Windows:检测右 alt 是否在当前布局中生成 Ctrl+Alt (AltGr),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58633725/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com