diff --git a/src/Snap.Hutao/Snap.Hutao/NativeMethods.txt b/src/Snap.Hutao/Snap.Hutao/NativeMethods.txt
index 29666b74..2e9fc3cb 100644
--- a/src/Snap.Hutao/Snap.Hutao/NativeMethods.txt
+++ b/src/Snap.Hutao/Snap.Hutao/NativeMethods.txt
@@ -8,6 +8,7 @@ WM_NULL
// Type & Enum definition
CWMO_FLAGS
MINMAXINFO
+LPTHREAD_START_ROUTINE
// COMCTL32
DefSubclassProc
@@ -23,11 +24,17 @@ GetDeviceCaps
// KERNEL32
CreateEvent
+CreateRemoteThread
CreateToolhelp32Snapshot
+GetModuleHandle
+GetProcAddress
Module32First
Module32Next
ReadProcessMemory
SetEvent
+VirtualAllocEx
+VirtualFreeEx
+WaitForSingleObject
WriteProcessMemory
// OLE32
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/MultiChannel.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/ChannelOptions.cs
similarity index 100%
rename from src/Snap.Hutao/Snap.Hutao/Service/Game/MultiChannel.cs
rename to src/Snap.Hutao/Snap.Hutao/Service/Game/ChannelOptions.cs
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/ProcessInterop.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/ProcessInterop.cs
index e59a3df9..e5c3838d 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Game/ProcessInterop.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/ProcessInterop.cs
@@ -5,6 +5,11 @@ using Snap.Hutao.Core;
using Snap.Hutao.Service.Game.Unlocker;
using System.Diagnostics;
using System.IO;
+using System.Runtime.InteropServices;
+using Windows.Win32.Foundation;
+using Windows.Win32.System.Memory;
+using Windows.Win32.System.Threading;
+using static Windows.Win32.PInvoke;
namespace Snap.Hutao.Service.Game;
@@ -83,4 +88,68 @@ internal static class ProcessInterop
return false;
}
+
+ ///
+ /// 加载并注入指定路径的库
+ ///
+ /// 进程句柄
+ /// 库的路径,不包含'\0'
+ [SuppressMessage("", "SH002")]
+ public static unsafe void LoadLibraryAndInject(HANDLE hProcess, ReadOnlySpan libraryPathu8)
+ {
+ HINSTANCE hKernelDll = GetModuleHandle("kernel32.dll");
+ Marshal.ThrowExceptionForHR(Marshal.GetLastPInvokeError());
+
+ FARPROC pLoadLibraryA = GetProcAddress(hKernelDll, libraryPathu8);
+ Marshal.ThrowExceptionForHR(Marshal.GetLastPInvokeError());
+
+ void* pNativeLibraryPath = default;
+ try
+ {
+ VIRTUAL_ALLOCATION_TYPE allocType = VIRTUAL_ALLOCATION_TYPE.MEM_RESERVE | VIRTUAL_ALLOCATION_TYPE.MEM_COMMIT;
+ pNativeLibraryPath = VirtualAllocEx(hProcess, default, unchecked((uint)libraryPathu8.Length + 1), allocType, PAGE_PROTECTION_FLAGS.PAGE_READWRITE);
+ Marshal.ThrowExceptionForHR(Marshal.GetLastPInvokeError());
+
+ WriteProcessMemory(hProcess, pNativeLibraryPath, libraryPathu8);
+ Marshal.ThrowExceptionForHR(Marshal.GetLastPInvokeError());
+
+ LPTHREAD_START_ROUTINE lpThreadLoadLibraryA = pLoadLibraryA.CreateDelegate();
+ HANDLE hLoadLibraryAThread = default;
+ try
+ {
+ hLoadLibraryAThread = CreateRemoteThread(hProcess, default, 0, lpThreadLoadLibraryA, pNativeLibraryPath, 0);
+ Marshal.ThrowExceptionForHR(Marshal.GetLastPInvokeError());
+
+ // What are we waiting for?
+ WaitForSingleObject(hLoadLibraryAThread, 2000);
+ Marshal.ThrowExceptionForHR(Marshal.GetLastPInvokeError());
+ }
+ finally
+ {
+ CloseHandle(hLoadLibraryAThread);
+ }
+ }
+ finally
+ {
+ VirtualFreeEx(hProcess, pNativeLibraryPath, 0, VIRTUAL_FREE_TYPE.MEM_RELEASE);
+ }
+ }
+
+ [SuppressMessage("", "SH002")]
+ private static unsafe FARPROC GetProcAddress(HINSTANCE hModule, ReadOnlySpan lpProcName)
+ {
+ fixed (byte* lpProcNameLocal = lpProcName)
+ {
+ return Windows.Win32.PInvoke.GetProcAddress(hModule, new PCSTR(lpProcNameLocal));
+ }
+ }
+
+ [SuppressMessage("", "SH002")]
+ private static unsafe BOOL WriteProcessMemory(HANDLE hProcess, void* lpBaseAddress, ReadOnlySpan buffer)
+ {
+ fixed (void* lpBuffer = buffer)
+ {
+ return Windows.Win32.PInvoke.WriteProcessMemory(hProcess, lpBaseAddress, lpBuffer, unchecked((uint)buffer.Length));
+ }
+ }
}
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Dialog/CultivatePromotionDeltaDialog.xaml b/src/Snap.Hutao/Snap.Hutao/View/Dialog/CultivatePromotionDeltaDialog.xaml
index 9d940364..72524122 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Dialog/CultivatePromotionDeltaDialog.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/View/Dialog/CultivatePromotionDeltaDialog.xaml
@@ -15,7 +15,10 @@
PrimaryButtonText="{shcm:ResourceString Name=ContentDialogConfirmPrimaryButtonText}"
Style="{StaticResource DefaultContentDialogStyle}"
mc:Ignorable="d">
-
+
+ 180
+ 1200
+
@@ -48,11 +51,11 @@
TextTrimming="CharacterEllipsis"/>
@@ -95,11 +98,11 @@
TextTrimming="CharacterEllipsis"/>