Eric Bowser
Eric Bowser

Reputation: 62

Windows Forms Screensaver Preview Window Handle

I have build a Windows Forms screensaver and I cannot seems to figure out why the preview functionality is not working.

enter image description here

Constructor Overload for Preview

public ScreenSaverForm(IntPtr PreviewWndHandle)
{
  InitializeComponent();

  //set the preview window as the parent of this window
  SetParent(this.Handle, PreviewWndHandle);

  //make this a child window, so when the select screensaver 
  //dialog closes, this will also close
  SetWindowLong(this.Handle, -16,
        new IntPtr(GetWindowLong(this.Handle, -16) | 0x40000000));

  //set our window's size to the size of our window's new parent
  Rectangle ParentRect;
  GetClientRect(PreviewWndHandle, out ParentRect);
  this.Size = ParentRect.Size;

  //set our location at (0, 0)
  this.Location = new Point(0, 0);

  previewMode = true;
}

Program.cs or the main entry point that takes command arguments

/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
 if (args.Length > 0)
        {
            if (args[0].ToLower().Trim().Substring(0,2) == "/s") //show
            {
                //show the screen saver
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
                ShowScreenSaver(); //this is the good stuff
                Application.Run();
            }
            else if (args[0].ToLower().Trim().Substring(0,2) == "/p") //preview
            {
              //show the screen saver preview
              Application.EnableVisualStyles();
              Application.SetCompatibleTextRenderingDefault(false);
              //args[1] is the handle to the preview window
              Application.Run(new ScreenSaverForm(new IntPtr(long.Parse(args[1]))));
            }
            else if (args[0].ToLower().Trim().Substring(0,2) == "/c") //configure
            {
              //nothing to configure
              MessageBox.Show(
              "This screensaver has no options that you can set",
              "Dancing Polygons",
              MessageBoxButtons.OK,
              MessageBoxIcon.Information);
            }
        }
        else
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            ShowScreenSaver(); //this is the good stuff
            Application.Run();
        }
}

Screen Saver Load Event

 private void ScreenSaver_Load(object sender, EventArgs e)
{
  Cursor.Hide();
  TopMost = false;
  MouseDown += new MouseEventHandler(MouseDownEvent);
  timer.Tick += new EventHandler(Run);
  timer.Interval = 30;
  watch.Start();
  timer.Start();
  //loopThread = new Thread(() => RunThread(this.CreateGraphics(), this, timer));
  //loopThread.Start();
}

So I cannot figure out why I am getting a null reference. I believe its the command arguments. I am running this screensaver in Windows 8.1.

Upvotes: 3

Views: 421

Answers (1)

Hans Passant
Hans Passant

Reputation: 941317

   SetParent(this.Handle, PreviewWndHandle);

Do not use the Handle property in the constructor. That forces the window to be created too early. Your Load event fires before the constructor has completed running. That's pretty unhealthy and of course very apt to cause a NullReferenceException when your Load event handler uses a field of the class that was not yet set.

You did not post the code that bombs (ScreenSaver_Load) so we can't guess what specific statement failed. Use Debug > Exceptions > CLR Exceptions > tick the Thrown checkbox to ensure the debugger stops when the exception is raised so you can see the failing statement.

Furthermore, the Handle property value can change when Winforms is forced to re-create the window. The proper place for this code is an override for the OnHandleCreated() method. At which point the Handle property is guaranteed to be valid and not cause side-effects.

   this.Size = ParentRect.Size;

Changing the Size property in the constructor is also wrong, the form's AutoScaleMode property will change it later to adjust for the video adapter's DPI setting. You must delay assigning Size until the Load event, when it fires the normal way then all auto-scaling has been completed.

Upvotes: 4

Related Questions