Reputation: 62
I have build a Windows Forms screensaver and I cannot seems to figure out why the preview functionality is not working.
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
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