необходимо эмулировать ключ Win в командном файле - PullRequest
0 голосов
/ 27 июня 2018

Я пытался найти ответ, но мое исследование не дало много полезного для моей ситуации. Мне нужно эмулировать ключ Windows в моем пакетном файле, который я создаю. Это поможет мне развернуть окно, а также легко перемещать его между мониторами без использования мыши. Рабочая станция, на которой запускается этот пакет, не будет иметь доступа к мыши или физической клавиатуре.

Это код, с которым я работаю.

    @if (@CodeSection == @Batch) @then

    @echo off

    rem Use %SendKeys% to send keys to the keyboard buffer
    set SendKeys=CScript //nologo //E:JScript "%~F0"

    rem Start the other program in the same Window
    Start "Progam"

    rem the script only works if the application in question is the active window. Set a timer

    timeout /t 10

    rem use the tab key to move the cursor to the login and password inputs.

    %SendKeys% "This would be the username{TAB}"
    %SendKeys% "this would be the password{ENTER}"

    rem I then have a timer to let the login happen

    timeout /t 10

    rem this where I am now trying to maximize and move the active window around.


    goto :EOF


    @end
    // JScript section

    var WshShell = WScript.CreateObject("WScript.Shell");
    WshShell.SendKeys(WScript.Arguments(0));

1 Ответ

0 голосов
/ 28 июня 2018

Отличный вопрос! Да, я должен был бороться с этим сам некоторое время назад. Я хотел автоматически запускать приложение, установленное из Магазина Microsoft, при запуске, и по самым простым причинам для меня это было закрепить его на панели задач, а затем имитировать нажатие +. 1 , чтобы запустить его. Я предполагаю, что сейчас дело обстоит так, как было тогда, но нет способа SendKeys() мета-ключа Windows. Но благодаря LandOfTheLostPass на reddit я обнаружил альтернативу SendKeys(): отправка кода сканирования клавиатуры.

Я изменил код из reddit, чтобы позволить мне имитировать нажатие нескольких клавиш, а затем отпустить их. Если я правильно помню, код на Reddit будет имитировать только нажатие, а затем отпустить одну клавишу за раз. Я только изменил функцию кода сканирования для многоключевых комбинаций. Пересматривая код сейчас, кажется, я мог сломать функцию SendChars(). Но я все равно никогда не использовал его.

<# : win+1.bat -- Batch portion
@echo off & setlocal

powershell -window minimized -noprofile "iex (${%~f0} | out-string)"

goto :EOF

: end batch / begin PowerShell chimera #>

# https://www.reddit.com/r/PowerShell/comments/3qk9mc/keyboard_keypress_script/
Add-Type @"

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 SendScanCodeCombo(short[] scanCodes) {
        lpInput[] KeyInputs = new lpInput[1];
        lpInput KeyInput = new lpInput();
        Enum[] actions = { KEYEVENTF.KEYDOWN, KEYEVENTF.KEYUP };

        // Generic Keyboard Event
        KeyInput.type = InputType.INPUT_KEYBOARD;
        KeyInput.Data.ki.wScan = 0;
        KeyInput.Data.ki.time = 0;
        KeyInput.Data.ki.dwExtraInfo = UIntPtr.Zero;

        // Press and release the correct key combination
        foreach (KEYEVENTF action in actions) {
            foreach (short scanCode in scanCodes) {
                KeyInput.Data.ki.wVk = scanCode;
                KeyInput.Data.ki.dwFlags = action;
                KeyInputs[0] = KeyInput;
                SendInput(1, KeyInputs, lpInput.Size);
            }
        }
        return;
    }

    public static void SendChars(char[] keys) {
        lpInput[] KeyInputs = new lpInput[1];
        lpInput KeyInput = new lpInput();
        Enum[] actions = { KEYEVENTF.KEYDOWN, KEYEVENTF.KEYUP };

        // Generic Keyboard Event
        KeyInput.type = InputType.INPUT_KEYBOARD;
        KeyInput.Data.ki.wScan = 0;
        KeyInput.Data.ki.time = 0;
        KeyInput.Data.ki.dwExtraInfo = UIntPtr.Zero;

        foreach (KEYEVENTF action in actions) {
            foreach (char ch in keys) {

                // Press the 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;
    }
}
"@ # end Add-Type

# Send LWin+1
[KBEmulator]::SendScanCodeCombo(@(0x5B, 0x31))

См. Коды виртуальных клавиш в Документах Microsoft , чтобы найти коды сканирования для других ключей.


Кстати, Windows API предоставляет другие способы для увеличения и перемещения окон без имитации сочетаний клавиш. (Пожалуйста, не судите слишком резко. Я написал этот хак, прежде чем научиться писать гибридные сценарии Batch + PowerShell или включать код .NET в PowerShell.)

...