首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用PeekMessage时,代码优化会导致空引用异常

使用PeekMessage时,代码优化会导致空引用异常
EN

Stack Overflow用户
提问于 2014-02-11 11:56:21
回答 2查看 527关注 0票数 1

我正在使用这个gamedev.stackexchange线程中讨论的游戏循环:https://gamedev.stackexchange.com/questions/67651/what-is-the-standard-c-windows-forms-game-loop

如果我使用Debug构建类型,一切都很好,但是当我去做Release时,我得到了一个空引用异常。看起来只有当我启用代码优化时才会发生这种情况。这是一个做同样事情的barebone示例。该表单是完全空白的,在本例中没有任何按钮/控件。

代码语言:javascript
复制
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和一堆外部代码。

我不确定这是什么原因造成的,但我知道这与调用PeekMessage有关,因为如果我注释掉对Idle事件的订阅,错误就不会发生。

作为一个次要问题,为什么我需要在这里声明"NativeMessage“结构?如果我剪掉它,它似乎不会引起问题,但是使用这个游戏循环的每个示例都包含它。

EN

回答 2

Stack Overflow用户

发布于 2014-02-11 13:17:32

虽然@shf301's answer正确地解释了如何在代码中使用PeekMessage来解决这个问题,但我建议您完全不要使用PeekMessage,因为它会带来一些不必要的开销。请改用GetQueueStatus

代码语言:javascript
复制
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);

有关更多详细信息,请查看我的answer on "Winforms updates with high performance"

票数 3
EN

Stack Overflow用户

发布于 2014-02-11 12:33:12

PeekMessage上的out应改为refPeekMessage不会为您分配消息结构,它会填充您传入的消息结构。不同之处在于,在传递到方法调用之前,必须初始化ref参数,而不需要初始化out参数。您将看到,当将out更改为ref时,编译器将强制您添加一个new调用来初始化result

在使用它的过程中,我发现只需添加对new Message()的调用来初始化result,并将参数保留为out就足以防止崩溃。我假设在优化代码时,没有为result分配内存,这会导致对PeekMessage的调用失败。

代码语言:javascript
复制
[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);
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/21692790

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档