Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/331.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何在窗口上执行方法。除非新的前台/活动窗口位于预定义的窗口列表中,否则是否停用事件?_C#_.net_Winforms_Pinvoke - Fatal编程技术网

C# 如何在窗口上执行方法。除非新的前台/活动窗口位于预定义的窗口列表中,否则是否停用事件?

C# 如何在窗口上执行方法。除非新的前台/活动窗口位于预定义的窗口列表中,否则是否停用事件?,c#,.net,winforms,pinvoke,C#,.net,Winforms,Pinvoke,当窗口W1的Deactivate事件被触发时,我希望它执行“离开&清理”例程,除非新的前景窗口包含在某些其他窗口的列表中。其他窗口都来自同一进程,但运行在不同的messagepump/GUI线程上。它们都是winforms窗口。 我创建了一个停用例程,它似乎在做我想做的事情,但从外观上看,它让人感到羞愧,所以我想请您提供一个“更干净”的解决方案。 下面发布的代码示例说明: 我锁定是因为我相信我真的不希望怪物在任何情况下同时运行两次。 我执行循环条件的原因如下: ForeGroundIndowHa

当窗口W1的Deactivate事件被触发时,我希望它执行“离开&清理”例程,除非新的前景窗口包含在某些其他窗口的列表中。其他窗口都来自同一进程,但运行在不同的messagepump/GUI线程上。它们都是winforms窗口。

我创建了一个停用例程,它似乎在做我想做的事情,但从外观上看,它让人感到羞愧,所以我想请您提供一个“更干净”的解决方案。

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

看,我(正在工作)试图解决这个问题:

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) 如果getForeGroundIndow()永远返回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);
            }
        }));
this.Deactivate+=neweventhandler((a,b)=>ThreadPool.QueueUserWorkItem((obj)=>
{
如果(!Monitor.TryEnter(停用锁定对象))
返回;
尝试
{
整数计数=0;
整数区间=200;
而(计数<60000)
{
IntPtr ForeGroundIndowHandle=CSUTIL.getForeGroundIndow();
如果(ForeGroundIndowHandle.ToString()!=“0”)
{
uint currentForegroundThreadId=CSUTIL.GetWindowThreadProcessId(ForeGroundIndowHandle,IntPtr.Zero);
如果(新uint[]{threadidW1、threadidW2、threadidW3等}.All((currentThreadId)=>{return currentThreadId!=currentForegroundThreadId;}))
{
//在gui线程上执行这些工作
this.Invoke(新操作(this.MakeInvisible));//执行关闭和清理例程
}
返回;
}
计数+=间隔;
睡眠(间隔);
}
this.Invoke(新操作(this.HandleSevereError));
}
最后
{
监视器。退出(停用锁定对象);
}
}));

“美是肤浅的,丑是骨子里的。”你是在寻找风格建议还是算法建议。我只是对无限循环等待一些行为不符合预期的pInvoke方法感到不舒服,我愿意接受任何会取代我的解决方案的更改。任何人都不会说这是非常丑陋的。编写这样的代码强烈地暗示了一个相当严重的设计问题。显然,您正在使用线程,而不应该使用线程。中提供了一个很好的调试示例。那是为了吓唬你。不要这样做。使用此线程池而不是Task.Factory.StartNew(()=>{})有什么好处吗?线程池.QueueUserWorkItem()将等待线程在池中出现。因此,如果您想限制应用程序使用的线程数量,线程池是一个很好的选择。在您的示例中,我不认为您不需要该功能。