Handle windows message in Maui Windows

Денис Полагаев 0 Reputation points
2023-04-18T02:09:36.0333333+00:00

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() {
      InitializeComponent();

      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) {
        Debug.WriteLine(wParam);
        Debug.WriteLine(lParam);
      }

      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());
    else
      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}");
  }

  //_action?.Invoke(message);

  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
.NET MAUI
.NET MAUI
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
    2023-04-21T16:38:24.55+00:00

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

    Code:

    
    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 WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;
    
            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)
    		}
    		 * */
    
            [Flags]
            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,
                Cancel,
                Abort,
                Retry,
                Ignore,
                Yes,
                No,
                Close,
                Help,
                TryAgain,
                Continue,
                Timeout = 32000
            }
    
            [StructLayout(LayoutKind.Sequential)]
            public struct POINT
            {
                public int X;
                public int Y;
    
                public POINT(int x, int y)
                {
                    X = x;
                    Y = y;
                }
            }
    
            [StructLayout(LayoutKind.Sequential)]
            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;
            }
    
            [StructLayout(LayoutKind.Sequential)]
            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;
            }
    
            [StructLayout(LayoutKind.Sequential)]
            public struct RECT
            {
                public int left, top, right, bottom;
            }
    
            [StructLayout(LayoutKind.Sequential)]
            public struct WNDCLASSEX
            {
                public uint cbSize;
                public ClassStyles style;
                [MarshalAs(UnmanagedType.FunctionPtr)]
                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;
            }
    
            [DllImport("user32.dll")]
            public static extern sbyte GetMessage(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin,
               uint wMsgFilterMax);
    
            [DllImport("user32.dll")]
            public static extern IntPtr DispatchMessage(ref MSG lpmsg);
    
            [DllImport("user32.dll")]
            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);
    
            [DllImport("user32.dll")]
            public static extern IntPtr BeginPaint(IntPtr hwnd, out PAINTSTRUCT lpPaint);
    
            [DllImport("user32.dll")]
            public static extern bool EndPaint(IntPtr hWnd, ref PAINTSTRUCT lpPaint);
    
            [DllImport("user32.dll")]
            public static extern void PostQuitMessage(int nExitCode);
    
            [DllImport("user32.dll")]
            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);
    
            [DllImport("user32.dll")]
            public static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC);
    
            [DllImport("user32.dll")]
            public static extern IntPtr LoadCursor(IntPtr hInstance, int lpCursorName);
    
            [DllImport("user32.dll")]
            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);
    
            //[DllImport("gdi.dll")]
            //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;
    
            #endregion
    
            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));
                    return;
                }
    
                //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));
                    return;
                }
    
                // 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)
                {
                    _action?.Invoke(lParam);
                }
                switch (message)
                {
                    case WM_DESTROY:
                        PostQuitMessage(0);
                        return IntPtr.Zero;
                    default:
                        return DefWindowProc(hWnd, message, wParam, lParam);
                }
            }
    
            #endregion
    
    
        }