refactor:迁移设置
This commit is contained in:
@@ -0,0 +1,463 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Principal;
|
||||
using System.Windows;
|
||||
using System.Windows.Interop;
|
||||
using System.Windows.Threading;
|
||||
using Ink_Canvas.Helpers;
|
||||
|
||||
namespace Ink_Canvas.Windows.SettingsViews.Helpers
|
||||
{
|
||||
public static class WindowSettingsHelper
|
||||
{
|
||||
#region Win32 API
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern int GetWindowLong(IntPtr hWnd, int nIndex);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern IntPtr GetForegroundWindow();
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern bool IsWindow(IntPtr hWnd);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern bool IsWindowVisible(IntPtr hWnd);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern bool IsIconic(IntPtr hWnd);
|
||||
|
||||
[DllImport("UIAccessDLL_x86.dll", EntryPoint = "PrepareUIAccess", CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern Int32 PrepareUIAccessX86();
|
||||
|
||||
[DllImport("UIAccessDLL_x64.dll", EntryPoint = "PrepareUIAccess", CallingConvention = CallingConvention.Cdecl)]
|
||||
private static extern Int32 PrepareUIAccessX64();
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
private static extern IntPtr GetModuleHandle(string lpModuleName);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
private static extern uint GetCurrentProcessId();
|
||||
|
||||
private const int GWL_EXSTYLE = -20;
|
||||
private const int WS_EX_NOACTIVATE = 0x08000000;
|
||||
private const int WS_EX_TOPMOST = 0x00000008;
|
||||
private const int WH_KEYBOARD_LL = 13;
|
||||
|
||||
private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
|
||||
private static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2);
|
||||
|
||||
private const uint SWP_NOMOVE = 0x0002;
|
||||
private const uint SWP_NOSIZE = 0x0001;
|
||||
private const uint SWP_NOACTIVATE = 0x0010;
|
||||
private const uint SWP_SHOWWINDOW = 0x0040;
|
||||
private const uint SWP_NOOWNERZORDER = 0x0200;
|
||||
|
||||
private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Keyboard Hook
|
||||
|
||||
private static LowLevelKeyboardProc _keyboardProc;
|
||||
private static IntPtr _keyboardHookId = IntPtr.Zero;
|
||||
|
||||
private static IntPtr KeyboardHookProc(int nCode, IntPtr wParam, IntPtr lParam)
|
||||
{
|
||||
return CallNextHookEx(_keyboardHookId, nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
public static void InstallKeyboardHook()
|
||||
{
|
||||
if (_keyboardHookId == IntPtr.Zero)
|
||||
{
|
||||
_keyboardProc = KeyboardHookProc;
|
||||
_keyboardHookId = SetWindowsHookEx(WH_KEYBOARD_LL, _keyboardProc,
|
||||
GetModuleHandle(null), 0);
|
||||
if (_keyboardHookId == IntPtr.Zero)
|
||||
{
|
||||
LogHelper.WriteLogToFile("安装低级键盘钩子失败", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void UninstallKeyboardHook()
|
||||
{
|
||||
if (_keyboardHookId != IntPtr.Zero)
|
||||
{
|
||||
UnhookWindowsHookEx(_keyboardHookId);
|
||||
_keyboardHookId = IntPtr.Zero;
|
||||
_keyboardProc = null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Timer Callbacks
|
||||
|
||||
public static Action OnStopKillProcessTimer { get; set; }
|
||||
public static Action OnStartKillProcessTimer { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Topmost Maintenance Timer
|
||||
|
||||
private static DispatcherTimer _topmostMaintenanceTimer;
|
||||
private static bool _isTopmostMaintenanceEnabled;
|
||||
private static Window _maintainedWindow;
|
||||
|
||||
#endregion
|
||||
|
||||
#region PPT Only Mode
|
||||
|
||||
private static DispatcherTimer _pptOnlyVisibilityProbeTimer;
|
||||
private static Window _pptModeWindow;
|
||||
private const int PptOnlyVisibilityProbeIntervalMs = 800;
|
||||
|
||||
public static Action<bool> OnPptOnlyModeChanged { get; set; }
|
||||
|
||||
public static void ApplyPptOnlyMode(Window window, bool isEnabled)
|
||||
{
|
||||
try
|
||||
{
|
||||
SettingsManager.Settings.ModeSettings.IsPPTOnlyMode = isEnabled;
|
||||
SettingsManager.SaveSettingsToFile();
|
||||
|
||||
if (isEnabled)
|
||||
{
|
||||
window.Hide();
|
||||
LogHelper.WriteLogToFile("已切换到仅PPT模式,主窗口已隐藏", LogHelper.LogType.Event);
|
||||
EnsurePptOnlyVisibilityProbeTimer(window);
|
||||
}
|
||||
else
|
||||
{
|
||||
StopPptOnlyVisibilityProbeTimer();
|
||||
window.Show();
|
||||
LogHelper.WriteLogToFile("已切换到正常模式,主窗口已显示", LogHelper.LogType.Event);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"切换模式时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private static void EnsurePptOnlyVisibilityProbeTimer(Window window)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!SettingsManager.Settings.ModeSettings.IsPPTOnlyMode)
|
||||
{
|
||||
StopPptOnlyVisibilityProbeTimer();
|
||||
return;
|
||||
}
|
||||
|
||||
_pptModeWindow = window;
|
||||
|
||||
if (_pptOnlyVisibilityProbeTimer == null)
|
||||
{
|
||||
_pptOnlyVisibilityProbeTimer = new DispatcherTimer
|
||||
{
|
||||
Interval = TimeSpan.FromMilliseconds(PptOnlyVisibilityProbeIntervalMs)
|
||||
};
|
||||
_pptOnlyVisibilityProbeTimer.Tick += PptOnlyVisibilityProbeTimer_Tick;
|
||||
}
|
||||
|
||||
if (!_pptOnlyVisibilityProbeTimer.IsEnabled)
|
||||
_pptOnlyVisibilityProbeTimer.Start();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"仅PPT可见性探测计时器启动失败: {ex.Message}", LogHelper.LogType.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
private static void StopPptOnlyVisibilityProbeTimer()
|
||||
{
|
||||
try
|
||||
{
|
||||
_pptOnlyVisibilityProbeTimer?.Stop();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
private static void PptOnlyVisibilityProbeTimer_Tick(object sender, EventArgs e)
|
||||
{
|
||||
OnPptOnlyModeChanged?.Invoke(true);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Window Settings Methods
|
||||
|
||||
public static bool IsTemporarilyDisablingNoFocusMode { get; set; }
|
||||
|
||||
public static void ApplyNoFocusMode(Window window)
|
||||
{
|
||||
var hwnd = new WindowInteropHelper(window).Handle;
|
||||
int exStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
|
||||
|
||||
bool shouldBeNoFocus = !IsTemporarilyDisablingNoFocusMode && SettingsManager.Settings.Advanced.IsNoFocusMode;
|
||||
|
||||
if (shouldBeNoFocus)
|
||||
{
|
||||
SetWindowLong(hwnd, GWL_EXSTYLE, exStyle | WS_EX_NOACTIVATE);
|
||||
InstallKeyboardHook();
|
||||
}
|
||||
else
|
||||
{
|
||||
SetWindowLong(hwnd, GWL_EXSTYLE, exStyle & ~WS_EX_NOACTIVATE);
|
||||
UninstallKeyboardHook();
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetWindowMode(Window window)
|
||||
{
|
||||
if (SettingsManager.Settings.Advanced.WindowMode)
|
||||
{
|
||||
window.WindowState = WindowState.Normal;
|
||||
window.Left = 0.0;
|
||||
window.Top = 0.0;
|
||||
window.Height = SystemParameters.PrimaryScreenHeight;
|
||||
window.Width = SystemParameters.PrimaryScreenWidth;
|
||||
}
|
||||
else
|
||||
{
|
||||
window.WindowState = WindowState.Maximized;
|
||||
}
|
||||
}
|
||||
|
||||
public static void ApplyAlwaysOnTop(Window window)
|
||||
{
|
||||
try
|
||||
{
|
||||
var hwnd = new WindowInteropHelper(window).Handle;
|
||||
if (SettingsManager.Settings.Advanced.IsAlwaysOnTop)
|
||||
{
|
||||
window.Topmost = true;
|
||||
|
||||
int exStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
|
||||
SetWindowLong(hwnd, GWL_EXSTYLE, exStyle | WS_EX_TOPMOST);
|
||||
|
||||
SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
|
||||
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_NOOWNERZORDER);
|
||||
|
||||
if (SettingsManager.Settings.Advanced.IsNoFocusMode && !SettingsManager.Settings.Advanced.EnableUIAccessTopMost)
|
||||
{
|
||||
StartTopmostMaintenance(window);
|
||||
}
|
||||
else
|
||||
{
|
||||
StopTopmostMaintenance();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
|
||||
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_NOOWNERZORDER);
|
||||
|
||||
int exStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
|
||||
SetWindowLong(hwnd, GWL_EXSTYLE, exStyle & ~WS_EX_TOPMOST);
|
||||
|
||||
StopTopmostMaintenance();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"应用窗口置顶失败: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
public static void ApplyUIAccessTopMost(Window window)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (SettingsManager.Settings.Advanced.EnableUIAccessTopMost && SettingsManager.Settings.Advanced.IsAlwaysOnTop)
|
||||
{
|
||||
var identity = WindowsIdentity.GetCurrent();
|
||||
var principal = new WindowsPrincipal(identity);
|
||||
|
||||
if (principal.IsInRole(WindowsBuiltInRole.Administrator))
|
||||
{
|
||||
try
|
||||
{
|
||||
OnStopKillProcessTimer?.Invoke();
|
||||
|
||||
if (App.watchdogProcess != null && !App.watchdogProcess.HasExited)
|
||||
{
|
||||
App.watchdogProcess.Kill();
|
||||
App.watchdogProcess = null;
|
||||
}
|
||||
|
||||
App.StartWatchdogIfNeeded();
|
||||
|
||||
if (Environment.Is64BitProcess)
|
||||
{
|
||||
PrepareUIAccessX64();
|
||||
}
|
||||
else
|
||||
{
|
||||
PrepareUIAccessX86();
|
||||
}
|
||||
|
||||
OnStartKillProcessTimer?.Invoke();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"启用UIA置顶功能时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogHelper.WriteLogToFile("UIA置顶功能需要管理员权限", LogHelper.LogType.Warning);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogHelper.WriteLogToFile("UIA置顶功能已禁用", LogHelper.LogType.Trace);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"应用UIA置顶功能时出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetTopmostBasedOnSettings(Window window, bool shouldBeTopmost)
|
||||
{
|
||||
if (SettingsManager.Settings.Advanced.IsAlwaysOnTop)
|
||||
{
|
||||
window.Topmost = true;
|
||||
ApplyAlwaysOnTop(window);
|
||||
}
|
||||
else
|
||||
{
|
||||
window.Topmost = shouldBeTopmost;
|
||||
if (!shouldBeTopmost)
|
||||
{
|
||||
ApplyAlwaysOnTop(window);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void PauseTopmostMaintenance()
|
||||
{
|
||||
if (_topmostMaintenanceTimer != null && _isTopmostMaintenanceEnabled)
|
||||
{
|
||||
_topmostMaintenanceTimer.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
public static void ResumeTopmostMaintenance(Window window)
|
||||
{
|
||||
if (SettingsManager.Settings.Advanced.IsAlwaysOnTop &&
|
||||
SettingsManager.Settings.Advanced.IsNoFocusMode &&
|
||||
!SettingsManager.Settings.Advanced.EnableUIAccessTopMost)
|
||||
{
|
||||
if (_topmostMaintenanceTimer != null && !_isTopmostMaintenanceEnabled)
|
||||
{
|
||||
_topmostMaintenanceTimer.Start();
|
||||
_isTopmostMaintenanceEnabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void StartTopmostMaintenance(Window window)
|
||||
{
|
||||
if (SettingsManager.Settings.Advanced.EnableUIAccessTopMost) return;
|
||||
if (_isTopmostMaintenanceEnabled) return;
|
||||
|
||||
_maintainedWindow = window;
|
||||
|
||||
if (_topmostMaintenanceTimer == null)
|
||||
{
|
||||
_topmostMaintenanceTimer = new DispatcherTimer();
|
||||
_topmostMaintenanceTimer.Interval = TimeSpan.FromMilliseconds(500);
|
||||
_topmostMaintenanceTimer.Tick += TopmostMaintenanceTimer_Tick;
|
||||
}
|
||||
|
||||
_topmostMaintenanceTimer.Start();
|
||||
_isTopmostMaintenanceEnabled = true;
|
||||
LogHelper.WriteLogToFile("启动置顶维护定时器", LogHelper.LogType.Trace);
|
||||
}
|
||||
|
||||
private static void StopTopmostMaintenance()
|
||||
{
|
||||
if (_topmostMaintenanceTimer != null && _isTopmostMaintenanceEnabled)
|
||||
{
|
||||
_topmostMaintenanceTimer.Stop();
|
||||
_isTopmostMaintenanceEnabled = false;
|
||||
LogHelper.WriteLogToFile("停止置顶维护定时器", LogHelper.LogType.Trace);
|
||||
}
|
||||
}
|
||||
|
||||
private static void TopmostMaintenanceTimer_Tick(object sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (SettingsManager.Settings.Advanced.EnableUIAccessTopMost)
|
||||
{
|
||||
StopTopmostMaintenance();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SettingsManager.Settings.Advanced.IsAlwaysOnTop || !SettingsManager.Settings.Advanced.IsNoFocusMode)
|
||||
{
|
||||
StopTopmostMaintenance();
|
||||
return;
|
||||
}
|
||||
|
||||
var window = _maintainedWindow;
|
||||
if (window == null) return;
|
||||
|
||||
var hwnd = new WindowInteropHelper(window).Handle;
|
||||
if (hwnd == IntPtr.Zero) return;
|
||||
|
||||
if (!IsWindow(hwnd) || !IsWindowVisible(hwnd) || IsIconic(hwnd)) return;
|
||||
|
||||
var foregroundWindow = GetForegroundWindow();
|
||||
if (foregroundWindow != hwnd)
|
||||
{
|
||||
GetWindowThreadProcessId(foregroundWindow, out uint processId);
|
||||
var currentProcessId = GetCurrentProcessId();
|
||||
|
||||
if (processId == currentProcessId) return;
|
||||
|
||||
SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
|
||||
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_NOOWNERZORDER);
|
||||
|
||||
int exStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
|
||||
if ((exStyle & WS_EX_TOPMOST) == 0)
|
||||
{
|
||||
SetWindowLong(hwnd, GWL_EXSTYLE, exStyle | WS_EX_TOPMOST);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogHelper.WriteLogToFile($"置顶维护定时器出错: {ex.Message}", LogHelper.LogType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user