FindWindow 查找窗口直接执行可能窗口还没有准备好,返回结果必然是0。通常使用 Thread.Sleep 进行阻塞等待,是一种有效的手段。因计算机CPU运算效率差异,不能保证及时准确的得到想要的结果。所以有必要使用一个保证效率的基础上并可控的方式,下面是我的处理方式,代码使用 C# 编写,其他语言照葫芦画瓢。
TimeoutMonitor.cs
/// <summary>
/// 超时监视器
/// </summary>
public class TimeoutMonitor
{
readonly int _tickCount;
/// <summary>
/// 创建一个超时监视器
/// </summary>
/// <param name="timeout">超时时间(毫秒)</param>
public TimeoutMonitor(int timeout)
{
_tickCount = Environment.TickCount;
Timeout = timeout;
}
/// <summary>
/// 超时时间(毫秒)
/// </summary>
public int Timeout { get; }
/// <summary>
/// 完成的
/// </summary>
public bool IsCompleted => Environment.TickCount - _tickCount > Timeout;
/// <summary>
/// 调用超时,如果超时将抛出 TimeoutException 错误
/// </summary>
/// <param name="error">错误描述</param>
public void ThrowIfTimeout(string error) => ThrowIfTimeout(error, 0);
/// <summary>
/// 调用超时,如果超时将抛出 TimeoutException 错误
/// </summary>
/// <param name="error">错误描述</param>
/// <param name="sleepTimeout">阻塞间隔(毫秒)</param>
public void ThrowIfTimeout(string error, int sleepTimeout)
{
if (IsCompleted) throw new TimeoutException(error);
if (sleepTimeout > 0) Thread.Sleep(sleepTimeout);
}
/// <summary>
/// 根据条件调用超时,如果超时将抛出 TimeoutException 错误
/// </summary>
/// <param name="func">返回 true 时立即跳出后续</param>
/// <param name="timeout">超时时间(毫秒)</param>
/// <param name="error">错误描述</param>
public static void ThrowIfTimeout(Func<bool> func, int timeout, string error)
{
var tickCount = Environment.TickCount;
while (Environment.TickCount - tickCount < timeout) if (func()) return;
throw new TimeoutException(error);
}
/// <summary>
/// 等待超时退出
/// </summary>
/// <param name="func">返回 true 时立即跳出后续</param>
/// <param name="timeout">超时时间(毫秒)</param>
/// <returns>返回 true 未超时,反之 false 超时</returns>
public static bool WaitTimeout(Func<bool> func, int timeout)
{
var tickCount = Environment.TickCount;
while (Environment.TickCount - tickCount < timeout) if (func()) return true;
return false;
}
}
例子
可适用大多数非阻塞的方法:等待文件完全写入、等待窗口关闭……
FindWindowTimeout
在指定时间内一直 FindWindow 如果非 0 立即返回,到达指定时间后返回 0。
[DllImport("user32.dll")]
static extern IntPtr FindWindow(string lpszClass, string lpszWindow);
public static IntPtr FindWindowTimeout(string lpszClass, string lpszWindow, int timeout)
{
var timeoutMonitor = new TimeoutMonitor(timeout);
while (!timeoutMonitor.IsCompleted)
{
var hWnd = FindWindow(lpszClass, lpszWindow);
if (hWnd != IntPtr.Zero) return hWnd;
}
return IntPtr.Zero;
}
等待文件完全写入
var fi = new FileInfo(@"C:\1.txt");
TimeoutMonitor.ThrowIfTimeout(() => fi.Exists && fi.Length > 0, 15000, "文件不存在");
var s = File.ReadAllText(fi.FullName);
等待窗口关闭
const int WM_CLOSE = 0x0010;
[DllImport("user32.dll")]
static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
[DllImport("user32.dll")]
static extern bool IsWindow(IntPtr hWnd);
public static bool WaitCloseWindow(IntPtr hWnd, int timeout)
{
SendMessage(hWnd, WM_CLOSE, 0, 0);
return TimeoutMonitor.WaitTimeout(() => !IsWindow(hWnd), timeout);
}