/* * MyCSharpProject - Shanxue-Education-Points-Coin-Converter * Copyright (C) 2026 ZerkyLiu * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ using System; using System.Drawing; using System.Globalization; using System.Net; using System.Net.Sockets; using System.IO; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.Threading.Tasks; using System.Windows.Forms; namespace 善学教育积分卡汇率计算器 { public class MainForm : Form { private static readonly decimal[] RatePoints = { 500m, 1000m, 1500m, 2000m, 2500m, 3000m, 3500m, 4000m }; private static readonly decimal[] RateRmb = { 10m, 20m, 35m, 45m, 60m, 75m, 85m, 100m }; private static readonly string[] Zodiac = { "鼠", "牛", "虎", "兔", "龙", "蛇", "马", "羊", "猴", "鸡", "狗", "猪" }; private static readonly string[] NtpServers = { "ntp.aliyun.com", "ntp.ntsc.ac.cn", "pool.ntp.org" }; private static readonly string RateTextHorizontal = BuildRateText(true); private static readonly string RateTextVertical = BuildRateText(false); private readonly Label _currencyLabel; private readonly Label _timeLabel; private readonly Label _timeSourceLabel; private readonly CheckBox _topMostToggle; private readonly Button _aboutButton; private readonly Label _inputLabel; private readonly Label _rmbLabel; private readonly TextBox _pointsInput; private readonly Label _rmbOutput; private readonly Label _rateLabel; private readonly Timer _clockTimer; private readonly Timer _syncTimer; private readonly Timer _fallbackTopMostTimer; private DateTime _currentTime; private string _timeSource; private IntPtr _uiAccessDll; private string? _uiAccessDllPath; private IsUIAccessDelegate? _isUiAccess; private bool _uiAccessEnabled; private bool _usingFallbackTopMost; private bool _usingUiAccessTopMost; private bool _suppressPointsTextChange; private readonly bool _forceFallbackTopMost; private const string UiAccessRelaunchArg = "--uiaccess-relaunch"; private const string UiAccessResourceSuffix = ".dll.uiaccess.dll"; private const string IconResourceSuffix = ".ico.logo_256x256.ico"; private float _rmbOutputBaseFontSize; public MainForm() { Text = "善学教育积分卡汇率计算器"; StartPosition = FormStartPosition.CenterScreen; ClientSize = new Size(640, 730); MinimumSize = SizeFromClientSize(ClientSize); Font = new Font("Microsoft YaHei UI", 10F); BackColor = Color.WhiteSmoke; AutoScaleMode = AutoScaleMode.Dpi; DoubleBuffered = true; SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true); var layout = new TableLayoutPanel { Dock = DockStyle.Fill, ColumnCount = 1, RowCount = 4, Padding = new Padding(28), }; layout.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F)); layout.RowStyles.Add(new RowStyle(SizeType.AutoSize)); layout.RowStyles.Add(new RowStyle(SizeType.AutoSize)); layout.RowStyles.Add(new RowStyle(SizeType.Percent, 100F)); layout.RowStyles.Add(new RowStyle(SizeType.AutoSize)); _currencyLabel = new Label { AutoSize = true, Font = new Font("Microsoft YaHei UI", 18F, FontStyle.Bold), ForeColor = Color.FromArgb(38, 50, 56), Dock = DockStyle.Fill, TextAlign = ContentAlignment.MiddleLeft }; layout.Controls.Add(_currencyLabel, 0, 0); _timeLabel = new Label { AutoSize = true, ForeColor = Color.FromArgb(55, 71, 79), Dock = DockStyle.Fill, TextAlign = ContentAlignment.MiddleLeft }; _topMostToggle = new CheckBox { Text = "超级置顶", AutoSize = true, Dock = DockStyle.Fill, TextAlign = ContentAlignment.MiddleRight }; _topMostToggle.CheckedChanged += (_, __) => ApplyTopMost(_topMostToggle.Checked); _aboutButton = new Button { Text = "关于", AutoSize = true, Dock = DockStyle.Fill }; _aboutButton.Click += (_, __) => ShowAboutDialog(); _timeSourceLabel = new Label { AutoSize = true, ForeColor = Color.FromArgb(96, 125, 139), Dock = DockStyle.Fill, TextAlign = ContentAlignment.MiddleLeft }; var rightPanel = new TableLayoutPanel { Dock = DockStyle.Fill, ColumnCount = 1, RowCount = 2, AutoSize = true }; rightPanel.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize)); rightPanel.RowStyles.Add(new RowStyle(SizeType.AutoSize)); rightPanel.RowStyles.Add(new RowStyle(SizeType.AutoSize)); rightPanel.Controls.Add(_aboutButton, 0, 0); rightPanel.Controls.Add(_topMostToggle, 0, 1); var timeRow = new TableLayoutPanel { Dock = DockStyle.Fill, ColumnCount = 2, RowCount = 1, AutoSize = true }; timeRow.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F)); timeRow.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize)); timeRow.RowStyles.Add(new RowStyle(SizeType.AutoSize)); timeRow.Controls.Add(_timeLabel, 0, 0); timeRow.Controls.Add(rightPanel, 1, 0); var timePanel = new TableLayoutPanel { Dock = DockStyle.Fill, ColumnCount = 1, RowCount = 2, AutoSize = true }; timePanel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F)); timePanel.RowStyles.Add(new RowStyle(SizeType.AutoSize)); timePanel.RowStyles.Add(new RowStyle(SizeType.AutoSize)); timePanel.Controls.Add(timeRow, 0, 0); timePanel.Controls.Add(_timeSourceLabel, 0, 1); layout.Controls.Add(timePanel, 0, 1); _inputLabel = new Label { Text = "输入积分币数量", AutoSize = true, Dock = DockStyle.Fill, TextAlign = ContentAlignment.MiddleLeft }; _pointsInput = new TextBox { Dock = DockStyle.Fill, TextAlign = HorizontalAlignment.Left, ImeMode = ImeMode.Disable }; _pointsInput.TextChanged += PointsInputOnTextChanged; _pointsInput.KeyPress += PointsInputOnKeyPress; _rmbLabel = new Label { Text = "换算人民币(元)", AutoSize = true, Dock = DockStyle.Fill, TextAlign = ContentAlignment.MiddleLeft }; _rmbOutput = new Label { Text = "0.00", AutoSize = true, Font = new Font("Microsoft YaHei UI", 22F, FontStyle.Bold), ForeColor = Color.FromArgb(0, 150, 136), Dock = DockStyle.Fill, TextAlign = ContentAlignment.MiddleCenter }; var convLayout = new TableLayoutPanel { Dock = DockStyle.Fill, ColumnCount = 2, RowCount = 2, AutoSize = false }; convLayout.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 40F)); convLayout.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 60F)); convLayout.RowStyles.Add(new RowStyle(SizeType.Percent, 50F)); convLayout.RowStyles.Add(new RowStyle(SizeType.Percent, 50F)); convLayout.Controls.Add(_inputLabel, 0, 0); convLayout.Controls.Add(_pointsInput, 1, 0); convLayout.Controls.Add(_rmbLabel, 0, 1); convLayout.Controls.Add(_rmbOutput, 1, 1); layout.Controls.Add(convLayout, 0, 2); _rateLabel = new Label { Text = GetRateText(true), AutoSize = true, ForeColor = Color.FromArgb(96, 125, 139), Dock = DockStyle.Fill, TextAlign = ContentAlignment.MiddleLeft }; layout.Controls.Add(_rateLabel, 0, 3); Controls.Add(layout); _timeSource = "本地"; _currentTime = DateTime.Now; _clockTimer = new Timer { Interval = 1000 }; _clockTimer.Tick += ClockTimerOnTick; _syncTimer = new Timer { Interval = 10 * 60 * 1000 }; _syncTimer.Tick += async (_, __) => await SyncNetworkTimeAsync(); _fallbackTopMostTimer = new Timer { Interval = 1 }; _fallbackTopMostTimer.Tick += (_, __) => ApplyFallbackTopMostTick(); _forceFallbackTopMost = IsWin7OrLower(); ClientSizeChanged += (_, __) => { UpdateRateLayout(); UpdateResponsiveFonts(); UpdateConversion(); }; Shown += (_, __) => _ = InitializeAsync(); } private Task InitializeAsync() { TryInitializeIcon(); TryInitializeUiAccess(); if (TryRelaunchWithUiAccess()) { return Task.CompletedTask; } UpdateTimeLabels(); UpdateRateLayout(); UpdateResponsiveFonts(); UpdateConversion(); StartInputFocus(); _ = SyncNetworkTimeAsync(); _clockTimer.Start(); _syncTimer.Start(); return Task.CompletedTask; } private void UpdateRateLayout() { var horizontal = ClientSize.Width >= 720; _rateLabel.MaximumSize = new Size(Math.Max(0, ClientSize.Width - 120), 0); _rateLabel.Text = horizontal ? RateTextHorizontal : RateTextVertical; } private void UpdateResponsiveFonts() { var w = ClientSize.Width; var h = ClientSize.Height; var resultSize = (float)Math.Max(24, Math.Min(w * 0.06, h * 0.08)); var inputSize = (float)Math.Max(12, Math.Min(w * 0.03, h * 0.04)); _rmbOutputBaseFontSize = resultSize; _rmbOutput.Font = new Font("Microsoft YaHei UI", resultSize, FontStyle.Bold); _pointsInput.Font = new Font("Microsoft YaHei UI", inputSize, FontStyle.Regular); } private static string GetRateText(bool horizontal) { return horizontal ? RateTextHorizontal : RateTextVertical; } private static string BuildRateText(bool horizontal) { var header = "汇率:"; if (horizontal) { var text = header; for (var i = 0; i < RatePoints.Length; i++) { text += $"{RatePoints[i]}币={RateRmb[i]}"; if (i < RatePoints.Length - 1) { text += ","; } } return text; } var newline = Environment.NewLine; var multi = header + newline; for (var i = 0; i < RatePoints.Length; i++) { multi += $"{RatePoints[i]}币={RateRmb[i]}"; if (i < RatePoints.Length - 1) { multi += newline; } } return multi; } private void UpdateTimeLabels() { var timeText = _currentTime.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture); _timeLabel.Text = $"当前时间:{timeText}"; _timeSourceLabel.Text = $"时间来源:{_timeSource}"; _currencyLabel.Text = $"当前积分币:{GetZodiacCurrency(_currentTime)}"; } private void ClockTimerOnTick(object? sender, EventArgs e) { _currentTime = _currentTime.AddSeconds(1); UpdateTimeLabels(); } private async Task SyncNetworkTimeAsync() { var networkTime = await Task.Run(TryGetNetworkTime); if (networkTime.HasValue) { _currentTime = networkTime.Value.ToLocalTime(); _timeSource = "NTP时间服务器"; } else { _currentTime = DateTime.Now; _timeSource = "本地"; } UpdateTimeLabels(); } private static DateTime? TryGetNetworkTime() { foreach (var server in NtpServers) { try { var ntpData = new byte[48]; ntpData[0] = 0x1B; var addresses = Dns.GetHostEntry(server).AddressList; foreach (var address in addresses) { if (address.AddressFamily != AddressFamily.InterNetwork) { continue; } var ipEndPoint = new IPEndPoint(address, 123); using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)) { socket.ReceiveTimeout = 1500; socket.SendTimeout = 1500; socket.Connect(ipEndPoint); socket.Send(ntpData); socket.Receive(ntpData); } const byte serverReplyTime = 40; var intPart = BitConverter.ToUInt32(ntpData, serverReplyTime); var fractPart = BitConverter.ToUInt32(ntpData, serverReplyTime + 4); intPart = SwapEndianness(intPart); fractPart = SwapEndianness(fractPart); var milliseconds = (intPart * 1000L) + ((fractPart * 1000L) / 0x100000000L); var networkDateTime = new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(milliseconds); return networkDateTime; } } catch { continue; } } return null; } private static uint SwapEndianness(uint x) { return (x & 0x000000FFU) << 24 | (x & 0x0000FF00U) << 8 | (x & 0x00FF0000U) >> 8 | (x & 0xFF000000U) >> 24; } private static string GetZodiacCurrency(DateTime time) { var index = Mod(time.Year - 2020, 12); return $"{Zodiac[index]}币"; } private static int Mod(int x, int m) { var r = x % m; return r < 0 ? r + m : r; } private void StartInputFocus() { if (!IsHandleCreated) { return; } BeginInvoke(new Action(() => { ActiveControl = _pointsInput; _pointsInput.Focus(); _pointsInput.SelectionStart = _pointsInput.TextLength; })); } private static bool IsWin7OrLower() { var version = Environment.OSVersion.Version; return version.Major < 6 || (version.Major == 6 && version.Minor <= 1); } private void PointsInputOnTextChanged(object? sender, EventArgs e) { if (_suppressPointsTextChange) { return; } var rawText = _pointsInput.Text; var filteredText = new string(rawText.Where(char.IsDigit).ToArray()); if (!string.Equals(rawText, filteredText, StringComparison.Ordinal)) { var selectionStart = _pointsInput.SelectionStart; var removedCount = rawText.Length - filteredText.Length; _suppressPointsTextChange = true; _pointsInput.Text = filteredText; _pointsInput.SelectionStart = Math.Max(0, Math.Min(filteredText.Length, selectionStart - removedCount)); _suppressPointsTextChange = false; } UpdateConversion(); } private void PointsInputOnKeyPress(object? sender, KeyPressEventArgs e) { if (char.IsControl(e.KeyChar) || char.IsDigit(e.KeyChar)) { return; } e.Handled = true; } private void UpdateConversion() { var rawText = _pointsInput.Text.Trim(); if (string.IsNullOrEmpty(rawText)) { SetRmbOutputText("0.00", GetRmbOutputBaseSize()); return; } if (!decimal.TryParse(rawText, out var points)) { SetRmbOutputText("TL", GetRmbOutputBaseSize()); return; } if (points <= 0) { SetRmbOutputText("0.00", GetRmbOutputBaseSize()); return; } try { var rmb = ConvertPointsToRmb(points); if (rmb < 0.01m) { SetRmbOutputText("0.00", GetRmbOutputBaseSize()); return; } UpdateRmbDisplay(rmb); } catch { SetRmbOutputText("TL", GetRmbOutputBaseSize()); } } private static decimal ConvertPointsToRmb(decimal points) { var pointSteps = new[] { 0m, RatePoints[0], RatePoints[1], RatePoints[2], RatePoints[3], RatePoints[4], RatePoints[5], RatePoints[6], RatePoints[7] }; var rmbSteps = new[] { 0m, RateRmb[0], RateRmb[1], RateRmb[2], RateRmb[3], RateRmb[4], RateRmb[5], RateRmb[6], RateRmb[7] }; if (points <= pointSteps[0]) { return 0m; } if (points >= pointSteps[pointSteps.Length - 1]) { return Interpolate(points, pointSteps[pointSteps.Length - 2], pointSteps[pointSteps.Length - 1], rmbSteps[rmbSteps.Length - 2], rmbSteps[rmbSteps.Length - 1]); } for (var i = 1; i < pointSteps.Length; i++) { if (points <= pointSteps[i]) { return Interpolate(points, pointSteps[i - 1], pointSteps[i], rmbSteps[i - 1], rmbSteps[i]); } } return 0m; } private static decimal Interpolate(decimal x, decimal x1, decimal x2, decimal y1, decimal y2) { if (x2 == x1) { return y1; } var ratio = (x - x1) / (x2 - x1); return y1 + (y2 - y1) * ratio; } private void UpdateRmbDisplay(decimal rmb) { var baseSize = GetRmbOutputBaseSize(); var minSize = _rmbLabel.Font.Size; var normalText = rmb.ToString("F2", CultureInfo.InvariantCulture); var normalResult = FitTextToRmbOutput(normalText, baseSize, minSize); if (normalResult.Fits) { SetRmbOutputText(normalResult.Text, normalResult.FontSize); return; } var sciText = rmb.ToString("0.###E+0", CultureInfo.InvariantCulture); var sciResult = FitTextToRmbOutput(sciText, baseSize, minSize); if (sciResult.Fits) { SetRmbOutputText(sciResult.Text, sciResult.FontSize); return; } var ellipsisText = "…请放大窗口"; var ellipsisResult = FitTextToRmbOutput(ellipsisText, baseSize, minSize); SetRmbOutputText(ellipsisResult.Text, ellipsisResult.FontSize); } private float GetRmbOutputBaseSize() { var baseSize = _rmbOutputBaseFontSize > 0 ? _rmbOutputBaseFontSize : _rmbOutput.Font.Size; return Math.Max(baseSize, _rmbLabel.Font.Size); } private (string Text, float FontSize, bool Fits) FitTextToRmbOutput(string text, float baseSize, float minSize) { var targetSize = Math.Max(baseSize, minSize); if (TryFitText(text, targetSize)) { return (text, targetSize, true); } for (var size = targetSize; size >= minSize; size -= 0.5f) { if (TryFitText(text, size)) { return (text, size, true); } } return (text, minSize, false); } private bool TryFitText(string text, float fontSize) { var availableWidth = _rmbOutput.ClientSize.Width; var availableHeight = _rmbOutput.ClientSize.Height; if (availableWidth <= 0 || availableHeight <= 0) { return true; } using (var font = new Font(_rmbOutput.Font.FontFamily, fontSize, _rmbOutput.Font.Style)) { var size = TextRenderer.MeasureText(text, font, new Size(int.MaxValue, int.MaxValue), TextFormatFlags.NoPadding); return size.Width <= availableWidth && size.Height <= availableHeight; } } private void SetRmbOutputText(string text, float fontSize) { if (Math.Abs(_rmbOutput.Font.Size - fontSize) > 0.1f) { _rmbOutput.Font = new Font(_rmbOutput.Font.FontFamily, fontSize, _rmbOutput.Font.Style); } _rmbOutput.Text = text; } protected override void OnFormClosed(FormClosedEventArgs e) { if (_fallbackTopMostTimer != null) { _fallbackTopMostTimer.Stop(); } if (_uiAccessDll != IntPtr.Zero) { FreeLibrary(_uiAccessDll); _uiAccessDll = IntPtr.Zero; } base.OnFormClosed(e); } private void TryInitializeUiAccess() { if (_uiAccessDll != IntPtr.Zero) { return; } var dllPath = EnsureUiAccessExtracted(); if (dllPath == null) { return; } _uiAccessDll = LoadLibraryW(dllPath); if (_uiAccessDll == IntPtr.Zero) { return; } var proc = GetProcAddress(_uiAccessDll, "IsUIAccess"); if (proc == IntPtr.Zero) { FreeLibrary(_uiAccessDll); _uiAccessDll = IntPtr.Zero; return; } _isUiAccess = Marshal.GetDelegateForFunctionPointer(proc); _uiAccessEnabled = _isUiAccess(); } private void TryInitializeIcon() { try { using (var stream = GetEmbeddedResourceStream(IconResourceSuffix)) { if (stream == null) { return; } using (var icon = new Icon(stream)) { Icon = (Icon)icon.Clone(); } } } catch { } } private void ShowAboutDialog() { var dialog = new Form { Text = "关于", StartPosition = FormStartPosition.CenterParent, FormBorderStyle = FormBorderStyle.FixedDialog, MaximizeBox = false, MinimizeBox = false, ShowInTaskbar = false, ClientSize = new Size(520, 420) }; var textBox = new TextBox { Multiline = true, ReadOnly = true, ScrollBars = ScrollBars.Vertical, Dock = DockStyle.Fill, Font = new Font("Microsoft YaHei UI", 10F), BackColor = SystemColors.Window, BorderStyle = BorderStyle.None }; var buildTime = File.Exists(Application.ExecutablePath) ? File.GetLastWriteTime(Application.ExecutablePath).ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture) : "未知"; textBox.Text = string.Join(Environment.NewLine, new[] { "善学教育积分卡汇率计算器", "版本号: 1.0.0", "作者: ZerkyLiu", $"构建时间:{buildTime}", "运行环境:.NET 4.7", $"系统信息:{Environment.OSVersion}", $"UIAccess状态: {(_uiAccessEnabled ? "已启用" : "未启用")}", "", "功能亮点:", "• 动态自适应结果展示", "• 网络时间同步", "• 高精度阶梯换算", "• UIAccess 置顶支持", "", "未来计划:", "• 联网自动更新", "• 修复已知问题" }); dialog.Controls.Add(textBox); dialog.ShowDialog(this); } private bool TryRelaunchWithUiAccess() { if (_uiAccessEnabled) { return false; } var args = Environment.GetCommandLineArgs(); foreach (var arg in args) { if (string.Equals(arg, UiAccessRelaunchArg, StringComparison.OrdinalIgnoreCase)) { return false; } } var dllPath = EnsureUiAccessExtracted(); if (string.IsNullOrEmpty(dllPath)) { return false; } var exePath = Application.ExecutablePath; var forwardedArgs = BuildForwardedArgs(args); var startInfo = new ProcessStartInfo { FileName = "rundll32.exe", Arguments = $"\"{dllPath}\",run \"{exePath}\" {forwardedArgs} {UiAccessRelaunchArg}", UseShellExecute = false, CreateNoWindow = true }; try { Process.Start(startInfo); BeginInvoke(new Action(Close)); return true; } catch { return false; } } private string? EnsureUiAccessExtracted() { if (!string.IsNullOrEmpty(_uiAccessDllPath) && File.Exists(_uiAccessDllPath)) { return _uiAccessDllPath; } var tempDir = Path.Combine(Path.GetTempPath(), "善学教育积分卡汇率计算器"); var dllPath = Path.Combine(tempDir, $"uiaccess_{Process.GetCurrentProcess().Id}.dll"); try { Directory.CreateDirectory(tempDir); if (File.Exists(dllPath)) { _uiAccessDllPath = dllPath; return dllPath; } using (var stream = GetEmbeddedResourceStream(UiAccessResourceSuffix)) { if (stream == null) { return null; } using (var file = new FileStream(dllPath, FileMode.CreateNew, FileAccess.Write, FileShare.ReadWrite)) { stream.CopyTo(file); } } _uiAccessDllPath = dllPath; return dllPath; } catch { if (File.Exists(dllPath)) { _uiAccessDllPath = dllPath; return dllPath; } return null; } } private static Stream? GetEmbeddedResourceStream(string suffix) { var assembly = typeof(MainForm).Assembly; var resourceName = assembly.GetManifestResourceNames() .FirstOrDefault(name => name.EndsWith(suffix, StringComparison.OrdinalIgnoreCase)); if (resourceName == null) { return null; } return assembly.GetManifestResourceStream(resourceName); } private static string BuildForwardedArgs(string[] args) { if (args.Length <= 1) { return string.Empty; } var builder = new System.Text.StringBuilder(); for (var i = 1; i < args.Length; i++) { if (string.Equals(args[i], UiAccessRelaunchArg, StringComparison.OrdinalIgnoreCase)) { continue; } if (builder.Length > 0) { builder.Append(' '); } builder.Append(QuoteArg(args[i])); } return builder.ToString(); } private static string QuoteArg(string value) { if (string.IsNullOrEmpty(value)) { return "\"\""; } if (value.IndexOfAny(new[] { ' ', '\t', '"', '\\' }) == -1) { return value; } var escaped = value.Replace("\\", "\\\\").Replace("\"", "\\\""); return $"\"{escaped}\""; } private void ApplyTopMost(bool enabled) { TopMost = enabled; if (!enabled) { SetFallbackTopMost(false); SetUiAccessTopMost(false); if (_uiAccessEnabled && IsHandleCreated) { SetWindowPos(Handle, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); } return; } if (_forceFallbackTopMost) { SetFallbackTopMost(true); ApplyFallbackTopMostTick(); return; } if (_uiAccessEnabled && IsHandleCreated) { SetFallbackTopMost(false); SetUiAccessTopMost(true); SetWindowPos(Handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); ApplyFallbackTopMostTick(); return; } SetUiAccessTopMost(false); SetFallbackTopMost(true); ApplyFallbackTopMostTick(); } private void SetUiAccessTopMost(bool enabled) { if (_usingUiAccessTopMost == enabled) { return; } _usingUiAccessTopMost = enabled; if (enabled) { _fallbackTopMostTimer.Start(); } else if (!_usingFallbackTopMost) { _fallbackTopMostTimer.Stop(); } } private void SetFallbackTopMost(bool enabled) { if (_usingFallbackTopMost == enabled) { return; } _usingFallbackTopMost = enabled; _topMostToggle.Text = enabled ? "超级置顶(备)" : "超级置顶"; if (enabled) { _fallbackTopMostTimer.Start(); } else if (!_usingUiAccessTopMost) { _fallbackTopMostTimer.Stop(); } } private void ApplyFallbackTopMostTick() { if ((!_usingFallbackTopMost && !_usingUiAccessTopMost) || !IsHandleCreated) { return; } TopMost = true; SetWindowPos(Handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); } [UnmanagedFunctionPointer(CallingConvention.Winapi)] private delegate bool IsUIAccessDelegate(); [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] private static extern IntPtr LoadLibraryW(string lpFileName); [DllImport("kernel32.dll", SetLastError = true)] private static extern IntPtr GetProcAddress(IntPtr hModule, string procName); [DllImport("kernel32.dll", SetLastError = true)] private static extern bool FreeLibrary(IntPtr hModule); [DllImport("user32.dll", SetLastError = true)] private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags); 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; } }