- android - RelativeLayout 背景可绘制重叠内容
- android - 如何链接 cpufeatures lib 以获取 native android 库?
- java - OnItemClickListener 不起作用,但 OnLongItemClickListener 在自定义 ListView 中起作用
- java - Android 文件转字符串
我想可靠地模拟用户对其他窗口的输入。我为此使用了 SendInput
,但是我需要等到目标应用程序处理完输入后再发送更多。据我所知,SendInput
,尽管它的名字,实际上是将消息发布到队列并且不会等到它们被处理。
我的尝试是基于等待消息队列至少一次为空的想法。由于我无法直接检查其他线程的消息队列(至少我不知道这样做的方法),我正在使用 AttachThreadInput
将目标线程的队列附加到该线程的队列,然后 PeekMessage
进行检查。
为了检查功能,我使用了带有一个窗口和一个按钮的小应用程序。单击按钮时,我调用 Thread.Sleep(15000)
有效地停止消息处理,从而确保接下来的 15 秒消息队列不会为空。
代码在这里:
public static void WaitForWindowInputIdle(IntPtr hwnd)
{
var currentThreadId = GetCurrentThreadId();
var targetThreadId = GetWindowThreadProcessId(hwnd, IntPtr.Zero);
Func<bool> checkIfMessageQueueIsEmpty = () =>
{
bool queueEmpty;
bool threadsAttached = false;
try
{
threadsAttached = AttachThreadInput(targetThreadId, currentThreadId, true);
if (threadsAttached)
{
NativeMessage nm;
queueEmpty = !PeekMessage(out nm, hwnd, 0, 0, RemoveMsg.PM_NOREMOVE | RemoveMsg.PM_NOYIELD);
}
else
throw new ThreadStateException("AttachThreadInput failed.");
}
finally
{
if (threadsAttached)
AttachThreadInput(targetThreadId, currentThreadId, false);
}
return queueEmpty;
};
var timeout = TimeSpan.FromMilliseconds(15000);
var retryInterval = TimeSpan.FromMilliseconds(500);
var start = DateTime.Now;
while (DateTime.Now - start < timeout)
{
if (checkIfMessageQueueIsEmpty()) return;
Thread.Sleep(retryInterval);
}
}
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool PeekMessage(out NativeMessage lpMsg,
IntPtr hWnd,
uint wMsgFilterMin,
uint wMsgFilterMax,
RemoveMsg wRemoveMsg);
[StructLayout(LayoutKind.Sequential)]
public struct NativeMessage
{
public IntPtr handle;
public uint msg;
public IntPtr wParam;
public IntPtr lParam;
public uint time;
public System.Drawing.Point p;
}
[Flags]
private enum RemoveMsg : uint
{
PM_NOREMOVE = 0x0000,
PM_REMOVE = 0x0001,
PM_NOYIELD = 0x0002,
}
[DllImport("user32.dll", SetLastError = true)]
private static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
[DllImport("user32.dll")]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr processId);
[DllImport("kernel32.dll")]
private static extern uint GetCurrentThreadId();
现在,由于某种原因它无法正常工作。它总是返回消息队列为空。有人知道我做错了什么,或者可能有其他方法可以实现我的需要吗?
编辑:关于为什么我首先需要等待。如果不间断地立即模拟其他 Action ,我会遇到只输入部分文本的情况。例如。当焦点位于某个文本框时,我通过 SendInput 模拟了“abcdefgh”,紧接着 - 一些鼠标点击。我得到的是输入“abcde”然后点击。如果我将 Thread.Sleep(100) 放在 SendInput 之后 - 该问题在我的机器上不可重现,但在硬件较低的 VM 上很少重现。所以我需要更可靠的方法来等待正确的时间。
我对可能发生的事情的猜测与 TranslateMessage
function 有关:
Translates virtual-key messages into character messages. The character messages are posted to the calling thread's message queue, to be read the next time the thread calls the GetMessage or PeekMessage function.
因此,我为“abcdefgh”调用 SendInput
- 发布到线程队列的一堆输入消息。然后它开始以 FIFO 顺序处理这些消息,翻译“abcde”,并将每个字符的消息发布到队列的尾部。然后模拟鼠标点击并在“abcde”的字符消息后发布。然后翻译完成,但是“fgh”的翻译消息发生在鼠标单击之后。最后应用程序看到“abcde”,然后点击,然后是“fgh”——显然去错了地方……
最佳答案
这是 UI 自动化中的常见需求。它实际上是由 WindowPattern.WaitForInputIdle() method 在 .NET 中实现的.
如果使用 System.Windows.Automation 命名空间来实现它,您会过得很好。然而,该方法很容易自己实现。您可以从引用源或反编译器中查看。他们是如何做到的让我有点惊讶,但它看起来很坚固。它不会尝试猜测消息队列是否为空,而只是查看拥有该窗口的 UI 线程的状态。如果它被阻塞并且它没有等待内部系统操作,那么你有一个非常强烈的信号表明线程正在等待 Windows 传递下一条消息。我是这样写的:
using namespace System.Diagnostics;
...
public static bool WaitForInputIdle(IntPtr hWnd, int timeout = 0) {
int pid;
int tid = GetWindowThreadProcessId(hWnd, out pid);
if (tid == 0) throw new ArgumentException("Window not found");
var tick = Environment.TickCount;
do {
if (IsThreadIdle(pid, tid)) return true;
System.Threading.Thread.Sleep(15);
} while (timeout > 0 && Environment.TickCount - tick < timeout);
return false;
}
private static bool IsThreadIdle(int pid, int tid) {
Process prc = System.Diagnostics.Process.GetProcessById(pid);
var thr = prc.Threads.Cast<ProcessThread>().First((t) => tid == t.Id);
return thr.ThreadState == ThreadState.Wait &&
thr.WaitReason == ThreadWaitReason.UserRequest;
}
[System.Runtime.InteropServices.DllImport("User32.dll")]
private static extern int GetWindowThreadProcessId(IntPtr hWnd, out int pid);
在调用 SendInput() 之前在您的代码中调用 WaitForInputIdle()。您必须传递的窗口句柄非常灵活,任何 窗口句柄都可以,只要它属于进程的 UI 线程。 Process.MainWindowHandle 已经是一个很好的候选者。请注意,如果进程终止,该方法将抛出异常。
关于c# - 是否可以等到其他线程处理输入消息发布到它?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21028326/
我们正在使用 VSTS 构建和发布通过 Xamarin 创建的 iOS 和 Android 应用程序。通过 VSTS 将 Android 应用发布到商店相对简单。有人可以指导我或提供一些如何通过 VS
我一直在研究 Spring Social Facebook 的 publish(objectId, connectionName, data) API ,但不确定此 API 的用法(遗憾的是,由于缺少
我正在使用 django viewflow 创建一个发布流程: 用户创建对象 它进入审核流程,其状态为待处理(公众不可见) 经过审核和批准后,就会发布并公开可见。 如果用户编辑同一实体,则会再次进入审
我正在尝试进行 API 调用,并且 API 需要格式为 XML: Security GetSessionInfo 999999999999 0 2 {
我已经查看了所有 StackOverflow,但没有找到适合我的案例的解决方案我有 405 HttpStatusCode 调用 API/Regions/Create 操作这是我的 baseContro
如果我切换到新版本的SpringBoot,我在启动应用程序时会得到上面的错误信息。这是为什么? 最美好的祝愿史蒂文 pom.xml 4.0.0 de.xyz.microservice spring
我有一个场景,页面导航是从一个域到另一个域完成的。例如,导航是从 http://www.foo.com到 http://www.bar.com在 JavaScript 中单击按钮 重定向时,我需要将用
这半年来一直深耕包头,这个城市比较不错,但是推进项目的难度确实挺大的。与开发产品相比,后者更省心。但是光研发产品,没有项目
我正在阅读有关 Github 版本 的信息,它似乎很适合您的项目。因为我们需要决定将哪些功能用于生产,哪些不用于。 我无法理解的部分是,master 和 release 分支如何在其中发挥作用。 Sh
我将一些代码推送到远程存储库,然后在 GitHub 上创建了第一个版本,并将其命名为 'v0.0.1'。 GitHub 现在显示我现在有一个版本,并且还在“标签”中显示我有一个标签 “v0.0.1”。
如果我有一个具有以下文件/文件夹结构的 GitHub 存储库 github.com/@product/template: /build /fileA /fileB /src /genera
我有一个 Maven 多模块项目。 当代码开发完成后,我们想在 Jenkins 中编写一个分支构建作业,它分支代码,增加主干中的 pom 版本,并删除 -SNAPSHOT 来自分支中的 pom 版本。
我有一个非常大的集合(约 40000 个文档,包含约 20-25 个字段,包括包含一组约 500 个项目的数组字段)和约 2000 个订阅者(他们现在只是机器人)。 因此,当用户订阅整个集合(不包括服
如果我正在使用消息队列构建一个包含数十个发布者/订阅者的系统,那么我似乎有一些网络配置选项: 我可以拥有一个所有机器都使用的集群代理 - 每台机器都没有本地队列 我可以在每台机器上本地安装代理,并使用
我正在使用 Flash Develop,并且创建了一个 ActionScript 3.0 项目。它启动并读取一个 xml 文件,其中包含图像的 url。我已将 url 保留在与 swf 相同的文件夹中
如果我在一个句子中使用 alloc 和 retain 声明一个 NSArray 那么我应该释放 NSArray 对象两次(即[arrayObject release] 2次)? 最佳答案 如果您在同一
我正在尝试在 Node 中实现发布/订阅模式,但不使用 Redis。功能应该是相同的;您可以发布到 channel ,订阅 channel 并收听数据(如果您已订阅);以下是 Redis 功能: pu
编辑:这个问题、一些答案和一些评论,包含很多错误信息。见 how Meteor collections, publications and subscriptions work准确理解发布和订阅同一服
我正在开发一款 DirectX 游戏,我发现在发布版本中我的平均帧速率为 170fps,但是在调试版本中我的帧速率约为 20fps。 我想知道发布和调试版本之间的巨大差异是否正常,特别是因为在调试中我
是否有办法回滚 Windows Azure 网站和 SQL 部署/发布? 我发布了一个网站,现在它导致了很多错误,我想回到之前的状态并进一步处理代码。 这可能吗? 最佳答案 如果您使用 Git 或 T
我是一名优秀的程序员,十分优秀!