diff --git a/.gitignore b/.gitignore
index 13219c40..d0650bd4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,3 +13,6 @@ src/Snap.Hutao/Snap.Hutao/Snap.Hutao_TemporaryKey.pfx
src/Snap.Hutao/Snap.Hutao.SourceGeneration/bin/
src/Snap.Hutao/Snap.Hutao.SourceGeneration/obj/
+
+src/Snap.Hutao/Snap.Hutao.Win32/bin/
+src/Snap.Hutao/Snap.Hutao.Win32/obj/
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao.Win32/NativeMethods.json b/src/Snap.Hutao/Snap.Hutao.Win32/NativeMethods.json
new file mode 100644
index 00000000..1694d869
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao.Win32/NativeMethods.json
@@ -0,0 +1,5 @@
+{
+ "$schema": "https://aka.ms/CsWin32.schema.json",
+ "allowMarshaling": true,
+ "public": true
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao.Win32/NativeMethods.txt b/src/Snap.Hutao/Snap.Hutao.Win32/NativeMethods.txt
new file mode 100644
index 00000000..b128e994
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao.Win32/NativeMethods.txt
@@ -0,0 +1,16 @@
+// ComCtl32
+SetWindowSubclass
+RemoveWindowSubclass
+DefSubclassProc
+
+// User32
+SetWindowText
+GetDpiForWindow
+GetWindowPlacement
+SetWindowPlacement
+
+// Type definition
+MINMAXINFO
+
+// Const value
+WM_GETMINMAXINFO
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao.Win32/Snap.Hutao.Win32.csproj b/src/Snap.Hutao/Snap.Hutao.Win32/Snap.Hutao.Win32.csproj
new file mode 100644
index 00000000..0d9fe8b5
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao.Win32/Snap.Hutao.Win32.csproj
@@ -0,0 +1,15 @@
+
+
+
+ net6.0
+ enable
+ enable
+
+
+
+
+ all
+
+
+
+
diff --git a/src/Snap.Hutao/Snap.Hutao.sln b/src/Snap.Hutao/Snap.Hutao.sln
index c5c47ce6..77abf5cc 100644
--- a/src/Snap.Hutao/Snap.Hutao.sln
+++ b/src/Snap.Hutao/Snap.Hutao.sln
@@ -14,6 +14,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SettingsUI", "SettingsUI\Se
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Snap.Hutao.SourceGeneration", "Snap.Hutao.SourceGeneration\Snap.Hutao.SourceGeneration.csproj", "{8B96721E-5604-47D2-9B72-06FEBAD0CE00}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Snap.Hutao.Win32", "Snap.Hutao.Win32\Snap.Hutao.Win32.csproj", "{29209B14-A6E1-442E-9287-2C65B03C96CD}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -82,6 +84,22 @@ Global
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Release|x64.Build.0 = Release|x64
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Release|x86.ActiveCfg = Release|Any CPU
{8B96721E-5604-47D2-9B72-06FEBAD0CE00}.Release|x86.Build.0 = Release|Any CPU
+ {29209B14-A6E1-442E-9287-2C65B03C96CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {29209B14-A6E1-442E-9287-2C65B03C96CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {29209B14-A6E1-442E-9287-2C65B03C96CD}.Debug|arm64.ActiveCfg = Debug|Any CPU
+ {29209B14-A6E1-442E-9287-2C65B03C96CD}.Debug|arm64.Build.0 = Debug|Any CPU
+ {29209B14-A6E1-442E-9287-2C65B03C96CD}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {29209B14-A6E1-442E-9287-2C65B03C96CD}.Debug|x64.Build.0 = Debug|Any CPU
+ {29209B14-A6E1-442E-9287-2C65B03C96CD}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {29209B14-A6E1-442E-9287-2C65B03C96CD}.Debug|x86.Build.0 = Debug|Any CPU
+ {29209B14-A6E1-442E-9287-2C65B03C96CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {29209B14-A6E1-442E-9287-2C65B03C96CD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {29209B14-A6E1-442E-9287-2C65B03C96CD}.Release|arm64.ActiveCfg = Release|Any CPU
+ {29209B14-A6E1-442E-9287-2C65B03C96CD}.Release|arm64.Build.0 = Release|Any CPU
+ {29209B14-A6E1-442E-9287-2C65B03C96CD}.Release|x64.ActiveCfg = Release|Any CPU
+ {29209B14-A6E1-442E-9287-2C65B03C96CD}.Release|x64.Build.0 = Release|Any CPU
+ {29209B14-A6E1-442E-9287-2C65B03C96CD}.Release|x86.ActiveCfg = Release|Any CPU
+ {29209B14-A6E1-442E-9287-2C65B03C96CD}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/src/Snap.Hutao/Snap.Hutao/App.xaml.cs b/src/Snap.Hutao/Snap.Hutao/App.xaml.cs
index cf45d67b..84626a97 100644
--- a/src/Snap.Hutao/Snap.Hutao/App.xaml.cs
+++ b/src/Snap.Hutao/Snap.Hutao/App.xaml.cs
@@ -28,7 +28,6 @@ public partial class App : Application
///
public App()
{
- AppInstance.GetCurrent().Activated += OnActivated;
// load app resource
InitializeComponent();
InitializeDependencyInjection();
@@ -79,14 +78,10 @@ public partial class App : Application
AppActivationArguments activatedEventArgs = AppInstance.GetCurrent().GetActivatedEventArgs();
AppInstance firstInstance = AppInstance.FindOrRegisterForKey("main");
- if (!firstInstance.IsCurrent)
- {
- // Redirect the activation (and args) to the "main" instance, and exit.
- await firstInstance.RedirectActivationToAsync(activatedEventArgs);
- Process.GetCurrentProcess().Kill();
- }
- else
+ if (firstInstance.IsCurrent)
{
+ firstInstance.Activated += OnActivated;
+
Window = Ioc.Default.GetRequiredService();
Window.Activate();
@@ -94,10 +89,16 @@ public partial class App : Application
Ioc.Default
.GetRequiredService()
- .As()?
+ .ImplictAs()?
.InitializeInternalAsync()
.SafeForget(logger);
}
+ else
+ {
+ // Redirect the activation (and args) to the "main" instance, and exit.
+ await firstInstance.RedirectActivationToAsync(activatedEventArgs);
+ Process.GetCurrentProcess().Kill();
+ }
}
private static void InitializeDependencyInjection()
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Backdroping/SystemBackdrop.cs b/src/Snap.Hutao/Snap.Hutao/Control/Backdroping/SystemBackdrop.cs
index 42c6e645..cc096891 100644
--- a/src/Snap.Hutao/Snap.Hutao/Control/Backdroping/SystemBackdrop.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Control/Backdroping/SystemBackdrop.cs
@@ -30,12 +30,6 @@ public class SystemBackdrop
this.window = window;
}
- private enum BackDropType
- {
- None,
- Mica,
- }
-
///
/// 尝试设置背景
///
@@ -63,8 +57,7 @@ public class SystemBackdrop
backdropController = new MicaController();
- ICompositionSupportsSystemBackdrop target = window.As();
- backdropController.AddSystemBackdropTarget(target);
+ backdropController.AddSystemBackdropTarget(window.As());
backdropController.SetSystemBackdropConfiguration(configurationSource);
return true;
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Image/CompositionImage.cs b/src/Snap.Hutao/Snap.Hutao/Control/Image/CompositionImage.cs
index 36bae9f7..caef3ac6 100644
--- a/src/Snap.Hutao/Snap.Hutao/Control/Image/CompositionImage.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Control/Image/CompositionImage.cs
@@ -64,18 +64,11 @@ public abstract class CompositionImage : Microsoft.UI.Xaml.Controls.Control
/// 文件
/// 取消令牌
/// 加载的图像表面
- protected virtual async Task LoadImageSurfaceAsync(StorageFile storageFile, CancellationToken token)
+ protected virtual async Task LoadImageSurfaceAsync(StorageFile storageFile, CancellationToken token)
{
- try
+ using (IRandomAccessStream imageStream = await storageFile.OpenAsync(FileAccessMode.Read).AsTask(token))
{
- using (IRandomAccessStream imageStream = await storageFile.OpenAsync(FileAccessMode.Read).AsTask(token))
- {
- return LoadedImageSurface.StartLoadFromStream(imageStream);
- }
- }
- catch (COMException ex) when (ex.Is(COMError.WINCODEC_ERR_COMPONENTNOTFOUND))
- {
- return null;
+ return LoadedImageSurface.StartLoadFromStream(imageStream);
}
}
@@ -130,7 +123,19 @@ public abstract class CompositionImage : Microsoft.UI.Xaml.Controls.Control
Compositor compositor = ElementCompositionPreview.GetElementVisual(this).Compositor;
- if (await LoadImageSurfaceAsync(storageFile, token) is LoadedImageSurface imageSurface)
+ LoadedImageSurface? imageSurface = null;
+
+ try
+ {
+ imageSurface = await LoadImageSurfaceAsync(storageFile, token);
+ }
+ catch (COMException ex) when (ex.Is(COMError.WINCODEC_ERR_COMPONENTNOTFOUND))
+ {
+ // Image is broken, remove it
+ await imageCache.RemoveAsync(uri.Enumerate());
+ }
+
+ if (imageSurface != null)
{
spriteVisual = CompositeSpriteVisual(compositor, imageSurface);
OnUpdateVisual(spriteVisual);
@@ -139,11 +144,6 @@ public abstract class CompositionImage : Microsoft.UI.Xaml.Controls.Control
await AnimationBuilder.Create().Opacity(1d).StartAsync(this, token);
}
- else
- {
- // Image is broken, remove it
- await imageCache.RemoveAsync(uri.Enumerate());
- }
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Control/Image/Gradient.cs b/src/Snap.Hutao/Snap.Hutao/Control/Image/Gradient.cs
index c94d282f..b46d0fc6 100644
--- a/src/Snap.Hutao/Snap.Hutao/Control/Image/Gradient.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Control/Image/Gradient.cs
@@ -30,7 +30,7 @@ public class Gradient : CompositionImage
}
///
- protected override async Task LoadImageSurfaceAsync(StorageFile storageFile, CancellationToken token)
+ protected override async Task LoadImageSurfaceAsync(StorageFile storageFile, CancellationToken token)
{
using (IRandomAccessStream imageStream = await storageFile.OpenAsync(FileAccessMode.Read).AsTask(token))
{
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Win32/POINT.cs b/src/Snap.Hutao/Snap.Hutao/Core/Win32/POINT.cs
deleted file mode 100644
index b69a64c9..00000000
--- a/src/Snap.Hutao/Snap.Hutao/Core/Win32/POINT.cs
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (c) DGP Studio. All rights reserved.
-// Licensed under the MIT license.
-
-using System.Runtime.InteropServices;
-
-namespace Snap.Hutao.Core.Win32;
-
-[SuppressMessage("", "SA1600")]
-[StructLayout(LayoutKind.Sequential)]
-public struct POINT
-{
- public int X;
- public int Y;
-
- public POINT(int x, int y)
- {
- X = x;
- Y = y;
- }
-
- public static implicit operator System.Drawing.Point(POINT p)
- {
- return new System.Drawing.Point(p.X, p.Y);
- }
-
- public static implicit operator POINT(System.Drawing.Point p)
- {
- return new POINT(p.X, p.Y);
- }
-}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Win32/RECT.cs b/src/Snap.Hutao/Snap.Hutao/Core/Win32/RECT.cs
deleted file mode 100644
index 91ae720e..00000000
--- a/src/Snap.Hutao/Snap.Hutao/Core/Win32/RECT.cs
+++ /dev/null
@@ -1,134 +0,0 @@
-// Copyright (c) DGP Studio. All rights reserved.
-// Licensed under the MIT license.
-
-using System.Globalization;
-using System.Runtime.InteropServices;
-
-namespace Snap.Hutao.Core.Win32;
-
-[SuppressMessage("", "SA1132")]
-[SuppressMessage("", "SA1600")]
-[StructLayout(LayoutKind.Sequential)]
-public struct RECT
-{
- public int Left, Top, Right, Bottom;
-
- public RECT(int left, int top, int right, int bottom)
- {
- Left = left;
- Top = top;
- Right = right;
- Bottom = bottom;
- }
-
- public RECT(System.Drawing.Rectangle r)
- : this(r.Left, r.Top, r.Right, r.Bottom)
- {
- }
-
- public int X
- {
- get => Left;
- set
- {
- Right -= Left - value;
- Left = value;
- }
- }
-
- public int Y
- {
- get => Top;
- set
- {
- Bottom -= Top - value;
- Top = value;
- }
- }
-
- public int Height
- {
- get => Bottom - Top;
- set => Bottom = value + Top;
- }
-
- public int Width
- {
- get => Right - Left;
- set => Right = value + Left;
- }
-
- public long Area
- {
- get => Math.BigMul(Width, Height);
- }
-
- public System.Drawing.Point Location
- {
- get => new(Left, Top);
- set
- {
- X = value.X;
- Y = value.Y;
- }
- }
-
- public System.Drawing.Size Size
- {
- get => new(Width, Height);
- set
- {
- Width = value.Width;
- Height = value.Height;
- }
- }
-
- public static implicit operator System.Drawing.Rectangle(RECT r)
- {
- return new System.Drawing.Rectangle(r.Left, r.Top, r.Width, r.Height);
- }
-
- public static implicit operator RECT(System.Drawing.Rectangle r)
- {
- return new RECT(r);
- }
-
- public static bool operator ==(RECT r1, RECT r2)
- {
- return r1.Equals(r2);
- }
-
- public static bool operator !=(RECT r1, RECT r2)
- {
- return !r1.Equals(r2);
- }
-
- public bool Equals(RECT r)
- {
- return r.Left == Left && r.Top == Top && r.Right == Right && r.Bottom == Bottom;
- }
-
- public override bool Equals(object? obj)
- {
- if (obj is RECT rect)
- {
- return Equals(rect);
- }
- else if (obj is System.Drawing.Rectangle rectangle)
- {
- return Equals(new RECT(rectangle));
- }
-
- return false;
- }
-
- public override int GetHashCode()
- {
- return ((System.Drawing.Rectangle)this).GetHashCode();
- }
-
- public override string ToString()
- {
- return string.Format(CultureInfo.CurrentCulture, "{{Left={0},Top={1},Right={2},Bottom={3}}}", Left, Top, Right, Bottom);
- }
-}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Win32/ShowWindowCommand.cs b/src/Snap.Hutao/Snap.Hutao/Core/Win32/ShowWindowCommand.cs
deleted file mode 100644
index a0821434..00000000
--- a/src/Snap.Hutao/Snap.Hutao/Core/Win32/ShowWindowCommand.cs
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright (c) DGP Studio. All rights reserved.
-// Licensed under the MIT license.
-
-namespace Snap.Hutao.Core.Win32;
-
-[SuppressMessage("", "SA1600")]
-public enum ShowWindowCommand
-{
- ///
- /// Hides the window and activates another window.
- ///
- Hide = 0,
-
- ///
- /// Activates and displays a window. If the window is minimized or
- /// maximized, the system restores it to its original size and position.
- /// An application should specify this flag when displaying the window
- /// for the first time.
- ///
- Normal = 1,
-
- ///
- /// Activates the window and displays it as a minimized window.
- ///
- ShowMinimized = 2,
-
- ///
- /// Maximizes the specified window.
- ///
- Maximize = 3, // is this the right value?
-
- ///
- /// Activates the window and displays it as a maximized window.
- ///
- ShowMaximized = 3,
-
- ///
- /// Displays a window in its most recent size and position. This value
- /// is similar to , except
- /// the window is not activated.
- ///
- ShowNoActivate = 4,
-
- ///
- /// Activates the window and displays it in its current size and position.
- ///
- Show = 5,
-
- ///
- /// Minimizes the specified window and activates the next top-level
- /// window in the Z order.
- ///
- Minimize = 6,
-
- ///
- /// Displays the window as a minimized window. This value is similar to
- /// , except the
- /// window is not activated.
- ///
- ShowMinNoActive = 7,
-
- ///
- /// Displays the window in its current size and position. This value is
- /// similar to , except the
- /// window is not activated.
- ///
- ShowNA = 8,
-
- ///
- /// Activates and displays the window. If the window is minimized or
- /// maximized, the system restores it to its original size and position.
- /// An application should specify this flag when restoring a minimized window.
- ///
- Restore = 9,
-
- ///
- /// Sets the show state based on the SW_* value specified in the
- /// STARTUPINFO structure passed to the CreateProcess function by the
- /// program that started the application.
- ///
- ShowDefault = 10,
-
- ///
- /// Windows 2000/XP: Minimizes a window, even if the thread
- /// that owns the window is not responding. This flag should only be
- /// used when minimizing windows from a different thread.
- ///
- ForceMinimize = 11,
-}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Win32/User32.cs b/src/Snap.Hutao/Snap.Hutao/Core/Win32/User32.cs
deleted file mode 100644
index 69ff9d9f..00000000
--- a/src/Snap.Hutao/Snap.Hutao/Core/Win32/User32.cs
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright (c) DGP Studio. All rights reserved.
-// Licensed under the MIT license.
-
-using System.Runtime.InteropServices;
-
-namespace Snap.Hutao.Core.Win32;
-
-///
-/// 包含 user32.dll 平台调用的代码
-///
-internal static class User32
-{
- [SuppressMessage("", "SA1600")]
- [DllImport("user32.dll", SetLastError = true)]
- [return: MarshalAs(UnmanagedType.Bool)]
- public static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl);
-
- ///
- /// Sets the show state and the restored, minimized, and maximized positions of the specified window.
- ///
- ///
- /// A handle to the window.
- ///
- ///
- /// A pointer to a WINDOWPLACEMENT structure that specifies the new show state and window positions.
- ///
- /// Before calling SetWindowPlacement, set the length member of the WINDOWPLACEMENT structure to sizeof(WINDOWPLACEMENT). SetWindowPlacement fails if the length member is not set correctly.
- ///
- ///
- ///
- /// If the function succeeds, the return value is nonzero.
- ///
- /// If the function fails, the return value is zero. To get extended error information, call GetLastError.
- ///
- ///
- [DllImport("user32.dll", SetLastError = true)]
- [return: MarshalAs(UnmanagedType.Bool)]
- public static extern bool SetWindowPlacement(IntPtr hWnd, [In] ref WINDOWPLACEMENT lpwndpl);
-
- ///
- /// Changes the text of the specified window's title bar (if it has one). If the specified window is a control, the
- /// text of the control is changed. However, SetWindowText cannot change the text of a control in another application.
- ///
- /// Go to for more
- /// information
- ///
- ///
- /// C++ ( hWnd [in]. Type: HWND )
A handle to the window or control whose text is to be changed.
- /// C++ ( lpString [in, optional]. Type: LPCTSTR )
The new title or control text.
- ///
- /// If the function succeeds, the return value is nonzero. If the function fails, the return value is zero.
- /// To get extended error information, call GetLastError.
- ///
- ///
- /// If the target window is owned by the current process, causes a WM_SETTEXT message to
- /// be sent to the specified window or control. If the control is a list box control created with the WS_CAPTION style,
- /// however, sets the text for the control, not for the list box entries.
To set the
- /// text of a control in another process, send the WM_SETTEXT message directly instead of calling
- /// . The function does not expand tab characters (ASCII code
- /// 0x09). Tab characters are displayed as vertical bar(|) characters.
For an example go to
- /// Sending a Message.
- ///
- [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
- public static extern bool SetWindowText(IntPtr hwnd, string lpString);
-}
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Win32/WINDOWPLACEMENT.cs b/src/Snap.Hutao/Snap.Hutao/Core/Win32/WINDOWPLACEMENT.cs
deleted file mode 100644
index 0d10be88..00000000
--- a/src/Snap.Hutao/Snap.Hutao/Core/Win32/WINDOWPLACEMENT.cs
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright (c) DGP Studio. All rights reserved.
-// Licensed under the MIT license.
-
-using System.Runtime.InteropServices;
-
-namespace Snap.Hutao.Core.Win32;
-
-///
-/// Contains information about the placement of a window on the screen.
-///
-[Serializable]
-[StructLayout(LayoutKind.Sequential)]
-internal struct WINDOWPLACEMENT
-{
- ///
- /// The length of the structure, in bytes. Before calling the GetWindowPlacement or SetWindowPlacement functions, set this member to sizeof(WINDOWPLACEMENT).
- ///
- /// GetWindowPlacement and SetWindowPlacement fail if this member is not set correctly.
- ///
- ///
- public int Length;
-
- ///
- /// Specifies flags that control the position of the minimized window and the method by which the window is restored.
- ///
- public int Flags;
-
- ///
- /// The current show state of the window.
- ///
- public ShowWindowCommand ShowCmd;
-
- ///
- /// The coordinates of the window's upper-left corner when the window is minimized.
- ///
- public POINT MinPosition;
-
- ///
- /// The coordinates of the window's upper-left corner when the window is maximized.
- ///
- public POINT MaxPosition;
-
- ///
- /// The window's coordinates when the window is in the restored position.
- ///
- public RECT NormalPosition;
-
- ///
- /// Gets the default (empty) value.
- ///
- public static WINDOWPLACEMENT Default
- {
- get
- {
- WINDOWPLACEMENT result = default(WINDOWPLACEMENT);
- result.Length = Marshal.SizeOf(result);
- return result;
- }
- }
-
- ///
- /// 构造一个新的
- ///
- /// 最大点
- /// 正常位置
- /// 显示命令
- /// 窗体位置
- public static WINDOWPLACEMENT Create(POINT max, RECT normal, ShowWindowCommand command)
- {
- WINDOWPLACEMENT result = Default;
-
- result.MaxPosition = max;
- result.NormalPosition = normal;
- result.ShowCmd = command;
-
- return result;
- }
-}
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/WindowManager.cs b/src/Snap.Hutao/Snap.Hutao/Core/WindowManager.cs
index ecaddc7a..74c36215 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/WindowManager.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/WindowManager.cs
@@ -5,21 +5,33 @@ using Microsoft.UI.Xaml;
using Snap.Hutao.Control.HostBackdrop;
using Snap.Hutao.Core.Logging;
using Snap.Hutao.Core.Setting;
-using Snap.Hutao.Core.Win32;
+using System.Runtime.InteropServices;
+using Windows.Win32;
+using Windows.Win32.Foundation;
+using Windows.Win32.UI.Shell;
+using Windows.Win32.UI.WindowsAndMessaging;
using WinRT.Interop;
namespace Snap.Hutao.Core;
///
/// 窗口状态管理器
+/// 主要包含了各类 P/Inoke 代码
///
internal class WindowManager
{
- private readonly IntPtr handle;
+ private const int MinWidth = 800;
+ private const int MinHeight = 600;
+
+ private readonly HWND handle;
private readonly Window window;
private readonly UIElement titleBar;
private readonly ILogger logger;
+ // We have to explictly hold a reference to the SUBCLASSPROC
+ // otherwise will casuse System.ExecutionEngineException
+ private SUBCLASSPROC? subClassProc;
+
///
/// 构造一个新的窗口状态管理器
///
@@ -30,9 +42,7 @@ internal class WindowManager
this.window = window;
this.titleBar = titleBar;
logger = Ioc.Default.GetRequiredService>();
-
- handle = WindowNative.GetWindowHandle(window);
-
+ handle = (HWND)WindowNative.GetWindowHandle(window);
InitializeWindow();
}
@@ -43,18 +53,22 @@ internal class WindowManager
int right = LocalSetting.GetValueType(SettingKeys.WindowRight);
int bottom = LocalSetting.GetValueType(SettingKeys.WindowBottom);
- return new RECT(left, top, right, bottom);
+ return new() { left = left, top = top, right = right, bottom = bottom };
}
- private static void SaveWindowRect(IntPtr handle)
+ private static void SaveWindowRect(HWND handle)
{
- WINDOWPLACEMENT windowPlacement = WINDOWPLACEMENT.Default;
- User32.GetWindowPlacement(handle, ref windowPlacement);
+ WINDOWPLACEMENT windowPlacement = new()
+ {
+ length = (uint)Marshal.SizeOf(),
+ };
- LocalSetting.Set(SettingKeys.WindowLeft, windowPlacement.NormalPosition.Left);
- LocalSetting.Set(SettingKeys.WindowTop, windowPlacement.NormalPosition.Top);
- LocalSetting.Set(SettingKeys.WindowRight, windowPlacement.NormalPosition.Right);
- LocalSetting.Set(SettingKeys.WindowBottom, windowPlacement.NormalPosition.Bottom);
+ PInvoke.GetWindowPlacement(handle, ref windowPlacement);
+
+ LocalSetting.Set(SettingKeys.WindowLeft, windowPlacement.rcNormalPosition.left);
+ LocalSetting.Set(SettingKeys.WindowTop, windowPlacement.rcNormalPosition.top);
+ LocalSetting.Set(SettingKeys.WindowRight, windowPlacement.rcNormalPosition.right);
+ LocalSetting.Set(SettingKeys.WindowBottom, windowPlacement.rcNormalPosition.bottom);
}
private void InitializeWindow()
@@ -63,20 +77,52 @@ internal class WindowManager
window.SetTitleBar(titleBar);
window.Closed += OnWindowClosed;
- User32.SetWindowText(handle, "胡桃");
+ PInvoke.SetWindowText(handle, "胡桃");
RECT rect = RetriveWindowRect();
- if (rect.Area > 0)
+ if ((rect.right - rect.left) * (rect.bottom - rect.top) > 0)
{
- WINDOWPLACEMENT windowPlacement = WINDOWPLACEMENT.Create(new POINT(-1, -1), rect, ShowWindowCommand.Normal);
- User32.SetWindowPlacement(handle, ref windowPlacement);
+ WINDOWPLACEMENT windowPlacement = new()
+ {
+ length = (uint)Marshal.SizeOf(),
+ showCmd = SHOW_WINDOW_CMD.SW_SHOWNORMAL,
+ ptMaxPosition = new() { x = -1, y = -1 },
+ rcNormalPosition = rect,
+ };
+
+ PInvoke.SetWindowPlacement(handle, in windowPlacement);
}
bool micaApplied = new SystemBackdrop(window).TrySetBackdrop();
logger.LogInformation(EventIds.BackdropState, "Apply {name} : {result}", nameof(SystemBackdrop), micaApplied ? "succeed" : "failed");
+
+ subClassProc = new(OnWindowProcedure);
+ _ = PInvoke.SetWindowSubclass(handle, subClassProc, 101, 0);
}
private void OnWindowClosed(object sender, WindowEventArgs args)
{
+ PInvoke.RemoveWindowSubclass(handle, subClassProc, 101);
+ subClassProc = null;
SaveWindowRect(handle);
}
+
+ private LRESULT OnWindowProcedure(HWND hwnd, uint uMsg, WPARAM wParam, LPARAM lParam, nuint uIdSubclass, nuint dwRefData)
+ {
+ switch (uMsg)
+ {
+ case PInvoke.WM_GETMINMAXINFO:
+ {
+ uint dpi = PInvoke.GetDpiForWindow(handle);
+ float scalingFactor = dpi / 96f;
+
+ MINMAXINFO minMaxInfo = Marshal.PtrToStructure(lParam);
+ minMaxInfo.ptMinTrackSize.x = (int)Math.Max(MinWidth * scalingFactor, minMaxInfo.ptMinTrackSize.x);
+ minMaxInfo.ptMinTrackSize.y = (int)Math.Max(MinHeight * scalingFactor, minMaxInfo.ptMinTrackSize.y);
+ Marshal.StructureToPtr(minMaxInfo, lParam, true);
+ break;
+ }
+ }
+
+ return PInvoke.DefSubclassProc(hwnd, uMsg, wParam, lParam);
+ }
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Extension/ObjectExtensions.cs b/src/Snap.Hutao/Snap.Hutao/Extension/ObjectExtensions.cs
index 3440d8aa..abe6a04f 100644
--- a/src/Snap.Hutao/Snap.Hutao/Extension/ObjectExtensions.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Extension/ObjectExtensions.cs
@@ -14,7 +14,7 @@ public static class ObjectExtensions
/// 目标转换类型
/// 对象
/// 转换类型后的对象
- public static T? As(this object? obj)
+ public static T? ImplictAs(this object? obj)
where T : class
{
return obj as T;
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/User.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/User.cs
index aa4aea83..e2420325 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/Entity/User.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Entity/User.cs
@@ -28,15 +28,4 @@ public class User
/// 用户的Cookie
///
public string? Cookie { get; set; }
-
- ///
- /// 设置用户的选中状态
- /// 同时更新用户选择的角色信息
- ///
- /// 用户
- /// 是否选中
- public static void SetSelectionState(User user, bool isSelected)
- {
- user!.IsSelected = isSelected;
- }
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest b/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest
index 2c886fd3..937607a1 100644
--- a/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest
+++ b/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest
@@ -9,7 +9,7 @@
+ Version="1.0.20.0" />
胡桃
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs
index c91f656c..87fc1297 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using Snap.Hutao.Context.Database;
+using Snap.Hutao.Model.Binding;
using Snap.Hutao.Service.Abstraction;
using Snap.Hutao.Web.Hoyolab.Bbs.User;
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
@@ -22,8 +23,8 @@ internal class UserService : IUserService
private readonly UserClient userClient;
private readonly UserGameRoleClient userGameRoleClient;
- private Model.Binding.User? currentUser;
- private ObservableCollection? userCollection = null;
+ private User? currentUser;
+ private ObservableCollection? userCollection = null;
///
/// 构造一个新的用户服务
@@ -39,7 +40,7 @@ internal class UserService : IUserService
}
///
- public Model.Binding.User? CurrentUser
+ public User? CurrentUser
{
get => currentUser;
set
@@ -68,12 +69,12 @@ internal class UserService : IUserService
}
///
- public async Task TryAddUserAsync(Model.Binding.User newUser, string uid)
+ public async Task TryAddUserAsync(User newUser, string uid)
{
Must.NotNull(userCollection!);
// 查找是否有相同的uid
- if (userCollection.SingleOrDefault(u => u.UserInfo!.Uid == uid) is Model.Binding.User userWithSameUid)
+ if (userCollection.SingleOrDefault(u => u.UserInfo!.Uid == uid) is User userWithSameUid)
{
// Prevent users from adding a completely same cookie.
if (userWithSameUid.Cookie == newUser.Cookie)
@@ -94,36 +95,39 @@ internal class UserService : IUserService
{
Verify.Operation(newUser.IsInitialized, "该用户尚未初始化");
+ // Sync cache
+ userCollection.Add(newUser);
+
// Sync database
appDbContext.Users.Add(newUser.Entity);
await appDbContext.SaveChangesAsync().ConfigureAwait(false);
- // Sync cache
- userCollection.Add(newUser);
-
return UserAddResult.Added;
}
}
///
- public Task RemoveUserAsync(Model.Binding.User user)
+ public Task RemoveUserAsync(User user)
{
+ // Sync cache
userCollection!.Remove(user);
+
+ // Sync database
appDbContext.Users.Remove(user.Entity);
return appDbContext.SaveChangesAsync();
}
///
- public async Task> GetUserCollectionAsync()
+ public async Task> GetUserCollectionAsync()
{
if (userCollection == null)
{
- List users = new();
+ List users = new();
- foreach (Model.Entity.User user in appDbContext.Users)
+ foreach (Model.Entity.User entity in appDbContext.Users)
{
- Model.Binding.User? initialized = await Model.Binding.User
- .CreateAsync(user, userClient, userGameRoleClient)
+ User? initialized = await User
+ .CreateAsync(entity, userClient, userGameRoleClient)
.ConfigureAwait(false);
if (initialized != null)
@@ -133,23 +137,22 @@ internal class UserService : IUserService
else
{
// User is unable to be initialized, remove it.
- appDbContext.Users.Remove(user);
+ appDbContext.Users.Remove(entity);
await appDbContext.SaveChangesAsync().ConfigureAwait(false);
}
}
- CurrentUser = users.SingleOrDefault(user => user.IsSelected);
-
userCollection = new(users);
+ CurrentUser = users.SingleOrDefault(user => user.IsSelected);
}
return userCollection;
}
///
- public Task CreateUserAsync(string cookie)
+ public Task CreateUserAsync(string cookie)
{
- return Model.Binding.User.CreateAsync(new() { Cookie = cookie }, userClient, userGameRoleClient);
+ return User.CreateAsync(new() { Cookie = cookie }, userClient, userGameRoleClient);
}
///
diff --git a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj
index af073e01..30498d74 100644
--- a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj
+++ b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj
@@ -95,6 +95,7 @@
+