处理主WPF窗口的IntPtr为零。

4

我最近尝试用C#开发一个简单的WPF应用程序,并且需要获取窗口的HwndSource以便添加事件处理程序。每次运行代码时,我都会在handleSource = HwndSource.FromHwnd(handle);这一行停止,并出现错误信息System.ArgumentException: 'Hwnd of zero is not valid.'。据我了解,当窗口没有完全初始化或者没有窗口,或者正在被销毁时,就会发生这种情况。如果我的理解是正确的,那么我不知道为什么会发生这种情况。
我在谷歌上没有找到其他人有相同的问题,下面是我的代码。

public partial class MainWindow:Window {

        SimConnect simConnect;
        bool connectedToSim = false;

        /// Window handle
        IntPtr handle;
        HwndSource handleSource;

        const int WM_USER_SIMCONNECT = 0x0402;

        /// <summary>
        /// Constructor and starting for the window
        /// </summary>
        public MainWindow() {
            InitializeComponent();

            handle = new WindowInteropHelper(this).Handle; // Get handle of main WPF Window
            handleSource = HwndSource.FromHwnd(handle); // Get source of handle in order to add event handlers to it
            handleSource.AddHook(HandleSimConnectEvents);

            Thread pollThread = new Thread(PollThread);
        }

        ~MainWindow() {
            if (handleSource != null) {
                handleSource.RemoveHook(HandleSimConnectEvents);
            }
        }

        private IntPtr HandleSimConnectEvents(IntPtr hWnd, int message, IntPtr wParam, IntPtr lParam, ref bool isHandled) {
            isHandled = false;

            switch (message) {
                case WM_USER_SIMCONNECT: {
                        if (simConnect != null) {
                            simConnect.ReceiveMessage();
                            isHandled = true;
                        }
                    }
                    break;

                default:
                    break;
            }

            return IntPtr.Zero;
        }
    }

非常感谢您的帮助!


我的直觉是你可能想在生命周期的那个点调用 EnsureHandle,即构造函数。如果原生窗口尚未创建,则此方法将创建原生窗口,设置 Handle 属性并返回 HWND。如果原生窗口已经被创建,则返回现有原生窗口的句柄。 - TheGeneral
2个回答

4
在窗口生命周期(即构建期)的那一点上,您可能需要使用EnsureHandle。该调用很轻便,您无需等待窗体显示并依赖于事件来获取访问句柄(如果这符合您的工作流程)。 WindowInteropHelper.EnsureHandle 方法 创建窗口的HWND,如果HWND尚未创建,则在需要时使用此方法。
备注 当您想将窗口句柄(HWND)的创建与托管窗口的实际显示分离时,请使用EnsureHandle方法... 如果尚未创建本机窗口,则此方法会创建本机窗口,设置Handle属性并返回HWND。 如果本机窗口已经创建,则返回现有本机窗口的句柄。

1
将类的构造函数中的代码移动到第一次调用Shown事件中,如此问题所述:WPF中的窗口显示事件? 一旦窗口显示,窗口句柄就保证存在。通常不建议像Michael Randall建议的使用EnsureHandle那样在构造函数中尽早创建窗口句柄。

1
也许这是一个愚蠢的问题,但为什么你不想强制创建窗口句柄呢?即,EnsureHandle有哪些缺点? - Pseudonym Patel
构造函数(ctor)是窗口被构建的地方。在构造函数之前,没有任何窗口可以有句柄。在构造函数期间,窗口不能出现,因此它不会有句柄。在窗口显示后,必须有一个具有句柄的窗口。因此,我认为“shown”似乎是显而易见的选择。 - Andy
@Andy,但这似乎是EnsureHandle添加到框架的确切情况。使用Shown的(小)缺点是它增加了可能不需要的复杂性。如果有其他原因要避免在ctor中使用EnsureHandle,那么详细说明这个答案将非常有用,现在它只是一个没有理由的断言。 - StayOnTarget
情况Ensurehandle的添加在文档中有解释。“当您拥有一个可以完成任务而不需要显示窗口的自动化客户端时,这非常有用。” 您应该始终尽量减少任何ctor中的代码,并且对于mainwindow来说尤其如此。如果您的代码存在任何问题,则会在ctor中得到错误。如果mainwindow ctor失败,则您将没有应用程序。 - Andy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接