Files
SteamTools/System.Common.DesktopLib/Application/Services/Implementation/WindowsDesktopPlatformServiceImpl.cs
T
2021-02-20 16:45:44 +08:00

363 lines
15 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#pragma warning disable CA1416 // 验证平台兼容性
#pragma warning disable CA1806 // 不要忽略方法结果
using Microsoft.Win32;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Management;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Windows;
namespace System.Application.Services.Implementation
{
internal sealed class WindowsDesktopPlatformServiceImpl : DesktopPlatformServiceImpl
{
#region 窗口右上角的三个按钮(最小化,最大化,关闭)
// https://stackoverflow.com/questions/339620/how-do-i-remove-minimize-and-maximize-from-a-resizable-window-in-wpf
// https://blog.magnusmontin.net/2014/11/30/disabling-or-hiding-the-minimize-maximize-or-close-button-of-a-wpf-window/
public const int MF_BYCOMMAND = 0x00000000;
public const int SC_CLOSE = 0xF060;
public const int SC_MINIMIZE = 0xF020;
public const int SC_MAXIMIZE = 0xF030;
[DllImport("user32.dll")]
public static extern int DeleteMenu(IntPtr hMenu, int nPosition, int wFlags);
[DllImport("user32.dll")]
public static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
public const int GWL_STYLE = -16,
WS_MAXIMIZEBOX = 0x10000,
WS_MINIMIZEBOX = 0x20000;
[DllImport("user32.dll")]
public extern static int GetWindowLong(IntPtr hwnd, int index);
[DllImport("user32.dll")]
public extern static int SetWindowLong(IntPtr hwnd, int index, int value);
public const int WS_SYSMENU = 0x80000;
public static void EnableMinimizeButton(IntPtr hWnd)
{
SetWindowLong(hWnd, GWL_STYLE, GetWindowLong(hWnd, GWL_STYLE) | WS_MINIMIZEBOX);
}
public static void EnableMaximizeButton(IntPtr hWnd)
{
SetWindowLong(hWnd, GWL_STYLE, GetWindowLong(hWnd, GWL_STYLE) | WS_MAXIMIZEBOX);
}
public static void ShowMinimizeAndMaximizeButtons(IntPtr hWnd)
{
SetWindowLong(hWnd, GWL_STYLE, GetWindowLong(hWnd, GWL_STYLE) | WS_MAXIMIZEBOX | WS_MINIMIZEBOX);
}
public static void ShowAllButtons(IntPtr hWnd)
{
SetWindowLong(hWnd, GWL_STYLE, GetWindowLong(hWnd, GWL_STYLE) | WS_SYSMENU);
}
public static void HideMinimizeAndMaximizeButtons(IntPtr hWnd)
{
SetWindowLong(hWnd, GWL_STYLE, GetWindowLong(hWnd, GWL_STYLE) & ~WS_MAXIMIZEBOX & ~WS_MINIMIZEBOX);
}
public static void DisableMinimizeButton(IntPtr hWnd)
{
SetWindowLong(hWnd, GWL_STYLE, GetWindowLong(hWnd, GWL_STYLE) & ~WS_MINIMIZEBOX);
}
public static void DisableMaximizeButton(IntPtr hWnd)
{
SetWindowLong(hWnd, GWL_STYLE, GetWindowLong(hWnd, GWL_STYLE) & ~WS_MAXIMIZEBOX);
}
public override void SetResizeMode(IntPtr hWnd, ResizeMode value)
{
switch (value)
{
case ResizeMode.NoResize:
DeleteMenu(GetSystemMenu(hWnd, false), SC_MAXIMIZE, MF_BYCOMMAND);
DeleteMenu(GetSystemMenu(hWnd, false), SC_MINIMIZE, MF_BYCOMMAND);
HideMinimizeAndMaximizeButtons(hWnd);
break;
case ResizeMode.CanMinimize:
DeleteMenu(GetSystemMenu(hWnd, false), SC_MAXIMIZE, MF_BYCOMMAND);
DisableMaximizeButton(hWnd);
break;
}
}
#endregion
#region 硬件信息
static IEnumerable<string> GetValueByManagementClass_(string managementClassPath, string propertiesName)
{
using var managementClass = new ManagementClass(managementClassPath);
using var instances = managementClass.GetInstances();
foreach (var instance in instances)
{
var value = instance.Properties[propertiesName].Value?.ToString()?.Trim();
if (string.IsNullOrWhiteSpace(value)) continue;
yield return value;
}
}
public static string[] GetValueByManagementClass(string managementClassPath, string propertiesName)
=> new HashSet<string>(GetValueByManagementClass_(managementClassPath, propertiesName)).ToArray();
public override string[] GetCPUName() => GetValueByManagementClass("Win32_Processor", "Name");
public override string[] GetGPUName() => GetValueByManagementClass("Win32_VideoController", "Name");
#endregion
#region OS
const int OS_ANYSERVER = 29;
/// <summary>
/// https://msdn.microsoft.com/en-us/library/bb773795%28v=VS.85%29.aspx?f=255&MSPPError=-2147217396
/// </summary>
/// <param name="os"></param>
/// <returns></returns>
[DllImport("shlwapi.dll", SetLastError = true, EntryPoint = "#437")]
static extern bool IsOS(int os);
static bool IsWindowsServer_() => IsOS(OS_ANYSERVER);
static readonly Lazy<bool> mIsWindowsServer = new Lazy<bool>(IsWindowsServer_);
public override bool IsWindowsServer() => mIsWindowsServer.Value;
static string GetOSVersion_()
{
try
{
const string subkey = @"SOFTWARE\Microsoft\Windows NT\CurrentVersion";
using var ndpKey = Registry.LocalMachine.OpenSubKey(subkey);
var productName = ndpKey?.GetValue("ProductName")?.ToString();
var major = Environment.OSVersion.Version.Major.ToString();
var minor = Environment.OSVersion.Version.Minor.ToString();
var build = Environment.OSVersion.Version.Build.ToString();
var revision = ndpKey?.GetValue("UBR")?.ToString();
if (string.IsNullOrEmpty(revision))
{
var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.SystemX86),
"kernel32.dll");
revision = File.Exists(path)
? FileVersionInfo.GetVersionInfo(path).ProductPrivatePart.ToString()
: null;
}
var releaseId = ndpKey?.GetValue("ReleaseId")?.ToString();
if (int.TryParse(releaseId, out var releaseIdInt32) && releaseIdInt32 >= 2009)
{
var displayVersion = ndpKey?.GetValue("DisplayVersion")?.ToString();
if (!string.IsNullOrWhiteSpace(displayVersion))
releaseId = displayVersion;
}
var servicePack = Environment.OSVersion.ServicePack;
var additional = string.IsNullOrEmpty(servicePack) ? null : " " + servicePack;
if (releaseId != null)
additional += " (" + releaseId + ")";
return $"{productName} {major}.{minor}.{build}{(string.IsNullOrEmpty(revision) ? null : "." + revision)}{additional}";
}
catch (Exception)
{
return Environment.OSVersion.VersionString;
}
}
static readonly Lazy<string> mGetOSVersion = new Lazy<string>(GetOSVersion_);
public override string GetOSVersion() => mGetOSVersion.Value;
#endregion
#region Process
[DllImport("kernel32.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool IsWow64Process([In] IntPtr process, [Out] out bool wow64Process);
protected override bool Is64Bit_(Process process)
{
// if this method is not available in your version of .NET, use GetNativeSystemInfo via P/Invoke instead
if (!IsWow64Process(process.Handle, out var isWow64))
throw new Win32Exception("function fails, the return value is zero. To get extended error information, call GetLastError.");
return !isWow64;
}
#endregion
#region Administrator
static readonly Lazy<bool> mIsAdministrator = new Lazy<bool>(IsAdministrator_);
static bool IsAdministrator_()
=> new WindowsPrincipal(WindowsIdentity.GetCurrent())
.IsInRole(WindowsBuiltInRole.Administrator);
/// <summary>
/// 是否是管理员权限
/// </summary>
/// <returns></returns>
public override bool IsAdministrator() => mIsAdministrator.Value;
#endregion
#region Fluent Design System
public override void Enable_Fluent_Design_System_Style_Blur(IntPtr hWnd)
{
WindowComposition.Set(hWnd, AccentState.ACCENT_ENABLE_BLURBEHIND, AccentFlags.DrawAllBorders);
}
internal static class WindowComposition
{
[DllImport("user32.dll")]
internal static extern int SetWindowCompositionAttribute(IntPtr hwnd, ref WindowCompositionAttributeData data);
internal static void Set(IntPtr hWnd, AccentState accentState, AccentFlags accentFlags)
{
var accent = new AccentPolicy
{
AccentState = accentState,
AccentFlags = accentFlags,
};
var accentStructSize = Marshal.SizeOf(accent);
var accentPtr = Marshal.AllocHGlobal(accentStructSize);
Marshal.StructureToPtr(accent, accentPtr, false);
var data = new WindowCompositionAttributeData
{
Attribute = WindowCompositionAttribute.WCA_ACCENT_POLICY,
SizeOfData = accentStructSize,
Data = accentPtr,
};
SetWindowCompositionAttribute(hWnd, ref data);
Marshal.FreeHGlobal(accentPtr);
}
}
internal enum AccentState
{
ACCENT_DISABLED = 1,
ACCENT_ENABLE_GRADIENT = 0,
ACCENT_ENABLE_TRANSPARENTGRADIENT = 2,
ACCENT_ENABLE_BLURBEHIND = 3,
ACCENT_INVALID_STATE = 4
}
[Flags]
internal enum AccentFlags
{
DrawLeftBorder = 0x20,
DrawTopBorder = 0x40,
DrawRightBorder = 0x80,
DrawBottomBorder = 0x100,
DrawTopLeftBorder = (DrawLeftBorder | DrawTopBorder),
DrawTopRightBorder = (DrawTopBorder | DrawRightBorder),
DrawBottomLeftBorder = (DrawLeftBorder | DrawBottomBorder),
DrawBottomRightBorder = (DrawRightBorder | DrawBottomBorder),
DrawAllBorders = (DrawLeftBorder | DrawTopBorder | DrawRightBorder | DrawBottomBorder)
}
[StructLayout(LayoutKind.Sequential)]
internal struct AccentPolicy
{
public AccentState AccentState;
public AccentFlags AccentFlags;
public int GradientColor;
public int AnimationId;
}
internal enum WindowCompositionAttribute
{
WCA_UNDEFINED = 0,
WCA_NCRENDERING_ENABLED = 1,
WCA_NCRENDERING_POLICY = 2,
WCA_TRANSITIONS_FORCEDISABLED = 3,
WCA_ALLOW_NCPAINT = 4,
WCA_CAPTION_BUTTON_BOUNDS = 5,
WCA_NONCLIENT_RTL_LAYOUT = 6,
WCA_FORCE_ICONIC_REPRESENTATION = 7,
WCA_EXTENDED_FRAME_BOUNDS = 8,
WCA_HAS_ICONIC_BITMAP = 9,
WCA_THEME_ATTRIBUTES = 10,
WCA_NCRENDERING_EXILED = 11,
WCA_NCADORNMENTINFO = 12,
WCA_EXCLUDED_FROM_LIVEPREVIEW = 13,
WCA_VIDEO_OVERLAY_ACTIVE = 14,
WCA_FORCE_ACTIVEWINDOW_APPEARANCE = 15,
WCA_DISALLOW_PEEK = 16,
WCA_CLOAK = 17,
WCA_CLOAKED = 18,
WCA_ACCENT_POLICY = 19,
WCA_FREEZE_REPRESENTATION = 20,
WCA_EVER_UNCLOAKED = 21,
WCA_VISUAL_OWNER = 22,
WCA_LAST = 23
}
[StructLayout(LayoutKind.Sequential)]
internal struct WindowCompositionAttributeData
{
public WindowCompositionAttribute Attribute;
public IntPtr Data;
public int SizeOfData;
}
#endregion
#region NETFramework Win32 Registry
static string GetDescription(int releaseKey) => releaseKey switch
{
// https://docs.microsoft.com/zh-cn/dotnet/framework/migration-guide/how-to-determine-which-versions-are-installed
378389 => ".NET Framework 4.5",
378675 => "使用 Windows 8.1 或 Windows Server 2012 R2 安装的 .NET Framework 4.5.1",
378758 => "安装在 Windows 8、Windows 7 SP1 或 Windows Vista SP2 上的 .NET Framework 4.5.1",
379893 => ".NET Framework 4.5.2",
393295 => "随 Windows 10 一起安装的 .NET Framework 4.6",
393297 => "在所有其他 Windows 操作系统版本上安装的 .NET Framework 4.6",
394254 => "Windows 10 上安装的 .NET Framework 4.6.1",
394271 => "在所有其他 Windows 操作系统版本上安装的 .NET Framework 4.6.1",
394802 => "在 Windows 10 周年更新上安装的 .NET Framework 4.6.2",
394806 => "在所有其他 Windows 操作系统版本上安装的 .NET Framework 4.6.2",
460798 => "在 Windows 10 创意者更新上安装的 .NET Framework 4.7",
460805 => "在所有其他 Windows 操作系统版本上安装的 .NET Framework 4.7",
461308 => ".NET framework 4.7.1 Windows 10 秋季创意者 Update 上安装",
461310 => ".NET framework 4.7.1 所有其他 Windows 操作系统版本上安装",
461808 => "在 Windows 10 2018 年 4 月更新和 Windows Server 版本 1803 上安装的 .NET Framework 4.7.2 ",
461814 => "在除 Windows 10 2018 年 4 月更新和 Windows Server 版本 1803 之外的所有 Windows 操作系统上安装的 .NET Framework 4.7.2",
528040 => "在 Windows 10 2019 年 5 月更新和 Windows 10 2019 年 11 月更新上安装的 .NET Framework 4.8",
528372 => "在 Windows 10 2020 年 5 月更新上安装的 .NET Framework 4.8",
528049 => "在所有其他 Windows 操作系统(包括早期版本的 Windows 10 操作系统)上安装的 .NET Framework 4.8",
_ => "未知版本" + (releaseKey > 528049 ? "(高于 .NET Framework 4.8)" : ""),
};
static readonly Lazy<string> mNETFrameworkDescription = new Lazy<string>(() =>
{
const string subkey = @"SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full";
using var ndpKey = Registry.LocalMachine.OpenSubKey(subkey);
var value = ndpKey?.GetValue("Release")?.ToString();
if (!int.TryParse(value, out var valueInt32)) return string.Empty;
return GetDescription(valueInt32);
});
public override string NETFrameworkDescription => mNETFrameworkDescription.Value;
#endregion
}
}
#pragma warning restore CA1416 // 验证平台兼容性
#pragma warning restore CA1806 // 不要忽略方法结果