Search code examples
c#wpfwindowsscreensaver

Windows screensaver in C# using WPF crashing during preview mode


I have just started to learn C# and am trying to write a screensaver. My App.xaml.cs contains the following code for when the /p argument is used:

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));
            }

Then in my MainWindow.xaml.cs, this construct handles the preview call:

public MainWindow(IntPtr previewHandle)
    {
        App.Current.Properties["isPreviewMode"] = true;
        App.Current.Properties["previewHandle"] = previewHandle;
        InitializeComponent();
    }

After this, it crashes. In my MainWindow.xaml I have Loaded="MainWindow_Loaded".

In MainWindows.xaml.cs this is 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);

I have other code in the App.xaml.cs to handle changing the images on a timer and other code in MainWindow.xaml.cs to handle mouse movement, clicks and keypresses. Everything works fine when running the screensaver normally. It is just the preview that fails. What am I doing wrong?


Solution

  • What I ended up doing was using the WPF windows to display when the screensaver runs normally in full screen with /s. I created a new regular windows form previewForm.cs with a picturebox to use for the preview. That works fine and there is no performance issues. I assume the picturebox is using GDI.

    This is my modified App.xaml.cs for handling the /p argument:

    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();
                }
    

    and my previewForm.cs construct to handle the preview:

    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);
        }
    

    So I used a mix of a WPF form and a regular windows form to accomplish this. And the performance is fine. Only uses like 14-18MB of RAM and practically no CPU.