Handle windows message in Maui Windows

Денис Полагаев 0 Reputation points

It is necessary to process the messages sent via the channel. In WPF, the code for processing messages is as follows:

public partial class MainWindow: Window 
    public MainWindow() {

      this.SourceInitialized += new EventHandler(OnSourceInitialized);
    void OnSourceInitialized(object sender, EventArgs e) {
      HwndSource source = (HwndSource) PresentationSource.FromVisual(this);
      source.AddHook(new HwndSourceHook(HandleMessages));
    private static IntPtr HandleMessages(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) {
      //Handle messages here
      if (msg == 49900) {

      return System.IntPtr.Zero;

The message arrives, I process it. Everything works.

I'm trying to do the same in Maui for Windows. Analogues of AddHook and so on.. there is no, I do it through the Windows API.

[DllImport("user32.dll", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
static extern IntPtr SetWindowsHookEx(int hookType, HookProc lpfn, IntPtr hMod, IntPtr dwThreadId);

private static IntPtr SetHook(HookTypes typeOfHook, HookProc callBack, bool onThread = true) 
  //IntPtr hInstance = LoadLibrary("User32");
  ProcessModule module = Process.GetCurrentProcess().MainModule;
  IntPtr hModule = GetModuleHandle(module.ModuleName);

  using(Process currentProcess = Process.GetCurrentProcess())
  using(ProcessModule currentModule = currentProcess.MainModule) {
    if (onThread)
      return SetWindowsHookEx((int) typeOfHook, callBack, 0, GetCurrentThreadId());
      return SetWindowsHookEx((int) typeOfHook, callBack, hModule, 0);
    //return SetWindowsHookEx((int)typeOfHook, callBack, GetModuleHandle(currentModule.ModuleName), 0);

private static IntPtr HookCallback(IntPtr hwnd, uint message, IntPtr wParam, IntPtr lParam)
  // Handle
  if (hwnd.ToInt32() == 49900) {
    Debug.WriteLine($ "{hwnd}{message} - {lParam} - {wParam}");


  return IntPtr.Zero;

public static void RegisterWindowMessage(string lpString, Action < string > action) 
  _action = action;
  _msg = RegisterWindowMessage(lpString.ToString());

  HoockProc = new HookProc(HookCallback);
              ProcessModule module = Process.GetCurrentProcess().MainModule;
              IntPtr hModule = GetModuleHandle(module.ModuleName);*/

  MainThread.BeginInvokeOnMainThread(() => {
    var WH_MSGFILTER = SetHook(HookTypes.WH_MSGFILTER, HoockProc);
    var WH_CALLWNDPROC = SetHook(HookTypes.WH_CALLWNDPROC, HoockProc);
    var WH_CALLWNDPROCRET = SetHook(HookTypes.WH_CALLWNDPROCRET, HoockProc);
    var WH_KEYBOARD = SetHook(HookTypes.WH_KEYBOARD, HoockProc);
    var WH_KEYBOARD_LL = SetHook(HookTypes.WH_KEYBOARD_LL, HoockProc, false);
    var WH_MOUSE = SetHook(HookTypes.WH_MOUSE, HoockProc);
    var WH_MOUSE_LL = SetHook(HookTypes.WH_MOUSE_LL, HoockProc, false);
    var WH_JOURNALRECORD = SetHook(HookTypes.WH_JOURNALRECORD, HoockProc, false); //
    var WH_JOURNALPLAYBACK = SetHook(HookTypes.WH_JOURNALPLAYBACK, HoockProc, false); //
    var WH_FOREGROUNDIDLE = SetHook(HookTypes.WH_FOREGROUNDIDLE, HoockProc);
    var WH_SYSMSGFILTER = SetHook(HookTypes.WH_SYSMSGFILTER, HoockProc, true); //
    var WH_GETMESSAGE = SetHook(HookTypes.WH_GETMESSAGE, HoockProc);
    var WH_CBT = SetHook(HookTypes.WH_CBT, HoockProc);
    var WH_HARDWARE = SetHook(HookTypes.WH_HARDWARE, HoockProc);
    var WH_DEBUG = SetHook(HookTypes.WH_DEBUG, HoockProc);
    var WH_SHELL = SetHook(HookTypes.WH_SHELL, HoockProc);


IntPtr for these hooks is set:
User's image

I don't get a reply to the message. What I'm doing wrong? I take the correct window descriptor, I checked it through Spy++

Windows API - Win32
Windows API - Win32
A core set of Windows application programming interfaces (APIs) for desktop and server applications. Previously known as Win32 API.
2,443 questions
A Microsoft open-source framework for building native device applications spanning mobile, tablet, and desktop.
2,968 questions
{count} votes

1 answer

Sort by: Most helpful
  1. Денис Полагаев 0 Reputation points

    I solved this with WinAPI by creating an empty invisible window inside Maui Blazor.


    internal class Win32
            public const uint WM_DESTROY = 0x0002;
            public const uint WM_CREATE = 0x0001;
            public const uint WM_PAINT = 0x000F;
            public const uint WS_OVERLAPPED = 0x00000000;
            public const uint WS_MAXIMIZEBOX = 0x00010000;
            public const uint WS_MINIMIZEBOX = 0x00020000;
            public const uint WS_THICKFRAME = 0x00040000;
            public const uint WS_SYSMENU = 0x00080000;
            public const uint WS_CAPTION = 0x00C00000;
            public const uint WS_VISIBLE = 0x10000000;
            public const uint IDI_APPLICATION = 32512;
            public const uint IDC_ARROW = 32512;
            public const uint COLOR_WINDOW = 5;
            public const uint MB_OK = 0x00000000;
            public const uint MB_ICONEXCLAMATION = 0x00000030;
            public const uint MB_SETFOREGROUND = 0x00010000;
            //public const uint CW_USEDEFAULT = 0x80000000;
            public delegate IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
    		public enum GetWindowLongConst
    			GWL_WNDPROC = (-4),
    			GWL_HINSTANCE = (-6),
    			GWL_HWNDPARENT = (-8),
    			GWL_STYLE = (-16),
    			GWL_EXSTYLE = (-20),
    			GWL_USERDATA = (-21),
    			GWL_ID = (-12)
    		 * */
            public enum ClassStyles : uint
                ByteAlignClient = 0x1000,
                ByteAlignWindow = 0x2000,
                ClassDC = 0x40,
                DoubleClicks = 0x8,
                DropShadow = 0x20000,
                GlobalClass = 0x4000,
                HorizontalRedraw = 0x2,
                NoClose = 0x200,
                OwnDC = 0x20,
                ParentDC = 0x80,
                SaveBits = 0x800,
                VerticalRedraw = 0x1
            public enum MessageBoxResult : uint
                Ok = 1,
                Timeout = 32000
            public struct POINT
                public int X;
                public int Y;
                public POINT(int x, int y)
                    X = x;
                    Y = y;
            public struct SIZE
                public int cx;
                public int cy;
                public SIZE(int cx, int cy)
                    this.cx = cx;
                    this.cy = cy;
            public struct MSG
                public IntPtr hwnd;
                public uint message;
                public IntPtr wParam;
                public IntPtr lParam;
                public uint time;
                public POINT pt;
            public struct PAINTSTRUCT
                public IntPtr hdc;
                public bool fErase;
                public RECT rcPaint;
                public bool fRestore;
                public bool fIncUpdate;
                [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
                public byte[] rgbReserved;
            public struct RECT
                public int left, top, right, bottom;
            public struct WNDCLASSEX
                public uint cbSize;
                public ClassStyles style;
                public WndProc lpfnWndProc;
                public int cbClsExtra;
                public int cbWndExtra;
                public IntPtr hInstance;
                public IntPtr hIcon;
                public IntPtr hCursor;
                public IntPtr hbrBackground;
                public string lpszMenuName;
                public string lpszClassName;
                public IntPtr hIconSm;
            public static extern sbyte GetMessage(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin,
               uint wMsgFilterMax);
            public static extern IntPtr DispatchMessage(ref MSG lpmsg);
            public static extern bool TranslateMessage(ref MSG lpMsg);
            //[DllImport("user32.dll", EntryPoint = "GetWindowLong")]		// 32 bits only
            //public static extern IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex);
            public static extern IntPtr BeginPaint(IntPtr hwnd, out PAINTSTRUCT lpPaint);
            public static extern bool EndPaint(IntPtr hWnd, ref PAINTSTRUCT lpPaint);
            public static extern void PostQuitMessage(int nExitCode);
            public static extern IntPtr DefWindowProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntPtr lParam);
            [DllImport("user32.dll", SetLastError = true)]
            public static extern IntPtr GetDC(IntPtr hWnd);
            public static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC);
            public static extern IntPtr LoadCursor(IntPtr hInstance, int lpCursorName);
            public static extern IntPtr LoadIcon(IntPtr hInstance, IntPtr lpIconName);
            [DllImport("user32.dll", CharSet = CharSet.Unicode)]
            public static extern MessageBoxResult MessageBox(IntPtr hWnd, string text, string caption, int options);
            [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            public static extern IntPtr CreateWindowEx(
               uint dwExStyle,
               string lpClassName,
               string lpWindowName,
               uint dwStyle,
               int x,
               int y,
               int nWidth,
               int nHeight,
               IntPtr hWndParent,
               IntPtr hMenu,
               IntPtr hInstance,
               IntPtr lpParam);
            [DllImport("user32.dll", SetLastError = true)]
            public static extern short RegisterClassEx(ref WNDCLASSEX lpwcx);
            //public static extern bool GetTextExtentPoint(IntPtr hdc, string lpString, int cbString, out SIZE lpSize);
            [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            public static extern uint RegisterWindowMessage(string lpString);
            [DllImport("gdi32.dll", CharSet = CharSet.Unicode)]
            public static extern bool TextOut(IntPtr hdc, int nXStart, int nYStart,
               string lpString, int cbString);
            //[DllImport("gdi32.dll", CharSet = CharSet.Auto)]
            //public static extern bool GetTextMetrics(IntPtr hdc, out TEXTMETRIC lptm);
        internal static class WindowExtensions
            #region Private static fields
            private static Action<nint> _action;
            private static uint _msgId;
            private static readonly string AppName = "DrawHello Program";
            private static readonly string ClassName = "DrawHelloClass";
            private static IntPtr hWnd;
            #region Public methods
            public static void RegisterWindowMessage(string lpString, Action<nint> action)
                _action = action;
                _msgId = Win32.RegisterWindowMessage(lpString.ToString());
                WNDCLASSEX wcex = new()
                    style = ClassStyles.DoubleClicks
                wcex.cbSize = (uint)Marshal.SizeOf(wcex);
                wcex.lpfnWndProc = WndProc;
                wcex.cbClsExtra = 0;
                wcex.cbWndExtra = 0;
                //wcex.hIcon = Win32.LoadIcon(IntPtr.Zero, (IntPtr)Win32.IDI_APPLICATION);
                //wcex.hCursor = Win32.LoadCursor(IntPtr.Zero, (int)Win32.IDC_ARROW);
                wcex.hIconSm = IntPtr.Zero;
                //wcex.hbrBackground = (IntPtr)(Win32.COLOR_WINDOW + 1);
                wcex.lpszMenuName = null;
                wcex.lpszClassName = ClassName;
                if (RegisterClassEx(ref wcex) == 0)
                    _ = MessageBox(IntPtr.Zero, "RegisterClassEx failed", AppName,
                        (int)(MB_OK | MB_ICONEXCLAMATION | MB_SETFOREGROUND));
                //uint visible = Win32.WS_OVERLAPPEDWINDOW | Win32.WS_VISIBLE;
                uint invisible = 0;
                var x = 0; // 250;
                var y = 0; // 250;
                var w = 0; // 250;
                var h = 0; // 250;
                hWnd = CreateWindowEx(0, ClassName, AppName, invisible,
                    x, y, w, h, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
                if (hWnd == IntPtr.Zero)
                    _ = MessageBox(IntPtr.Zero, "CreateWindow failed", AppName, (int)(Win32.MB_OK | Win32.MB_ICONEXCLAMATION | Win32.MB_SETFOREGROUND));
                // Main message loop:
                /*int rv;
                while ((rv = GetMessage(out MSG Msg, IntPtr.Zero, 0, 0)) > 0)
                    _ = Win32.TranslateMessage(ref Msg);
                    _ = Win32.DispatchMessage(ref Msg);
            private static IntPtr WndProc(IntPtr hWnd, uint message, IntPtr wParam, IntPtr lParam)
                if (message == _msgId)
                switch (message)
                    case WM_DESTROY:
                        return IntPtr.Zero;
                        return DefWindowProc(hWnd, message, wParam, lParam);