这是我用来在某个过程中模拟tab-keypress的代码:
[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秒的键,而不是仅仅点击它?
请注意,我对阻止UI线程的Thread.Sleep()
解决方案不感兴趣。
答案 0 :(得分:6)
按住键时重复按键是键盘控制器内置的功能。键盘内置的微处理器。 8042 microcontroller是传统的选择,键盘设备驱动程序仍然有其名称。
所以,不,这不是由Windows完成的,PostMessage()不会为你做。这不是一个问题,你可以简单地用Timer模拟它。
答案 1 :(得分:4)
如果您想模拟 Windows对消息的处理方式,您可能希望了解密钥重复率的速度。这可以在HKEY_CURRENT_USER\Control Panel\Keyboard\KeyboardSpeed
找到。还有KeyboardDelay
值。
当最初按下某个键时,Windows所做的是发送WM_KEYDOWN
和WM_CHAR
。然后,如果在KeyboardDelay
时间跨度后仍然按下该键,则每WM_KEYDOWN
重复一次WM_CHAR
和KeyboardSpeed
对,直到按下该键为止 - 此时WM_KEYUP
发送1}}。
我建议使用Timer
以特定频率发送消息。
例如:
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);
}
答案 2 :(得分:1)
当按住键在物理键盘上时,重复的击键将传递到活动窗口。这是不内置在键盘中,但是是Windows功能。
您可以按顺序执行以下步骤来模拟此操作:
答案 3 :(得分: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);
});
当然,你必须开始它。
答案 4 :(得分: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)));
}
答案 5 :(得分:0)
不确定您使用PostMessage做了什么,但是从这里修改了一些代码: SendKey.Send() Not working
[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);
}
也许这对你有用。它只是一个按键,然后是一个关键的睡眠之间。您甚至可以进一步添加此值,并传递一个时间值,表示您希望密钥保持多久。