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
out
onPeekMessage
should beref
instead.PeekMessage
doesn't allocation a message structure for you, it fills in a message structure that you pass in. The different would be that aref
parameter must be initialized before being passed into the method call, where anout
parameter does not need to be initialized. You'll see that when changingout
toref
the compiler will force you to add anew
call to initializeresult
.In playing with this I found that just adding the call to
new Message()
to initializeresult
and leaving the parameter asout
was enough to prevent the crash. I would assume that when the code is optimized no memory forresult
is allocated causing the call toPeekMessage
to fail.