support show icon

This commit is contained in:
Lightczx
2024-05-10 17:30:03 +08:00
parent be223909d3
commit e8d3a065e6
24 changed files with 469 additions and 40 deletions

View File

@@ -12,6 +12,7 @@ internal static class SettingKeys
{
#region MainWindow
public const string WindowRect = "WindowRect";
public const string GuideWindowRect = "GuideWindowRect";
public const string IsNavPaneOpen = "IsNavPaneOpen";
public const string IsInfoBarToggleChecked = "IsInfoBarToggleChecked";
public const string ExcludedAnnouncementIds = "ExcludedAnnouncementIds";

View File

@@ -0,0 +1,54 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Win32.UI.WindowsAndMessaging;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Windows.Storage;
using static Snap.Hutao.Win32.ConstValues;
namespace Snap.Hutao.Core.Windowing.NotifyIcon;
internal sealed class NotifyIconController : IDisposable
{
private readonly NotifyIconMessageWindow messageWindow;
private readonly System.Drawing.Icon icon;
public NotifyIconController()
{
messageWindow = new();
NotifyIconMethods.Delete(Id);
StorageFile iconFile = StorageFile.GetFileFromApplicationUriAsync("ms-appx:///Assets/Logo.ico".ToUri()).AsTask().GetAwaiter().GetResult();
icon = new(iconFile.Path);
if (!NotifyIconMethods.Add(Id, messageWindow.HWND, "Snap Hutao", NotifyIconMessageWindow.WM_NOTIFYICON_CALLBACK, (HICON)icon.Handle))
{
HutaoException.InvalidOperation("Failed to create NotifyIcon");
}
if (!NotifyIconMethods.SetVersion(Id, NOTIFYICON_VERSION_4))
{
HutaoException.InvalidOperation("Failed to set NotifyIcon version");
}
}
private static ref readonly Guid Id
{
get
{
// MD5 for "Snap.Hutao"
ReadOnlySpan<byte> data = [0xEE, 0x01, 0x5C, 0xCB, 0xF3, 0x97, 0xC6, 0x93, 0xE8, 0x77, 0xCE, 0x09, 0x54, 0x90, 0xEE, 0xAC];
return ref Unsafe.As<byte, Guid>(ref MemoryMarshal.GetReference(data));
}
}
public void Dispose()
{
messageWindow.Dispose();
NotifyIconMethods.Delete(Id);
icon.Dispose();
}
}

View File

@@ -0,0 +1,116 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Win32.Foundation;
using Snap.Hutao.Win32.UI.WindowsAndMessaging;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using static Snap.Hutao.Win32.User32;
namespace Snap.Hutao.Core.Windowing.NotifyIcon;
[SuppressMessage("", "SA1310")]
internal sealed class NotifyIconMessageWindow : IDisposable
{
public const uint WM_NOTIFYICON_CALLBACK = 0x444U;
private const string WindowClassName = "SnapHutaoNotifyIconMessageWindowClass";
private static readonly ConcurrentDictionary<HWND, NotifyIconMessageWindow> WindowTable = [];
public readonly HWND HWND;
[SuppressMessage("", "SA1306")]
private uint WM_TASKBARCREATED;
private bool isDisposed;
public unsafe NotifyIconMessageWindow()
{
ushort atom;
fixed (char* className = WindowClassName)
{
WNDCLASSW wc = new()
{
lpfnWndProc = WNDPROC.Create(&OnWindowProcedure),
lpszClassName = className,
};
atom = RegisterClassW(&wc);
}
ArgumentOutOfRangeException.ThrowIfEqual<ushort>(atom, 0);
// https://learn.microsoft.com/zh,cn/windows/win32/shell/taskbar#taskbar,creation,notification
WM_TASKBARCREATED = RegisterWindowMessageW("TaskbarCreated");
HWND = CreateWindowExW(0, WindowClassName, WindowClassName, 0, 0, 0, 0, 0, default, default, default, default);
if (HWND == default)
{
Marshal.ThrowExceptionForHR(Marshal.GetLastPInvokeError());
}
WindowTable.TryAdd(HWND, this);
}
~NotifyIconMessageWindow()
{
Dispose();
}
public void Dispose()
{
if (isDisposed)
{
return;
}
isDisposed = true;
DestroyWindow(HWND);
WindowTable.TryRemove(HWND, out _);
GC.SuppressFinalize(this);
}
[SuppressMessage("", "SH002")]
[UnmanagedCallersOnly(CallConvs = [typeof(CallConvStdcall)])]
private static unsafe LRESULT OnWindowProcedure(HWND hwnd, uint uMsg, WPARAM wParam, LPARAM lParam)
{
if (WindowTable.TryGetValue(hwnd, out NotifyIconMessageWindow? window))
{
if (uMsg == window.WM_TASKBARCREATED)
{
// TODO: Re-add the notify icon.
}
// https://learn.microsoft.com/zh-cn/windows/win32/api/shellapi/ns-shellapi-notifyicondataw
if (uMsg == WM_NOTIFYICON_CALLBACK)
{
LPARAM2 lParam2 = *(LPARAM2*)&lParam;
WPARAM2 wParam2 = *(WPARAM2*)&wParam;
Debug.WriteLine($"[uMsg: 0x{uMsg:X8}] [X: {wParam2.X} Y: {wParam2.Y}] [Low: 0x{lParam2.Low:X8} High: 0x{lParam2.High:X8}]");
switch (lParam.Value)
{
}
}
}
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}
private readonly struct LPARAM2
{
public readonly uint Low;
public readonly uint High;
}
private readonly struct WPARAM2
{
public readonly ushort X;
public readonly ushort Y;
}
}

View File

@@ -0,0 +1,75 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Win32.Foundation;
using Snap.Hutao.Win32.UI.Shell;
using Snap.Hutao.Win32.UI.WindowsAndMessaging;
using static Snap.Hutao.Win32.Shell32;
namespace Snap.Hutao.Core.Windowing.NotifyIcon;
internal sealed class NotifyIconMethods
{
public static BOOL Add(ref readonly NOTIFYICONDATAW data)
{
return Shell_NotifyIconW(NOTIFY_ICON_MESSAGE.NIM_ADD, in data);
}
[SuppressMessage("", "SH002")]
public static unsafe BOOL Add(Guid id, HWND hWnd, string tip, uint uCallbackMessage, HICON hIcon)
{
NOTIFYICONDATAW data = default;
data.cbSize = (uint)sizeof(NOTIFYICONDATAW);
data.uFlags = NOTIFY_ICON_DATA_FLAGS.NIF_MESSAGE | NOTIFY_ICON_DATA_FLAGS.NIF_ICON | NOTIFY_ICON_DATA_FLAGS.NIF_TIP | NOTIFY_ICON_DATA_FLAGS.NIF_GUID;
data.guidItem = id;
data.hWnd = hWnd;
tip.AsSpan().CopyTo(new(data.szTip, 128));
data.uCallbackMessage = uCallbackMessage;
data.hIcon = hIcon;
data.dwState = NOTIFY_ICON_STATE.NIS_HIDDEN;
data.dwStateMask = NOTIFY_ICON_STATE.NIS_HIDDEN;
return Add(in data);
}
public static BOOL Modify(ref readonly NOTIFYICONDATAW data)
{
return Shell_NotifyIconW(NOTIFY_ICON_MESSAGE.NIM_MODIFY, in data);
}
public static BOOL Delete(ref readonly NOTIFYICONDATAW data)
{
return Shell_NotifyIconW(NOTIFY_ICON_MESSAGE.NIM_DELETE, in data);
}
public static unsafe BOOL Delete(Guid id)
{
NOTIFYICONDATAW data = default;
data.cbSize = (uint)sizeof(NOTIFYICONDATAW);
data.uFlags = NOTIFY_ICON_DATA_FLAGS.NIF_GUID;
data.guidItem = id;
return Delete(in data);
}
public static BOOL SetFocus(ref readonly NOTIFYICONDATAW data)
{
return Shell_NotifyIconW(NOTIFY_ICON_MESSAGE.NIM_SETFOCUS, in data);
}
public static BOOL SetVersion(ref readonly NOTIFYICONDATAW data)
{
return Shell_NotifyIconW(NOTIFY_ICON_MESSAGE.NIM_SETVERSION, in data);
}
public static unsafe BOOL SetVersion(Guid id, uint version)
{
NOTIFYICONDATAW data = default;
data.cbSize = (uint)sizeof(NOTIFYICONDATAW);
data.uFlags = NOTIFY_ICON_DATA_FLAGS.NIF_GUID;
data.guidItem = id;
data.Anonymous.uVersion = version;
return SetVersion(in data);
}
}

View File

@@ -89,9 +89,9 @@ internal sealed class WindowController
SizeInt32 scaledSize = options.InitSize.Scale(scale);
RectInt32 rect = StructMarshal.RectInt32(scaledSize);
if (options.PersistSize)
if (!string.IsNullOrEmpty(options.PersistRectKey))
{
RectInt32 persistedRect = (CompactRect)LocalSetting.Get(SettingKeys.WindowRect, (CompactRect)rect);
RectInt32 persistedRect = (CompactRect)LocalSetting.Get(options.PersistRectKey, (CompactRect)rect);
if (persistedRect.Size() >= options.InitSize.Size())
{
rect = persistedRect.Scale(scale);
@@ -104,7 +104,7 @@ internal sealed class WindowController
private void SaveOrSkipWindowSize()
{
if (!options.PersistSize)
if (string.IsNullOrEmpty(options.PersistRectKey))
{
return;
}
@@ -116,7 +116,7 @@ internal sealed class WindowController
if (!windowPlacement.ShowCmd.HasFlag(SHOW_WINDOW_CMD.SW_SHOWMAXIMIZED))
{
double scale = 1.0 / options.GetRasterizationScale();
LocalSetting.Set(SettingKeys.WindowRect, (CompactRect)window.AppWindow.GetRect().Scale(scale));
LocalSetting.Set(options.PersistRectKey, (CompactRect)window.AppWindow.GetRect().Scale(scale));
}
}

View File

@@ -38,15 +38,18 @@ internal readonly struct WindowOptions
/// <summary>
/// 是否持久化尺寸
/// </summary>
[Obsolete]
public readonly bool PersistSize;
public WindowOptions(Window window, FrameworkElement titleBar, SizeInt32 initSize, bool persistSize = false)
public readonly string? PersistRectKey;
public WindowOptions(Window window, FrameworkElement titleBar, SizeInt32 initSize, string? persistSize = default)
{
Hwnd = WindowNative.GetWindowHandle(window);
InputNonClientPointerSource = InputNonClientPointerSource.GetForWindowId(window.AppWindow.Id);
TitleBar = titleBar;
InitSize = initSize;
PersistSize = persistSize;
PersistRectKey = persistSize;
}
/// <summary>

View File

@@ -4,9 +4,12 @@
using Microsoft.UI.Xaml;
using Snap.Hutao.Core.Windowing.Backdrop;
using Snap.Hutao.Core.Windowing.HotKey;
using Snap.Hutao.Win32;
using Snap.Hutao.Win32.Foundation;
using Snap.Hutao.Win32.UI.Shell;
using Snap.Hutao.Win32.UI.WindowsAndMessaging;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using static Snap.Hutao.Win32.ComCtl32;
using static Snap.Hutao.Win32.ConstValues;
@@ -27,6 +30,7 @@ internal sealed class WindowSubclass : IDisposable
// We have to explicitly hold a reference to SUBCLASSPROC
private SUBCLASSPROC windowProc = default!;
private UnmanagedAccess<WindowSubclass> unmanagedAccess = default!;
public WindowSubclass(Window window, in WindowOptions options, IServiceProvider serviceProvider)
{
@@ -41,10 +45,11 @@ internal sealed class WindowSubclass : IDisposable
/// 尝试设置窗体子类
/// </summary>
/// <returns>是否设置成功</returns>
public bool Initialize()
public unsafe bool Initialize()
{
windowProc = OnSubclassProcedure;
bool windowHooked = SetWindowSubclass(options.Hwnd, windowProc, WindowSubclassId, 0);
windowProc = SUBCLASSPROC.Create(&OnSubclassProcedure);
unmanagedAccess = UnmanagedAccess.Create(this);
bool windowHooked = SetWindowSubclass(options.Hwnd, windowProc, WindowSubclassId, unmanagedAccess);
hotKeyController.RegisterAll();
return windowHooked;
@@ -57,18 +62,23 @@ internal sealed class WindowSubclass : IDisposable
RemoveWindowSubclass(options.Hwnd, windowProc, WindowSubclassId);
windowProc = default!;
unmanagedAccess.Dispose();
}
[SuppressMessage("", "SH002")]
private unsafe LRESULT OnSubclassProcedure(HWND hwnd, uint uMsg, WPARAM wParam, LPARAM lParam, nuint uIdSubclass, nuint dwRefData)
[UnmanagedCallersOnly(CallConvs = [typeof(CallConvStdcall)])]
private static unsafe LRESULT OnSubclassProcedure(HWND hwnd, uint uMsg, WPARAM wParam, LPARAM lParam, nuint uIdSubclass, nuint dwRefData)
{
WindowSubclass? state = UnmanagedAccess.Get<WindowSubclass>(dwRefData);
ArgumentNullException.ThrowIfNull(state);
switch (uMsg)
{
case WM_GETMINMAXINFO:
{
if (window is IMinMaxInfoHandler handler)
if (state.window is IMinMaxInfoHandler handler)
{
handler.HandleMinMaxInfo(ref *(MINMAXINFO*)lParam, options.GetRasterizationScale());
handler.HandleMinMaxInfo(ref *(MINMAXINFO*)lParam, state.options.GetRasterizationScale());
}
break;
@@ -82,13 +92,13 @@ internal sealed class WindowSubclass : IDisposable
case WM_HOTKEY:
{
hotKeyController.OnHotKeyPressed(*(HotKeyParameter*)&lParam);
state.hotKeyController.OnHotKeyPressed(*(HotKeyParameter*)&lParam);
break;
}
case WM_ERASEBKGND:
{
if (window.SystemBackdrop is IBackdropNeedEraseBackground)
if (state.window.SystemBackdrop is IBackdropNeedEraseBackground)
{
return (LRESULT)(int)BOOL.TRUE;
}

View File

@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using Microsoft.UI.Xaml;
using Snap.Hutao.Core.Setting;
using Snap.Hutao.Core.Windowing;
using Snap.Hutao.Win32.UI.WindowsAndMessaging;
@@ -24,7 +25,7 @@ internal sealed partial class GuideWindow : Window, IWindowOptionsSource, IMinMa
public GuideWindow(IServiceProvider serviceProvider)
{
InitializeComponent();
windowOptions = new(this, DragableGrid, new(MinWidth, MinHeight));
windowOptions = new(this, DragableGrid, new(MinWidth, MinHeight), SettingKeys.GuideWindowRect);
this.InitializeController(serviceProvider);
}

View File

@@ -3,6 +3,7 @@
using Microsoft.UI.Xaml;
using Snap.Hutao.Control.Extension;
using Snap.Hutao.Core.Setting;
using Snap.Hutao.Core.Windowing;
using Snap.Hutao.ViewModel.Game;
using Snap.Hutao.Win32.UI.WindowsAndMessaging;

View File

@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using Microsoft.UI.Xaml;
using Snap.Hutao.Core.Setting;
using Snap.Hutao.Core.Windowing;
using Snap.Hutao.Win32.UI.WindowsAndMessaging;
@@ -27,7 +28,7 @@ internal sealed partial class MainWindow : Window, IWindowOptionsSource, IMinMax
public MainWindow(IServiceProvider serviceProvider)
{
InitializeComponent();
windowOptions = new(this, TitleBarView.DragArea, new(1200, 741), true);
windowOptions = new(this, TitleBarView.DragArea, new(1200, 741), SettingKeys.WindowRect);
this.InitializeController(serviceProvider);
}

View File

@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using Microsoft.UI.Xaml;
using Snap.Hutao.Core.Windowing.NotifyIcon;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using WinRT;
@@ -49,9 +50,12 @@ public static partial class Program
// By adding the using statement, we can dispose the injected services when we closing
using (ServiceProvider serviceProvider = DependencyInjection.Initialize())
{
// In a Desktop app this runs a message pump internally,
// and does not return until the application shuts down.
Application.Start(AppInitializationCallback);
using (NotifyIconController notifyIconController = new())
{
// In a Desktop app this runs a message pump internally,
// and does not return until the application shuts down.
Application.Start(AppInitializationCallback);
}
}
}

View File

@@ -14,7 +14,7 @@ namespace Snap.Hutao.Win32;
[SuppressMessage("", "SYSLIB1054")]
internal static class AdvApi32
{
[DllImport("ADVAPI32.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
[DllImport("ADVAPI32.dll", CallingConvention = CallingConvention.Winapi, ExactSpelling = true, SetLastError = true)]
[SupportedOSPlatform("windows5.1.2600")]
public static unsafe extern BOOL ConvertSidToStringSidW(PSID Sid, PWSTR* StringSid);
@@ -26,7 +26,7 @@ internal static class AdvApi32
}
}
[DllImport("ADVAPI32.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
[DllImport("ADVAPI32.dll", CallingConvention = CallingConvention.Winapi, ExactSpelling = true, SetLastError = true)]
[SupportedOSPlatform("windows5.1.2600")]
public static unsafe extern BOOL ConvertStringSidToSidW(PCWSTR StringSid, PSID* Sid);
@@ -53,7 +53,7 @@ internal static class AdvApi32
[SupportedOSPlatform("windows5.0")]
public static extern WIN32_ERROR RegNotifyChangeKeyValue(HKEY hKey, BOOL bWatchSubtree, REG_NOTIFY_FILTER dwNotifyFilter, [AllowNull] HANDLE hEvent, BOOL fAsynchronous);
[DllImport("ADVAPI32.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode, ExactSpelling = true)]
[DllImport("ADVAPI32.dll", CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
[SupportedOSPlatform("windows5.0")]
public static unsafe extern WIN32_ERROR RegOpenKeyExW(HKEY hKey, [AllowNull] PCWSTR lpSubKey, [AllowNull] uint ulOptions, REG_SAM_FLAGS samDesired, HKEY* phkResult);

View File

@@ -18,9 +18,9 @@ internal static class ComCtl32
[DllImport("COMCTL32.dll", CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
[SupportedOSPlatform("windows5.1.2600")]
public static extern BOOL RemoveWindowSubclass(HWND hWnd, [MarshalAs(UnmanagedType.FunctionPtr)] SUBCLASSPROC pfnSubclass, nuint uIdSubclass);
public static extern BOOL RemoveWindowSubclass(HWND hWnd, SUBCLASSPROC pfnSubclass, nuint uIdSubclass);
[DllImport("COMCTL32.dll", CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
[SupportedOSPlatform("windows5.1.2600")]
public static unsafe extern BOOL SetWindowSubclass(HWND hWnd, [MarshalAs(UnmanagedType.FunctionPtr)] SUBCLASSPROC pfnSubclass, nuint uIdSubclass, nuint dwRefData);
public static unsafe extern BOOL SetWindowSubclass(HWND hWnd, SUBCLASSPROC pfnSubclass, nuint uIdSubclass, nuint dwRefData);
}

View File

@@ -7,6 +7,8 @@ namespace Snap.Hutao.Win32;
internal static class ConstValues
{
public const uint D3D11_SDK_VERSION = 0x00000007U;
public const uint NOTIFYICON_VERSION = 0x00000003U;
public const uint NOTIFYICON_VERSION_4 = 0x00000004U;
public const uint WM_NULL = 0x00000000U;
public const uint WM_ERASEBKGND = 0x00000014U;
public const uint WM_GETMINMAXINFO = 0x00000024U;

View File

@@ -7,8 +7,6 @@ internal readonly struct HWND
{
public readonly nint Value;
public HWND(nint value) => Value = value;
public static unsafe implicit operator HWND(nint value) => *(HWND*)&value;
public static unsafe implicit operator nint(HWND value) => *(nint*)&value;

View File

@@ -22,7 +22,7 @@ internal static class Kernel32
[SupportedOSPlatform("windows5.0")]
public static extern BOOL CloseHandle(HANDLE hObject);
[DllImport("KERNEL32.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
[DllImport("KERNEL32.dll", CallingConvention = CallingConvention.Winapi, ExactSpelling = true, SetLastError = true)]
[SupportedOSPlatform("windows5.1.2600")]
public static unsafe extern HANDLE CreateEventW([AllowNull] SECURITY_ATTRIBUTES* lpEventAttributes, BOOL bManualReset, BOOL bInitialState, [AllowNull] PCWSTR lpName);
@@ -72,7 +72,7 @@ internal static class Kernel32
}
}
[DllImport("KERNEL32.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode, ExactSpelling = true)]
[DllImport("KERNEL32.dll", CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern uint K32GetModuleBaseNameW(HANDLE hProcess, [AllowNull] HMODULE hModule, PWSTR lpBaseName, uint nSize);
[DebuggerStepThrough]
@@ -128,7 +128,7 @@ internal static class Kernel32
[DllImport("KERNEL32.dll", CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern BOOL SetConsoleMode(HANDLE hConsoleHandle, CONSOLE_MODE dwMode);
[DllImport("KERNEL32.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
[DllImport("KERNEL32.dll", CallingConvention = CallingConvention.Winapi, ExactSpelling = true, SetLastError = true)]
public static extern BOOL SetConsoleTitleW(PCWSTR lpConsoleTitle);
[DebuggerStepThrough]

View File

@@ -6,5 +6,17 @@ using System.Runtime.InteropServices;
namespace Snap.Hutao.Win32.UI.Shell;
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
internal delegate LRESULT SUBCLASSPROC(HWND hWnd, uint uMsg, WPARAM wParam, LPARAM lParam, nuint uIdSubclass, nuint dwRefData);
internal unsafe readonly struct SUBCLASSPROC
{
private readonly delegate* unmanaged[Stdcall]<HWND, uint, WPARAM, LPARAM, nuint, nuint, LRESULT> value;
public SUBCLASSPROC(delegate* unmanaged[Stdcall]<HWND, uint, WPARAM, LPARAM, nuint, nuint, LRESULT> method)
{
value = method;
}
public static SUBCLASSPROC Create(delegate* unmanaged[Stdcall]<HWND, uint, WPARAM, LPARAM, nuint, nuint, LRESULT> method)
{
return new(method);
}
}

View File

@@ -8,4 +8,8 @@ namespace Snap.Hutao.Win32.UI.WindowsAndMessaging;
internal readonly struct HICON
{
public readonly nint Value;
public static unsafe implicit operator HICON(nint value) => *(HICON*)&value;
public static unsafe implicit operator nint(HICON value) => *(nint*)&value;
}

View File

@@ -0,0 +1,11 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Win32.UI.WindowsAndMessaging;
// RAIIFree: DestroyMenu
// InvalidHandleValue: -1, 0
internal readonly struct HMENU
{
public readonly nint Value;
}

View File

@@ -0,0 +1,38 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Win32.UI.WindowsAndMessaging;
[Flags]
[SuppressMessage("", "CA1069")]
internal enum WINDOW_STYLE : uint
{
WS_OVERLAPPED = 0x0U,
WS_POPUP = 0x80000000U,
WS_CHILD = 0x40000000U,
WS_MINIMIZE = 0x20000000U,
WS_VISIBLE = 0x10000000U,
WS_DISABLED = 0x8000000U,
WS_CLIPSIBLINGS = 0x4000000U,
WS_CLIPCHILDREN = 0x2000000U,
WS_MAXIMIZE = 0x1000000U,
WS_CAPTION = 0xC00000U,
WS_BORDER = 0x800000U,
WS_DLGFRAME = 0x400000U,
WS_VSCROLL = 0x200000U,
WS_HSCROLL = 0x100000U,
WS_SYSMENU = 0x80000U,
WS_THICKFRAME = 0x40000U,
WS_GROUP = 0x20000U,
WS_TABSTOP = 0x10000U,
WS_MINIMIZEBOX = 0x20000U,
WS_MAXIMIZEBOX = 0x10000U,
WS_TILED = 0x0U,
WS_ICONIC = 0x20000000U,
WS_SIZEBOX = 0x40000U,
WS_TILEDWINDOW = 0xCF0000U,
WS_OVERLAPPEDWINDOW = 0xCF0000U,
WS_POPUPWINDOW = 0x80880000U,
WS_CHILDWINDOW = 0x40000000U,
WS_ACTIVECAPTION = 0x1U,
}

View File

@@ -10,8 +10,6 @@ namespace Snap.Hutao.Win32.UI.WindowsAndMessaging;
internal struct WNDCLASSW
{
public WNDCLASS_STYLES style;
[MarshalAs(UnmanagedType.FunctionPtr)]
public WNDPROC lpfnWndProc;
public int cbClsExtra;
public int cbWndExtra;

View File

@@ -2,9 +2,20 @@
// Licensed under the MIT license.
using Snap.Hutao.Win32.Foundation;
using System.Runtime.InteropServices;
namespace Snap.Hutao.Win32.UI.WindowsAndMessaging;
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
internal delegate LRESULT WNDPROC(HWND param0, uint param1, WPARAM param2, LPARAM param3);
internal unsafe readonly struct WNDPROC
{
private readonly delegate* unmanaged[Stdcall]<HWND, uint, WPARAM, LPARAM, LRESULT> value;
public WNDPROC(delegate* unmanaged[Stdcall]<HWND, uint, WPARAM, LPARAM, LRESULT> method)
{
value = method;
}
public static WNDPROC Create(delegate* unmanaged[Stdcall]<HWND, uint, WPARAM, LPARAM, LRESULT> method)
{
return new(method);
}
}

View File

@@ -0,0 +1,53 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using System.Runtime.InteropServices;
namespace Snap.Hutao.Win32;
internal readonly struct UnmanagedAccess<T> : IDisposable
where T : class
{
private readonly nint handle;
public UnmanagedAccess(T value)
{
handle = GCHandle.ToIntPtr(GCHandle.Alloc(value));
}
public static implicit operator nint(UnmanagedAccess<T> access)
{
return access.handle;
}
public static implicit operator nuint(UnmanagedAccess<T> access)
{
return (nuint)access.handle;
}
public void Dispose()
{
GCHandle.FromIntPtr(handle).Free();
}
}
internal static class UnmanagedAccess
{
public static UnmanagedAccess<T> Create<T>(T value)
where T : class
{
return new UnmanagedAccess<T>(value);
}
public static T? Get<T>(nint handle)
where T : class
{
return GCHandle.FromIntPtr(handle).Target as T;
}
public static T? Get<T>(nuint handle)
where T : class
{
return GCHandle.FromIntPtr((nint)handle).Target as T;
}
}

View File

@@ -12,6 +12,7 @@ using System.Runtime.Versioning;
namespace Snap.Hutao.Win32;
[SuppressMessage("", "SH002")]
[SuppressMessage("", "SA1313")]
[SuppressMessage("", "SYSLIB1054")]
internal static class User32
{
@@ -19,7 +20,30 @@ internal static class User32
[SupportedOSPlatform("windows5.1.2600")]
public static extern BOOL AttachThreadInput(uint idAttach, uint idAttachTo, BOOL fAttach);
[DllImport("USER32.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
[DllImport("USER32.dll", ExactSpelling = true, SetLastError = true)]
[SupportedOSPlatform("windows5.0")]
public static unsafe extern HWND CreateWindowExW(WINDOW_EX_STYLE dwExStyle, [AllowNull] PCWSTR lpClassName, [AllowNull] PCWSTR lpWindowName, WINDOW_STYLE dwStyle, int X, int Y, int nWidth, int nHeight, [AllowNull] HWND hWndParent, [AllowNull] HMENU hMenu, [AllowNull] HINSTANCE hInstance, [AllowNull] void* lpParam);
public static unsafe HWND CreateWindowExW(WINDOW_EX_STYLE dwExStyle, [AllowNull] string className, [AllowNull] string windowName, WINDOW_STYLE dwStyle, int X, int Y, int nWidth, int nHeight, [AllowNull] HWND hWndParent, [AllowNull] HMENU hMenu, [AllowNull] HINSTANCE hInstance, [AllowNull] void* lpParam)
{
fixed (char* lpClassName = className)
{
fixed (char* lpWindowName = windowName)
{
return CreateWindowExW(dwExStyle, lpClassName, lpWindowName, dwStyle, X, Y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam);
}
}
}
[DllImport("USER32.dll", CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
[SupportedOSPlatform("windows5.0")]
public static extern LRESULT DefWindowProcW(HWND hWnd, uint Msg, WPARAM wParam, LPARAM lParam);
[DllImport("USER32.dll", CallingConvention = CallingConvention.Winapi, ExactSpelling = true, SetLastError = true)]
[SupportedOSPlatform("windows5.0")]
public static extern BOOL DestroyWindow(HWND hWnd);
[DllImport("USER32.dll", CallingConvention = CallingConvention.Winapi, ExactSpelling = true, SetLastError = true)]
[SupportedOSPlatform("windows5.0")]
public static extern HWND FindWindowExW([AllowNull] HWND hWndParent, [AllowNull] HWND hWndChildAfter, [AllowNull] PCWSTR lpszClass, [AllowNull] PCWSTR lpszWindow);
@@ -47,7 +71,7 @@ internal static class User32
[SupportedOSPlatform("windows5.0")]
public static extern HWND GetForegroundWindow();
[DllImport("USER32.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
[DllImport("USER32.dll", CallingConvention = CallingConvention.Winapi, ExactSpelling = true, SetLastError = true)]
[SupportedOSPlatform("windows5.0")]
public static extern nint GetWindowLongPtrW(HWND hWnd, WINDOW_LONG_PTR_INDEX nIndex);
@@ -93,6 +117,18 @@ internal static class User32
[SupportedOSPlatform("windows6.0.6000")]
public static extern BOOL RegisterHotKey([AllowNull] HWND hWnd, int id, HOT_KEY_MODIFIERS fsModifiers, uint vk);
[DllImport("USER32.dll", CallingConvention = CallingConvention.Winapi, ExactSpelling = true, SetLastError = true)]
[SupportedOSPlatform("windows5.0")]
public static extern uint RegisterWindowMessageW(PCWSTR lpString);
public static unsafe uint RegisterWindowMessageW(string @string)
{
fixed (char* lpString = @string)
{
return RegisterWindowMessageW(lpString);
}
}
[DllImport("USER32.dll", CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
[SupportedOSPlatform("windows5.0")]
public static extern int ReleaseDC([AllowNull] HWND hWnd, HDC hDC);
@@ -127,7 +163,7 @@ internal static class User32
[SupportedOSPlatform("windows5.0")]
public static extern BOOL SetForegroundWindow(HWND hWnd);
[DllImport("USER32.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
[DllImport("USER32.dll", CallingConvention = CallingConvention.Winapi, ExactSpelling = true, SetLastError = true)]
[SupportedOSPlatform("windows5.0")]
public static extern BOOL SetPropW(HWND hWnd, PCWSTR lpString, [AllowNull] HANDLE hData);
@@ -140,7 +176,7 @@ internal static class User32
}
}
[DllImport("USER32.dll", CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
[DllImport("USER32.dll", CallingConvention = CallingConvention.Winapi, ExactSpelling = true, SetLastError = true)]
[SupportedOSPlatform("windows5.0")]
public static extern nint SetWindowLongPtrW(HWND hWnd, WINDOW_LONG_PTR_INDEX nIndex, nint dwNewLong);