如何在窗口上执行方法.取消激活事件,除非新的前台/活动窗口在预定义的窗口列表中

本文关键字:窗口 前台 预定义 列表 活动 执行 方法 取消 事件 激活 | 更新日期: 2023-09-27 18:12:54

当window W1的Deactivate事件被触发时,我想让它离开&清除例程,除非新的前台窗口包含在某些其他窗口的列表中。其他窗口都来自同一个进程,但运行在不同的messagepump/gui线程上。它们都是windows窗体

我创建了一个Deactivate Routine,它似乎在做我想做的事情,但从它的外观来看,感觉像是一件令人羞愧的事情,所以我想问你一个"更干净"的解决方案。

下面发布的代码示例解释:
我锁定是因为我相信我真的不希望怪物在任何情况下同时运行两次。
我执行循环条件的原因如下:
foregroundwindowwhandle有时是"0",而不是实际在前台的窗口的句柄,除非我在Deactivate事件开始时等待几毫秒(我尝试过100毫秒)。因为我不确定这100毫秒保证我不是0 foregroundWindowHandle我只是等待,直到我可以确定它不是0。
GetForegroundWindow()和GetWindowThreadProcessId()是同名的pInvoked方法。

看,我(工作)尝试解决这个问题:

this.Deactivate += new EventHandler((a, b) =>
{
    if (!Monitor.TryEnter(deactivateLockObject))
        return;
    try
    {
        while (true)
        {
            IntPtr foregroundWindowHandle = CSUTIL.GetForegroundWindow(); 
            if (foregroundWindowHandle.ToString() == "0")
            {
                Thread.Sleep(1);
                continue;
            }
            uint currentForegroundThreadId = CSUTIL.GetWindowThreadProcessId(foregroundWindowHandle, IntPtr.Zero);
            if (new uint[] { threadidW1,threadidW2,threadidW3,etc. }.All((currentThreadId) => { return currentThreadId != currentForegroundThreadId; }))
                this.MakeInvisible(); // Executes the closing & cleanup routine
            break;
        }
    }
    finally
    {
        Monitor.Exit(deactivateLockObject);
    }
});

如何在窗口上执行方法.取消激活事件,除非新的前台/活动窗口在预定义的窗口列表中

忽略编程风格,这里有一些建议。

1)你的事件处理程序可以在主gui线程或其他线程中运行。如果在gui线程上运行,那么"Sleep()"是不合适的,因为它会阻止许多其他gui事件获得一个回合。您可以将处理程序强制到线程池中以避免这种情况。

2)在过去,任何小于200毫秒的延迟都可能阻止其他事件的处理。据推测,调度器认为没有足够的时间来证明保存状态切换到新事件并返回的开销。现在可能不是这样了,但我仍然遵循使用200毫秒或更长时间的习惯。当处理程序不在gui线程中时,它可能不会添加任何内容。

3)如果GetForegroundWindow()永远返回null,那么你写的代码只是一个无限循环。在这种情况下,您有一个系统错误或损坏的程序。您应该限制在循环中花费的时间,当超过该限制时,将其视为严重或不可恢复的错误。

这里是一个建议的修改,做这些事情。请注意,在您的代码中,Deactivate事件处理程序直到完成才返回,而本例将工作放在线程上并立即返回。在返回到线程池之前,线程任务将最多存活60秒。

        this.Deactivate += new EventHandler((a, b) => ThreadPool.QueueUserWorkItem((obj) =>
        {
            if (!Monitor.TryEnter(deactivateLockObject))
                return;                
            try
            {
                int count = 0;
                int interval = 200;
                while (count < 60000)
                {
                    IntPtr foregroundWindowHandle = CSUTIL.GetForegroundWindow();
                    if (foregroundWindowHandle.ToString() != "0")
                    {
                        uint currentForegroundThreadId = CSUTIL.GetWindowThreadProcessId(foregroundWindowHandle, IntPtr.Zero);
                        if (new uint[] { threadidW1,threadidW2,threadidW3,etc. }.All((currentThreadId) => { return currentThreadId != currentForegroundThreadId; }))
                        {
                            // do the work on the gui thread
                            this.Invoke(new Action(this.MakeInvisible)); // Executes the closing & cleanup routine
                        }
                        return;
                    }
                    count += interval;
                    Thread.Sleep(interval);
                }
                this.Invoke(new Action(this.HandleSevereError));
            }
            finally
            {
                Monitor.Exit(deactivateLockObject);
            }
        }));