如何从 Windows 窗体创建仅显示消息的窗口?

发布于 2024-07-23 00:54:31 字数 364 浏览 5 评论 0原文

我正在尝试创建一个仅消息窗口,以在 winforms 应用程序中从 MFC 库类接收窗口消息。

我尝试过对 NativeWindow 进行子类化,并在构造函数中请求如下所示的窗口句柄:

CreateParams cp = new CreateParams();
cp.Parent = (IntPtr)HWND_MESSAGE;
this.CreateHandle(cp);

但我收到了抛出的 Win32Exception 消息,并显示消息“创建窗口句柄时出错”。 如何从 Windows 窗体创建仅显示消息的窗口? 使用 NativeWindow 是正确的方法吗?

I'm trying to create a message-only window to receive window messages from an MFC library class, within a winforms application.

I've tried subclassing NativeWindow, and in the constructor requesting a window handle like this:

CreateParams cp = new CreateParams();
cp.Parent = (IntPtr)HWND_MESSAGE;
this.CreateHandle(cp);

but I get a Win32Exception thrown with the message "Error creating window handle". How do I create a message-only window from windows forms? Is using NativeWindow the right approach?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

云朵有点甜 2024-07-30 00:54:31

我知道这已经有 7.5 年历史了,但以防万一有人发现这个,我想我会回应。 我使用了微软的 TimerNativeWindow 代码 并删除了计时器功能。 我最终使用了这种方法:

    public class MyNativeWindow : NativeWindow
    {
        private readonly string _caption;
        private const int WmClose = 0x0010;

        [SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")]
        private static readonly HandleRef HwndMessage = new HandleRef(null, new IntPtr(-3));

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        [ResourceExposure(ResourceScope.None)]
        private static extern IntPtr PostMessage(HandleRef hwnd, int msg, int wparam, int lparam);

        [DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
        [ResourceExposure(ResourceScope.Process)]
        private static extern int GetWindowThreadProcessId(HandleRef hWnd, out int lpdwProcessId);

        [DllImport("kernel32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
        [ResourceExposure(ResourceScope.Process)]
        private static extern int GetCurrentThreadId();

        public MyNativeWindow(string caption)
        {
            _caption = caption;
        }

        public bool CreateWindow()
        {
            if (Handle == IntPtr.Zero)
            {
                CreateHandle(new CreateParams
                {
                    Style = 0,
                    ExStyle = 0,
                    ClassStyle = 0,
                    Caption = _caption,
                    Parent = (IntPtr)HwndMessage
                });
            }
            return Handle != IntPtr.Zero;
        }


        public void DestroyWindow()
        {
            DestroyWindow(true, IntPtr.Zero);
        }

        private bool GetInvokeRequired(IntPtr hWnd)
        {
            if (hWnd == IntPtr.Zero) return false;
            int pid;
            var hwndThread = GetWindowThreadProcessId(new HandleRef(this, hWnd), out pid);
            var currentThread = GetCurrentThreadId();
            return (hwndThread != currentThread);
        }

        private void DestroyWindow(bool destroyHwnd, IntPtr hWnd)
        {
            if (hWnd == IntPtr.Zero)
            {
                hWnd = Handle;
            }

            if (GetInvokeRequired(hWnd))
            {
                PostMessage(new HandleRef(this, hWnd), WmClose, 0, 0);
                return;
            }

            lock (this)
            {
                if (destroyHwnd)
                {
                    base.DestroyHandle();
                }
            }
        }

        public override void DestroyHandle()
        {
            DestroyWindow(false, IntPtr.Zero);
            base.DestroyHandle();
        }
    }

I know this is 7.5 years old, but just in case anyone finds this, I thought I would respond. I used Microsoft's TimerNativeWindow code and removed the timer functionality. I ended up using this approach:

    public class MyNativeWindow : NativeWindow
    {
        private readonly string _caption;
        private const int WmClose = 0x0010;

        [SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")]
        private static readonly HandleRef HwndMessage = new HandleRef(null, new IntPtr(-3));

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        [ResourceExposure(ResourceScope.None)]
        private static extern IntPtr PostMessage(HandleRef hwnd, int msg, int wparam, int lparam);

        [DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
        [ResourceExposure(ResourceScope.Process)]
        private static extern int GetWindowThreadProcessId(HandleRef hWnd, out int lpdwProcessId);

        [DllImport("kernel32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
        [ResourceExposure(ResourceScope.Process)]
        private static extern int GetCurrentThreadId();

        public MyNativeWindow(string caption)
        {
            _caption = caption;
        }

        public bool CreateWindow()
        {
            if (Handle == IntPtr.Zero)
            {
                CreateHandle(new CreateParams
                {
                    Style = 0,
                    ExStyle = 0,
                    ClassStyle = 0,
                    Caption = _caption,
                    Parent = (IntPtr)HwndMessage
                });
            }
            return Handle != IntPtr.Zero;
        }


        public void DestroyWindow()
        {
            DestroyWindow(true, IntPtr.Zero);
        }

        private bool GetInvokeRequired(IntPtr hWnd)
        {
            if (hWnd == IntPtr.Zero) return false;
            int pid;
            var hwndThread = GetWindowThreadProcessId(new HandleRef(this, hWnd), out pid);
            var currentThread = GetCurrentThreadId();
            return (hwndThread != currentThread);
        }

        private void DestroyWindow(bool destroyHwnd, IntPtr hWnd)
        {
            if (hWnd == IntPtr.Zero)
            {
                hWnd = Handle;
            }

            if (GetInvokeRequired(hWnd))
            {
                PostMessage(new HandleRef(this, hWnd), WmClose, 0, 0);
                return;
            }

            lock (this)
            {
                if (destroyHwnd)
                {
                    base.DestroyHandle();
                }
            }
        }

        public override void DestroyHandle()
        {
            DestroyWindow(false, IntPtr.Zero);
            base.DestroyHandle();
        }
    }
蓝颜夕 2024-07-30 00:54:31

尝试一下:

[DllImport("user32.dll")]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);

static IntPtr HWND_MESSAGE = new IntPtr(-3);

protected override void OnHandleCreated(EventArgs e)
{
    base.OnHandleCreated(e);
    SetParent(this.Handle, HWND_MESSAGE);
}

Try that :

[DllImport("user32.dll")]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);

static IntPtr HWND_MESSAGE = new IntPtr(-3);

protected override void OnHandleCreated(EventArgs e)
{
    base.OnHandleCreated(e);
    SetParent(this.Handle, HWND_MESSAGE);
}
白云悠悠 2024-07-30 00:54:31

我担心您必须从 Form 派生,并强制窗口不可见。

另一种方法(在类库可修改的情况下)是运行没有窗口的消息泵(请参阅 Application.RunApplication.AddMessageFilter,或者如果您更喜欢使用 PeekMessage & Co)。

在这种情况下,您可以使用 PostThreadMessage 发送消息通过拥有运行 Application.Run 的线程 ID,但实际上您无法与应用程序消息泵线程同步,因为它不等待消息确认。

I fear that you must derive from a Form, and force the window invisible.

Another approach (in the case the class library is modifiable) is to run a message pump without a window (see Application.Run and Application.AddMessageFilter, or if you prefer pinvokes using PeekMessage & Co).

In this case you can send messages using PostThreadMessage by having the thread id which as run Application.Run, but actually you cannot synch with the application message pump thread because it doesn't wait message acknowledge.

无力看清 2024-07-30 00:54:31

我相信您还需要指定一个窗口类。

I believe that you'll need to also specify a window class.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文