当显示器关闭/睡眠时如何强制操作系统绘制新的窗口框架

问题描述 投票:0回答:1

我正在开发 WPF 应用程序。 该应用程序定期截取其他可见窗口的屏幕截图。 我的问题是,如果显示器进入睡眠模式或用户手动关闭显示器,其他窗口框架就会冻结。

为了防止我尝试使用 -

SetThreadExecutionState
https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setthreadexecutionstate

我在应用程序主线程上使用它,如下 -

SetThreadExecutionState(EXECUTION_STATE.ES_CONTINUOUS | EXECUTION_STATE.ES_SYSTEM_REQUIRED | EXECUTION_STATE.ES_AWAYMODE_REQUIRED | EXECUTION_STATE.ES_DISPLAY_REQUIRED);

当应用程序关闭时,我像这样重置它 -

SetThreadExecutionState(EXECUTION_STATE.ES_CONTINUOUS);

上面的方法似乎有效,有时...... 在使用 Windows 10/11 的多台计算机上进行了测试。 在某些机器上它按预期工作,在其他机器上则不然,关闭显示器时窗框仍然冻结。

是否有一种有保证的方法可以实现我正在寻找的目标? 只是为了澄清,我希望用户仍然能够关闭他们的显示器,但我希望当显示器关闭时,新的框架仍将被绘制,以便我的应用程序获得这些窗口的最新状态。

为了重现我所做的 - 开始屏幕录制,手动关闭和打开显示器。 查看记录,您可以清楚地看到当显示器关闭时窗口冻结(在某些机器上,无论操作系统版本如何)

谢谢!

c# wpf windows
1个回答
0
投票

您可能会考虑创建一个隐藏窗口,定期从目标窗口请求更新(“

RequestWindowUpdates
”)。这样,即使主显示器关闭,系统仍然会处理这些请求并相应地更新窗口框架(“
RefreshWindow
”)。

要在

RefreshWindow
类中实现计时器以定期请求更新,您可以使用
DispatcherTimer
。该计时器适用于 WPF 应用程序,因为它与 WPF UI 在同一线程上运行,确保所有计时器调用的操作都是 UI 安全的。

using System.Windows;
using System.Windows.Threading;

class RefreshWindow : Window
{
    private DispatcherTimer updateTimer;

    public RefreshWindow()
    {
        this.Visibility = Visibility.Hidden; // Make window invisible

        // Initialize the timer
        updateTimer = new DispatcherTimer();
        updateTimer.Tick += UpdateTimer_Tick;
        updateTimer.Interval = TimeSpan.FromSeconds(5); // Set interval to 5 seconds
        updateTimer.Start();
    }

    private void UpdateTimer_Tick(object sender, EventArgs e)
    {
        RequestWindowUpdates();
    }

    private void RequestWindowUpdates()
    {
        // See below
    }
}

要实现

RequestWindowUpdates
,您需要与外部窗口交互。这通常涉及使用平台调用 (P/Invoke) 来调用本机 Windows API 函数。然而,具体的实现可能会有所不同,具体取决于您到底需要做什么来“刷新”这些窗口。

举个例子,

RequestWindowUpdates
可以向目标窗口发送一条简单的消息来重绘自己。这可以使用 User32.dll 库中的
RedrawWindow
函数来完成

using System;
using System.Runtime.InteropServices;
using System.Windows;

class RefreshWindow : Window
{
    // P/Invoke declaration for RedrawWindow
    [DllImport("user32.dll")]
    private static extern bool RedrawWindow(IntPtr hWnd, IntPtr lprcUpdate, IntPtr hrgnUpdate, uint flags);

    // Constants for RedrawWindow
    private const uint RDW_INVALIDATE = 0x0001;
    private const uint RDW_ALLCHILDREN = 0x0080;

    // other parts of your RefreshWindow class 

    private void RequestWindowUpdates()
    {
        // You need to obtain handles to the windows you want to update.
        // That is just a placeholder for demonstration purposes.
        IntPtr[] windowHandles = GetTargetWindowHandles();

        foreach (IntPtr hWnd in windowHandles)
        {
            // Redraw the window and all of its child windows
            RedrawWindow(hWnd, IntPtr.Zero, IntPtr.Zero, RDW_INVALIDATE | RDW_ALLCHILDREN);
        }
    }

    private IntPtr[] GetTargetWindowHandles()
    {
        // Implement the logic to obtain handles of the target windows.
        // That can be done using various User32.dll functions depending on your requirements.
        // For now, returning an empty array as a placeholder.
        return new IntPtr[0];
    }
}

RedrawWindow
函数更新指定的窗口。标志
RDW_INVALIDATE
RDW_ALLCHILDREN
用于使窗口及其所有子窗口无效,从而导致它们被重绘。
GetTargetWindowHandles
方法应包含获取要刷新的窗口句柄 (
IntPtr
) 的逻辑。这可能很复杂,具体取决于您如何识别这些窗口(例如,通过标题、类名等)。

© www.soinside.com 2019 - 2024. All rights reserved.