1

この gamedev.stackexchange スレッドで説明されているゲーム ループを使用しています: https://gamedev.stackexchange.com/questions/67651/what-is-the-standard-c-windows-forms-game-loop

Debug ビルド タイプを使用している場合はすべて問題なく動作しますが、Release を実行すると、null 参照例外が発生します。コードの最適化を有効にした場合にのみ発生するようです。これは、同じことを行うベアボーンの例です。フォームは完全に空白で、この例ではボタン/コントロールはありません。

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);
        }
    }
}

これを実行すると、forms.dll 内の外部コードで例外が発生すると言われ、このフォームを開始する Application.Run("etc") の後にスローされます。スタック トレースはあまり役に立ちません。Application.Run と一連の外部コードだけです。

何が原因なのかはわかりませんが、Idle イベントへのサブスクリプションをコメントアウトするとエラーが発生しないため、PeekMessage の呼び出しに関係があることはわかっています。

副次的な質問として、ここで "NativeMessage" 構造体を宣言する必要があるのはなぜですか? カットしても問題はないようですが、このゲームループを使用するすべての例にはそれが含まれています。

4

2 に答える 2

3

@ shf301 の回答はコード内で問題を解決する方法を正しく説明していますが、不要なオーバーヘッドが発生するため、この目的にはPeekMessageまったく使用しないことをお勧めします。代わりにPeekMessage使用してください:GetQueueStatus

public static bool IsApplicationIdle()
{
    // The high-order word of the return value indicates
    // the types of messages currently in the queue. 
    return 0 == (GetQueueStatus(QS_MASK) >> 16 & QS_MASK);
}

const uint QS_MASK = 0x1FF;

[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern uint GetQueueStatus(uint flags);

詳細については、「Winforms updates with high performance」に関する私の回答を確認してください。

于 2014-02-11T05:17:31.523 に答える
1

代わりにoutオンにするPeekMessage必要がありrefます。 PeekMessageはメッセージ構造を割り当てません。渡すメッセージ構造を埋めます。違いは、refパラメーターを初期化する必要がないメソッド呼び出しに渡す前に、outパラメーターを初期化する必要があることです。コンパイラに変更outすると、 initialize への呼び出しを追加する必要があることがわかります。refnewresult

これをいじってみると、呼び出しをnew Message()初期化resultしてパラメーターをそのままにしておくoutだけで、クラッシュを防ぐことができることがわかりました。コードが最適化されると、メモリresultが割り当てられず、呼び出しがPeekMessage失敗すると思います。

[DllImport("user32.dll")]
static extern bool PeekMessage(ref Message message, IntPtr window, uint    messageFilterMinimum, uint messageFilterMaximum, uint shouldRemoveMessage);

private bool IsApplicationIdle()
{
    Message result = new Message();
    return !PeekMessage(ref result, IntPtr.Zero, 0, 0, 0);
}
于 2014-02-11T04:33:12.373 に答える