我正在嘗試以編程方式模擬按下鍵盤上的向上箭頭鍵以進行端到端測驗。使用下面的代碼,GUI 控制元件不會收到任何訊息(WM_KEYDOWN、WM_GETDLGCODE、WM_KEYUP)。我使用 Spy 來檢測傳入的訊息。你知道為什么\你能建議另一種解決方案嗎?
該控制元件繼承自 UserControl 類 (WinForms)。也沒有跨執行緒例外,因為我使用Control.InvokeRequired()了所有相關的東西。
我做了2次嘗試。第一個是:
[System.Runtime.InteropServices.DllImport("User32.dll")]
public static extern IntPtr PostMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
public static int WM_KEYDOWN = 0x100;
public static int WM_KEYUP = 0x101;
public static int VK_UP = 0x26;
public void SimulateKeystrokeEventMethod()
{
Thread.Sleep(20000);
Control control = Control.FromHandle(MyGUICOntrol.Handle);
if (control.InvokeRequired)
{
System.Action safeStimulation = delegate
{
SetForegroundWindow(Handle);
const uint MK_LBUTTON = 0x0001;
Point ClickPos = new Point(330, 401); //The position on top of the objects
IntPtr lParam = (IntPtr)((401 << 16) | 330);
IntPtr result = IntPtr.Zero;
SendMessage(Handle, WM_LBUTTONDOWN, new IntPtr(MK_LBUTTON), lParam);
SendMessage(Handle, WM_LBUTTONUP, IntPtr.Zero, lParam);
for (int i = 0; i < 100; i )
{
lParam = (IntPtr)(0);
PostMessage(Handle, WM_KEYDOWN, new IntPtr(VK_UP), lParam);
SendMessage(Handle, WM_GETDLGCODE, 0x00, 0x00);
PostMessage(Handle, WM_KEYUP, new IntPtr(VK_UP), lParam);
}
};
control.Invoke(safeStimulation);
}
第二次嘗試:
public void SimulateKeystrokeEventMethod()
{
Thread.Sleep(20000);
Control control = Control.FromHandle(MyGUICOntrol.Handle);
if (control.InvokeRequired)
{
System.Action safeStimulation = delegate
{
SetForegroundWindow(Handle);
const uint MK_LBUTTON = 0x0001;
Point ClickPos = new Point(330, 401); //The position on top of the objects
IntPtr lParam = (IntPtr)((401 << 16) | 330);
IntPtr result = IntPtr.Zero;
SendMessage(Handle, WM_LBUTTONDOWN, new IntPtr(MK_LBUTTON), lParam);
SendMessage(Handle, WM_LBUTTONUP, IntPtr.Zero, lParam);
////for (ushort tryScanCode = 0x79; tryScanCode < 0xFF; tryScanCode )
{
ushort tryScanCode = 0x48;
Input[] inputs1 = new Input[]
{
new Input
{
type = (int) InputType.Keyboard,
u = new InputUnion
{
ki = new KeyboardInput
{
wVk = 0,
wScan = 0x48,
dwFlags = (uint) (KeyEventF.KeyDown | KeyEventF.Scancode),
dwExtraInfo = GetMessageExtraInfo()
}
}
}
};
Input[] inputs2 = new Input[]
{
new Input
{
type = (int)InputType.Keyboard,
u = new InputUnion
{
ki = new KeyboardInput
{
wVk = 0,
wScan = 0x48,
dwFlags = (uint)(KeyEventF.KeyUp | KeyEventF.Scancode),
dwExtraInfo = GetMessageExtraInfo()
}
}
}
};
SetForegroundWindow(Handle);
SendInput((uint)inputs1.Length, inputs1, Marshal.SizeOf(typeof(Input)));
SendMessage(Handle, WM_GETDLGCODE, 0x00, 0x00);
SendInput((uint)inputs2.Length, inputs2, Marshal.SizeOf(typeof(Input)));
}
};
control.Invoke(safeStimulation);
}
}

標準的 PictureBox 無法聚焦,因此SetFocus()呼叫時會看到 Button 閃爍
用于測驗的示例專案(Google Drive)
本機方法:
internal class NativeMethods {
public static bool SendKeyboardInput(IntPtr hWnd, Keys key, Keys[] modifiers = null, int delay = 0)
{
uint targetThreadID = GetWindowThreadProcessId(hWnd, IntPtr.Zero);
uint currentThreadID = GetCurrentThreadId();
if (targetThreadID != currentThreadID) {
try {
if (!AttachThreadInput(currentThreadID, targetThreadID, true)) return false;
var parentWindow = GetAncestor(hWnd, GetAncestorFlags.GA_ROOT);
if (IsIconic(parentWindow)) {
if (!RestoreWindow(parentWindow)) return false;
}
if (!BringWindowToTop(parentWindow)) return false;
if (SetFocus(hWnd) == IntPtr.Zero) return false;
}
finally {
AttachThreadInput(currentThreadID, targetThreadID, false);
}
}
else {
SetFocus(hWnd);
}
var flagsKeyDw = IsExtendedKey(key) ? KeyboardInputFlags.ExtendedKey : KeyboardInputFlags.KeyDown;
var flagsKeyUp = KeyboardInputFlags.KeyUp | (IsExtendedKey(key) ? KeyboardInputFlags.ExtendedKey : 0);
var inputs = new List<INPUT>();
var input = new INPUT(SendInputType.InputKeyboard);
// Key Modifiers Down
if (!(modifiers is null)) {
foreach (var modifier in modifiers) {
input.Union.Keyboard.Flags = KeyboardInputFlags.KeyDown;
input.Union.Keyboard.VirtKeys = (ushort)modifier;
inputs.Add(input);
}
}
// Key Down
input.Union.Keyboard.Flags = flagsKeyDw | KeyboardInputFlags.Unicode;
input.Union.Keyboard.VirtKeys = (ushort)key;
inputs.Add(input);
// Key Up
input.Union.Keyboard.Flags = flagsKeyUp | KeyboardInputFlags.Unicode;
input.Union.Keyboard.VirtKeys = (ushort)key;
inputs.Add(input);
// Key Modifiers Up
if (!(modifiers is null)) {
foreach (var modifier in modifiers) {
input.Union.Keyboard.Flags = KeyboardInputFlags.KeyUp;
input.Union.Keyboard.VirtKeys = (ushort)modifier;
inputs.Add(input);
}
}
uint sent = SendInput((uint)inputs.Count(), inputs.ToArray(), Marshal.SizeOf<INPUT>());
return sent > 0;
}
private static Keys[] extendedKeys = { Keys.Up, Keys.Down, Keys.Left, Keys.Right, Keys.Home, Keys.End, Keys.Prior, Keys.Next, Keys.Insert, Keys.Delete };
private static bool IsExtendedKey(Keys key) => extendedKeys.Contains(key);
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-input
[StructLayout(LayoutKind.Sequential)]
public struct INPUT {
public SendInputType InputType;
public InputUnion Union;
public INPUT(SendInputType type) {
InputType = type;
Union = new InputUnion();
}
}
public enum SendInputType : uint {
InputMouse = 0,
InputKeyboard = 1,
InputHardware = 2
}
[StructLayout(LayoutKind.Explicit)]
public struct InputUnion {
[FieldOffset(0)]
public MOUSEINPUT Mouse;
[FieldOffset(0)]
public KEYBDINPUT Keyboard;
[FieldOffset(0)]
public HARDWAREINPUT Hardware;
}
[StructLayout(LayoutKind.Sequential)]
public struct MOUSEINPUT {
public int dx;
public int dy;
public uint mouseData;
public MouseEventdwFlags dwFlags;
public uint time;
public IntPtr dwExtraInfo;
}
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-keybdinput
[StructLayout(LayoutKind.Sequential)]
public struct KEYBDINPUT {
public ushort VirtKeys;
public ushort wScan;
public KeyboardInputFlags Flags;
public uint time;
public IntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
public struct HARDWAREINPUT {
public int uMsg;
public short wParamL;
public short wParamH;
}
[Flags]
public enum MouseEventdwFlags : uint {
MOUSEEVENTF_MOVE = 0x0001,
MOUSEEVENTF_LEFTDOWN = 0x0002,
MOUSEEVENTF_LEFTUP = 0x0004,
MOUSEEVENTF_RIGHTDOWN = 0x0008,
MOUSEEVENTF_RIGHTUP = 0x0010,
MOUSEEVENTF_MIDDLEDOWN = 0x0020,
MOUSEEVENTF_MIDDLEUP = 0x0040,
MOUSEEVENTF_XDOWN = 0x0080,
MOUSEEVENTF_XUP = 0x0100,
MOUSEEVENTF_WHEEL = 0x0800,
MOUSEEVENTF_VIRTUALDESK = 0x4000,
MOUSEEVENTF_ABSOLUTE = 0x8000
}
[Flags]
public enum KeyboardInputFlags : uint {
KeyDown = 0x0,
ExtendedKey = 0x0001,
KeyUp = 0x0002,
Scancode = 0x0008,
Unicode = 0x0004
}
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-windowplacement
[StructLayout(LayoutKind.Sequential)]
public struct WINDOWPLACEMENT {
public int length;
public WplFlags flags;
public SW_Flags showCmd;
public POINT ptMinPosition;
public POINT ptMaxPosition;
public RECT rcNormalPosition;
}
public enum WplFlags : uint {
WPF_ASYNCWINDOWPLACEMENT = 0x0004, // If the calling thread and the thread that owns the window are attached to different input queues, the system posts the request to the thread that owns the window. This prevents the calling thread from blocking its execution while other threads process the request.
WPF_RESTORETOMAXIMIZED = 0x0002, // The restored window will be maximized, regardless of whether it was maximized before it was minimized. This setting is only valid the next time the window is restored. It does not change the default restoration behavior.
// This flag is only valid when the SW_SHOWMINIMIZED value is specified for the showCmd member.
WPF_SETMINPOSITION = 0x0001 // The coordinates of the minimized window may be specified. This flag must be specified if the coordinates are set in the ptMinPosition member.
}
[Flags]
public enum SW_Flags : uint {
SW_HIDE = 0X00,
SW_SHOWNORMAL = 0x01,
SW_MAXIMIZE = 0x03,
SW_SHOWNOACTIVATE = 0x04,
SW_SHOW = 0x05,
SW_MINIMIZE = 0x06,
SW_RESTORE = 0x09,
SW_SHOWDEFAULT = 0x0A,
SW_FORCEMINIMIZE = 0x0B
}
public enum GetAncestorFlags : uint {
GA_PARENT = 1, // Retrieves the parent window.This does not include the owner, as it does with the GetParent function.
GA_ROOT = 2, // Retrieves the root window by walking the chain of parent windows.
GA_ROOTOWNER = 3 // Retrieves the owned root window by walking the chain of parent and owner windows returned by GetParent.
}
[StructLayout(LayoutKind.Sequential)]
public class POINT {
public int x;
public int y;
public POINT() { }
public POINT(int x, int y) {
this.x = x;
this.y = y;
}
public Point ToPoint() => new Point(this.x, this.y);
public PointF ToPointF() => new PointF((float)this.x, (float)this.y);
public POINT FromPoint(Point p) => new POINT(p.X, p.Y);
}
[StructLayout(LayoutKind.Sequential)]
public struct RECT {
public int Left;
public int Top;
public int Right;
public int Bottom;
public RECT(int left, int top, int right, int bottom) {
Left = left; Top = top; Right = right; Bottom = bottom;
}
public Rectangle ToRectangle() => Rectangle.FromLTRB(Left, Top, Right, Bottom);
public Rectangle ToRectangleOffset(POINT p) => Rectangle.FromLTRB(p.x, p.y, Right p.x, Bottom p.y);
public RECT FromRectangle(RectangleF rectangle) => FromRectangle(Rectangle.Round(rectangle));
public RECT FromRectangle(Rectangle rectangle) => new RECT() {
Left = rectangle.Left,
Top = rectangle.Top,
Bottom = rectangle.Bottom,
Right = rectangle.Right
};
public RECT FromXYWH(int x, int y, int width, int height) => new RECT(x, y, x width, y height);
}
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowplacement
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool GetWindowPlacement(IntPtr hWnd, [In, Out] ref WINDOWPLACEMENT lpwndpl);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool SetWindowPlacement(IntPtr hWnd, [In] ref WINDOWPLACEMENT lpwndpl);
[DllImport("user32.dll", SetLastError = true)]
internal static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr voidProcessId);
// https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getcurrentthreadid
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern uint GetCurrentThreadId();
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-attachthreadinput
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool AttachThreadInput([In] uint idAttach, [In] uint idAttachTo, [In, MarshalAs(UnmanagedType.Bool)] bool fAttach);
[ResourceExposure(ResourceScope.None)]
[DllImport("User32", ExactSpelling = true, CharSet = CharSet.Auto)]
internal static extern IntPtr GetAncestor(IntPtr hWnd, GetAncestorFlags flags);
[DllImport("user32.dll")]
internal static extern bool IsIconic(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool BringWindowToTop(IntPtr hWnd);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern IntPtr SetFocus(IntPtr hWnd);
//https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-sendinput
[DllImport("user32.dll", SetLastError = true)]
internal static extern uint SendInput(uint nInputs, [In, MarshalAs(UnmanagedType.LPArray)] INPUT[] pInputs, int cbSize);
public static bool RestoreWindow(IntPtr hWnd)
{
var wpl = new WINDOWPLACEMENT() {
length = Marshal.SizeOf<WINDOWPLACEMENT>()
};
if (!GetWindowPlacement(hWnd, ref wpl)) return false;
wpl.flags = WplFlags.WPF_ASYNCWINDOWPLACEMENT;
wpl.showCmd = SW_Flags.SW_RESTORE;
return SetWindowPlacement(hWnd, ref wpl);
}
}
uj5u.com熱心網友回復:
吉米,你很有幫助!根據您的回答,我修復了我的代碼。我對我的問題提交了另一個答案。
public void SimulateKeystrokeEventMethod()
{
Thread.Sleep(10000);
IntPtr handle = DesignPlanHelper.Handle;
ushort key = (ushort)KB_UP;
int repetitions = 30;
Control control = Control.FromHandle(handle);
if (control.InvokeRequired)
{
System.Action safeStimulation = delegate
{
SetFocus(handle);
// Select an image lying in the control (a descendant of UserControl in WinForms)
Clicks(handle, 330, 401);
Input keyDown = new Input
{
type = (int) InputType.Keyboard,
u = new InputUnion
{
ki = new KeyboardInput
{
wVk = 0, wScan = key, dwFlags = (uint) (KeyEventF.KeyDown | KeyEventF.Scancode), dwExtraInfo = GetMessageExtraInfo()
}
}
};
Input keyUp = new Input
{
type = (int) InputType.Keyboard,
u = new InputUnion
{
ki = new KeyboardInput
{
wVk = 0, wScan = key, dwFlags = (uint) (KeyEventF.KeyUp | KeyEventF.Scancode), dwExtraInfo = GetMessageExtraInfo()
}
}
};
Input[] inputs = new Input[repetitions * 2];
for (int input = 0; input < repetitions*2;)
{
inputs[input] = keyDown;
input ;
inputs[input] = keyUp;
input ;
}
// You must pass all Key presses in a single call to this function.
SendInput((uint)inputs.Length, inputs, Marshal.SizeOf(typeof(Input)));
};
control.Invoke(safeStimulation);
}
else
{
//copy some code from above.
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/449960.html
