我正在开发 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 的多台计算机上进行了测试。 在某些机器上它按预期工作,在其他机器上则不然,关闭显示器时窗框仍然冻结。
是否有一种有保证的方法可以实现我正在寻找的目标? 只是为了澄清,我希望用户仍然能够关闭他们的显示器,但我希望当显示器关闭时,新的框架仍将被绘制,以便我的应用程序获得这些窗口的最新状态。
为了重现我所做的 - 开始屏幕录制,手动关闭和打开显示器。 查看记录,您可以清楚地看到当显示器关闭时窗口冻结(在某些机器上,无论操作系统版本如何)
谢谢!
您可能会考虑创建一个隐藏窗口,定期从目标窗口请求更新(“
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
) 的逻辑。这可能很复杂,具体取决于您如何识别这些窗口(例如,通过标题、类名等)。