-1

I am trying to subscribe to window messages using .Net Core

I am able to receive initial messages to create a window ( through pinvoke) and destroy messages. But after that my created windows gets blocked and does not receive any other messages.

 public class CustomWindow : IDisposable
{
    delegate IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);

    [StructLayout(LayoutKind.Sequential,CharSet = CharSet.Unicode)]
    struct WNDCLASS
    {
        public uint style;
        public IntPtr lpfnWndProc;
        public int cbClsExtra;
        public int cbWndExtra;
        public IntPtr hInstance;
        public IntPtr hIcon;
        public IntPtr hCursor;
        public IntPtr hbrBackground;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string lpszMenuName;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string lpszClassName;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct MSG
    {
        public IntPtr hwnd;
        public uint message;
        public IntPtr wParam;
        public IntPtr lParam;
        public uint time;
    }

    [DllImport("user32.dll", SetLastError = true)]
    static extern UInt16 RegisterClassW([In] ref WNDCLASS lpWndClass);

    [DllImport("user32.dll", SetLastError = true)]
    static extern IntPtr CreateWindowExW(
       UInt32 dwExStyle,
       [MarshalAs(UnmanagedType.LPWStr)]string lpClassName,
       [MarshalAs(UnmanagedType.LPWStr)]string lpWindowName,
       UInt32 dwStyle,
       Int32 x,
       Int32 y,
       Int32 nWidth,
       Int32 nHeight,
       IntPtr hWndParent,
       IntPtr hMenu,
       IntPtr hInstance,
       IntPtr lpParam
    );

    [DllImport("user32.dll", SetLastError = true)]
    static extern IntPtr DefWindowProcW(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);

    [DllImport("user32.dll", SetLastError = true)]
    static extern bool DestroyWindow(IntPtr hWnd);

    private const int ERROR_CLASS_ALREADY_EXISTS = 1410;

    private bool _mDisposed;
    public IntPtr Hwnd;

    public List<uint> Messages { get; set; }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    private void Dispose(bool disposing)
    {
        if (!_mDisposed)
        {
            if (disposing)
            {
                // Dispose managed resources
            }

            // Dispose unmanaged resources
            if (Hwnd != IntPtr.Zero)
            {
                DestroyWindow(Hwnd);
                Hwnd = IntPtr.Zero;
            }
        }
    }

    public CustomWindow()
    {
        Messages = new List<uint>();
        var className = "InvisibleWindow";

        _mWndProcDelegate = CustomWndProc;

        // Create WNDCLASS
        WNDCLASS windClass = new WNDCLASS
        {
            lpszClassName = className,
            lpfnWndProc = Marshal.GetFunctionPointerForDelegate(_mWndProcDelegate)
        };

        UInt16 classAtom = RegisterClassW(ref windClass);

        int lastError = Marshal.GetLastWin32Error();

        if (classAtom == 0 && lastError != ERROR_CLASS_ALREADY_EXISTS)
        {
            throw new Exception("Could not register window class");
        }

        const UInt32 WS_OVERLAPPEDWINDOW = 0xcf0000;
        const UInt32 WS_VISIBLE = 0x10000000;


        // Create window
        Hwnd = CreateWindowExW(
            0,
            className,
            "My WIndow",
            WS_OVERLAPPEDWINDOW | WS_VISIBLE, 0, 0, 300, 400,
            IntPtr.Zero,
            IntPtr.Zero,
            IntPtr.Zero,
            IntPtr.Zero
        );

        Importer.ShowWindow(Hwnd, 1);
        Importer.UpdateWindow(Hwnd);
    }

    private  IntPtr CustomWndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
    {
        Messages.Add(msg);
        return DefWindowProcW(hWnd, msg, wParam, lParam);
    }

    private WndProc _mWndProcDelegate;
}

Class to create a custom window CustomWndProc receives messages. I am not performing any logic there yet, just trying to get things working.

I am trying to figure out how I could do this from a separate thread, to keep listening to messages while also not blocking main api/gui/console interface

Can I create this class in separate thread and still use dependency injection and have access to its data/messages/events on a whim. I know there are many ways of doing this in .net, but this is .net Core.

What's up with downvotes, there are literally no resources how to implement this in Net Core, I have no access to any of the .Net forms/controls that simplify this, I've also provided full working class, If there is lack of details in my post, leave a comment. Don't down vote people randomly without a good reason. If there is another thread that explains how to do it, link it and then downvote this one.

 private void GetMessage()
    {
       IntPtr hwnd = _customWindow.Hwnd;
        int bRet;
        while ((bRet = Importer.GetMessage(out var msg, _customWindow.GetHandle, 0, 0)) !=0 )
        {
            if (bRet == -1)
            {
                Console.WriteLine("Error");
            }
            else
            {
                Importer.TranslateMessage(ref msg);
                Importer.DispatchMessage(ref msg);
                Console.WriteLine(_customWindow.Messages.LastOrDefault());
            }
        }
    }

Implemented GetMessage Loop, my window now receives all messages and does not get blocked, but it now blocks main thread. I'll see if I can create window on separate thread and run message loop on that thread.

Aistis Taraskevicius
  • 781
  • 2
  • 10
  • 31
  • You need to code the message loop yourself (GetMessage/DispatchMessage): https://en.wikipedia.org/wiki/Message_loop_in_Microsoft_Windows do you have done that? – Simon Mourier May 03 '18 at 17:10
  • @SimonMourier it's exactly what I am asking, how do I go about doing that – Aistis Taraskevicius May 04 '18 at 08:38
  • You must p/invoke these Windows API methods, you must mimic a Windows app, just like you did with CreateWindow, etc. – Simon Mourier May 04 '18 at 08:42
  • @SimonMourier Do i use mimic getMessage in main thread? That works in c++ gui applications, since thats how gui actions are executed. But in in .Net Core that would lock execution of its own interface. How do I make sure that getMessage loop is always listening, but so is my .Net Core api/UI – Aistis Taraskevicius May 04 '18 at 08:49
  • Do you have a full reproducing project so we can test this? – Simon Mourier May 04 '18 at 09:12
  • @SimonMourier not one that's easy to share, but basically what I am currently doing to test is to create CustomWindow object through DI ( but standard way should suffice) and call _customWindow.Messages.ForEach(Console.WriteLine); You will see 4 messages or so from window creation. Window that will be shown will be locket and not tab-able to, also will not receive messages its subscribing to. Once _customWindow.Dispose(); is called , it will receive messages to destroy and calling show messages again, will show extra messages. But nothing in between creation and deletion. – Aistis Taraskevicius May 04 '18 at 09:25

1 Answers1

1

Eventually managed to find a working solution.

While in C++ created window works in its own thread automatically without any input, and is able to handle its own message queue. Also because its primarily GUI, that's how input is received anyway.

In .Net you cannot assign objects to a specific thread. Thus even if you start GetMessage Loop in new thread or task, window itself will be blocked, due the fact that its in main thread. To overcome this, run C++ Window and GetMessage loop in main Thread and run your console/Gui/APi in second thread.

Aistis Taraskevicius
  • 781
  • 2
  • 10
  • 31