Last active
September 10, 2022 11:11
-
-
Save 0xF6/d1f21ce173da491e4a681eded5537cf7 to your computer and use it in GitHub Desktop.
native splash screen when using native aot without wpf or win forms
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.ComponentModel; | |
using static NativeApi; | |
public class SplashScreen : IDisposable | |
{ | |
private ushort _wndClass; | |
private IntPtr _hwnd; | |
private const string CLASS_NAME = "SplashScreen"; | |
private Bitmap _bitmap; | |
private static HandleRef nullHandle = new HandleRef(null, IntPtr.Zero); | |
private WndProc? proc; | |
public SplashScreen(FileInfo bitmapFile) | |
{ | |
if (!bitmapFile.Exists) | |
throw new FileNotFoundException($"File '{bitmapFile.FullName}' is not found"); | |
_bitmap = (Bitmap)Bitmap.FromFile(bitmapFile.FullName); | |
} | |
public void Show(bool topmost) | |
{ | |
if (isDisposed) | |
throw new ObjectDisposedException(""); | |
_hwnd = CreateWindow(_bitmap.GetHbitmap(), _bitmap.Width, _bitmap.Height, topmost); | |
} | |
public void Hide() | |
=> Dispose(); | |
private nint CreateWindow(nint hBitmap, int width, int height, bool topMost) | |
{ | |
proc = new WndProc(DefWindowProc); | |
var wndClass = new WNDCLASSEX(); | |
wndClass.cbSize = 80; | |
wndClass.style = 3; /* CS_HREDRAW | CS_VREDRAW */ | |
wndClass.hInstance = IntPtr.Zero; | |
wndClass.hCursor = IntPtr.Zero; | |
wndClass.lpszClassName = CLASS_NAME; | |
wndClass.lpszMenuName = string.Empty; | |
wndClass.lpfnWndProc = proc; | |
_wndClass = IntRegisterClassEx(wndClass); | |
var screenWidth = GetSystemMetrics(SystemMetric.SM_CXSCREEN); | |
var screenHeight = GetSystemMetrics(SystemMetric.SM_CYSCREEN); | |
var x = (screenWidth - width) / 2; | |
var y = (screenHeight - height) / 2; | |
var windowCreateFlags = | |
WS_EX_WINDOWEDGE | | |
WS_EX_TOOLWINDOW | | |
WS_EX_LAYERED | | |
(topMost ? WS_EX_TOPMOST : 0); | |
var hWnd = CreateWindowEx( | |
windowCreateFlags, | |
CLASS_NAME, "", | |
unchecked((int)( WindowStyles.WS_POPUP | WindowStyles.WS_VISIBLE)), | |
x, y, width, height, | |
nullHandle, nullHandle, /*new HandleRef(null, _hInstance)*/ nullHandle, IntPtr.Zero); | |
var hScreenDC = GetDC(new HandleRef()); | |
var memDC = CreateCompatibleDC(new HandleRef(null, hScreenDC)); | |
var hOldBitmap = SelectObject(new HandleRef(null, memDC), hBitmap); | |
var newSize = new NativeApi.Size(width, height); | |
var newLocation = new NativeApi.Point(x, y); | |
var sourceLocation = new NativeApi.Point(0, 0); | |
var _blendFunc = new BLENDFUNCTION(); | |
_blendFunc.BlendOp = AC_SRC_OVER; | |
_blendFunc.BlendFlags = 0; | |
_blendFunc.SourceConstantAlpha = 255; | |
_blendFunc.AlphaFormat = 1; /*AC_SRC_ALPHA*/ | |
var result = UpdateLayeredWindow(hWnd, hScreenDC, ref newLocation, ref newSize, | |
memDC, ref sourceLocation, 0, ref _blendFunc, ULW_ALPHA); | |
SelectObject(new HandleRef(null, memDC), hOldBitmap); | |
ReleaseDC(new HandleRef(), new HandleRef(null, memDC)); | |
ReleaseDC(new HandleRef(), new HandleRef(null, hScreenDC)); | |
if (result == Bool.False) | |
throw new Win32Exception(Marshal.GetHRForLastWin32Error()); | |
return hWnd; | |
} | |
private void ReleaseUnmanagedResources() | |
{ | |
SetActiveWindow(new HandleRef(null, _hwnd)); | |
IntDestroyWindow(new HandleRef(null, _hwnd)); | |
IntUnregisterClass(new IntPtr(_wndClass), IntPtr.Zero); | |
} | |
private void Dispose(bool disposing) | |
{ | |
ReleaseUnmanagedResources(); | |
if (disposing) _bitmap.Dispose(); | |
isDisposed = true; | |
} | |
private bool isDisposed = false; | |
public void Dispose() | |
{ | |
Dispose(true); | |
GC.SuppressFinalize(this); | |
} | |
~SplashScreen() | |
=> Dispose(false); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class NativeApi | |
{ | |
public enum Bool: int | |
{ | |
@False = 0, | |
@True = 1 | |
} | |
public struct Point | |
{ | |
public int x; | |
public int y; | |
public Point(int x, int y) | |
{ | |
this.x = x; | |
this.y = y; | |
} | |
} | |
public struct Size | |
{ | |
public int cx; | |
public int cy; | |
public Size(int cx, int cy) | |
{ | |
this.cx = cx; | |
this.cy = cy; | |
} | |
} | |
public enum Protection | |
{ | |
PAGE_NOACCESS = 0x01, | |
PAGE_READONLY = 0x02, | |
PAGE_READWRITE = 0x04, | |
PAGE_WRITECOPY = 0x08, | |
PAGE_EXECUTE = 0x10, | |
PAGE_EXECUTE_READ = 0x20, | |
PAGE_EXECUTE_READWRITE = 0x40, | |
PAGE_EXECUTE_WRITECOPY = 0x80, | |
PAGE_GUARD = 0x100, | |
PAGE_NOCACHE = 0x200, | |
PAGE_WRITECOMBINE = 0x400 | |
} | |
public struct BLENDFUNCTION | |
{ | |
public byte BlendOp; | |
public byte BlendFlags; | |
public byte SourceConstantAlpha; | |
public byte AlphaFormat; | |
} | |
public const int AC_SRC_OVER = 0x00000000; | |
public const int ULW_COLORKEY = 0x00000001; | |
public const int ULW_ALPHA = 0x00000002; | |
public const int ULW_OPAQUE = 0x00000004; | |
public const byte AC_SRC_ALPHA = 1; | |
public const int WS_POPUP = unchecked((int)0x80000000); | |
public const int WS_EX_LAYERED = 0x00080000, | |
WS_EX_WINDOWEDGE = 0x00000100, | |
WS_EX_TOOLWINDOW = 0x00000080, | |
WS_EX_TOPMOST = 0x00000008; | |
[DllImport("kernel32.dll")] | |
internal static extern bool FlushInstructionCache(IntPtr hProcess, IntPtr lpBaseAddress, | |
UIntPtr dwSize); | |
[DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)] | |
internal static extern IntPtr GetProcAddress(IntPtr hModule, string procName); | |
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Ansi)] | |
internal static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)]string lpFileName); | |
[DllImport("kernel32.dll")] | |
internal static extern void Sleep(uint dwMilliseconds); | |
[DllImport("kernel32.dll", SetLastError = true)] | |
internal static extern bool VirtualProtect(IntPtr lpAddress, uint dwSize, | |
Protection flNewProtect, out Protection lpflOldProtect); | |
[DllImport("user32.dll", CharSet = CharSet.Unicode, EntryPoint = "DefWindowProcW")] | |
public static extern IntPtr DefWindowProc(IntPtr hWnd, Int32 Msg, IntPtr wParam, IntPtr lParam); | |
public delegate IntPtr WndProc(IntPtr hWnd, Int32 msg, IntPtr wParam, IntPtr lParam); | |
[DllImport("user32.dll", EntryPoint="RegisterClassEx", CharSet=CharSet.Unicode, SetLastError=true, BestFitMapping=false)] | |
internal static extern UInt16 IntRegisterClassEx(WNDCLASSEX wc_d); | |
[DllImport("user32.dll", SetLastError = true)] | |
public static extern IntPtr SetActiveWindow(HandleRef hWnd); | |
[DllImport("user32.dll", SetLastError = true, EntryPoint="DestroyWindow", CharSet=CharSet.Auto)] | |
public static extern bool IntDestroyWindow(HandleRef hWnd); | |
[DllImport("user32.dll", EntryPoint="UnregisterClass",CharSet = CharSet.Auto, SetLastError = true, BestFitMapping=false)] | |
internal static extern int IntUnregisterClass(IntPtr atomString /*lpClassName*/ , IntPtr hInstance); | |
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] | |
public struct WNDCLASSEX | |
{ | |
[MarshalAs(UnmanagedType.U4)] | |
public int cbSize = 80; | |
[MarshalAs(UnmanagedType.U4)] | |
public int style; | |
public WndProc lpfnWndProc; // not WndProc | |
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 WNDCLASSEX() | |
{ | |
} | |
} | |
[DllImport("user32.dll")] | |
[return: MarshalAs(UnmanagedType.U2)] | |
static extern short RegisterClassEx([In] ref WNDCLASSEX lpwcx); | |
[DllImport("user32.dll")] | |
public extern static Bool UpdateLayeredWindow(IntPtr handle, IntPtr hdcDst, ref Point pptDst, ref Size psize, IntPtr hdcSrc, ref Point pprSrc, int crKey, ref BLENDFUNCTION pblend, int dwFlags); | |
[DllImport("user32.dll")] | |
public extern static IntPtr GetDC(HandleRef handle); | |
[DllImport("user32.dll", ExactSpelling=true)] | |
public extern static int ReleaseDC(HandleRef handle, HandleRef hDC); | |
[DllImport("gdi32.dll")] | |
public extern static IntPtr CreateCompatibleDC(HandleRef hDC); | |
[DllImport("gdi32.dll")] | |
public extern static Bool DeleteDC(HandleRef hdc); | |
[DllImport("gdi32.dll")] | |
public extern static IntPtr SelectObject(HandleRef hDC, IntPtr hObject); | |
[DllImport("gdi32.dll")] | |
public extern static Bool DeleteObject(HandleRef hObject); | |
[DllImport("user32.dll", EntryPoint = "CreateWindowEx", CharSet = CharSet.Unicode)] | |
internal static extern IntPtr CreateWindowEx(int dwExStyle, | |
string lpszClassName, | |
string lpszWindowName, | |
int style, | |
int x, int y, | |
int width, int height, | |
HandleRef hwndParent, | |
HandleRef hMenu, | |
HandleRef hInst, | |
[MarshalAs(UnmanagedType.AsAny)] object pvParam); | |
[DllImport("user32.dll")] | |
public static extern int GetSystemMetrics(SystemMetric smIndex); | |
public enum SystemMetric | |
{ | |
SM_CXSCREEN = 0, // 0x00 | |
SM_CYSCREEN = 1, // 0x01 | |
SM_CXVSCROLL = 2, // 0x02 | |
SM_CYHSCROLL = 3, // 0x03 | |
SM_CYCAPTION = 4, // 0x04 | |
SM_CXBORDER = 5, // 0x05 | |
SM_CYBORDER = 6, // 0x06 | |
SM_CXDLGFRAME = 7, // 0x07 | |
SM_CXFIXEDFRAME = 7, // 0x07 | |
SM_CYDLGFRAME = 8, // 0x08 | |
SM_CYFIXEDFRAME = 8, // 0x08 | |
SM_CYVTHUMB = 9, // 0x09 | |
SM_CXHTHUMB = 10, // 0x0A | |
SM_CXICON = 11, // 0x0B | |
SM_CYICON = 12, // 0x0C | |
SM_CXCURSOR = 13, // 0x0D | |
SM_CYCURSOR = 14, // 0x0E | |
SM_CYMENU = 15, // 0x0F | |
SM_CXFULLSCREEN = 16, // 0x10 | |
SM_CYFULLSCREEN = 17, // 0x11 | |
SM_CYKANJIWINDOW = 18, // 0x12 | |
SM_MOUSEPRESENT = 19, // 0x13 | |
SM_CYVSCROLL = 20, // 0x14 | |
SM_CXHSCROLL = 21, // 0x15 | |
SM_DEBUG = 22, // 0x16 | |
SM_SWAPBUTTON = 23, // 0x17 | |
SM_CXMIN = 28, // 0x1C | |
SM_CYMIN = 29, // 0x1D | |
SM_CXSIZE = 30, // 0x1E | |
SM_CYSIZE = 31, // 0x1F | |
SM_CXSIZEFRAME = 32, // 0x20 | |
SM_CXFRAME = 32, // 0x20 | |
SM_CYSIZEFRAME = 33, // 0x21 | |
SM_CYFRAME = 33, // 0x21 | |
SM_CXMINTRACK = 34, // 0x22 | |
SM_CYMINTRACK = 35, // 0x23 | |
SM_CXDOUBLECLK = 36, // 0x24 | |
SM_CYDOUBLECLK = 37, // 0x25 | |
SM_CXICONSPACING = 38, // 0x26 | |
SM_CYICONSPACING = 39, // 0x27 | |
SM_MENUDROPALIGNMENT = 40, // 0x28 | |
SM_PENWINDOWS = 41, // 0x29 | |
SM_DBCSENABLED = 42, // 0x2A | |
SM_CMOUSEBUTTONS = 43, // 0x2B | |
SM_SECURE = 44, // 0x2C | |
SM_CXEDGE = 45, // 0x2D | |
SM_CYEDGE = 46, // 0x2E | |
SM_CXMINSPACING = 47, // 0x2F | |
SM_CYMINSPACING = 48, // 0x30 | |
SM_CXSMICON = 49, // 0x31 | |
SM_CYSMICON = 50, // 0x32 | |
SM_CYSMCAPTION = 51, // 0x33 | |
SM_CXSMSIZE = 52, // 0x34 | |
SM_CYSMSIZE = 53, // 0x35 | |
SM_CXMENUSIZE = 54, // 0x36 | |
SM_CYMENUSIZE = 55, // 0x37 | |
SM_ARRANGE = 56, // 0x38 | |
SM_CXMINIMIZED = 57, // 0x39 | |
SM_CYMINIMIZED = 58, // 0x3A | |
SM_CXMAXTRACK = 59, // 0x3B | |
SM_CYMAXTRACK = 60, // 0x3C | |
SM_CXMAXIMIZED = 61, // 0x3D | |
SM_CYMAXIMIZED = 62, // 0x3E | |
SM_NETWORK = 63, // 0x3F | |
SM_CLEANBOOT = 67, // 0x43 | |
SM_CXDRAG = 68, // 0x44 | |
SM_CYDRAG = 69, // 0x45 | |
SM_SHOWSOUNDS = 70, // 0x46 | |
SM_CXMENUCHECK = 71, // 0x47 | |
SM_CYMENUCHECK = 72, // 0x48 | |
SM_SLOWMACHINE = 73, // 0x49 | |
SM_MIDEASTENABLED = 74, // 0x4A | |
SM_MOUSEWHEELPRESENT = 75, // 0x4B | |
SM_XVIRTUALSCREEN = 76, // 0x4C | |
SM_YVIRTUALSCREEN = 77, // 0x4D | |
SM_CXVIRTUALSCREEN = 78, // 0x4E | |
SM_CYVIRTUALSCREEN = 79, // 0x4F | |
SM_CMONITORS = 80, // 0x50 | |
SM_SAMEDISPLAYFORMAT = 81, // 0x51 | |
SM_IMMENABLED = 82, // 0x52 | |
SM_CXFOCUSBORDER = 83, // 0x53 | |
SM_CYFOCUSBORDER = 84, // 0x54 | |
SM_TABLETPC = 86, // 0x56 | |
SM_MEDIACENTER = 87, // 0x57 | |
SM_STARTER = 88, // 0x58 | |
SM_SERVERR2 = 89, // 0x59 | |
SM_MOUSEHORIZONTALWHEELPRESENT = 91, // 0x5B | |
SM_CXPADDEDBORDER = 92, // 0x5C | |
SM_DIGITIZER = 94, // 0x5E | |
SM_MAXIMUMTOUCHES = 95, // 0x5F | |
SM_REMOTESESSION = 0x1000, // 0x1000 | |
SM_SHUTTINGDOWN = 0x2000, // 0x2000 | |
SM_REMOTECONTROL = 0x2001, // 0x2001 | |
SM_CONVERTIBLESLATEMODE = 0x2003, | |
SM_SYSTEMDOCKED = 0x2004, | |
} | |
[Flags] | |
public enum WindowStyles : uint | |
{ | |
WS_BORDER = 0x800000, | |
WS_CAPTION = 0xc00000, | |
WS_CHILD = 0x40000000, | |
WS_CLIPCHILDREN = 0x2000000, | |
WS_CLIPSIBLINGS = 0x4000000, | |
WS_DISABLED = 0x8000000, | |
WS_DLGFRAME = 0x400000, | |
WS_GROUP = 0x20000, | |
WS_HSCROLL = 0x100000, | |
WS_MAXIMIZE = 0x1000000, | |
WS_MAXIMIZEBOX = 0x10000, | |
WS_MINIMIZE = 0x20000000, | |
WS_MINIMIZEBOX = 0x20000, | |
WS_OVERLAPPED = 0x0, | |
WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_SIZEFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX, | |
WS_POPUP = 0x80000000u, | |
WS_POPUPWINDOW = WS_POPUP | WS_BORDER | WS_SYSMENU, | |
WS_SIZEFRAME = 0x40000, | |
WS_SYSMENU = 0x80000, | |
WS_TABSTOP = 0x10000, | |
WS_VISIBLE = 0x10000000, | |
WS_VSCROLL = 0x200000 | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment