C#/Powershell 发送组合键

Posted

技术标签:

【中文标题】C#/Powershell 发送组合键【英文标题】:C# / Powershell send a combination of keys 【发布时间】:2019-10-08 04:01:54 【问题描述】:

我发现这个函数利用 C# 代码通过 PowerShell 模拟按键。

我可以像这样模拟一个WINDOWS键:

[KBEmulator]::SendScanCode(0x5B)

但是我想发送组合 WINDOWS+CTRL+SHIFT+B

其中,就 ScanCode 而言是:

0x5B + 0x11 + 0x10 + 0x42 

不幸的是,我只精通 PowerShell,而对于 C# 却一无所知。

有没有一种方法可以调整此代码以使用组合键而不是一次只使用一个键?

功能按键:

Function KeyPress 

Param (
    [Parameter(position=0, mandatory=$true, parametersetname='key')]
    [char]$Key,
    [Parameter(position=0, mandatory=$true, parametersetname='scancode')]
    [int]$ScanCode
)

$code = @"
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;

public static class KBEmulator     
    public enum InputType : uint 
        INPUT_MOUSE = 0,
        INPUT_KEYBOARD = 1,
        INPUT_HARDWARE = 3
    

    [Flags]
    internal enum KEYEVENTF : uint
    
        KEYDOWN = 0x0,
        EXTENDEDKEY = 0x0001,
        KEYUP = 0x0002,
        SCANCODE = 0x0008,
        UNICODE = 0x0004
    

    [Flags]
    internal enum MOUSEEVENTF : uint
    
        ABSOLUTE = 0x8000,
        HWHEEL = 0x01000,
        MOVE = 0x0001,
        MOVE_NOCOALESCE = 0x2000,
        LEFTDOWN = 0x0002,
        LEFTUP = 0x0004,
        RIGHTDOWN = 0x0008,
        RIGHTUP = 0x0010,
        MIDDLEDOWN = 0x0020,
        MIDDLEUP = 0x0040,
        VIRTUALDESK = 0x4000,
        WHEEL = 0x0800,
        XDOWN = 0x0080,
        XUP = 0x0100
    

    // Master Input structure
    [StructLayout(LayoutKind.Sequential)]
    public struct lpInput 
        internal InputType type;
        internal InputUnion Data;
        internal static int Size  get  return Marshal.SizeOf(typeof(lpInput));              
    

    // Union structure
    [StructLayout(LayoutKind.Explicit)]
    internal struct InputUnion 
        [FieldOffset(0)]
        internal MOUSEINPUT mi;
        [FieldOffset(0)]
        internal KEYBDINPUT ki;
        [FieldOffset(0)]
        internal HARDWAREINPUT hi;
    

    // Input Types
    [StructLayout(LayoutKind.Sequential)]
    internal struct MOUSEINPUT
    
        internal int dx;
        internal int dy;
        internal int mouseData;
        internal MOUSEEVENTF dwFlags;
        internal uint time;
        internal UIntPtr dwExtraInfo;
    

    [StructLayout(LayoutKind.Sequential)]
    internal struct KEYBDINPUT
    
        internal short wVk;
        internal short wScan;
        internal KEYEVENTF dwFlags;
        internal int time;
        internal UIntPtr dwExtraInfo;
    

    [StructLayout(LayoutKind.Sequential)]
    internal struct HARDWAREINPUT
    
        internal int uMsg;
        internal short wParamL;
        internal short wParamH;
    

    private class unmanaged 
        [DllImport("user32.dll", SetLastError = true)]
        internal static extern uint SendInput (
            uint cInputs, 
            [MarshalAs(UnmanagedType.LPArray)]
            lpInput[] inputs,
            int cbSize
        );

        [DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        public static extern short VkKeyScan(char ch);
    

    internal static short VkKeyScan(char ch) 
        return unmanaged.VkKeyScan(ch);
    

    internal static uint SendInput(uint cInputs, lpInput[] inputs, int cbSize) 
        return unmanaged.SendInput(cInputs, inputs, cbSize);
    

    public static void SendScanCode(short scanCode) 
        lpInput[] KeyInputs = new lpInput[1];
        lpInput KeyInput = new lpInput();
        // Generic Keyboard Event
        KeyInput.type = InputType.INPUT_KEYBOARD;
        KeyInput.Data.ki.wScan = 0;
        KeyInput.Data.ki.time = 0;
        KeyInput.Data.ki.dwExtraInfo = UIntPtr.Zero;


        // Push the correct key
        KeyInput.Data.ki.wVk = scanCode;
        KeyInput.Data.ki.dwFlags = KEYEVENTF.KEYDOWN;
        KeyInputs[0] = KeyInput;
        SendInput(1, KeyInputs, lpInput.Size);

        // Release the key
        KeyInput.Data.ki.dwFlags = KEYEVENTF.KEYUP;
        KeyInputs[0] = KeyInput;
        SendInput(1, KeyInputs, lpInput.Size);

        return;
    

    public static void SendKeyboard(char ch) 
        lpInput[] KeyInputs = new lpInput[1];
        lpInput KeyInput = new lpInput();
        // Generic Keyboard Event
        KeyInput.type = InputType.INPUT_KEYBOARD;
        KeyInput.Data.ki.wScan = 0;
        KeyInput.Data.ki.time = 0;
        KeyInput.Data.ki.dwExtraInfo = UIntPtr.Zero;


        // Push the correct key
        KeyInput.Data.ki.wVk = VkKeyScan(ch);
        KeyInput.Data.ki.dwFlags = KEYEVENTF.KEYDOWN;
        KeyInputs[0] = KeyInput;
        SendInput(1, KeyInputs, lpInput.Size);

        // Release the key
        KeyInput.Data.ki.dwFlags = KEYEVENTF.KEYUP;
        KeyInputs[0] = KeyInput;
        SendInput(1, KeyInputs, lpInput.Size);

        return;
    

"@

if(([System.AppDomain]::CurrentDomain.GetAssemblies() | ?$_ -match "KBEmulator") -eq $null) 
    Add-Type -TypeDefinition $code


switch($PSCmdlet.ParameterSetName)
    'key'  [KBEmulator]::SendKeyboard($Key) 
    'scancode'  [KBEmulator]::SendScanCode($ScanCode) 




[KBEmulator]::SendScanCode(0x5B)

【问题讨论】:

通读 MSDN (docs.microsoft.com/en-us/windows/desktop/api/winuser/…) 上的文档,看起来您可以拥有一组输入结构,而不是在 SendKeyboard 函数中仅指定一个。如果您可以重载(不太熟悉 C#),您是否可以执行诸如 public static void SendKeyboard(char[] ch) 之类的操作,这将使 KeyInputs 长度等于字符数组? 【参考方案1】:

这是我的尝试,至少应该是一个开始的地方。有一段时间没有在 C# 中编写任何代码,所以不确定如何在其中准确编程,但希望它应该接收字符数组并按下数组中的键并作为数组结构发送。虽然不确定,但 for 循环可能是一个索引关闭,并且可能存在我在代码中没有意识到的随机错误,我什至不确定它是否会同时执行所有按键或一次按键,但希望这可以帮助您确定从哪里开始。

public static void SendKeyboard(char ch) 
    lpInput[] KeyInputs = new lpInput[1];
    lpInput KeyInput = new lpInput();
    // Generic Keyboard Event
    KeyInput.type = InputType.INPUT_KEYBOARD;
    KeyInput.Data.ki.wScan = 0;
    KeyInput.Data.ki.time = 0;
    KeyInput.Data.ki.dwExtraInfo = UIntPtr.Zero;


    // Push the correct key
    KeyInput.Data.ki.wVk = VkKeyScan(ch);
    KeyInput.Data.ki.dwFlags = KEYEVENTF.KEYDOWN;
    KeyInputs[0] = KeyInput;
    SendInput(1, KeyInputs, lpInput.Size);

    // Release the key
    KeyInput.Data.ki.dwFlags = KEYEVENTF.KEYUP;
    KeyInputs[0] = KeyInput;
    SendInput(1, KeyInputs, lpInput.Size);

    return;  
    

 // Attempt at overloading for multiple key presses
 public static void SendKeyboard(char[] ch) 
    lpInput[] KeyInputs = new lpInput[ch.length];

    // Push the correct key
    for (int i = 0; i < ch.length; i++) 
    // Generate new memory address for KeyInput each time
    lpInput KeyInput = new lpInput();
    // Generic Keyboard Event
    KeyInput.type = InputType.INPUT_KEYBOARD;
    KeyInput.Data.ki.wScan = 0;
    KeyInput.Data.ki.time = 0;
    KeyInput.Data.ki.dwExtraInfo = UIntPtr.Zero;
    KeyInput.Data.ki.wVk = VkKeyScan(ch[i]);
    KeyInput.Data.ki.dwFlags = KEYEVENTF.KEYDOWN;
    KeyInputs[i] = KeyInput;
    
    SendInput( ch.length, KeyInputs, ( lpInput.Size * ch.length ) );

    // Release the key
    for (int i = 0; i < ch.length; i++) 
    // Generate new memory address for KeyInput each time
    lpInput KeyInput = new lpInput();
    // Generic Keyboard Event
    KeyInput.type = InputType.INPUT_KEYBOARD;
    KeyInput.Data.ki.wScan = 0;
    KeyInput.Data.ki.time = 0;
    KeyInput.Data.ki.dwExtraInfo = UIntPtr.Zero;
    KeyInput.Data.ki.wVk = VkKeyScan(ch[i]);
    KeyInput.Data.ki.dwFlags = KEYEVENTF.KEYUP;
    KeyInputs[i] = KeyInput;
    
    SendInput(ch.length, KeyInputs, ( lpInput.Size * ch.length ) );

    return;  
    

【讨论】:

以上是关于C#/Powershell 发送组合键的主要内容,如果未能解决你的问题,请参考以下文章

Powershell:捕获组合输出,仅错误输出,将组合输出发送到控制台

Powershell 查看日志并发送电子邮件通知

如何在VB中用PostMessage向指定窗口发送带有修饰键的组合键?

在Powershell中,如何设置$?不将此值发送到管道?

vb中postmessage中怎么用组合键ctrl+c

win7怎样运行powershell脚本