Tuesday, October 6, 2009

The Inverse Law of Bug Complexity

"The harder a bug is to track down, the simpler the fix tends to be." (c) James Tauber

I was implementing the c# DirectX rendering loop. As everyone who was doing it I read Tom Miller - Rick Hoskinson discussion, and copied this piece of code to Puppeteer:

public static class Native
{
[StructLayout(LayoutKind.Sequential)]
public struct Message
{
public IntPtr hWnd;
public int msg;
public IntPtr wParam;
public IntPtr lParam;
public uint time;
public System.Drawing.Point p;
}

[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("user32", CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool PeekMessage(out Message msg, IntPtr hWnd, uint messageFilterMin, uint messageFilterMax, uint flags);
}


Guess what? I forgot to put "struct Message" declaration. The compiler didn't complain. It took System.Windows.Forms.Message for a function argument instead. Tom's loop used PeekMessage to know if there were messages in the queue. The message itself was not needed in the loop, so compiler couldn't notice the difference. So did I.

Basically as far as it was not used, there was no difference. But there was a small problem: sizeof(Native.Message) was 28; sizeof(System.Windows.Forms.Message) - 20. Oops.

It didn't crash under debugger because.. To be honest, I do not know why. May be because of some special heap alignment under debugger, may be because the things it corrupted were not that critical.

Guess how much time it took me to track this down? I can not answer myself. The first time I saw this crash was in April. I could run Puppeteer under the debugger, so I did not push it hard. I thought I could fix it later, but still - I fixed it only last week.

No comments:

Post a Comment