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
}