将线程输入附加到目标进程后,SendInput 不起作用
Posted
技术标签:
【中文标题】将线程输入附加到目标进程后,SendInput 不起作用【英文标题】:SendInput not working after attaching thread input to a target process 【发布时间】:2013-07-17 11:53:22 【问题描述】:我正在制作一个需要与似乎没有实现 UI 自动化元素的程序的 UI 配合使用的应用程序(Inspect.Exe 仅显示主窗格,没有子窗格)。
所以我研究了实现我需要的功能的最佳方法是什么,发现了 SendInput(),它显然是 keybd_event() 和 mouse_event() 的更新版本。
但是,由于它需要键盘焦点,而且我无法将目标窗口设置为前台(以避免在它运行时打扰用户),我一直在搜索,直到找到this answer。我按照 Skurmedel 所说的做了,并将我的应用程序线程加入了目标的窗口线程。但是现在,即使我 SetFocus() 到目标然后 SendInput(),目标窗口也不会受到影响。
我的问题要么是“为什么这不起作用?”或“我做错了什么?”,但我想一个代码示例将有助于解决这个问题:
线程处理类
class ThreadHandler
#region P/Invoking and constants definition
const uint WM_GETTEXT = 0x000D;
[DllImport("user32.dll")]
static extern IntPtr SetFocus(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern int GetWindowThreadProcessId(IntPtr hWnd, uint lpdwProcessId = 0);
delegate bool EnumThreadDelegate(IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll")]
static extern bool EnumThreadWindows(int dwThreadId, EnumThreadDelegate lpfn,
IntPtr lParam);
[DllImport("user32.dll")]
static extern bool AttachThreadInput(int idAttach, int idAttachTo, bool fAttach);
[DllImport("kernel32.dll")]
static extern int GetCurrentThreadId();
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, int wParam, StringBuilder lParam);
#endregion
public readonly string ProcessName, WindowName;
protected readonly int TargetThreadID, CurrentThreadID;
protected readonly IntPtr TargetWindowHandle;
public ThreadHandler(string processName, string windowName)
CurrentThreadID = GetCurrentThreadId();
ProcessName = processName;
WindowName = windowName;
object[] objs = GetWindowThread(processName, windowName);
if (objs == null)
throw new ArgumentException("Could not find the specified process/window.");
TargetThreadID = (int)objs[0];
TargetWindowHandle = (IntPtr)objs[1];
public ThreadHandler(string processName)
CurrentThreadID = GetCurrentThreadId();
ProcessName = processName;
var processes = Process.GetProcessesByName(ProcessName);
if (processes.Length == 0)
throw new ArgumentException("Could not find the specified process.");
var appProc = processes[0];
WindowName = appProc.MainWindowTitle;
TargetThreadID = GetWindowThreadProcessId(appProc.MainWindowHandle);
TargetWindowHandle = appProc.MainWindowHandle;
public bool AttachThreadInput()
return AttachThreadInput(CurrentThreadID, TargetThreadID, true);
public bool DetachThreadInput()
return AttachThreadInput(CurrentThreadID, TargetThreadID, false);
public void SetFocus()
SetFocus(TargetWindowHandle);
static object[] GetWindowThread(string processName, string windowName)
var processes = Process.GetProcessesByName(processName);
if (processes.Length > 0)
//Fill a list of handles
var handles = new List<IntPtr>();
foreach (ProcessThread thread in processes[0].Threads)
EnumThreadWindows(thread.Id,
(hWnd, lParam) => handles.Add(hWnd); return true; , IntPtr.Zero);
//Create a stringbuilder to function as storage unit
StringBuilder nameBuffer = new StringBuilder(64);
foreach (var hWnd in handles)
//And finally compare the caption of the window with the requested name
nameBuffer.Clear();
SendMessage(hWnd, WM_GETTEXT, nameBuffer.Capacity, nameBuffer);
if (nameBuffer.ToString() == windowName)
return new object[2] GetWindowThreadProcessId(hWnd), hWnd ;
return null;
应用程序的主要方法
static void Main(string[] args)
Console.WriteLine("Please input the name of the process to hook: ");
string pName = Console.ReadLine();
Console.WriteLine("Input the name of a specific window, or leave blank: ");
string pWnd = Console.ReadLine();
ThreadHandler threadHandler;
try
if(!String.IsNullOrWhiteSpace(pWnd))
threadHandler = new ThreadHandler(pName, pWnd);
else
threadHandler = new ThreadHandler(pName);
catch
Console.WriteLine("Error: " + pName +" does not seem to be running.");
Console.ReadKey();
return;
if (!threadHandler.AttachThreadInput())
Console.WriteLine("Error: The application tried to attach its Input Processing Mechanism to " + threadHandler.ProcessName + ", but failed.");
Console.ReadKey();
return;
Console.WriteLine("Input Processing Mechanism correctly attached to " + threadHandler.ProcessName + ".");
threadHandler.SetFocus();
InputSimulator.SimulateTextEntry("test"); //InputSimulator is a seemingly famous SendInput wrapper. Replacing this line with the code for a keystroke also doesn't work.
Console.ReadLine();
Console.WriteLine("Detaching Input Processing Mechanism.");
threadHandler.DetachThreadInput();
如果您能向我解释一下 SendInput() 的神秘艺术,请提前致谢。
【问题讨论】:
你最后是怎么解决的?我正在使用 PostMessage 将密钥发送到窗口。 @LifeH2O 遗憾的是,我认为我从来没有解决过它。要使用 PostMessage,您的应用程序需要运行 Windows 事件循环,但 IIRC 如果您尝试与之交互的窗口没有焦点,则它根本不起作用。 【参考方案1】:确保您向其发送击键的特定控件的焦点正确。
您应该能够使用SetFocus 将焦点集中到您将击键发送到的控件上。
SendMessage 和 PostMessage 也可用于发送击键,但应避免使用 BAD PRACTICE。
查看System.Windows.Forms.SendKeys,了解有关通过 .NET 中的 Forms 类发送击键的信息。
在很多情况下,如果您不需要按键本身,您可以使用 SendMessage 和 WM_SETTEXT 更改窗口上的文本(如果您希望这样做)。
【讨论】:
我正在使用记事本进行测试,但显然它不起作用。我会尝试使用 SendKeys 并报告。编辑:我认为你以一种意想不到的方式帮助了我。调用 Send 方法会产生以下异常: SendKeys 无法在此应用程序中运行,因为该应用程序未处理 Windows 消息。更改应用程序以处理消息,或使用 SendKeys.SendWait 方法。我认为这是问题所在。 Welp,看来我是对的。我刚刚将我的代码移植到一个 Windows 窗体项目并且它工作。我会感谢您的回答,但是,SetFocus 不仅设置键盘焦点,而且还将目标窗口置于前台。猜猜我又回到零根了。以上是关于将线程输入附加到目标进程后,SendInput 不起作用的主要内容,如果未能解决你的问题,请参考以下文章