How do I create a message-only window from windows forms?
Asked Answered
M

4

5

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?

Macfadyn answered 1/6, 2009 at 16:31 Comment(1)
None of the answers given worked for me - in the end I had to use C++/CLI and delve into the Windows API. I'd post the code I used, but it belongs to a former employer.Macfadyn
U
4

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();
        }
    }
Uprising answered 15/12, 2016 at 20:43 Comment(0)
G
3

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);
}
Garcia answered 1/6, 2009 at 20:19 Comment(3)
ATTENTION: You must set ShowInTaskbar = false. ATTENTION: A message-only window is useless if you want to send messages from another process because despite the MSDN says that a message-only window should be found with FindWindowEx, it is not.Brothers
@Elmue, it's still useful if you want to receive broadcasted messagesGarcia
You should read the MSDN: A message-only window enables you to send and receive messages. It is not visible, has no z-order, cannot be enumerated, and does not receive broadcast messages.Brothers
W
1

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.

Winfordwinfred answered 2/8, 2010 at 10:35 Comment(0)
S
0

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

Selvage answered 1/6, 2009 at 16:40 Comment(1)
what should I set it to? I've tried "Message" and the name of the class, but neither worked. "Message" gives me the same error, and the class name gives me "Window class name is not valid".Macfadyn

© 2022 - 2024 — McMap. All rights reserved.