6

这是我用来在某个过程中模拟制表键的代码:

[DllImport("user32.dll")]
static extern bool PostMessage(IntPtr hWnd, UInt32 Msg, int wParam, int lParam);

public Form1()
{
    PostMessage(MemoryHandler.GetMainWindowHandle(), 
               (int)KeyCodes.WMessages.WM_KEYDOWN, 
               (int)KeyCodes.VKeys.VK_TAB, 0);

    InitializeComponent();
}

有什么方法可以扩展它,使其按下键(例如)1秒,而不是仅仅点击它?

请注意,我对Thread.Sleep()阻止 UI 线程的解决方案不感兴趣。

4

6 回答 6

6

按住时重复击键是键盘控制器内置的一项功能。内置于键盘本身的微处理器。8042 微控制器是传统选择,键盘设备驱动程序仍然以其名字命名。

所以,不,这不是由 Windows 完成的,而且 PostMessage() 不会为你做这件事。不完全是一个问题,你可以简单地用一个定时器来模拟它。

于 2013-08-01T21:34:11.217 回答
4

如果你想模拟Windows 对消息的处理,你可能想知道键重复率有多快。可以在HKEY_CURRENT_USER\Control Panel\Keyboard\KeyboardSpeed. 还有KeyboardDelay价值。

Windows 所做的是在最初按下某个键时发送 aWM_KEYDOWN和 a 。然后,如果在时间跨度WM_CHAR之后仍然按下键,则重复和对,直到按下键——此时发送。KeyboardDelayWM_KEYDOWNWM_CHARKeyboardSpeedWM_KEYUP

我建议使用 aTimer以特定频率发送消息。

更新:

例如:

int keyboardDelay, keyboardSpeed;
using (var key = Registry.CurrentUser.OpenSubKey(@"Control Panel\Keyboard"))
{
    Debug.Assert(key != null);
    keyboardDelay = 1;
    int.TryParse((String)key.GetValue("KeyboardDelay", "1"), out keyboardDelay);
    keyboardSpeed = 31;
    int.TryParse((String)key.GetValue("KeyboardSpeed", "31"), out keyboardSpeed);
}

maxRepeatedCharacters = 30; // repeat char 30 times
var startTimer = new System.Windows.Forms.Timer {Interval = keyboardSpeed};
startTimer.Tick += startTimer_Tick;
startTimer.Start();
var repeatTimer = new System.Windows.Forms.Timer();
repeatTimer.Interval += keyboardDelay;
repeatTimer.Tick += repeatTimer_Tick;

//...

private static void repeatTimer_Tick(object sender, EventArgs e)
{
    PostMessage(MemoryHandler.GetMainWindowHandle(),
               (int)KeyCodes.WMessages.WM_KEYDOWN,
               (int)KeyCodes.VKeys.VK_TAB, 0);
    PostMessage(MemoryHandler.GetMainWindowHandle(),
                (int)KeyCodes.WMessages.WM_CHAR,
                (int)KeyCodes.VKeys.VK_TAB, 0);

    counter++;
    if (counter > maxRepeatedCharacters)
    {
        Timer timer = sender as Timer;
        timer.Stop();
    }
}

private static void startTimer_Tick(object sender, EventArgs eventArgs)
{
    Timer timer = sender as Timer;
    timer.Stop();
    PostMessage(MemoryHandler.GetMainWindowHandle(),
               (int)KeyCodes.WMessages.WM_KEYDOWN,
               (int)KeyCodes.VKeys.VK_TAB, 0);
    PostMessage(MemoryHandler.GetMainWindowHandle(),
               (int)KeyCodes.WMessages.WM_CHAR,
               (int)KeyCodes.VKeys.VK_TAB, 0);
}
于 2013-08-11T21:38:57.323 回答
1

在物理键盘上按住某个键时,重复的击键将传递到活动窗口。这不是键盘内置的,而是 Windows 功能。

您可以通过按顺序执行以下步骤来模拟这一点:

  1. 发送按键消息。
  2. 以 30 毫秒的间隔运行一个计时器(Windows 中的默认设置,可通过“轻松访问”设置进行更改),在每次滴答时向窗口发送按键消息。
  3. 发送按键消息。
于 2013-08-05T00:33:54.483 回答
1

我会在一个线程中执行此操作,用于睡眠和不阻塞 UI 线程。看这个:

System.Threading.Thread KeyThread = new System.Threading.Thread(() => {
       //foreach process
       // press key now
       PostMessage(MemoryHandler.GetMainWindowHandle(), 
              (int)KeyCodes.WMessages.WM_KEYDOWN, 
              (int)KeyCodes.VKeys.VK_TAB, 0);
       System.Threading.Thread.Sleep(1000); // wait 1 second

       //foreach process
       // release keys again
       PostMessage(MemoryHandler.GetMainWindowHandle(), 
              (int)KeyCodes.WMessages.WM_KEYUP, 
              (int)KeyCodes.VKeys.VK_TAB, 0);
});

当然,你必须开始它。

于 2013-08-09T12:10:43.960 回答
1

我不确定您要实现什么,但下面是我用来模拟使用 SendInput 的文本输入的函数。

如果您稍微改变一下以从新线程中对 SendInput 进行最终调用,然后使用计时器分离 down 和 up 事件,这是否达到了您的需要?

[DllImport("user32.dll", SetLastError = true)]
static extern UInt32 SendInput(UInt32 numberOfInputs, INPUT[] inputs, Int32 sizeOfInputStructure);

public enum InputType : uint
{
    MOUSE = 0,
    KEYBOARD = 1,
    HARDWARE = 2,
}

struct INPUT
{
    public UInt32 Type;
    public MOUSEKEYBDHARDWAREINPUT Data;
}

struct KEYBDINPUT
{
    public UInt16 Vk;
    public UInt16 Scan;
    public UInt32 Flags;
    public UInt32 Time;
    public IntPtr ExtraInfo;
}

public enum KeyboardFlag : uint // UInt32
{
    EXTENDEDKEY = 0x0001,
    KEYUP = 0x0002,
    UNICODE = 0x0004,
    SCANCODE = 0x0008,
}

public static void SimulateTextEntry(string text)
{
    if (text.Length > UInt32.MaxValue / 2) throw new ArgumentException(string.Format("The text parameter is too long. It must be less than {0} characters.", UInt32.MaxValue / 2), "text");

    var chars = UTF8Encoding.ASCII.GetBytes(text);
    var len = chars.Length;
    INPUT[] inputList = new INPUT[len * 2];
    for (int x = 0; x < len; x++)
    {
        UInt16 scanCode = chars[x];

        var down = new INPUT();
        down.Type = (UInt32)InputType.KEYBOARD;
        down.Data.Keyboard = new KEYBDINPUT();
        down.Data.Keyboard.Vk = 0;
        down.Data.Keyboard.Scan = scanCode;
        down.Data.Keyboard.Flags = (UInt32)KeyboardFlag.UNICODE;
        down.Data.Keyboard.Time = 0;
        down.Data.Keyboard.ExtraInfo = IntPtr.Zero;

        var up = new INPUT();
        up.Type = (UInt32)InputType.KEYBOARD;
        up.Data.Keyboard = new KEYBDINPUT();
        up.Data.Keyboard.Vk = 0;
        up.Data.Keyboard.Scan = scanCode;
        up.Data.Keyboard.Flags = (UInt32)(KeyboardFlag.KEYUP | KeyboardFlag.UNICODE);
        up.Data.Keyboard.Time = 0;
        up.Data.Keyboard.ExtraInfo = IntPtr.Zero;

        // Handle extended keys:
        // If the scan code is preceded by a prefix byte that has the value 0xE0 (224),
        // we need to include the KEYEVENTF_EXTENDEDKEY flag in the Flags property. 
        if ((scanCode & 0xFF00) == 0xE000)
        {
            down.Data.Keyboard.Flags |= (UInt32)KeyboardFlag.EXTENDEDKEY;
            up.Data.Keyboard.Flags |= (UInt32)KeyboardFlag.EXTENDEDKEY;
        }

        inputList[2*x] = down;
        inputList[2*x + 1] = up;

    }

    var numberOfSuccessfulSimulatedInputs = SendInput((UInt32)len*2, inputList, Marshal.SizeOf(typeof(INPUT)));
}
于 2013-08-10T19:33:14.023 回答
0

不确定你在用 PostMessage 做什么,但从这里修改了一些代码: SendKey.Send() 不工作

   [DllImport("user32.dll", SetLastError = true)]
    static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, UIntPtr dwExtraInfo);
    public static void PressKey(Keys key, bool up)
    {
        const int KEYEVENTF_EXTENDEDKEY = 0x1;
        const int KEYEVENTF_KEYUP = 0x2;
        if (up)
        {
            keybd_event((byte)key, 0x45, KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, (UIntPtr)0);
        }
        else
        {
            keybd_event((byte)key, 0x45, KEYEVENTF_EXTENDEDKEY, (UIntPtr)0);
        }
    }

    void TestProc()
    {
        PressKey(Keys.Tab, false);
        Thread.Sleep(1000);
        PressKey(Keys.Tab, true);
    }

也许这对你有用。它只是一个按键,然后一个按键,中间有一个睡眠。您甚至可以进一步添加它并传递一个时间值,以确定您希望密钥保持多长时间。

于 2013-08-01T01:58:33.900 回答