Windows 使用 WPF 的 C# 屏幕保护程序在预览模式下崩溃
Windows screensaver in C# using WPF crashing during preview mode
我刚开始学习 C#,正在尝试编写一个屏幕保护程序。当使用 /p 参数时,我的 App.xaml.cs 包含以下代码:
else if (arg1 == "/p")
{
if (e.Args[1] == null)
{
System.Windows.Forms.MessageBox.Show("Invalid or missing window handle.");
System.Windows.Application.Current.Shutdown();
}
IntPtr previewHandle = new IntPtr(long.Parse(e.Args[1]));
System.Windows.Application.Current.Run(new MainWindow(previewHandle));
}
然后在我的 MainWindow.xaml.cs 中,这个构造处理预览调用:
public MainWindow(IntPtr previewHandle)
{
App.Current.Properties["isPreviewMode"] = true;
App.Current.Properties["previewHandle"] = previewHandle;
InitializeComponent();
}
在此之后,它崩溃了。在我的 MainWindow.xaml 我有 Loaded="MainWindow_Loaded".
在 MainWindows.xaml.cs 这是 MainWindow_Loaded:
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
if ((bool)App.Current.Properties["isPreviewMode"])
{
IntPtr myHandle = new WindowInteropHelper(this).Handle;
SetParent(myHandle, (IntPtr)App.Current.Properties["previewHandle"]);
SetWindowLong(myHandle, -16, new IntPtr(GetWindowLong(myHandle, -16) | 0x40000000));
Rectangle ParentRect;
GetClientRect((IntPtr)App.Current.Properties["previewHandle"], out ParentRect);
this.Top = ParentRect.X;
this.Left = ParentRect.Y;
this.Height = ParentRect.Height;
this.Width = ParentRect.Width;
}
ssimage.Source = new BitmapImage(new Uri("pack://application:,,,/Resources/EmbeddedImage.PNG"));
}
[DllImport("user32.dll")]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
[DllImport("user32.dll", SetLastError = true)]
static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll")]
static extern bool GetClientRect(IntPtr hWnd, out Rectangle lpRect);
我在 App.xaml.cs 中有其他代码来处理更改计时器上的图像,在 MainWindow.xaml.cs 中有其他代码来处理鼠标移动、点击和按键。 运行 屏保正常时一切正常。只是预览失败了。我做错了什么?
据我所知,WPF 不允许您重新定位 window 句柄,因此无法使用您的技术在 WPF 中执行屏幕保护程序预览。
但是,有一个解决方法:如果您将 WPF 配置为呈现到位图目标(请参阅 RenderTargetBitmap
),您可以将该位图 blit 到所需的目标 window 句柄 - 但是这将涉及 GDI 和 WPF 的邪恶混合,并且可能具有糟糕的运行时性能;我怀疑这样做是否值得。
我最后做的是使用 WPF windows 在屏幕保护程序以 /s 全屏正常运行时显示。我创建了一个新的常规 windows 表单 previewForm.cs,其中包含一个用于预览的图片框。这工作正常,没有性能问题。我假设图片框使用的是 GDI。
这是我为处理 /p 参数而修改的 App.xaml.cs:
else if (arg1 == "/p")
{
if (e.Args[1] == null)
{
System.Windows.Forms.MessageBox.Show("Invalid or missing window handle.");
System.Windows.Application.Current.Shutdown();
}
IntPtr previewHandle = new IntPtr(long.Parse(e.Args[1]));
pw = new previewForm(previewHandle);
GetImages();
pw.Show();
}
和我的 previewForm.cs 构造来处理预览:
public previewForm(IntPtr previewHandle)
{
InitializeComponent();
IntPtr myHandle = this.Handle;
SetParent(myHandle, previewHandle);
SetWindowLong(myHandle, -16, new IntPtr(GetWindowLong(myHandle, -16) | 0x40000000));
Rectangle ParentRect;
GetClientRect(previewHandle, out ParentRect);
this.Size = ParentRect.Size;
this.pictureBox1.Size = ParentRect.Size;
this.Location = new Point(0, 0);
}
所以我混合使用了 WPF 表单和常规 windows 表单来完成此操作。而且性能还不错。只使用 14-18MB 的 RAM,几乎没有 CPU.
我刚开始学习 C#,正在尝试编写一个屏幕保护程序。当使用 /p 参数时,我的 App.xaml.cs 包含以下代码:
else if (arg1 == "/p")
{
if (e.Args[1] == null)
{
System.Windows.Forms.MessageBox.Show("Invalid or missing window handle.");
System.Windows.Application.Current.Shutdown();
}
IntPtr previewHandle = new IntPtr(long.Parse(e.Args[1]));
System.Windows.Application.Current.Run(new MainWindow(previewHandle));
}
然后在我的 MainWindow.xaml.cs 中,这个构造处理预览调用:
public MainWindow(IntPtr previewHandle)
{
App.Current.Properties["isPreviewMode"] = true;
App.Current.Properties["previewHandle"] = previewHandle;
InitializeComponent();
}
在此之后,它崩溃了。在我的 MainWindow.xaml 我有 Loaded="MainWindow_Loaded".
在 MainWindows.xaml.cs 这是 MainWindow_Loaded:
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
if ((bool)App.Current.Properties["isPreviewMode"])
{
IntPtr myHandle = new WindowInteropHelper(this).Handle;
SetParent(myHandle, (IntPtr)App.Current.Properties["previewHandle"]);
SetWindowLong(myHandle, -16, new IntPtr(GetWindowLong(myHandle, -16) | 0x40000000));
Rectangle ParentRect;
GetClientRect((IntPtr)App.Current.Properties["previewHandle"], out ParentRect);
this.Top = ParentRect.X;
this.Left = ParentRect.Y;
this.Height = ParentRect.Height;
this.Width = ParentRect.Width;
}
ssimage.Source = new BitmapImage(new Uri("pack://application:,,,/Resources/EmbeddedImage.PNG"));
}
[DllImport("user32.dll")]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
[DllImport("user32.dll", SetLastError = true)]
static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll")]
static extern bool GetClientRect(IntPtr hWnd, out Rectangle lpRect);
我在 App.xaml.cs 中有其他代码来处理更改计时器上的图像,在 MainWindow.xaml.cs 中有其他代码来处理鼠标移动、点击和按键。 运行 屏保正常时一切正常。只是预览失败了。我做错了什么?
据我所知,WPF 不允许您重新定位 window 句柄,因此无法使用您的技术在 WPF 中执行屏幕保护程序预览。
但是,有一个解决方法:如果您将 WPF 配置为呈现到位图目标(请参阅 RenderTargetBitmap
),您可以将该位图 blit 到所需的目标 window 句柄 - 但是这将涉及 GDI 和 WPF 的邪恶混合,并且可能具有糟糕的运行时性能;我怀疑这样做是否值得。
我最后做的是使用 WPF windows 在屏幕保护程序以 /s 全屏正常运行时显示。我创建了一个新的常规 windows 表单 previewForm.cs,其中包含一个用于预览的图片框。这工作正常,没有性能问题。我假设图片框使用的是 GDI。
这是我为处理 /p 参数而修改的 App.xaml.cs:
else if (arg1 == "/p")
{
if (e.Args[1] == null)
{
System.Windows.Forms.MessageBox.Show("Invalid or missing window handle.");
System.Windows.Application.Current.Shutdown();
}
IntPtr previewHandle = new IntPtr(long.Parse(e.Args[1]));
pw = new previewForm(previewHandle);
GetImages();
pw.Show();
}
和我的 previewForm.cs 构造来处理预览:
public previewForm(IntPtr previewHandle)
{
InitializeComponent();
IntPtr myHandle = this.Handle;
SetParent(myHandle, previewHandle);
SetWindowLong(myHandle, -16, new IntPtr(GetWindowLong(myHandle, -16) | 0x40000000));
Rectangle ParentRect;
GetClientRect(previewHandle, out ParentRect);
this.Size = ParentRect.Size;
this.pictureBox1.Size = ParentRect.Size;
this.Location = new Point(0, 0);
}
所以我混合使用了 WPF 表单和常规 windows 表单来完成此操作。而且性能还不错。只使用 14-18MB 的 RAM,几乎没有 CPU.