I'm using the game loop discussed in this gamedev.stackexchange thread: https://gamedev.stackexchange.com/questions/67651/what-is-the-standard-c-windows-forms-game-loop
Everything is working great if I'm using Debug build type, but when I go to do Release, I get a null reference exception. It looks like it only happens if I enable code optimization. This is a barebone example that does the same thing. The form is totally blank, there are no buttons/controls on it in this example.
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace Sharp8
{
public partial class DebugForm : Form
{
public DebugForm()
{
InitializeComponent();
Application.Idle += GameLoop;
}
private void GameLoop(object sender, EventArgs e)
{
while (IsApplicationIdle())
{
Console.WriteLine("Game Updates/Rendering!");
}
}
[StructLayout(LayoutKind.Sequential)]
public struct NativeMessage
{
public IntPtr Handle;
public uint Message;
public IntPtr WParameter;
public IntPtr LParameter;
public uint Time;
public Point Location;
}
[DllImport("user32.dll")]
static extern bool PeekMessage(out Message message, IntPtr window, uint messageFilterMinimum, uint messageFilterMaximum, uint shouldRemoveMessage);
private bool IsApplicationIdle()
{
Message result;
return !PeekMessage(out result, IntPtr.Zero, 0, 0, 0);
}
}
}
When I run this, the exception is said to happen in external code inside forms.dll, and it's throw after my Application.Run("etc") that starts this form. The stack trace isn't really helpful, it's just the Application.Run and a bunch of external code.
I'm not sure what is causing this, but I know it has something to do with calling PeekMessage, because if I comment out the subscription to the Idle event the error does not happen.
As a side question, why do I need to declare the "NativeMessage" struct here? It doesn't seem to cause issues if I cut it, yet every example of using this game loop includes it.
The
outonPeekMessageshould berefinstead.PeekMessagedoesn't allocation a message structure for you, it fills in a message structure that you pass in. The different would be that arefparameter must be initialized before being passed into the method call, where anoutparameter does not need to be initialized. You'll see that when changingouttorefthe compiler will force you to add anewcall to initializeresult.In playing with this I found that just adding the call to
new Message()to initializeresultand leaving the parameter asoutwas enough to prevent the crash. I would assume that when the code is optimized no memory forresultis allocated causing the call toPeekMessageto fail.