diff --git a/WindowsTools/App.xaml b/WindowsTools/App.xaml index d507acfc..8fda2c1f 100644 --- a/WindowsTools/App.xaml +++ b/WindowsTools/App.xaml @@ -6,6 +6,7 @@ + diff --git a/WindowsTools/Properties/AssemblyInfo.cs b/WindowsTools/Properties/AssemblyInfo.cs index 6f339d30..0369f528 100644 --- a/WindowsTools/Properties/AssemblyInfo.cs +++ b/WindowsTools/Properties/AssemblyInfo.cs @@ -5,11 +5,11 @@ [assembly: AssemblyCompany("高怡飞")] [assembly: AssemblyCopyright("Copyright ©2024 高怡飞, All Rights Reserved.")] [assembly: AssemblyDescription("Windows 工具箱")] -[assembly: AssemblyFileVersion("3.2.1128.0")] -[assembly: AssemblyInformationalVersion("3.2.1128.0")] +[assembly: AssemblyFileVersion("3.2.1204.0")] +[assembly: AssemblyInformationalVersion("3.2.1204.0")] [assembly: AssemblyProduct("Windows 工具箱")] [assembly: AssemblyTitle("Windows 工具箱")] -[assembly: AssemblyVersion("3.2.1128.0")] +[assembly: AssemblyVersion("3.2.1204.0")] // 应用程序默认区域性的资源控制器设置 [assembly: NeutralResourcesLanguage("en-us")] diff --git a/WindowsTools/Styles/Button.xaml b/WindowsTools/Styles/Button.xaml new file mode 100644 index 00000000..2a9de1de --- /dev/null +++ b/WindowsTools/Styles/Button.xaml @@ -0,0 +1,198 @@ + + + + + + #C42B1C + + + + + + + + Transparent + Black + Black + + + + + + + + #00C42B1C + + + + + #C42B1C + + + + + + + + Transparent + White + White + + + + + + + + #00C42B1C + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 30.0 + 32.0 + + + diff --git a/WindowsTools/Views/Pages/MainPage.xaml b/WindowsTools/Views/Pages/MainPage.xaml index a3ce0cb3..17fcc339 100644 --- a/WindowsTools/Views/Pages/MainPage.xaml +++ b/WindowsTools/Views/Pages/MainPage.xaml @@ -9,47 +9,17 @@ xmlns:root="using:WindowsTools.Services.Root" xmlns:service="using:WindowsTools.Services.Controls.Settings" x:Name="MainContent" - ActualThemeChanged="{x:Bind OnActualThemeChanged}" FlowDirection="{x:Bind GetControlDirection(service:LanguageService.RightToLeft)}" RequestedTheme="{x:Bind WindowTheme, Mode=OneWay}" mc:Ignorable="d"> - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + public sealed partial class MainPage : Page, INotifyPropertyChanged { + private CaptionButton? pressedButton; + + // 用于避免重复设置状态 + private bool allInNormal = true; + + private bool isWindowActive = true; + private ElementTheme _windowTheme; public ElementTheme WindowTheme @@ -58,6 +65,22 @@ public bool IsBackEnabled } } + private bool _isWindowMinimizeEnabled; + + public bool IsWindowMinimizeEnabled + { + get { return _isWindowMinimizeEnabled; } + + set + { + if (!Equals(_isWindowMinimizeEnabled, value)) + { + _isWindowMinimizeEnabled = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsWindowMaximized))); + } + } + } + private bool _isWindowMaximized; public bool IsWindowMaximized @@ -158,18 +181,6 @@ protected override void OnKeyDown(KeyRoutedEventArgs args) #endregion 第一部分:重写父类事件 - #region 第二部分:窗口内容挂载的事件 - - /// - /// 应用主题发生变化时修改应用的背景色 - /// - private void OnActualThemeChanged(FrameworkElement sender, object args) - { - MainWindow.Current.SetTitleBarColor(sender.ActualTheme); - } - - #endregion 第二部分:窗口内容挂载的事件 - #region 第三部分:窗口右键菜单事件 /// @@ -622,6 +633,215 @@ await Task.Run(() => } } + /// + /// 修改窗口按钮的激活状态 + /// + public void ChangeButtonActiveState(bool value) + { + isWindowActive = value; + string activeState = value ? "Normal" : "NotActive"; + + VisualStateManager.GoToState(this, value ? "Active" : "NotActive", false); + + if (IsWindowMinimizeEnabled) + { + VisualStateManager.GoToState(MinimizeButton, activeState, false); + } + + if (IsWindowMaximizeEnabled) + { + VisualStateManager.GoToState(MaximizeButton, activeState, false); + } + + VisualStateManager.GoToState(CloseButton, activeState, false); + } + + /// + /// 修改按钮的可用状态 + /// + public void ChangeButtonEnabledState(CaptionButton button, bool enabled) + { + if (button is CaptionButton.Minimize) + { + if (enabled) + { + VisualStateManager.GoToState(MinimizeButton, "Normal", false); + } + else + { + VisualStateManager.GoToState(MinimizeButton, "Disabled", false); + } + } + else if (button is CaptionButton.Maximize) + { + if (enabled) + { + VisualStateManager.GoToState(MaximizeButton, "Normal", false); + } + else + { + VisualStateManager.GoToState(MaximizeButton, "Disabled", false); + } + } + } + + /// + /// 修改最大化按钮的图标 + /// + public void ChangeMaximizeButtonIcon(bool isMaximized) + { + VisualStateManager.GoToState(MaximizeButton, isMaximized ? "WindowStateMaximized" : "WindowStateNormal", false); + } + + /// + /// 鼠标移动到某个按钮上时调用 + /// + public void HoverButton(CaptionButton button) + { + if (pressedButton is not null && pressedButton.HasValue) + { + bool hoveringOnPressedButton = pressedButton.Value == button; + allInNormal = !hoveringOnPressedButton; + + if (IsWindowMinimizeEnabled) + { + VisualStateManager.GoToState(MinimizeButton, hoveringOnPressedButton && button is CaptionButton.Minimize ? "Pressed" : "Normal", false); + } + + if (IsWindowMaximizeEnabled) + { + VisualStateManager.GoToState(MaximizeButton, hoveringOnPressedButton && button is CaptionButton.Maximize ? "Pressed" : "Normal", false); + } + + VisualStateManager.GoToState(CloseButton, hoveringOnPressedButton && button is CaptionButton.Close ? "Pressed" : "Normal", false); + } + else + { + allInNormal = false; + string activeState = isWindowActive ? "Normal" : "NotActive"; + + if (IsWindowMinimizeEnabled) + { + VisualStateManager.GoToState(MinimizeButton, button is CaptionButton.Minimize ? "PointerOver" : activeState, false); + } + + if (IsWindowMaximizeEnabled) + { + VisualStateManager.GoToState(MaximizeButton, button is CaptionButton.Maximize ? "PointerOver" : activeState, false); + } + + VisualStateManager.GoToState(CloseButton, button is CaptionButton.Close ? "PointerOver" : activeState, false); + } + } + + /// + /// 在某个按钮上按下鼠标时调用 + /// + public void PressButton(CaptionButton button) + { + allInNormal = false; + pressedButton = button; + + if (IsWindowMinimizeEnabled) + { + VisualStateManager.GoToState(MinimizeButton, button is CaptionButton.Minimize ? "Pressed" : "Normal", false); + } + + if (IsWindowMaximizeEnabled) + { + VisualStateManager.GoToState(MaximizeButton, button is CaptionButton.Maximize ? "Pressed" : "Normal", false); + } + + VisualStateManager.GoToState(CloseButton, button is CaptionButton.Close ? "Pressed" : "Normal", false); + } + + /// + /// 在标题栏按钮上释放鼠标时调用 + /// + public void ReleaseButton(CaptionButton button) + { + // 在某个按钮上按下然后释放视为点击,即使中途离开过也是如此,因为 HoverButton 和 + // LeaveButtons 都不改变 _pressedButton + bool clicked = pressedButton.HasValue && pressedButton.Value == button; + + if (clicked) + { + // 用户点击了某个按钮 + switch (pressedButton.Value) + { + case CaptionButton.Minimize: + { + User32Library.PostMessage(MainWindow.Current.Handle, WindowMessage.WM_SYSCOMMAND, new UIntPtr((uint)SYSTEMCOMMAND.SC_MINIMIZE), IntPtr.Zero); + break; + } + case CaptionButton.Maximize: + { + User32Library.PostMessage(MainWindow.Current.Handle, WindowMessage.WM_SYSCOMMAND, IsWindowMaximized ? new UIntPtr((uint)SYSTEMCOMMAND.SC_RESTORE) : new UIntPtr((uint)SYSTEMCOMMAND.SC_MAXIMIZE), IntPtr.Zero); + break; + } + case CaptionButton.Close: + { + User32Library.PostMessage(MainWindow.Current.Handle, WindowMessage.WM_SYSCOMMAND, new UIntPtr((uint)SYSTEMCOMMAND.SC_CLOSE), IntPtr.Zero); + break; + } + } + } + + pressedButton = null; + + // 如果点击了某个按钮则清空状态,因为此时窗口状态已改变。如果在某个按钮上按下然后在 + allInNormal = clicked; + + if (IsWindowMinimizeEnabled) + { + VisualStateManager.GoToState(MinimizeButton, !clicked && button is CaptionButton.Minimize ? "PointerOver" : "Normal", false); + } + + if (IsWindowMaximizeEnabled) + { + VisualStateManager.GoToState(MaximizeButton, !clicked && button is CaptionButton.Maximize ? "PointerOver" : "Normal", false); + } + + VisualStateManager.GoToState(CloseButton, !clicked && button is CaptionButton.Close ? "PointerOver" : "Normal", false); + } + + /// + /// 在非标题按钮上释放鼠标时调用 + /// + public void ReleaseButtons() + { + if (pressedButton is not null || pressedButton.HasValue) + { + pressedButton = null; + LeaveButtons(); + } + } + + /// + /// 离开标题按钮时调用,不更改 PressedButton + /// + public void LeaveButtons() + { + if (!allInNormal) + { + allInNormal = true; + + string activeState = isWindowActive ? "Normal" : "NotActive"; + + if (IsWindowMinimizeEnabled) + { + VisualStateManager.GoToState(MinimizeButton, activeState, true); + } + + if (IsWindowMaximizeEnabled) + { + VisualStateManager.GoToState(MaximizeButton, activeState, true); + } + + VisualStateManager.GoToState(CloseButton, activeState, true); + } + } + /// /// 获取控件的文字转向 /// diff --git a/WindowsTools/Views/Windows/MainWindow.cs b/WindowsTools/Views/Windows/MainWindow.cs index e4633847..0f37460b 100644 --- a/WindowsTools/Views/Windows/MainWindow.cs +++ b/WindowsTools/Views/Windows/MainWindow.cs @@ -1,18 +1,20 @@ -using Microsoft.UI.Windowing; -using Mile.Xaml; +using Mile.Xaml; using Mile.Xaml.Interop; using System; using System.Collections.Generic; using System.ComponentModel; +using System.Diagnostics; +using System.Drawing; +using System.Drawing.Printing; using System.Reflection; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; -using Windows.UI; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls.Primitives; +using Windows.UI.Xaml.Hosting; using Windows.UI.Xaml.Media; using WindowsTools.Helpers.Root; using WindowsTools.Services.Controls.Download; @@ -21,12 +23,14 @@ using WindowsTools.UI.Backdrop; using WindowsTools.Views.Pages; using WindowsTools.WindowsAPI.PInvoke.Comctl32; +using WindowsTools.WindowsAPI.PInvoke.Dwmapi; +using WindowsTools.WindowsAPI.PInvoke.Gdi32; using WindowsTools.WindowsAPI.PInvoke.Shell32; using WindowsTools.WindowsAPI.PInvoke.User32; using WindowsTools.WindowsAPI.PInvoke.Uxtheme; -// 抑制 CA1806 警告 -#pragma warning disable CA1806 +// 抑制 CA1806,CA1822 警告 +#pragma warning disable CA1806,CA1822 namespace WindowsTools.Views.Windows { @@ -35,12 +39,22 @@ namespace WindowsTools.Views.Windows /// public class MainWindow : Form { - private readonly IntPtr inputNonClientPointerSourceHandle; + private readonly IntPtr hwndTitleBar; + private readonly IntPtr hwndXamlIsland; + private readonly IntPtr hwndMaximizeButton; + private readonly int AUTO_HIDE_TASKBAR_HEIGHT = 2; + private readonly int lightTintColor = ColorTranslator.ToWin32(Color.FromArgb(243, 243, 243)); + private readonly int darkTintColor = ColorTranslator.ToWin32(System.Drawing.Color.FromArgb(32, 32, 32)); private readonly Container components = new(); - private readonly WindowsXamlHost windowsXamlHost = new(); - private readonly SUBCLASSPROC inputNonClientPointerSourceSubClassProc; + private readonly DesktopWindowXamlSource desktopWindowXamlSource = new(); + private readonly WNDPROC TitleBarWndProc; + private readonly SUBCLASSPROC windowClassProc; - private AppWindow AppWindow { get; } + private readonly bool isDarkTheme = false; + private bool trackingMouse = false; + private int nativeTopBorderHeight = 1; + + public bool ExtendsContentIntoTitleBar { get; set; } = true; public UIElement Content { get; set; } @@ -52,15 +66,19 @@ public MainWindow() AutoScaleMode = AutoScaleMode.Font; Current = this; Content = new MainPage(); - Controls.Add(windowsXamlHost); - Icon = System.Drawing.Icon.ExtractAssociatedIcon(System.Windows.Forms.Application.ExecutablePath); - MinimumSize = new System.Drawing.Size(Convert.ToInt32(1024 * ((double)DeviceDpi) / 96), Convert.ToInt32(768 * ((double)DeviceDpi / 96))); - Size = new System.Drawing.Size(Convert.ToInt32(1024 * ((double)DeviceDpi) / 96), Convert.ToInt32(768 * ((double)DeviceDpi / 96))); + + desktopWindowXamlSource.Content = Content; + IDesktopWindowXamlSourceNative2 desktopWindowXamlSourceInterop = desktopWindowXamlSource.GetInterop(); + desktopWindowXamlSourceInterop.AttachToWindow(Handle); + hwndXamlIsland = desktopWindowXamlSourceInterop.GetWindowHandle(); + desktopWindowXamlSource.TakeFocusRequested += OnTakeFocusRequested; + + Icon = Icon.ExtractAssociatedIcon(System.Windows.Forms.Application.ExecutablePath); + MinimumSize = new Size(Convert.ToInt32(1024 * ((double)DeviceDpi) / 96), Convert.ToInt32(768 * ((double)DeviceDpi / 96))); + Size = new Size(Convert.ToInt32(1024 * ((double)DeviceDpi) / 96), Convert.ToInt32(768 * ((double)DeviceDpi / 96))); StartPosition = FormStartPosition.CenterScreen; Text = ResourceService.WindowResource.GetString("AppTitle"); - windowsXamlHost.AutoSize = true; - windowsXamlHost.Dock = DockStyle.Fill; - windowsXamlHost.Child = Content; + RightToLeft = LanguageService.RightToLeft; RightToLeftLayout = LanguageService.RightToLeft is RightToLeft.Yes; @@ -70,24 +88,83 @@ public MainWindow() { cbSize = Marshal.SizeOf() }; - User32Library.ChangeWindowMessageFilterEx(Handle, WindowMessage.WM_DROPFILES, ChangeFilterAction.MSGFLT_ALLOW, in changeFilterStatus); - User32Library.ChangeWindowMessageFilterEx(Handle, WindowMessage.WM_COPYGLOBALDATA, ChangeFilterAction.MSGFLT_ALLOW, in changeFilterStatus); + User32Library.ChangeWindowMessageFilterEx(Handle, WindowMessage.WM_DROPFILES, ChangeFilterAction.MSGFLT_ALLOW, changeFilterStatus); + User32Library.ChangeWindowMessageFilterEx(Handle, WindowMessage.WM_COPYGLOBALDATA, ChangeFilterAction.MSGFLT_ALLOW, changeFilterStatus); Shell32Library.DragAcceptFiles(Handle, true); } - AppWindow = AppWindow.GetFromWindowId(new Microsoft.UI.WindowId() { Value = (ulong)Handle }); - AppWindow.TitleBar.ExtendsContentIntoTitleBar = true; + windowClassProc = new SUBCLASSPROC(OnWindowSubClassProc); + Comctl32Library.SetWindowSubclass(Handle, windowClassProc, 0, IntPtr.Zero); - SetTitleBarColor((Content as FrameworkElement).ActualTheme); + // 注册标题栏窗口类 + TitleBarWndProc = new WNDPROC(OnTitleBarWndProc); + WNDCLASSEX wcex = new() + { + cbSize = Marshal.SizeOf(), + style = WNDCLASS_STYLES.CS_DBLCLKS, + hbrBackground = IntPtr.Zero, + cbClsExtra = 0, + cbWndExtra = 0, + hInstance = Process.GetCurrentProcess().Handle, + hIcon = IntPtr.Zero, + hCursor = Cursors.Arrow.Handle, + lpszMenuName = null, + lpszClassName = "DesktopWindowTitleBarArea", + lpfnWndProc = TitleBarWndProc, + hIconSm = IntPtr.Zero + }; + int result = User32Library.RegisterClassEx(ref wcex); + + // 隐藏原生标题栏上的图标 + WTA_OPTIONS wtaOptions = new() + { + dwFlags = WTNCA.WTNCA_NODRAWCAPTION | WTNCA.WTNCA_NODRAWICON | WTNCA.WTNCA_NOSYSMENU | WTNCA.WTNCA_NOMIRRORHELP, + dwMask = 2 | 4 + }; - inputNonClientPointerSourceHandle = User32Library.FindWindowEx(Handle, IntPtr.Zero, "InputNonClientPointerSource", null); + UxthemeLibrary.SetWindowThemeAttribute(Handle, WINDOWTHEMEATTRIBUTETYPE.WTA_NONCLIENT, ref wtaOptions, (uint)Marshal.SizeOf()); - if (inputNonClientPointerSourceHandle != IntPtr.Zero) + // 1. 刷新窗口边框 + // 2. 防止窗口显示时背景闪烁: https://stackoverflow.com/questions/69715610/how-to-initialize-the-background-color-of-win32-app-to-something-other-than-whit + User32Library.SetWindowPos(Handle, IntPtr.Zero, 0, 0, 0, 0, SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE | SetWindowPosFlags.SWP_FRAMECHANGED | SetWindowPosFlags.SWP_NOACTIVATE | SetWindowPosFlags.SWP_NOCOPYBITS); + + // 创建标题栏窗口,它是主窗口的子窗口。将它置于 XAML Islands 窗口之上以防止鼠标事件被吞掉 + // 出于未知的原因,必须添加 WS_EX_LAYERED 样式才能发挥作用,见 + // https://github.com/microsoft/terminal/blob/0ee2c74cd432eda153f3f3e77588164cde95044f/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp#L79 + // WS_EX_NOREDIRECTIONBITMAP 可以避免 WS_EX_LAYERED 导致的额外内存开销 + // WS_MINIMIZEBOX 和 WS_MAXIMIZEBOX 使得鼠标悬停时显示文字提示,Win11 的贴靠布局不依赖它们 + WindowExStyle windowExStyle = WindowExStyle.WS_EX_LAYERED | WindowExStyle.WS_EX_NOPARENTNOTIFY | WindowExStyle.WS_EX_NOREDIRECTIONBITMAP | WindowExStyle.WS_EX_NOACTIVATE; + if (RightToLeft is RightToLeft.Yes && RightToLeftLayout is true) + { + windowExStyle |= WindowExStyle.WS_EX_LAYOUTRTL; + } + hwndTitleBar = User32Library.CreateWindowEx(windowExStyle, "DesktopWindowTitleBarArea", string.Empty, WindowStyle.WS_CHILD | WindowStyle.WS_MINIMIZEBOX | WindowStyle.WS_MAXIMIZEBOX, 0, 0, 0, 0, Handle, IntPtr.Zero, Process.GetCurrentProcess().Handle, IntPtr.Zero); + User32Library.SetLayeredWindowAttributes(hwndTitleBar, 0, 255, LWA.LWA_ALPHA); + + if (Environment.Version.Build >= 22000) { - inputNonClientPointerSourceSubClassProc = new SUBCLASSPROC(InputNonClientPointerSourceSubClassProc); - Comctl32Library.SetWindowSubclass(inputNonClientPointerSourceHandle, inputNonClientPointerSourceSubClassProc, 0, IntPtr.Zero); + // 如果鼠标正位于一个按钮上,贴靠布局弹窗会出现在按钮下方。我们利用这个特性来修正贴靠布局弹窗的位置 + // FIXME: 以管理员身份运行时这不起作用。Office 也有这个问题,所以可能没有解决方案 + windowExStyle = WindowExStyle.WS_EX_LEFT; + if (RightToLeft is RightToLeft.Yes && RightToLeftLayout is true) + { + windowExStyle |= WindowExStyle.WS_EX_LAYOUTRTL; + } + hwndMaximizeButton = User32Library.CreateWindowEx(windowExStyle, "Button", string.Empty, WindowStyle.WS_VISIBLE | WindowStyle.WS_CHILD | WindowStyle.WS_DISABLED | (WindowStyle)0x0000000B, 0, 0, 0, 0, hwndTitleBar, IntPtr.Zero, Process.GetCurrentProcess().Handle, IntPtr.Zero); } + (Content as MainPage).IsWindowMinimizeEnabled = MinimizeBox; + (Content as MainPage).IsWindowMaximizeEnabled = MaximizeBox; + (Content as MainPage).IsWindowMaximized = User32Library.IsZoomed(Handle); + (Content as MainPage).ChangeButtonEnabledState(CaptionButton.Minimize, MinimizeBox); + (Content as MainPage).ChangeButtonEnabledState(CaptionButton.Maximize, MaximizeBox); + (Content as MainPage).ChangeMaximizeButtonIcon(User32Library.IsZoomed(Handle)); + + (Content as MainPage).AppTitlebar.SizeChanged += (s, e) => + { + ResizeTitleBarWindow(); + }; + AlwaysShowBackdropService.PropertyChanged += OnServicePropertyChanged; ThemeService.PropertyChanged += OnServicePropertyChanged; BackdropService.PropertyChanged += OnServicePropertyChanged; @@ -98,9 +175,12 @@ public MainWindow() User32Library.SetWindowPos(coreWindowhandle, IntPtr.Zero, 0, 0, Size.Width, Size.Height, SetWindowPosFlags.SWP_NOOWNERZORDER); // 修改托管 CoreWindow 窗口的大小和位置 - Form CoreWindowHostWindowForm = typeof(XamlApplicationExtensions).GetField("CoreWindowHostWindow", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null) as Form; - CoreWindowHostWindowForm.Location = Location; - CoreWindowHostWindowForm.Size = Size; + Form coreWindowHostWindowForm = typeof(XamlApplicationExtensions).GetField("CoreWindowHostWindow", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null) as Form; + if (coreWindowHostWindowForm is not null) + { + coreWindowHostWindowForm.Location = Location; + coreWindowHostWindowForm.Size = Size; + } } #region 第一部分:窗口类内置需要重载的事件 @@ -118,16 +198,34 @@ protected override void Dispose(bool disposing) } /// - /// 设备的每英寸像素 (PPI) 显示更改修改后触发的事件 + /// 窗口激活时触发的事件 + /// + protected override void OnActivated(EventArgs args) + { + base.OnActivated(args); + (Content as MainPage).ChangeButtonActiveState(true); + } + + /// + /// 窗口不处于焦点状态时触发的事件 + /// + protected override void OnDeactivate(EventArgs args) + { + base.OnDeactivate(args); + (Content as MainPage).ChangeButtonActiveState(false); + } + + /// + /// 窗口 DPI 发生变化时触发的事件 /// protected override void OnDpiChanged(DpiChangedEventArgs args) { base.OnDpiChanged(args); + Rectangle rectangle = args.SuggestedRectangle; - if (inputNonClientPointerSourceHandle != IntPtr.Zero && Width is not 0) - { - User32Library.SetWindowPos(inputNonClientPointerSourceHandle, IntPtr.Zero, (int)(45 * ((double)DeviceDpi / 96)), 0, (int)((Width - 45) * ((double)DeviceDpi / 96)), (int)(45 * ((double)DeviceDpi / 96)), SetWindowPosFlags.SWP_NOOWNERZORDER | SetWindowPosFlags.SWP_NOREDRAW | SetWindowPosFlags.SWP_NOZORDER); - } + UpdateFrameBorderThickness(); + User32Library.SetWindowPos(Handle, IntPtr.Zero, rectangle.Left, rectangle.Top, rectangle.Right - rectangle.Left, rectangle.Bottom - rectangle.Top, SetWindowPosFlags.SWP_NOZORDER | SetWindowPosFlags.SWP_NOACTIVATE + ); } /// @@ -151,10 +249,11 @@ protected override void OnFormClosing(FormClosingEventArgs args) { cbSize = Marshal.SizeOf() }; - User32Library.ChangeWindowMessageFilterEx(Handle, WindowMessage.WM_COPYDATA, ChangeFilterAction.MSGFLT_RESET, in changeFilterStatus); + User32Library.ChangeWindowMessageFilterEx(Handle, WindowMessage.WM_COPYDATA, ChangeFilterAction.MSGFLT_RESET, changeFilterStatus); } - Comctl32Library.RemoveWindowSubclass(inputNonClientPointerSourceHandle, inputNonClientPointerSourceSubClassProc, 0); + desktopWindowXamlSource.Dispose(); + desktopWindowXamlSource.TakeFocusRequested -= OnTakeFocusRequested; ThemeService.PropertyChanged -= OnServicePropertyChanged; BackdropService.PropertyChanged -= OnServicePropertyChanged; TopMostService.PropertyChanged -= OnServicePropertyChanged; @@ -165,29 +264,31 @@ protected override void OnFormClosing(FormClosingEventArgs args) } /// - /// 窗体程序加载时初始化应用程序设置 + /// 窗口创建时触发的事件 /// - protected override void OnLoad(EventArgs args) + protected override void OnHandleCreated(EventArgs args) { - base.OnLoad(args); - SetWindowTheme(); - TopMost = TopMostService.TopMostValue; - SetClassicMenuTheme(); - SetWindowBackdrop(); + base.OnHandleCreated(args); + UpdateFrameBorderThickness(); - if (inputNonClientPointerSourceHandle != IntPtr.Zero && Width is not 0) + if (Environment.Version.Build >= 22000) { - User32Library.SetWindowPos(inputNonClientPointerSourceHandle, IntPtr.Zero, (int)(45 * ((double)DeviceDpi / 96)), 0, (int)((Width - 45) * ((double)DeviceDpi / 96)), (int)(45 * ((double)DeviceDpi / 96)), SetWindowPosFlags.SWP_NOOWNERZORDER | SetWindowPosFlags.SWP_NOREDRAW | SetWindowPosFlags.SWP_NOZORDER); + // 初始化双缓冲绘图 + UxthemeLibrary.BufferedPaintInit(); + UpdateFrameMargins(); } } /// - /// 窗体样式发生改变后的事件 + /// 窗体程序加载时初始化应用程序设置 /// - protected override void OnStyleChanged(EventArgs args) + protected override void OnLoad(EventArgs args) { - base.OnStyleChanged(args); - (Content as MainPage).IsWindowMaximizeEnabled = MaximizeBox; + base.OnLoad(args); + SetWindowTheme(); + TopMost = TopMostService.TopMostValue; + SetClassicMenuTheme(); + SetWindowBackdrop(); } /// @@ -208,10 +309,12 @@ protected override void OnMove(EventArgs args) } // 修改托管 CoreWindow 窗口的大小和位置 - Form CoreWindowHostWindowForm = typeof(XamlApplicationExtensions).GetField("CoreWindowHostWindow", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null) as Form; - - CoreWindowHostWindowForm.Location = Location; - CoreWindowHostWindowForm.Size = Size; + Form coreWindowHostWindowForm = typeof(XamlApplicationExtensions).GetField("CoreWindowHostWindow", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null) as Form; + if (coreWindowHostWindowForm is not null) + { + coreWindowHostWindowForm.Location = Location; + coreWindowHostWindowForm.Size = Size; + } } /// @@ -221,32 +324,21 @@ protected override void OnSizeChanged(EventArgs args) { base.OnSizeChanged(args); - if (Content is not null) + if (Content is not null && Content.XamlRoot is not null) { - (Content as MainPage).IsWindowMaximizeEnabled = MaximizeBox; - (Content as MainPage).IsWindowMaximized = WindowState is FormWindowState.Maximized; - - if (Content.XamlRoot is not null) + foreach (Popup popupRoot in VisualTreeHelper.GetOpenPopupsForXamlRoot(Content.XamlRoot)) { - foreach (Popup popupRoot in VisualTreeHelper.GetOpenPopupsForXamlRoot(Content.XamlRoot)) + // 关闭内容对话框 + if (popupRoot.Child as ContentDialog is not null) { - // 关闭内容对话框 - if (popupRoot.Child as ContentDialog is not null) - { - (popupRoot.Child as ContentDialog).Hide(); - } - - // 关闭浮出控件 - if (popupRoot.Child as FlyoutPresenter is not null) - { - popupRoot.IsOpen = false; - } + (popupRoot.Child as ContentDialog).Hide(); } - } - if (inputNonClientPointerSourceHandle != IntPtr.Zero && Width is not 0) - { - User32Library.SetWindowPos(inputNonClientPointerSourceHandle, IntPtr.Zero, (int)(45 * ((double)DeviceDpi / 96)), 0, (int)((Width - 45) * ((double)DeviceDpi / 96)), (int)(45 * ((double)DeviceDpi / 96)), SetWindowPosFlags.SWP_NOOWNERZORDER | SetWindowPosFlags.SWP_NOREDRAW | SetWindowPosFlags.SWP_NOZORDER); + // 关闭浮出控件 + if (popupRoot.Child as FlyoutPresenter is not null) + { + popupRoot.IsOpen = false; + } } } @@ -255,15 +347,45 @@ protected override void OnSizeChanged(EventArgs args) User32Library.SetWindowPos(coreWindowhandle, IntPtr.Zero, 0, 0, Size.Width, Size.Height, SetWindowPosFlags.SWP_NOOWNERZORDER); // 修改托管 CoreWindow 窗口的大小和位置 - Form CoreWindowHostWindowForm = typeof(XamlApplicationExtensions).GetField("CoreWindowHostWindow", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null) as Form; - CoreWindowHostWindowForm.Location = Location; - CoreWindowHostWindowForm.Size = Size; + Form coreWindowHostWindowForm = typeof(XamlApplicationExtensions).GetField("CoreWindowHostWindow", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null) as Form; + if (coreWindowHostWindowForm is not null) + { + coreWindowHostWindowForm.Location = Location; + coreWindowHostWindowForm.Size = Size; + } + } + + /// + /// 窗口样式发生改变时触发的事件 + /// + protected override void OnStyleChanged(EventArgs args) + { + base.OnStyleChanged(args); + (Content as MainPage).IsWindowMinimizeEnabled = MinimizeBox; + (Content as MainPage).IsWindowMaximizeEnabled = MaximizeBox; + (Content as MainPage).IsWindowMaximized = User32Library.IsZoomed(Handle); + (Content as MainPage).ChangeButtonEnabledState(CaptionButton.Minimize, MinimizeBox); + (Content as MainPage).ChangeButtonEnabledState(CaptionButton.Maximize, MaximizeBox); + (Content as MainPage).ChangeMaximizeButtonIcon(User32Library.IsZoomed(Handle)); } #endregion 第一部分:窗口类内置需要重载的事件 #region 第二部分:自定义事件 + /// + /// 例如,当主机桌面应用程序收到从 DesktopWindowXamlSource 对象 (获取焦点的请求时发生,用户位于 DesktopWindowXamlSource 中的最后一个可聚焦元素上,然后按 Tab) 。 + /// + private void OnTakeFocusRequested(DesktopWindowXamlSource sender, DesktopWindowXamlSourceTakeFocusRequestedEventArgs args) + { + XamlSourceFocusNavigationReason reason = args.Request.Reason; + + if (reason < XamlSourceFocusNavigationReason.Left) + { + sender.NavigateFocus(args.Request); + } + } + /// /// 设置选项发生变化时触发的事件 /// @@ -296,23 +418,324 @@ private void OnServicePropertyChanged(object sender, PropertyChangedEventArgs ar #region 第三部分:窗口过程 /// - /// 处理 Windows 消息 + /// 应用主窗口消息处理 /// - protected override void WndProc(ref Message m) + private IntPtr OnWindowSubClassProc(IntPtr hWnd, WindowMessage Msg, UIntPtr wParam, IntPtr lParam, uint uIdSubclass, IntPtr dwRefData) { - switch (m.Msg) + switch (Msg) { + // 窗口大小发生变化时对应的消息 + case WindowMessage.WM_SIZE: + { + if (wParam.ToUInt32() is not 1) + { + int width = (int)LOWORD((uint)lParam); + int height = (int)HIWORD((uint)lParam); + + if (!User32Library.IsWindowVisible(Handle) && User32Library.IsZoomed(Handle)) + { + // 初始化过程中此函数会被调用两次。如果窗口以最大化显示,则两次传入的尺寸不一致。第一次调用此函数时主窗口尚未显示,因此无法最大化,我们必须估算最大化窗口的尺寸。不执行这个操作可能导致窗口显示时展示 NavigationView 导航展开的动画。 + Screen screen = Screen.FromHandle(Handle); + + if (screen is not null) + { + // 最大化窗口的尺寸为当前屏幕工作区的尺寸 + width = screen.WorkingArea.Right - screen.Bounds.Left; + height = screen.WorkingArea.Bottom - screen.Bounds.Top; + } + } + + (Content as MainPage).IsWindowMaximized = User32Library.IsZoomed(Handle); + (Content as MainPage).ChangeMaximizeButtonIcon(User32Library.IsZoomed(Handle)); + + // Win10 中上边框被涂黑来显示系统原始边框,Win11 中 DWM 绘制的上边框也位于客户区内, + // 很可能是为了和 Win10 兼容。XAML Islands 不应该和上边框重叠。 + int topBorderHeight = GetTopBorderHeight(); + + // SWP_NOZORDER 确保 XAML Islands 窗口始终在标题栏窗口下方,否则主窗口在调整大小时会闪烁 + User32Library.SetWindowPos(hwndXamlIsland, IntPtr.Zero, 0, topBorderHeight, width, height - topBorderHeight, SetWindowPosFlags.SWP_NOACTIVATE | SetWindowPosFlags.SWP_NOZORDER | SetWindowPosFlags.SWP_SHOWWINDOW); + } + ResizeTitleBarWindow(); + return IntPtr.Zero; + } + // 重绘窗口时对应的窗口消息 + case WindowMessage.WM_PAINT: + { + if (Environment.Version.Build < 22621) + { + IntPtr hdc = User32Library.BeginPaint(Handle, out PAINTSTRUCT ps); + if (hdc == IntPtr.Zero) + { + return IntPtr.Zero; + } + + int topBorderHeight = Environment.Version.Build >= 22000 ? 0 : GetTopBorderHeight(); + + // Win10 中在顶部绘制黑色实线以显示系统原始边框,见 UpdateFrameMargins + if (ps.rcPaint.top < topBorderHeight) + { + RECT rcTopBorder = ps.rcPaint; + rcTopBorder.bottom = topBorderHeight; + + IntPtr hBrush = Gdi32Library.GetStockObject(StockObject.BLACK_BRUSH); + User32Library.FillRect(hdc, rcTopBorder, hBrush); + } + + // 绘制客户区,它会在调整窗口尺寸时短暂可见 + // 绘制客户区,它会在调整窗口尺寸时短暂可见 + if (ps.rcPaint.bottom > topBorderHeight) + { + RECT rcRest = ps.rcPaint; + rcRest.top = topBorderHeight; + + if (Content is not null) + { + bool isDarkBrush = (Content as FrameworkElement).ActualTheme is ElementTheme.Dark; + IntPtr backgroundBrush = Gdi32Library.CreateSolidBrush(isDarkBrush ? + darkTintColor : lightTintColor); + + if (isDarkBrush != isDarkTheme) + { + isDarkBrush = isDarkTheme; + Gdi32Library.DeleteObject(backgroundBrush); + backgroundBrush = Gdi32Library.CreateSolidBrush(isDarkBrush ? + darkTintColor : lightTintColor); + } + + if (isDarkBrush && Environment.Version.Build < 22000) + { + // 这里我们想要黑色背景而不是原始边框 + // 来自 https://github.com/microsoft/terminal/blob/0ee2c74cd432eda153f3f3e77588164cde95044f/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp#L1030-L1047 + BP_PAINTPARAMS bp_PaintParams = new() + { + cbSize = (uint)Marshal.SizeOf(), + dwFlags = BPPF.BPPF_NOCLIP | BPPF.BPPF_ERASE + }; + IntPtr buf = UxthemeLibrary.BeginBufferedPaint(hdc, ref rcRest, BP_BUFFERFORMAT.BPBF_TOPDOWNDIB, ref bp_PaintParams, out IntPtr opaqueDc); + if (buf != IntPtr.Zero && opaqueDc != IntPtr.Zero) + { + User32Library.FillRect(opaqueDc, rcRest, backgroundBrush); + RECT rect = new(); + UxthemeLibrary.BufferedPaintSetAlpha(buf, ref rect, 255); + UxthemeLibrary.EndBufferedPaint(buf, true); + } + } + else + { + User32Library.FillRect(hdc, rcRest, backgroundBrush); + } + } + } + User32Library.EndPaint(Handle, ref ps); + return IntPtr.Zero; + } + + break; + } // 应用主题设置跟随系统发生变化时,当系统主题设置发生变化时修改修改应用背景色 - case (int)WindowMessage.WM_SETTINGCHANGE: + case WindowMessage.WM_SETTINGCHANGE: { SetWindowTheme(); SetClassicMenuTheme(); break; } + // 处理非客户区左键按下的窗口消息 + case WindowMessage.WM_NCLBUTTONDOWN: + { + if (Content is not null && Content.XamlRoot is not null) + { + if ((Content as MainPage).TitlebarMenuFlyout.IsOpen) + { + (Content as MainPage).TitlebarMenuFlyout.Hide(); + } + } + break; + } + // 处理客户区右键按下后释放的窗口消息 + case WindowMessage.WM_NCRBUTTONUP: + { + if (ExtendsContentIntoTitleBar && wParam.ToUInt32() == (int)HITTEST.HTCAPTION) + { + // 显示自定义标题栏右键菜单 + if (wParam.ToUInt32() is 2 && Content is not null && Content.XamlRoot is not null) + { + Point clientPoint = PointToClient(Cursor.Position); + FlyoutShowOptions options = new() + { + Placement = FlyoutPlacementMode.BottomEdgeAlignedLeft, + ShowMode = FlyoutShowMode.Standard, + }; + + if (RightToLeft is RightToLeft.Yes) + { + if (InfoHelper.SystemVersion.Build >= 22000) + { + options.Position = new global::Windows.Foundation.Point((ClientSize.Width - clientPoint.X) / ((double)DeviceDpi / 96), clientPoint.Y / ((double)DeviceDpi / 96)); + } + else + { + options.Position = new global::Windows.Foundation.Point(ClientSize.Width - clientPoint.X, clientPoint.Y); + } + } + else + { + if (InfoHelper.SystemVersion.Build >= 22000) + { + options.Position = new global::Windows.Foundation.Point(clientPoint.X / ((double)DeviceDpi / 96), clientPoint.Y / ((double)DeviceDpi / 96)); + } + else + { + options.Position = new global::Windows.Foundation.Point(clientPoint.X, clientPoint.Y); + } + } + + (Content as MainPage).TitlebarMenuFlyout.ShowAt(null, options); + } + + // 我们自己处理标题栏右键,不知为何 DefWindowProc 没有作用 + // 在标题栏上按下右键,在其他地方释放也会收到此消息。确保只有在标题栏上释放时才显示菜单 + User32Library.GetWindowRect(hwndTitleBar, out RECT titleBarRect); + if (!User32Library.PtInRect(ref titleBarRect, Cursor.Position)) + { + break; + } + + return IntPtr.Zero; + } + + break; + } + // 重新计算窗口大小时对应的窗口消息 + case WindowMessage.WM_NCCALCSIZE: + { + if (ExtendsContentIntoTitleBar) + { + // 移除标题栏的逻辑基本来自 Windows Terminal + // https://github.com/microsoft/terminal/blob/0ee2c74cd432eda153f3f3e77588164cde95044f/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp + if (wParam == UIntPtr.Zero) + { + return IntPtr.Zero; + } + + // 保存原始上边框位置 + int originalTop = Marshal.PtrToStructure(lParam).rgrc[0].top; + + // 应用默认边框 + IntPtr result = Comctl32Library.DefSubclassProc(Handle, WindowMessage.WM_NCCALCSIZE, wParam, lParam); + + if (result != IntPtr.Zero) + { + return result; + } + + // 重新应用原始上边框,因此我们完全移除了默认边框中的上边框和标题栏,但保留了其他方向的边框 + NCCALCSIZE_PARAMS nccalcsize_params = Marshal.PtrToStructure(lParam); + RECT clientRect = nccalcsize_params.rgrc[0]; + clientRect.top = originalTop; + + // WM_NCCALCSIZE 在 WM_SIZE 前 + if (User32Library.IsZoomed(Handle)) + { + // 最大化的窗口的实际尺寸比屏幕的工作区更大一点,这是为了将可调整窗口大小的区域隐藏在屏幕外面 + clientRect.top += GetResizeHandleHeight(); + + // 如果有自动隐藏的任务栏,我们在它的方向稍微减小客户区,这样用户就可以用鼠标呼出任务栏 + Screen screen = Screen.FromHandle(Handle); + + // 检查是否有自动隐藏的任务栏 + APPBARDATA appbarData = new() + { + cbSize = Marshal.SizeOf() + }; + + IntPtr appbarResult = Shell32Library.SHAppBarMessage(ABM.ABM_GETSTATE, ref appbarData); + + if (appbarResult != IntPtr.Zero) + { + if (HasAutoHideTaskbar(screen, ABE.Top)) + { + clientRect.top += AUTO_HIDE_TASKBAR_HEIGHT; + } + + if (HasAutoHideTaskbar(screen, ABE.Bottom)) + { + clientRect.bottom -= AUTO_HIDE_TASKBAR_HEIGHT; + } + + if (HasAutoHideTaskbar(screen, ABE.Left)) + { + clientRect.left += AUTO_HIDE_TASKBAR_HEIGHT; + } + + if (HasAutoHideTaskbar(screen, ABE.Right)) + { + clientRect.right -= AUTO_HIDE_TASKBAR_HEIGHT; + } + } + } + + nccalcsize_params.rgrc[0] = clientRect; + Marshal.StructureToPtr(nccalcsize_params, lParam, true); + + // 如果在 WM_SIZE 中处理会导致窗口闪烁 + UpdateFrameMargins(); + return IntPtr.Zero; + } + else + { + UpdateFrameMargins(); + break; + } + } + // 确定窗口坐标位置对应的消息 + case WindowMessage.WM_NCHITTEST: + { + if (ExtendsContentIntoTitleBar) + { + // 让 OS 处理左右下三边,由于我们移除了标题栏,上边框会被视为客户区 + IntPtr result = Comctl32Library.DefSubclassProc(Handle, WindowMessage.WM_NCHITTEST, UIntPtr.Zero, lParam); + + if (result != (IntPtr)HITTEST.HTCLIENT) + { + return result; + } + + // XAML Islands 和它上面的标题栏窗口都会吞掉鼠标事件,因此能到达这里的唯一机会是上边框。保险起见做一些额外检查。 + if (!User32Library.IsZoomed(Handle)) + { + User32Library.GetWindowRect(Handle, out RECT rcWindow); + if (Cursor.Position.Y < rcWindow.top + GetResizeHandleHeight()) + { + return (IntPtr)HITTEST.HTTOP; + } + } + + return (IntPtr)HITTEST.HTCAPTION; + } + + break; + } + // 窗口键盘按键对应的窗口消息 + case WindowMessage.WM_KEYDOWN: + { + if (wParam.ToUInt32() == (int)Keys.Tab) + { + // 处理焦点 + if (desktopWindowXamlSource is not null) + { + XamlSourceFocusNavigationReason reason = (User32Library.GetKeyState(Keys.ShiftKey) & (int)Keys.F17) is not 0 ? + XamlSourceFocusNavigationReason.Last : XamlSourceFocusNavigationReason.First; + desktopWindowXamlSource.NavigateFocus(new XamlSourceFocusNavigationRequest(reason)); + } + + return IntPtr.Zero; + } + break; + } // 选择窗口右键菜单的条目时接收到的消息 - case (int)WindowMessage.WM_SYSCOMMAND: + case WindowMessage.WM_SYSCOMMAND: { - SYSTEMCOMMAND sysCommand = (SYSTEMCOMMAND)(m.WParam.ToInt32() & 0xFFF0); + SYSTEMCOMMAND sysCommand = (SYSTEMCOMMAND)(wParam.ToUInt32() & 0xFFF0); if (sysCommand is SYSTEMCOMMAND.SC_MOUSEMENU) { @@ -322,11 +745,17 @@ protected override void WndProc(ref Message m) ShowMode = FlyoutShowMode.Standard }; (Content as MainPage).TitlebarMenuFlyout.ShowAt(null, options); - return; + return IntPtr.Zero; } else if (sysCommand is SYSTEMCOMMAND.SC_KEYMENU) { - if (m.LParam.ToInt32() is (int)Keys.Space) + // 禁用按 Alt 键会激活窗口菜单的行为,它使用户界面无法交互 + if (lParam == IntPtr.Zero) + { + return IntPtr.Zero; + } + + if (lParam.ToInt32() is (int)Keys.Space) { FlyoutShowOptions options = new() { @@ -336,29 +765,35 @@ protected override void WndProc(ref Message m) (Content as MainPage).TitlebarMenuFlyout.ShowAt(null, options); } - return; + return IntPtr.Zero; } + break; } + // 当菜单处于活动状态并且用户按下与任何助记键或快捷键不对应的键时发送的消息 + case WindowMessage.WM_MENUCHAR: + { + // 防止按 Alt+Key 时发出铃声 + return new IntPtr((0 & 0xffff) | ((1 & 0xffff) << 16)); + } // 提升权限时允许应用接收拖放消息 - case (int)WindowMessage.WM_DROPFILES: + case WindowMessage.WM_DROPFILES: { - IntPtr wParam = m.WParam; Task.Run(() => { List filesList = []; StringBuilder stringBuilder = new(260); - uint numFiles = Shell32Library.DragQueryFile(wParam, 0xffffffffu, null, 0); + uint numFiles = Shell32Library.DragQueryFile((IntPtr)wParam.ToUInt32(), 0xffffffffu, null, 0); for (uint index = 0; index < numFiles; index++) { - if (Shell32Library.DragQueryFile(wParam, index, stringBuilder, Convert.ToUInt32(stringBuilder.Capacity) * 2) > 0) + if (Shell32Library.DragQueryFile((IntPtr)wParam.ToUInt32(), index, stringBuilder, Convert.ToUInt32(stringBuilder.Capacity) * 2) > 0) { filesList.Add(stringBuilder.ToString()); } } - Shell32Library.DragQueryPoint(wParam, out System.Drawing.Point point); - Shell32Library.DragFinish(wParam); + Shell32Library.DragQueryPoint((IntPtr)wParam.ToUInt32(), out Point point); + Shell32Library.DragFinish((IntPtr)wParam.ToUInt32()); BeginInvoke(async () => { await (Content as MainPage).SendReceivedFilesListAsync(filesList); @@ -368,123 +803,310 @@ protected override void WndProc(ref Message m) break; } } - - base.WndProc(ref m); + return Comctl32Library.DefSubclassProc(hWnd, Msg, wParam, lParam); } /// /// 应用拖拽区域窗口消息处理 /// - private IntPtr InputNonClientPointerSourceSubClassProc(IntPtr hWnd, WindowMessage Msg, UIntPtr wParam, IntPtr lParam, uint uIdSubclass, IntPtr dwRefData) + private IntPtr OnTitleBarWndProc(IntPtr hWnd, WindowMessage Msg, UIntPtr wParam, IntPtr lParam) { switch (Msg) { - // 处理自定义标题栏窗口最大化时,窗口顶部标题栏还可以修改窗口大小的问题 + // 绘制按钮时触发的消息 + case WindowMessage.WM_CTLCOLORBTN: + { + // 使原生按钮控件透明,虽然整个标题栏窗口都是不可见的 + return IntPtr.Zero; + } + // 确定窗口坐标位置对应的消息 case WindowMessage.WM_NCHITTEST: { - if (WindowState is FormWindowState.Maximized) + Point cursorPos = new((int)LOWORD((uint)lParam.ToInt32()), (int)HIWORD((uint)lParam.ToInt32())); + User32Library.MapWindowPoints(IntPtr.Zero, hwndTitleBar, ref cursorPos, 2); + User32Library.GetClientRect(hwndTitleBar, out RECT titleBarClientRect); + if (!User32Library.PtInRect(ref titleBarClientRect, cursorPos)) + { + // 先检查鼠标是否在窗口内。在标题栏按钮上按下鼠标时我们会捕获光标,从而收到 WM_MOUSEMOVE 和 WM_LBUTTONUP 消息。 + // 它们使用 WM_NCHITTEST 测试鼠标位于哪个区域 + return new IntPtr((int)HITTEST.HTNOWHERE); + } + + if (!User32Library.IsZoomed(Handle) && cursorPos.Y + GetTopBorderHeight() < GetResizeHandleHeight()) + { + // 鼠标位于上边框 + return new IntPtr((int)HITTEST.HTTOP); + } + + // 标题栏按钮的宽度和高度 + Size buttonSizeInDips = new(46, 32); + double dpiScale = (double)DeviceDpi / 96; + double buttonWidthInPixels = buttonSizeInDips.Width * dpiScale; + double buttonHeightInPixels = buttonSizeInDips.Height * dpiScale; + + if (cursorPos.Y >= buttonHeightInPixels) + { + // 鼠标位于标题按钮下方,如果标题栏很宽,这里也可以拖动 + return new IntPtr((int)HITTEST.HTCAPTION); + } + + // 从右向左检查鼠标是否位于某个标题栏按钮上 + long cursorToRight = titleBarClientRect.right - cursorPos.X; + if (cursorToRight < buttonWidthInPixels) + { + return new IntPtr((int)HITTEST.HTCLOSE); + } + else if (cursorToRight < buttonWidthInPixels * 2) + { + // 支持 Win11 的贴靠布局 + return MaximizeBox ? new IntPtr((int)HITTEST.HTMAXBUTTON) : new IntPtr((int)HITTEST.HTCLIENT); + } + else if (cursorToRight < buttonWidthInPixels * 3) + { + return MinimizeBox ? new IntPtr((int)HITTEST.HTMINBUTTON) : new IntPtr((int)HITTEST.HTCLIENT); + } + else + { + // 不在任何标题栏按钮上则在可拖拽区域 + return new IntPtr((int)HITTEST.HTCAPTION); + } + } + // 窗口鼠标在工作区移动时收到的窗口消息 + case WindowMessage.WM_MOUSEMOVE: + { + Point cursorPos = new((int)LOWORD((uint)lParam.ToInt32()), (int)HIWORD((uint)lParam.ToInt32())); + User32Library.MapWindowPoints(hwndTitleBar, IntPtr.Zero, ref cursorPos, 2); + wParam = (UIntPtr)(uint)User32Library.SendMessage(hwndTitleBar, WindowMessage.WM_NCHITTEST, UIntPtr.Zero, new IntPtr(MakeLParam(cursorPos.X, cursorPos.Y))); + goto case WindowMessage.WM_NCMOUSEMOVE; + } + // 窗口鼠标在非工作区移动时收到的窗口消息 + case WindowMessage.WM_NCMOUSEMOVE: + { + MainPage mainPage = (Content as MainPage); + + // 将 hover 状态通知 CaptionButtons。标题栏窗口拦截了 XAML Islands 中的标题栏 + // 控件的鼠标消息,标题栏按钮的状态由我们手动控制。 + if (wParam == (UIntPtr)(uint)HITTEST.HTTOP || wParam == (UIntPtr)(uint)HITTEST.HTCAPTION) { - if (lParam.ToInt32() >> 16 < 4) + mainPage.LeaveButtons(); + + // 将 HTTOP 传给主窗口才能通过上边框调整窗口高度 + return User32Library.SendMessage(Handle, Msg, wParam, lParam); + } + else if (wParam == (UIntPtr)(uint)HITTEST.HTMINBUTTON || wParam == (UIntPtr)(uint)HITTEST.HTMAXBUTTON || wParam == (UIntPtr)(uint)HITTEST.HTCLOSE) + { + if (wParam.ToUInt32() == (uint)HITTEST.HTMINBUTTON) + { + mainPage.HoverButton(CaptionButton.Minimize); + } + else if (wParam.ToUInt32() == (uint)HITTEST.HTMAXBUTTON) + { + mainPage.HoverButton(CaptionButton.Maximize); + } + else if (wParam.ToUInt32() == (uint)HITTEST.HTCLOSE) { - return (IntPtr)2; // HTCAPTION (在标题栏中) + mainPage.HoverButton(CaptionButton.Close); } + + // 追踪鼠标以确保鼠标离开标题栏时我们能收到 WM_NCMOUSELEAVE 消息,否则无法 + // 可靠的收到这个消息,尤其是在用户快速移动鼠标的时候。 + if (!trackingMouse && Msg == WindowMessage.WM_NCMOUSEMOVE) + { + TRACKMOUSEEVENT ev = new() + { + cbSize = Marshal.SizeOf(), + dwFlags = TRACKMOUSEEVENT_FLAGS.TME_LEAVE | TRACKMOUSEEVENT_FLAGS.TME_NONCLIENT, + hwndTrack = hwndTitleBar, + dwHoverTime = 0xFFFFFFFF + }; + User32Library.TrackMouseEvent(ev); + trackingMouse = true; + } + } + else + { + mainPage.LeaveButtons(); } + break; } - // 当用户按下鼠标左键时,光标位于窗口的非工作区内的消息 - case WindowMessage.WM_NCLBUTTONDOWN: + // 光标离开之前对 TrackMouseEvent 的调用中指定的窗口的非工作区域时,发布到窗口的消息 + case WindowMessage.WM_NCMOUSELEAVE: { - if ((Content as MainPage).TitlebarMenuFlyout.IsOpen) + // 我们需要检查鼠标是否真的离开了标题栏按钮,因为在某些情况下 OS 会错误汇报。 + // 比如:鼠标在关闭按钮上停留了一段时间,系统会显示文字提示,这时按下左键,便会收 + // 到 WM_NCMOUSELEAVE,但此时鼠标并没有离开标题栏按钮 + Point cursorPos = new((int)LOWORD((uint)lParam.ToInt32()), (int)HIWORD((uint)lParam.ToInt32())); + // 先检查鼠标是否在主窗口上,如果正在显示文字提示,会返回 _hwndTitleBar + IntPtr hwndUnderCursor = User32Library.WindowFromPoint(cursorPos); + if (hwndUnderCursor != Handle && hwndUnderCursor != hwndTitleBar) { - (Content as MainPage).TitlebarMenuFlyout.Hide(); + (Content as MainPage).LeaveButtons(); + } + else + { + // 然后检查鼠标在标题栏上的位置 + IntPtr hit = User32Library.SendMessage(hwndTitleBar, WindowMessage.WM_NCHITTEST, UIntPtr.Zero, new IntPtr(MakeLParam(cursorPos.X, cursorPos.Y))); + if (hit != new IntPtr((int)HITTEST.HTMINBUTTON) && hit != new IntPtr((int)HITTEST.HTMAXBUTTON) && hit != new IntPtr((int)HITTEST.HTCLOSE)) + { + (Content as MainPage).LeaveButtons(); + } } + + trackingMouse = false; break; } - // 当用户按下鼠标右键并释放时,光标位于窗口的非工作区内的消息 - case WindowMessage.WM_NCRBUTTONUP: + // 光标离开之前对 TrackMouseEvent 的调用中指定的窗口的工作区域时,发布到窗口的消息 + case WindowMessage.WM_MOUSELEAVE: { - // HTCAPTION 在标题栏中 - if (wParam.ToUInt32() is 2 && Content is not null && Content.XamlRoot is not null) + // 我们需要检查鼠标是否真的离开了标题栏按钮,因为在某些情况下 OS 会错误汇报。 + // 比如:鼠标在关闭按钮上停留了一段时间,系统会显示文字提示,这时按下左键,便会收到 WM_NCMOUSELEAVE,但此时鼠标并没有离开标题栏按钮 + Point cursorPos = new((int)LOWORD((uint)lParam.ToInt32()), (int)HIWORD((uint)lParam.ToInt32())); + // 先检查鼠标是否在主窗口上,如果正在显示文字提示,会返回 _hwndTitleBar + IntPtr hwndUnderCursor = User32Library.WindowFromPoint(cursorPos); + if (hwndUnderCursor != Handle && hwndUnderCursor != hwndTitleBar) { - System.Drawing.Point clientPoint = PointToClient(MousePosition); - FlyoutShowOptions options = new() + (Content as MainPage).LeaveButtons(); + } + else + { + // 然后检查鼠标在标题栏上的位置 + IntPtr hit = User32Library.SendMessage(hwndTitleBar, WindowMessage.WM_NCHITTEST, UIntPtr.Zero, new IntPtr(MakeLParam(cursorPos.X, cursorPos.Y))); + if (hit != new IntPtr((int)HITTEST.HTMINBUTTON) && hit != new IntPtr((int)HITTEST.HTMAXBUTTON) && hit != new IntPtr((int)HITTEST.HTCLOSE)) { - Placement = FlyoutPlacementMode.BottomEdgeAlignedLeft, - ShowMode = FlyoutShowMode.Standard, - }; + (Content as MainPage).LeaveButtons(); + } + } - if (RightToLeft is RightToLeft.Yes) + trackingMouse = false; + break; + } + // 处理非客户区左键按下的窗口消息 + case WindowMessage.WM_NCLBUTTONDOWN: + { + // 手动处理标题栏上的点击。如果在标题栏按钮上,则通知 CaptionButtons,否则将消息传递 + // 给主窗口。 + if (wParam == new UIntPtr((int)HITTEST.HTTOP) || wParam == new UIntPtr((int)HITTEST.HTCAPTION)) + { + // 将 HTTOP 传给主窗口才能通过上边框调整窗口高度 + return User32Library.SendMessage(Handle, Msg, wParam, lParam); + } + else if (wParam == new UIntPtr((int)HITTEST.HTMINBUTTON) || wParam == new UIntPtr((int)HITTEST.HTMAXBUTTON) || wParam == new UIntPtr((int)HITTEST.HTCLOSE)) + { + if (wParam.ToUInt32() == (uint)HITTEST.HTMINBUTTON) { - if (InfoHelper.SystemVersion.Build >= 22000) - { - options.Position = new global::Windows.Foundation.Point((ClientSize.Width - clientPoint.X) / ((double)DeviceDpi / 96), clientPoint.Y / ((double)DeviceDpi / 96)); - } - else - { - options.Position = new global::Windows.Foundation.Point(ClientSize.Width - clientPoint.X, clientPoint.Y); - } + (Content as MainPage).PressButton(CaptionButton.Minimize); } - else + else if (wParam.ToUInt32() == (uint)HITTEST.HTMAXBUTTON) { - if (InfoHelper.SystemVersion.Build >= 22000) - { - options.Position = new global::Windows.Foundation.Point(clientPoint.X / ((double)DeviceDpi / 96), clientPoint.Y / ((double)DeviceDpi / 96)); - } - else - { - options.Position = new global::Windows.Foundation.Point(clientPoint.X, clientPoint.Y); - } + (Content as MainPage).PressButton(CaptionButton.Maximize); + } + else if (wParam.ToUInt32() == (uint)HITTEST.HTCLOSE) + { + (Content as MainPage).PressButton(CaptionButton.Close); } - (Content as MainPage).TitlebarMenuFlyout.ShowAt(null, options); + // 在标题栏按钮上按下左键后我们便捕获光标,这样才能在释放时得到通知。注意捕获光标后 + // 便不会再收到 NC 族消息,这就是为什么我们要处理 WM_MOUSEMOVE 和 WM_LBUTTONUP + User32Library.SetCapture(hwndTitleBar); } + return IntPtr.Zero; + } + // 处理非客户区左键双击的窗口消息 + case WindowMessage.WM_NCLBUTTONDBLCLK: + { + // 手动处理标题栏上的点击。如果在标题栏按钮上,则通知 CaptionButtons,否则将消息传递 + // 给主窗口。 + if (wParam == new UIntPtr((int)HITTEST.HTTOP) || wParam == new UIntPtr((int)HITTEST.HTCAPTION)) + { + // 将 HTTOP 传给主窗口才能通过上边框调整窗口高度 + return User32Library.SendMessage(Handle, Msg, wParam, lParam); + } + else if (wParam == new UIntPtr((int)HITTEST.HTMINBUTTON) || wParam == new UIntPtr((int)HITTEST.HTMAXBUTTON) || wParam == new UIntPtr((int)HITTEST.HTCLOSE)) + { + if (wParam.ToUInt32() == (uint)HITTEST.HTMINBUTTON) + { + (Content as MainPage).PressButton(CaptionButton.Minimize); + } + else if (wParam.ToUInt32() == (uint)HITTEST.HTMINBUTTON) + { + (Content as MainPage).PressButton(CaptionButton.Maximize); + } + else if (wParam.ToUInt32() == (uint)HITTEST.HTCLOSE) + { + (Content as MainPage).PressButton(CaptionButton.Close); + } + // 在标题栏按钮上按下左键后我们便捕获光标,这样才能在释放时得到通知。注意捕获光标后 + // 便不会再收到 NC 族消息,这就是为什么我们要处理 WM_MOUSEMOVE 和 WM_LBUTTONUP + User32Library.SetCapture(hwndTitleBar); + } return IntPtr.Zero; } + // 处理客户区左键释放的窗口消息 + case WindowMessage.WM_LBUTTONUP: + { + User32Library.ReleaseCapture(); + Point cursorPos = new((int)LOWORD((uint)lParam.ToInt32()), (int)HIWORD((uint)lParam.ToInt32())); + User32Library.MapWindowPoints(hwndTitleBar, IntPtr.Zero, ref cursorPos, 2); + wParam = (UIntPtr)(uint)User32Library.SendMessage(hwndTitleBar, WindowMessage.WM_NCHITTEST, UIntPtr.Zero, new IntPtr(MakeLParam(cursorPos.X, cursorPos.Y))); + goto case WindowMessage.WM_NCLBUTTONUP; + } + // 处理非客户区左键释放的窗口消息 + case WindowMessage.WM_NCLBUTTONUP: + { + // 处理鼠标在标题栏上释放。如果位于标题栏按钮上,则传递给 CaptionButtons,不在则将消息传递给主窗口 + // 给主窗口。 + if (wParam == new UIntPtr((int)HITTEST.HTTOP) || wParam == new UIntPtr((int)HITTEST.HTCAPTION)) + { + // 在可拖拽区域或上边框释放左键,将此消息传递给主窗口 + (Content as MainPage).ReleaseButtons(); + return User32Library.SendMessage(Handle, Msg, wParam, lParam); + } + else if (wParam == new UIntPtr((int)HITTEST.HTMINBUTTON) || wParam == new UIntPtr((int)HITTEST.HTMAXBUTTON) || wParam == new UIntPtr((int)HITTEST.HTCLOSE)) + { + if (wParam.ToUInt32() == (uint)HITTEST.HTMINBUTTON) + { + (Content as MainPage).ReleaseButton(CaptionButton.Minimize); + } + else if (wParam.ToUInt32() == (uint)HITTEST.HTMAXBUTTON) + { + (Content as MainPage).ReleaseButton(CaptionButton.Maximize); + } + else if (wParam.ToUInt32() == (uint)HITTEST.HTCLOSE) + { + (Content as MainPage).ReleaseButton(CaptionButton.Close); + } + (Content as MainPage).ReleaseButtons(); + } + return IntPtr.Zero; + } + // 处理非客户区右键按下的窗口消息 + case WindowMessage.WM_NCRBUTTONDOWN: + { + // 不关心右键,将它们传递给主窗口 + return User32Library.SendMessage(Handle, Msg, wParam, lParam); + } + // 处理非客户区右键双击的窗口消息 + case WindowMessage.WM_NCRBUTTONDBLCLK: + { + // 不关心右键,将它们传递给主窗口 + return User32Library.SendMessage(Handle, Msg, wParam, lParam); + } + // 处理非客户区右键释放的窗口消息 + case WindowMessage.WM_NCRBUTTONUP: + { + // 不关心右键,将它们传递给主窗口 + return User32Library.SendMessage(Handle, Msg, wParam, lParam); + } } - return Comctl32Library.DefSubclassProc(hWnd, Msg, wParam, lParam); + return User32Library.DefWindowProc(hWnd, Msg, wParam, lParam); } #endregion 第三部分:窗口过程 #region 第四部分:窗口属性设置 - /// - /// 设置标题栏按钮的颜色 - /// - public void SetTitleBarColor(ElementTheme theme) - { - AppWindowTitleBar titleBar = AppWindow.TitleBar; - - titleBar.BackgroundColor = Colors.Transparent; - titleBar.ForegroundColor = Colors.Transparent; - titleBar.InactiveBackgroundColor = Colors.Transparent; - titleBar.InactiveForegroundColor = Colors.Transparent; - - if (theme is ElementTheme.Light) - { - titleBar.ButtonBackgroundColor = Colors.Transparent; - titleBar.ButtonForegroundColor = Colors.Black; - titleBar.ButtonHoverBackgroundColor = Color.FromArgb(20, 0, 0, 0); - titleBar.ButtonHoverForegroundColor = Colors.Black; - titleBar.ButtonPressedBackgroundColor = Color.FromArgb(30, 0, 0, 0); - titleBar.ButtonPressedForegroundColor = Colors.Black; - titleBar.ButtonInactiveBackgroundColor = Colors.Transparent; - titleBar.ButtonInactiveForegroundColor = Colors.Black; - } - else - { - titleBar.ButtonBackgroundColor = Colors.Transparent; - titleBar.ButtonForegroundColor = Colors.White; - titleBar.ButtonHoverBackgroundColor = Color.FromArgb(20, 255, 255, 255); - titleBar.ButtonHoverForegroundColor = Colors.White; - titleBar.ButtonPressedBackgroundColor = Color.FromArgb(30, 255, 255, 255); - titleBar.ButtonPressedForegroundColor = Colors.White; - titleBar.ButtonInactiveBackgroundColor = Colors.Transparent; - titleBar.ButtonInactiveForegroundColor = Colors.White; - } - } - /// /// 设置应用的主题色 /// @@ -558,6 +1180,143 @@ private static void SetClassicMenuTheme() } } + /// + /// 获取窗口属性 + /// + private static int GetWindowLongAuto(IntPtr hWnd, WindowLongIndexFlags nIndex) + { + return IntPtr.Size is 8 ? User32Library.GetWindowLongPtr(hWnd, nIndex) : User32Library.GetWindowLong(hWnd, nIndex); + } + + /// + /// 更改窗口属性 + /// + private static IntPtr SetWindowLongAuto(IntPtr hWnd, WindowLongIndexFlags nIndex, IntPtr dwNewLong) + { + return IntPtr.Size is 8 ? User32Library.SetWindowLongPtr(hWnd, nIndex, dwNewLong) : User32Library.SetWindowLong(hWnd, nIndex, dwNewLong); + } + + /// + /// 更新此窗口周围绘制的外部边框的宽度 + /// + private void UpdateFrameBorderThickness() + { + // Win10 中窗口边框始终只有一个像素宽,Win11 中的窗口边框宽度和 DPI 缩放有关 + if (Environment.Version.Build >= 22000) + { + DwmapiLibrary.DwmGetWindowAttribute(Handle, DWMWINDOWATTRIBUTE.DWMWA_VISIBLE_FRAME_BORDER_THICKNESS, out IntPtr value, sizeof(uint)); + nativeTopBorderHeight = value.ToInt32(); + } + } + + /// + /// 更新此窗口周围绘制的外部边框的边距 + /// + private void UpdateFrameMargins() + { + if (Environment.Version.Build < 22000) + { + Margins margins = new(); + + if (GetTopBorderHeight() > 0) + { + // 在 Win10 中,移除标题栏时上边框也被没了。我们的解决方案是:使用 DwmExtendFrameIntoClientArea 将边框扩展到客户区,然后在顶部绘制了一个黑色实线来显示系统原始边框(这种情况下操作系统将黑色视为透明)。因此我们有完美的上边框! + // 见 https://docs.microsoft.com/en-us/windows/win32/dwm/customframe#extending-the-client-frame + // 有的软件自己绘制了假的上边框,如 Chromium 系、WinUI 3 等,但窗口失去焦点时边框是半透明的,无法完美模拟。 + // 我们选择扩展到标题栏高度,这是最好的选择。一个自然的想法是,既然上边框只有一个像素高,我们扩展一个像素即可,可惜因为 DWM 的 bug,这会使窗口失去焦点时上边框变为透明。那么能否传一个负值,让边框扩展到整个客户区?这大部分情况下可以工作,有一个小 bug:不显示边框颜色的设置下深色模式的边框会变为纯黑而不是半透明。 + RECT frame = new(); + User32Library.AdjustWindowRectExForDpi(ref frame, (WindowStyle)GetWindowLongAuto(Handle, WindowLongIndexFlags.GWL_STYLE), false, 0, Convert.ToUInt32(DeviceDpi)); + margins.Top -= frame.top; + + DwmapiLibrary.DwmExtendFrameIntoClientArea(Handle, ref margins); + } + } + } + + /// + /// 获取窗口上边框的高度 + /// + private int GetTopBorderHeight() + { + return ExtendsContentIntoTitleBar && !User32Library.IsZoomed(Handle) ? nativeTopBorderHeight : 0; + } + + /// + /// 调整标题栏区域 + /// + private void ResizeTitleBarWindow() + { + if (ExtendsContentIntoTitleBar && hwndTitleBar != IntPtr.Zero) + { + // 获取标题栏的边框矩形 + Grid titleBar = (Content as MainPage).AppTitlebar; + + global::Windows.Foundation.Rect rect = new(0, 0, titleBar.ActualWidth, titleBar.ActualHeight); + rect = titleBar.TransformToVisual(Content).TransformBounds(rect); + + double dpiScale = (double)DeviceDpi / 96; + + // 将标题栏窗口置于 XAML Islands 窗口上方 + int titleBarWidth = (int)Math.Ceiling(rect.Width * dpiScale); + User32Library.SetWindowPos(hwndTitleBar, IntPtr.Zero, (int)Math.Floor(rect.X * dpiScale), Convert.ToInt32(Math.Floor(rect.Y * dpiScale)) + GetTopBorderHeight(), titleBarWidth, (int)Math.Floor(rect.Height * dpiScale + 1), SetWindowPosFlags.SWP_SHOWWINDOW); + + if (hwndMaximizeButton != IntPtr.Zero) + { + double captionButtonHeightInDips = (Content as MainPage).CaptionButtons.Height; + int captionButtonHeightInPixels = (int)Math.Ceiling(captionButtonHeightInDips * dpiScale); + User32Library.MoveWindow(hwndMaximizeButton, 0, 0, titleBarWidth, captionButtonHeightInPixels, false); + } + + // 设置标题栏窗口的最大化样式,这样才能展示正确的文字提示 + WindowStyle style = (WindowStyle)GetWindowLongAuto(hwndTitleBar, WindowLongIndexFlags.GWL_STYLE); + SetWindowLongAuto(hwndTitleBar, WindowLongIndexFlags.GWL_STYLE, (IntPtr)(User32Library.IsZoomed(Handle) ? style | WindowStyle.WS_MAXIMIZE : style & ~WindowStyle.WS_MAXIMIZE)); + } + } + + private int GetResizeHandleHeight() + { + // 没有 SM_CYPADDEDBORDER + return User32Library.GetSystemMetricsForDpi(SM.SM_CXPADDEDBORDER, DeviceDpi) + User32Library.GetSystemMetricsForDpi(SM.SM_CYSIZEFRAME, DeviceDpi); + } + + /// + /// 检查显示器的每一条边是否都隐藏了任务栏 + /// + + private bool HasAutoHideTaskbar(Screen screen, ABE edge) + { + APPBARDATA appbarData = new() + { + cbSize = Marshal.SizeOf(), + uEdge = edge, + rc = new RECT() + { + left = screen.Bounds.Left, + right = screen.Bounds.Right, + top = screen.Bounds.Top, + bottom = screen.Bounds.Bottom + } + }; + + IntPtr hTaskbar = Shell32Library.SHAppBarMessage(ABM.ABM_GETAUTOHIDEBAREX, ref appbarData); + return hTaskbar != IntPtr.Zero; + } + + private int MakeLParam(int LoWord, int HiWord) + { + return ((HiWord << 16) | LoWord); + } + + private uint HIWORD(uint dword) + { + return (dword >> 16) & 0xffff; + } + + private uint LOWORD(uint dword) + { + return dword & 0xffff; + } + #endregion 第四部分:窗口属性设置 } } diff --git a/WindowsTools/WindowsAPI/PInvoke/Advapi32/Advapi32Library.cs b/WindowsTools/WindowsAPI/PInvoke/Advapi32/Advapi32Library.cs index 7d8ff126..76aa467e 100644 --- a/WindowsTools/WindowsAPI/PInvoke/Advapi32/Advapi32Library.cs +++ b/WindowsTools/WindowsAPI/PInvoke/Advapi32/Advapi32Library.cs @@ -22,7 +22,7 @@ public static class Advapi32Library /// 事件的句柄。 如果 fAsynchronous 参数为 TRUE,则函数将立即返回 ,并通过发出此事件信号来报告更改。 如果 fAsynchronous 为 FALSE,则忽略 hEvent 。 /// 如果此参数为 TRUE,则函数将立即返回并通过向指定事件发出信号来报告更改。 如果此参数为 FALSE,则函数在发生更改之前不会返回 。 /// 如果函数成功,则返回值为 ERROR_SUCCESS。如果函数失败,则返回值为 Winerror.h 中定义的非零错误代码。 - [DllImport(Advapi32, CharSet = CharSet.Unicode, ExactSpelling = true, EntryPoint = "RegNotifyChangeKeyValue", SetLastError = false, PreserveSig = true)] + [DllImport(Advapi32, CharSet = CharSet.Unicode, ExactSpelling = true, EntryPoint = "RegNotifyChangeKeyValue", PreserveSig = true, SetLastError = false)] public static extern int RegNotifyChangeKeyValue(IntPtr hKey, [MarshalAs(UnmanagedType.Bool)] bool watchSubtree, REG_NOTIFY_FILTER notifyFilter, IntPtr hEvent, [MarshalAs(UnmanagedType.Bool)] bool asynchronous); } } diff --git a/WindowsTools/WindowsAPI/PInvoke/Combase/CombaseLibrary.cs b/WindowsTools/WindowsAPI/PInvoke/Combase/CombaseLibrary.cs index aa0364ee..3ae58681 100644 --- a/WindowsTools/WindowsAPI/PInvoke/Combase/CombaseLibrary.cs +++ b/WindowsTools/WindowsAPI/PInvoke/Combase/CombaseLibrary.cs @@ -17,7 +17,7 @@ public static class CombaseLibrary /// 接口的引用 ID。 /// 激活工厂。 /// 如果此函数成功,则返回 S_OK。 否则,将返回 HRESULT 错误代码。 - [DllImport(Combase, CharSet = CharSet.Unicode, ExactSpelling = true, EntryPoint = "RoGetActivationFactory", SetLastError = false, PreserveSig = true)] + [DllImport(Combase, CharSet = CharSet.Unicode, ExactSpelling = true, EntryPoint = "RoGetActivationFactory", PreserveSig = true, SetLastError = false)] public static extern int RoGetActivationFactory([MarshalAs(UnmanagedType.HString)] string activatableClassId, Guid iid, [MarshalAs(UnmanagedType.IInspectable)] out object factory); } } diff --git a/WindowsTools/WindowsAPI/PInvoke/Comctl32/Comctl32Library.cs b/WindowsTools/WindowsAPI/PInvoke/Comctl32/Comctl32Library.cs index 664a675c..cfe13247 100644 --- a/WindowsTools/WindowsAPI/PInvoke/Comctl32/Comctl32Library.cs +++ b/WindowsTools/WindowsAPI/PInvoke/Comctl32/Comctl32Library.cs @@ -22,7 +22,7 @@ public static partial class Comctl32Library /// 子类 ID。 此 ID 与子类过程一起唯一标识子类。 若要删除子类,请将子类过程和此值传递给 RemoveWindowSubclass 函数。 此值将传递给 uIdSubclass 参数中的子类过程。 /// 用于 引用数据的DWORD_PTR。 此值的含义由调用应用程序确定。 此值将传递给 dwRefData 参数中的子类过程。 不同的 dwRefData 与窗口句柄、子类过程和 uIdSubclass 的每个组合相关联。 /// 如果成功安装子类回调,则为 TRUE;否则为 FALSE。 - [DllImport(Comctl32, CharSet = CharSet.Unicode, EntryPoint = "SetWindowSubclass", SetLastError = false, PreserveSig = true)] + [DllImport(Comctl32, CharSet = CharSet.Unicode, EntryPoint = "SetWindowSubclass", PreserveSig = true, SetLastError = false)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool SetWindowSubclass(IntPtr hWnd, SUBCLASSPROC pfnSubclass, uint uIdSubclass, IntPtr dwRefData); @@ -34,7 +34,7 @@ public static partial class Comctl32Library /// 指定附加消息信息。 此参数的内容取决于窗口消息的值。 /// 指定附加消息信息。 此参数的内容取决于窗口消息的值。 注意:在 64 位版本的 Windows LPARAM 上是一个 64 位值。 /// 返回的值特定于发送的消息。 应忽略此值。 - [DllImport(Comctl32, CharSet = CharSet.Unicode, EntryPoint = "DefSubclassProc", SetLastError = false, PreserveSig = true)] + [DllImport(Comctl32, CharSet = CharSet.Unicode, EntryPoint = "DefSubclassProc", PreserveSig = true, SetLastError = false)] public static extern IntPtr DefSubclassProc(IntPtr hWnd, WindowMessage uMsg, UIntPtr wParam, IntPtr lParam); /// @@ -44,7 +44,7 @@ public static partial class Comctl32Library /// 指向窗口过程的指针。 此指针和子类 ID 唯一标识此子类回调。 有关回调函数原型,请参阅 SUBCLASSPROC。 /// UINT_PTR子类 ID。 此 ID 和回调指针唯一标识此子类回调。 注意:在 64 位版本的 Windows 上,这是一个 64 位值。 /// 如果成功删除子类回调,则为 TRUE;否则为 FALSE。 - [DllImport(Comctl32, CharSet = CharSet.Unicode, EntryPoint = "RemoveWindowSubclass", SetLastError = false, PreserveSig = true)] + [DllImport(Comctl32, CharSet = CharSet.Unicode, EntryPoint = "RemoveWindowSubclass", PreserveSig = true, SetLastError = false)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool RemoveWindowSubclass(IntPtr hWnd, SUBCLASSPROC pfnSubclass, uint uIdSubclass); } diff --git a/WindowsTools/WindowsAPI/PInvoke/Comctl32/SUBCLASSPROC.cs b/WindowsTools/WindowsAPI/PInvoke/Comctl32/SUBCLASSPROC.cs index db290a3b..1a61af52 100644 --- a/WindowsTools/WindowsAPI/PInvoke/Comctl32/SUBCLASSPROC.cs +++ b/WindowsTools/WindowsAPI/PInvoke/Comctl32/SUBCLASSPROC.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.InteropServices; using WindowsTools.WindowsAPI.PInvoke.User32; namespace WindowsTools.WindowsAPI.PInvoke.Comctl32 @@ -13,5 +14,6 @@ namespace WindowsTools.WindowsAPI.PInvoke.Comctl32 /// 子类 ID。 /// 提供给 SetWindowSubclass 函数的引用数据。 这可用于将子类实例与“this”指针相关联。 /// 返回值是消息处理的结果,取决于发送的消息。 + [UnmanagedFunctionPointer(CallingConvention.Winapi)] public delegate IntPtr SUBCLASSPROC(IntPtr hWnd, WindowMessage uMsg, UIntPtr wParam, IntPtr lParam, uint uIdSubclass, IntPtr dwRefData); } diff --git a/WindowsTools/WindowsAPI/PInvoke/Dwmapi/DWMWINDOWATTRIBUTE.cs b/WindowsTools/WindowsAPI/PInvoke/Dwmapi/DWMWINDOWATTRIBUTE.cs new file mode 100644 index 00000000..0e9b58a6 --- /dev/null +++ b/WindowsTools/WindowsAPI/PInvoke/Dwmapi/DWMWINDOWATTRIBUTE.cs @@ -0,0 +1,159 @@ +using System; + +namespace WindowsTools.WindowsAPI.PInvoke.Dwmapi +{ + /// + /// DwmGetWindowAttribute 和 DwmSetWindowAttribute 函数使用的选项。 + /// + [Flags] + public enum DWMWINDOWATTRIBUTE + { + /// + /// 与 DwmGetWindowAttribute 一起使用。 发现是否启用了非客户端呈现。 检索到的值的类型为 BOOL。 如果启用非客户端呈现,则为 TRUE;否则为 FALSE。 + /// + DWMWA_NCRENDERING_ENABLED = 1, + + /// + /// 与 DwmSetWindowAttribute 一起使用。 设置非客户端呈现策略。 pvAttribute 参数指向 DWMNCRENDERINGPOLICY 枚举中的值。 + /// + DWMWA_NCRENDERING_POLICY = 2, + + /// + /// 与 DwmSetWindowAttribute 一起使用。 启用或强制禁用 DWM 转换。 pvAttribute 参数指向 BOOL 类型的值。 如果为 TRUE ,则禁用转换; 如果为 FALSE ,则为启用转换。 + /// + DWMWA_TRANSITIONS_FORCEDISABLED = 3, + + /// + /// 与 DwmSetWindowAttribute 一起使用。 使非工作区中呈现的内容在 DWM 绘制的帧上可见。 pvAttribute 参数指向 BOOL 类型的值。 如果为 TRUE ,则启用在非工作区中呈现的内容在帧上可见;否则为 FALSE。 + /// + DWMWA_ALLOW_NCPAINT = 4, + + /// + /// 与 DwmGetWindowAttribute 一起使用。 检索窗口相对空间中描述文字按钮区域的边界。 检索到的值的类型为 RECT。 如果窗口最小化或对用户不可见,则检索到的 RECT 的值未定义。 应检查检索到的 RECT 是否包含可以使用的边界,如果没有,则可以认为窗口已最小化或不可见。 + /// + DWMWA_CAPTION_BUTTON_BOUNDS = 5, + + /// + /// 与 DwmSetWindowAttribute 一起使用。 指定非客户端内容是否为从右到左 (RTL) 镜像。 pvAttribute 参数指向 BOOL 类型的值。 如果非客户端内容从右到左 (RTL) 镜像,则为 TRUE;否则为 FALSE。 + /// + DWMWA_NONCLIENT_RTL_LAYOUT = 6, + + /// + /// 与 DwmSetWindowAttribute 一起使用。 强制窗口显示图标缩略图或速览表示形式 (静态位图) ,即使窗口的实时或快照表示形式可用。 此值通常在创建窗口期间设置,在窗口的整个生存期内不会更改。 但是,某些方案可能需要值随时间推移而更改。 pvAttribute 参数指向 BOOL 类型的值。 如果为 TRUE ,则要求使用图标缩略图或速览表示形式;否则为 FALSE。 + /// + DWMWA_FORCE_ICONIC_REPRESENTATION = 7, + + /// + /// 与 DwmSetWindowAttribute 一起使用。 设置 Flip3D 如何处理窗口。 pvAttribute 参数指向DWMFLIP3DWINDOWPOLICY枚举中的值。 + /// + DWMWA_FLIP3D_POLICY = 8, + + /// + /// 与 DwmGetWindowAttribute 一起使用。 检索屏幕空间中的扩展框架边界矩形。 检索到的值的类型为 RECT。 + /// + DWMWA_EXTENDED_FRAME_BOUNDS = 9, + + /// + /// 与 DwmSetWindowAttribute 一起使用。 窗口将提供一个位图,供 DWM 用作图标缩略图或速览表示形式, (窗口的静态位图) 。 可以使用 DWMWA_FORCE_ICONIC_REPRESENTATION 指定 DWMWA_HAS_ICONIC_BITMAP。 DWMWA_HAS_ICONIC_BITMAP 通常在创建窗口期间设置,在窗口的整个生存期内不会更改。 但是,某些方案可能需要值随时间推移而更改。 pvAttribute 参数指向 BOOL 类型的值。 如果为 TRUE ,则通知 DWM 窗口将提供图标缩略图或速览表示形式;否则为 FALSE。 + /// Windows Vista 及更早版本: 不支持此值。 + /// + DWMWA_HAS_ICONIC_BITMAP = 10, + + /// + /// 与 DwmSetWindowAttribute 一起使用。 不显示窗口的速览预览。 当鼠标悬停在任务栏中窗口的缩略图上时,速览视图会显示窗口的全尺寸预览。 如果设置了此属性,将鼠标指针悬停在窗口的缩略图上会消除速览 (以防组中的另一个窗口具有显示) 的速览预览。 pvAttribute 参数指向 BOOL 类型的值。 如果为 TRUE ,则阻止速览功能; 如果为 FALSE ,则允许它。 + /// Windows Vista 及更早版本: 不支持此值。 + /// + DWMWA_DISALLOW_PEEK = 11, + + /// + /// 与 DwmSetWindowAttribute 一起使用。 防止在调用速览时窗口褪色到玻璃板。 pvAttribute 参数指向 BOOL 类型的值。 如果为 TRUE ,则防止窗口在另一个窗口的速览期间褪色;对于正常行为,则为 FALSE 。 + /// + DWMWA_EXCLUDED_FROM_PEEK = 12, + + /// + /// 与 DwmSetWindowAttribute 一起使用。 遮盖窗口,使其对用户不可见。 窗口仍由 DWM 组成。 + /// 与 DirectComposition 一起使用: 当通过与分层子窗口关联的 DirectComposition 视觉对象对窗口内容的表示形式进行动画处理时,请使用 DWMWA_CLOAK 标志来遮盖分层子窗口。 有关此用例的更多详细信息,请参阅 如何对分层子窗口的位图进行动画处理。 + /// + DWMWA_CLOAK = 13, + + /// + /// 与 DwmGetWindowAttribute 一起使用。 如果窗口被遮盖,请提供以下值之一来解释原因。 + /// DWM_CLOAKED_APP (值0x00000001) 。 窗口被其所有者应用程序遮盖。 + /// DWM_CLOAKED_SHELL (值0x00000002) 。 窗户被 Shell 遮盖了。 + /// DWM_CLOAKED_INHERITED (值0x00000004) 。 cloak 值继承自其所有者窗口。 + /// Windows 7 及更早版本: 不支持此值。 + /// + DWMWA_CLOAKED = 14, + + /// + /// 与 DwmSetWindowAttribute 一起使用。 使用当前视觉对象冻结窗口的缩略图。 不要对缩略图执行进一步的实时更新,以匹配窗口的内容。 + /// Windows 7 及更早版本: 不支持此值。 + /// + DWMWA_FREEZE_REPRESENTATION = 15, + + DWMWA_PASSIVE_UPDATE_MODE = 16, + + /// + /// 与 DwmSetWindowAttribute 一起使用。 允许非 UWP 窗口使用主机背景画笔。 如果设置了此标志,则调用 Windows::UI::Composition API 的 Win32 应用可以使用主机背景画笔生成透明度效果 (请参阅 Compositor.CreateHostBackdropBrush) 。 pvAttribute 参数指向 BOOL 类型的值。 如果为 TRUE ,则为窗口启用主机背景画笔,如果为 FALSE ,则禁用它。 + /// 从 Windows 11 内部版本 22000 开始支持此值。 + /// + DWMWA_USE_HOSTBACKDROPBRUSH = 17, + + /// + /// 与 DwmSetWindowAttribute 一起使用。 启用深色模式系统设置时,允许以深色模式绘制此窗口的窗口框架。 出于兼容性原因,无论系统设置如何,所有窗口都默认为浅色模式。 pvAttribute 参数指向 BOOL 类型的值。 如果为 TRUE ,则为窗口采用深色模式, 如果为 FALSE ,则始终使用浅色模式。 + /// 该值仅支持在 Windows 10 20H1 使用 + /// + DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = 19, + + /// + /// 与 DwmSetWindowAttribute 一起使用。 启用深色模式系统设置时,允许以深色模式绘制此窗口的窗口框架。 出于兼容性原因,无论系统设置如何,所有窗口都默认为浅色模式。 pvAttribute 参数指向 BOOL 类型的值。 如果为 TRUE ,则为窗口采用深色模式, 如果为 FALSE ,则始终使用浅色模式。 + /// 从 Windows 11 内部版本 22000 开始支持此值。 + /// + DWMWA_USE_IMMERSIVE_DARK_MODE = 20, + + /// + /// 与 DwmSetWindowAttribute 一起使用。 指定窗口的圆角首选项。 pvAttribute 参数指向 DWM_WINDOW_CORNER_PREFERENCE 类型的值。 + /// 从 Windows 11 内部版本 22000 开始支持此值。 + /// + DWMWA_WINDOW_CORNER_PREFERENCE = 33, + + /// + /// 与 DwmSetWindowAttribute 一起使用。 指定窗口边框的颜色。 pvAttribute 参数指向 COLORREF 类型的值。 应用负责根据状态更改(例如窗口激活中的更改)更改边框颜色。 + /// 为颜色指定 DWMWA_COLOR_NONE (值0xFFFFFFFE) 将禁止绘制窗口边框。 这样就可以有一个没有边框的圆角窗口。 + /// 为颜色指定 DWMWA_COLOR_DEFAULT (值0xFFFFFFFF) 会将窗口重置回使用系统对边框颜色的默认行为。 + /// 从 Windows 11 版本 22000 开始支持此值。 + /// + DWMWA_BORDER_COLOR = 34, + + /// + /// 与 DwmSetWindowAttribute 一起使用。 指定描述文字的颜色。 pvAttribute 参数指向 COLORREF 类型的值。 + /// 为颜色指定DWMWA_COLOR_DEFAULT (值0xFFFFFFFF) 会将窗口重置回使用系统对描述文字颜色的默认行为。 + /// 从 Windows 11 版本 22000 开始支持此值。 + /// + DWMWA_CAPTION_COLOR = 35, + + /// + /// 与 DwmSetWindowAttribute 一起使用。 指定描述文字文本的颜色。 pvAttribute 参数指向 COLORREF 类型的值。 + /// 为颜色指定DWMWA_COLOR_DEFAULT (值0xFFFFFFFF) 会将窗口重置回使用系统对描述文字文本颜色的默认行为。 + /// 从 Windows 11 版本 22000 开始支持此值。 + /// + DWMWA_TEXT_COLOR = 36, + + /// + /// 与 DwmGetWindowAttribute 一起使用。 检索 DWM 将围绕此窗口绘制的外部边框的宽度。 该值可能因窗口的 DPI 而异。 pvAttribute 参数指向 UINT 类型的值。 + /// 从 Windows 11 版本 22000 开始支持此值。 + /// + DWMWA_VISIBLE_FRAME_BORDER_THICKNESS = 37, + + /// + /// 与 DwmGetWindowAttribute 或 DwmSetWindowAttribute 一起使用。 检索或指定窗口的系统绘制背景材料,包括非工作区后面。 pvAttribute 参数指向 DWM_SYSTEMBACKDROP_TYPE 类型的值。 + /// 从 Windows 11 版本 22621 开始支持此值。 + /// + DWMWA_SYSTEMBACKDROP_TYPE = 38, + + /// + /// 最大可识别的 DWMWINDOWATTRIBUTE 值,用于验证目的。 + /// + DWMWA_LAST = 39 + } +} diff --git a/WindowsTools/WindowsAPI/PInvoke/Dwmapi/DwmapiLibrary.cs b/WindowsTools/WindowsAPI/PInvoke/Dwmapi/DwmapiLibrary.cs new file mode 100644 index 00000000..43ff8938 --- /dev/null +++ b/WindowsTools/WindowsAPI/PInvoke/Dwmapi/DwmapiLibrary.cs @@ -0,0 +1,37 @@ +using System; +using System.Drawing.Printing; +using System.Runtime.InteropServices; + +// 抑制 CA1401 警告 +#pragma warning disable CA1401 + +namespace WindowsTools.WindowsAPI.PInvoke.Dwmapi +{ + /// + /// Dwmapi.dll 函数库 + /// + public static partial class DwmapiLibrary + { + private const string Dwmapi = "dwmapi.dll"; + + /// + /// 将窗口框架扩展到工作区。 + /// + /// 框架将扩展到工作区的窗口的句柄。 + /// 指向 MARGINS 结构的指针,该结构描述在将帧扩展到工作区时要使用的边距。 + /// 如果此函数成功,则返回 S_OK。 否则,将返回 HRESULT 错误代码。 + [DllImport(Dwmapi, CharSet = CharSet.Unicode, EntryPoint = "DwmExtendFrameIntoClientArea", PreserveSig = true, SetLastError = false)] + public static extern int DwmExtendFrameIntoClientArea(IntPtr hwnd, ref Margins pMarInset); + + /// + /// 检索应用于窗口的指定桌面窗口管理器(DWM)属性的当前值。 有关编程指南和代码示例,请参阅 控制非客户端区域呈现。 + /// + /// 要从中检索属性值的窗口的句柄。 + /// 描述要检索的值的标志,指定为 DWMWINDOWATTRIBUTE 枚举的值。 此参数指定要检索的属性,pvAttribute 参数指向在其中检索属性值的对象。 + /// 指向一个值的指针,当此函数成功返回时,将接收该属性的当前值。 检索的值的类型取决于 dwAttribute 参数的值。 DWMWINDOWATTRIBUTE 枚举主题指示,在每个标志的行中,应向 pvAttribute 参数传递指向的值类型。 + /// 通过 pvAttribute 参数接收的属性值的大小(以字节为单位)。 检索到的值的类型及其大小(以字节为单位)取决于 dwAttribute 参数的值。 + /// 如果函数成功,则返回 S_OK。 否则,它将返回 HRESULT错误代码。 + [DllImport(Dwmapi, CharSet = CharSet.Unicode, EntryPoint = "DwmGetWindowAttribute", PreserveSig = true, SetLastError = false)] + public static extern int DwmGetWindowAttribute(IntPtr hwnd, DWMWINDOWATTRIBUTE dwAttribute, out IntPtr pvAttribute, int cbAttribute); + } +} diff --git a/WindowsTools/WindowsAPI/PInvoke/FirewallAPI/FirewallAPILibrary.cs b/WindowsTools/WindowsAPI/PInvoke/FirewallAPI/FirewallAPILibrary.cs index 3a2d9cb5..6a150afe 100644 --- a/WindowsTools/WindowsAPI/PInvoke/FirewallAPI/FirewallAPILibrary.cs +++ b/WindowsTools/WindowsAPI/PInvoke/FirewallAPI/FirewallAPILibrary.cs @@ -8,7 +8,7 @@ namespace WindowsTools.WindowsAPI.PInvoke.FirewallAPI { public static class FirewallAPILibrary { - public const string FirewallAPI = "FirewallAPI.dll"; + private const string FirewallAPI = "FirewallAPI.dll"; /// /// 枚举已在系统中创建的所有应用容器。 @@ -17,7 +17,7 @@ public static class FirewallAPILibrary /// 应用容器数。 /// 应用容器结构元素的列表。 /// 如果成功,则返回ERROR_SUCCESS,否则返回错误值。如果内存不可用,将返回ERROR_OUTOFMEMORY。 - [DllImport(FirewallAPI, CharSet = CharSet.Unicode, EntryPoint = "NetworkIsolationEnumAppContainers", SetLastError = false, PreserveSig = true)] + [DllImport(FirewallAPI, CharSet = CharSet.Unicode, EntryPoint = "NetworkIsolationEnumAppContainers", PreserveSig = true, SetLastError = false)] public static extern uint NetworkIsolationEnumAppContainers(NETISO_FLAG Flags, out uint pdwCntPublicACs, out IntPtr ppPublicACs); /// @@ -25,7 +25,7 @@ public static class FirewallAPILibrary /// /// 要释放的应用容器内存资源。 /// 返回 ERROR_SUCCESS。 - [DllImport(FirewallAPI, CharSet = CharSet.Unicode, EntryPoint = "NetworkIsolationFreeAppContainers", SetLastError = false, PreserveSig = true)] + [DllImport(FirewallAPI, CharSet = CharSet.Unicode, EntryPoint = "NetworkIsolationFreeAppContainers", PreserveSig = true, SetLastError = false)] public static extern int NetworkIsolationFreeAppContainers(IntPtr pPublicAppCs); /// @@ -34,7 +34,7 @@ public static class FirewallAPILibrary /// appContainerSids 成员中的应用容器数。 /// 安全标识符 (允许发送环回流量的应用容器) SID。 用于调试目的。 /// 如果成功,则返回ERROR_SUCCESS,否则返回错误值。 - [DllImport(FirewallAPI, CharSet = CharSet.Unicode, EntryPoint = "NetworkIsolationGetAppContainerConfig", SetLastError = false, PreserveSig = true)] + [DllImport(FirewallAPI, CharSet = CharSet.Unicode, EntryPoint = "NetworkIsolationGetAppContainerConfig", PreserveSig = true, SetLastError = false)] public static extern uint NetworkIsolationGetAppContainerConfig(out uint pdwNumPublicAppCs, out IntPtr appContainerSids); /// @@ -43,7 +43,7 @@ public static class FirewallAPILibrary /// appContainerSids 成员中的应用容器数。 /// 安全标识符 (允许发送环回流量的应用容器) SID。 用于调试目的。 /// 如果成功,则返回ERROR_SUCCESS,否则返回错误值。 - [DllImport(FirewallAPI, CharSet = CharSet.Unicode, EntryPoint = "NetworkIsolationSetAppContainerConfig", SetLastError = false, PreserveSig = true)] + [DllImport(FirewallAPI, CharSet = CharSet.Unicode, EntryPoint = "NetworkIsolationSetAppContainerConfig", PreserveSig = true, SetLastError = false)] public static extern uint NetworkIsolationSetAppContainerConfig(int dwNumPublicAppCs, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] SID_AND_ATTRIBUTES[] appContainerSids); } } diff --git a/WindowsTools/WindowsAPI/PInvoke/Gdi32/BP_PAINTPARAMS.cs b/WindowsTools/WindowsAPI/PInvoke/Gdi32/BP_PAINTPARAMS.cs new file mode 100644 index 00000000..2af7c6c4 --- /dev/null +++ b/WindowsTools/WindowsAPI/PInvoke/Gdi32/BP_PAINTPARAMS.cs @@ -0,0 +1,31 @@ +using System; +using WindowsTools.WindowsAPI.PInvoke.Uxtheme; + +namespace WindowsTools.WindowsAPI.PInvoke.Gdi32 +{ + /// + /// 定义 BeginBufferedPaint 的绘制操作参数。 + /// + public unsafe struct BP_PAINTPARAMS + { + /// + /// 此结构的大小(以字节为单位)。 + /// + public uint cbSize; + + /// + /// 以下一个或多个值。 + /// + public BPPF dwFlags; + + /// + /// 指向排除 RECT 结构的指针。 此矩形从剪裁区域中排除。 对于没有排除矩形,可以为 NULL 。 + /// + public IntPtr prcExclude; + + /// + /// 指向 BLENDFUNCTION 结构的指针,该结构通过指定源位图和目标位图的混合函数来控制混合。 如果 为 NULL,则源缓冲区将复制到目标,不进行混合。 + /// + public IntPtr pBlendFunction; + } +} diff --git a/WindowsTools/WindowsAPI/PInvoke/Gdi32/Gdi32Library.cs b/WindowsTools/WindowsAPI/PInvoke/Gdi32/Gdi32Library.cs new file mode 100644 index 00000000..4518907e --- /dev/null +++ b/WindowsTools/WindowsAPI/PInvoke/Gdi32/Gdi32Library.cs @@ -0,0 +1,32 @@ +using System; +using System.Runtime.InteropServices; + +// 抑制 CA1401 警告 +#pragma warning disable CA1401 + +namespace WindowsTools.WindowsAPI.PInvoke.Gdi32 +{ + public static class Gdi32Library + { + private const string Gdi32 = "gdi32.dll"; + + /// + /// CreateSolidBrush 函数创建具有指定纯色的逻辑画笔。 + /// + /// 画笔的颜色。 若要创建 COLORREF 颜色值,请使用 RGB 宏。 + /// 如果函数成功,则返回值将标识逻辑画笔。如果函数失败,则返回值为 NULL。 + [DllImport(Gdi32, CharSet = CharSet.Unicode, EntryPoint = "CreateSolidBrush", PreserveSig = true, SetLastError = false)] + public static extern IntPtr CreateSolidBrush(int crColor); + + [DllImport(Gdi32, CharSet = CharSet.Unicode, EntryPoint = "DeleteObject", PreserveSig = true, SetLastError = false)] + public static extern int DeleteObject(IntPtr hObject); + + /// + /// GetStockObject 函数检索其中一支股票笔、画笔、字体或调色板的句柄。 + /// + /// 常用对象的类型。 + /// 如果函数成功,则返回值是请求的逻辑对象的句柄。如果函数失败,则返回值为 NULL。 + [DllImport(Gdi32, CharSet = CharSet.Unicode, EntryPoint = "GetStockObject", PreserveSig = true, SetLastError = false)] + public static extern IntPtr GetStockObject(StockObject i); + } +} diff --git a/WindowsTools/WindowsAPI/PInvoke/Gdi32/StockObject.cs b/WindowsTools/WindowsAPI/PInvoke/Gdi32/StockObject.cs new file mode 100644 index 00000000..f75dc7bd --- /dev/null +++ b/WindowsTools/WindowsAPI/PInvoke/Gdi32/StockObject.cs @@ -0,0 +1,110 @@ +namespace WindowsTools.WindowsAPI.PInvoke.Gdi32 +{ + /// + /// 常用对象的类型。 + /// + public enum StockObject : int + { + /// + /// 白色画笔。 + /// + WHITE_BRUSH = 0, + + /// + /// 浅灰色画笔。 + /// + LTGRAY_BRUSH = 1, + + /// + /// 灰色画笔。 + /// + GRAY_BRUSH = 2, + + /// + /// 深灰色画笔。 + /// + DKGRAY_BRUSH = 3, + + /// + /// 黑色画笔。 + /// + BLACK_BRUSH = 4, + + /// + /// null 画笔 (等效于 HOLLOW_BRUSH) 。 + /// + NULL_BRUSH = 5, + + /// + /// 空心画笔 (等效于NULL_BRUSH) 。 + /// + HOLLOW_BRUSH = NULL_BRUSH, + + /// + /// 白色触笔。 + /// + WHITE_PEN = 6, + + /// + /// 黑色触笔。 + /// + BLACK_PEN = 7, + + /// + /// null 触笔。 null 触控笔不绘制任何内容。 + /// + NULL_PEN = 8, + + /// + /// 原始设备制造商 (OEM) 依赖固定间距 (正方形) 字体。 + /// + OEM_FIXED_FONT = 10, + + /// + /// Windows 固定间距 (正) 系统字体。 + /// + ANSI_FIXED_FONT = 11, + + /// + /// Windows 可变间距 (比例空间) 系统字体。 + /// + ANSI_VAR_FONT = 12, + + /// + /// 系统字体。 默认情况下,系统使用系统字体来绘制菜单、对话框控件和文本。 不建议使用DEFAULT_GUI_FONT或SYSTEM_FONT来获取对话框和窗口使用的字体;有关详细信息,请参阅备注部分。 + /// 默认系统字体为 Tahoma。 + /// + SYSTEM_FONT = 13, + + /// + /// 设备依赖字体。 + /// + DEVICE_DEFAULT_FONT = 14, + + /// + /// 默认调色板。 此调色板由系统调色板中的静态颜色组成。 + /// + DEFAULT_PALETTE = 15, + + /// + /// 固定间距 (单调) 系统字体。 此库存对象仅用于与 3.0 之前的 16 位 Windows 版本兼容。 + /// + SYSTEM_FIXED_FONT = 16, + + /// + /// 用户界面对象(如菜单和对话框)的默认字体。 不建议使用DEFAULT_GUI_FONT或SYSTEM_FONT来获取对话框和窗口使用的字体;有关详细信息,请参阅备注部分。 + /// 默认字体为 Tahoma。 + /// + DEFAULT_GUI_FONT = 17, + + /// + /// 纯色画笔。 默认颜色为白色。 + /// + DC_BRUSH = 18, + + /// + /// 纯色笔颜色。 默认颜色为黑色。 + /// + DC_PEN = 19, + } +} diff --git a/WindowsTools/WindowsAPI/PInvoke/Imagehlp/ImagehlpLibrary.cs b/WindowsTools/WindowsAPI/PInvoke/Imagehlp/ImagehlpLibrary.cs index 3b8dd19d..b5e9e8b6 100644 --- a/WindowsTools/WindowsAPI/PInvoke/Imagehlp/ImagehlpLibrary.cs +++ b/WindowsTools/WindowsAPI/PInvoke/Imagehlp/ImagehlpLibrary.cs @@ -19,7 +19,7 @@ public static class ImagehlpLibrary /// 要修改的图像文件的句柄。 必须打开此句柄才能FILE_READ_DATA和FILE_WRITE_DATA访问。 /// 要删除的证书的索引。 /// 如果函数成功,则返回值为 TRUE。如果函数失败,则返回值为 FALSE。 - [DllImport(Imagehlp, CharSet = CharSet.Unicode, EntryPoint = "ImageRemoveCertificate", SetLastError = false, PreserveSig = true)] + [DllImport(Imagehlp, CharSet = CharSet.Unicode, EntryPoint = "ImageRemoveCertificate", PreserveSig = true, SetLastError = false)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool ImageRemoveCertificate(IntPtr handle, int index); } diff --git a/WindowsTools/WindowsAPI/PInvoke/Kernel32/Kernel32Library.cs b/WindowsTools/WindowsAPI/PInvoke/Kernel32/Kernel32Library.cs index 84ffdfcd..a8325785 100644 --- a/WindowsTools/WindowsAPI/PInvoke/Kernel32/Kernel32Library.cs +++ b/WindowsTools/WindowsAPI/PInvoke/Kernel32/Kernel32Library.cs @@ -21,7 +21,7 @@ public static class Kernel32Library /// 输入时, applicationUserModelId 缓冲区的大小(以宽字符为单位)。 成功时,使用的缓冲区大小,包括 null 终止符。 /// 指向接收应用程序用户模型 ID 的缓冲区的指针。 /// 如果该函数成功,则返回 ERROR_SUCCESS。 否则,该函数将返回错误代码。 - [DllImport(Kernel32, CharSet = CharSet.Unicode, EntryPoint = "GetCurrentApplicationUserModelId", SetLastError = false, PreserveSig = true)] + [DllImport(Kernel32, CharSet = CharSet.Unicode, EntryPoint = "GetCurrentApplicationUserModelId", PreserveSig = true, SetLastError = false)] public static extern int GetCurrentApplicationUserModelId(ref uint applicationUserModelIdLength, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder applicationUserModelId); /// @@ -30,14 +30,14 @@ public static class Kernel32Library /// 输入时, packageFamilyName 缓冲区的大小(以字符为单位),包括 null 终止符。 输出时,返回的包系列名称的大小(以字符为单位),包括 null 终止符。 /// 包系列名称。 /// 如果函数成功,则返回 ERROR_SUCCESS。 否则,函数将返回错误代码。 - [DllImport(Kernel32, CharSet = CharSet.Unicode, EntryPoint = "GetCurrentPackageFamilyName", SetLastError = false, PreserveSig = true)] + [DllImport(Kernel32, CharSet = CharSet.Unicode, EntryPoint = "GetCurrentPackageFamilyName", PreserveSig = true, SetLastError = false)] public static extern int GetCurrentPackageFamilyName(ref int packageFamilyNameLength, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder packageFamilyName); /// /// 检索系统的电源状态。 状态指示系统是使用交流还是直流电源运行,电池当前是否正在充电,剩余的电池使用时间,以及节电模式是打开还是关闭。 /// /// 指向接收状态信息的 SYSTEM_POWER_STATUS 结构的指针。 - [DllImport(Kernel32, CharSet = CharSet.Unicode, EntryPoint = "GetSystemPowerStatus", SetLastError = false, PreserveSig = true)] + [DllImport(Kernel32, CharSet = CharSet.Unicode, EntryPoint = "GetSystemPowerStatus", PreserveSig = true, SetLastError = false)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool GetSystemPowerStatus(out SYSTEM_POWER_STATUS lpSystemPowerStatus); @@ -46,7 +46,7 @@ public static class Kernel32Library /// /// 线程的执行要求。 /// 如果函数成功,则返回值为上一个线程执行状态。如果函数失败,则返回值为 NULL。 - [DllImport(Kernel32, CharSet = CharSet.Unicode, EntryPoint = "SetThreadExecutionState", SetLastError = false, PreserveSig = true)] + [DllImport(Kernel32, CharSet = CharSet.Unicode, EntryPoint = "SetThreadExecutionState", PreserveSig = true, SetLastError = false)] public static extern EXECUTION_STATE SetThreadExecutionState(EXECUTION_STATE esFlags); } } diff --git a/WindowsTools/WindowsAPI/PInvoke/KernelAppCore/KernelAppCoreLibrary.cs b/WindowsTools/WindowsAPI/PInvoke/KernelAppCore/KernelAppCoreLibrary.cs index 507fa0de..bd33a7ef 100644 --- a/WindowsTools/WindowsAPI/PInvoke/KernelAppCore/KernelAppCoreLibrary.cs +++ b/WindowsTools/WindowsAPI/PInvoke/KernelAppCore/KernelAppCoreLibrary.cs @@ -21,7 +21,7 @@ public static class KernelAppCoreLibrary /// 包信息,表示为 PACKAGE_INFO 结构的数组。 /// 缓冲区中的结构数。 /// 如果该函数成功,则返回 ERROR_SUCCESS。 否则,该函数将返回错误代码。 - [DllImport(KernelAppCore, CharSet = CharSet.Unicode, EntryPoint = "GetCurrentPackageInfo2", SetLastError = false, PreserveSig = true)] + [DllImport(KernelAppCore, CharSet = CharSet.Unicode, EntryPoint = "GetCurrentPackageInfo2", PreserveSig = true, SetLastError = false)] public static extern int GetCurrentPackageInfo2(PACKAGE_FLAGS flags, PackagePathType packagePathType, ref uint bufferLength, [MarshalAs(UnmanagedType.LPArray)] byte[] buffer, out uint count); } } diff --git a/WindowsTools/WindowsAPI/PInvoke/Ole32/Ole32Library.cs b/WindowsTools/WindowsAPI/PInvoke/Ole32/Ole32Library.cs index a7d699c9..f94d75da 100644 --- a/WindowsTools/WindowsAPI/PInvoke/Ole32/Ole32Library.cs +++ b/WindowsTools/WindowsAPI/PInvoke/Ole32/Ole32Library.cs @@ -37,7 +37,7 @@ public static class Ole32Library /// 如果设置了 pAuthInfo 并在 dwCapabilities 参数中设置了其中一个隐藏标志,CoSetProxyBlanket 将失败。 /// /// 此代理的功能。 有关可能值的列表,请参阅 EOLE_AUTHENTICATION_CAPABILITIES 枚举。 唯一可以通过此函数设置的标志是EOAC_MUTUAL_AUTH、EOAC_STATIC_CLOAKING、EOAC_DYNAMIC_CLOAKING,EOAC_ANY_AUTHORITY (此标志已弃用) 、EOAC_MAKE_FULLSIC和EOAC_DEFAULT。 如果未设置 pAuthInfo 且 Schannel 不是身份验证服务,则可以设置 EOAC_STATIC_CLOAKING 或 EOAC_DYNAMIC_CLOAKING。 (有关详细信息,请参阅 隐藏 。) 如果设置了此处提及的功能以外的任何功能标志, CoSetProxyBlanket 将失败。 - [DllImport(Ole32, CharSet = CharSet.Unicode, EntryPoint = "CoSetProxyBlanket", SetLastError = false, PreserveSig = true)] + [DllImport(Ole32, CharSet = CharSet.Unicode, EntryPoint = "CoSetProxyBlanket", PreserveSig = true, SetLastError = false)] public static extern int CoSetProxyBlanket(IntPtr proxy, uint dwAuthnSvc, uint dwAuthzSvc, IntPtr pServerPrincName, uint dwAuthLevel, uint dwImpLevel, IntPtr pAuthInfo, uint dwCapabilities); } } diff --git a/WindowsTools/WindowsAPI/PInvoke/Rstrtmgr/RstrtmgrLibrary.cs b/WindowsTools/WindowsAPI/PInvoke/Rstrtmgr/RstrtmgrLibrary.cs index 38d565db..076f4167 100644 --- a/WindowsTools/WindowsAPI/PInvoke/Rstrtmgr/RstrtmgrLibrary.cs +++ b/WindowsTools/WindowsAPI/PInvoke/Rstrtmgr/RstrtmgrLibrary.cs @@ -7,7 +7,7 @@ namespace WindowsTools.WindowsAPI.PInvoke.Rstrtmgr { public static class RstrtmgrLibrary { - public const string Rstrtmgr = "rstrtmgr.dll"; + private const string Rstrtmgr = "rstrtmgr.dll"; /// /// 将资源注册到重启管理器会话。 重启管理器使用向会话注册的资源列表来确定必须关闭和重启哪些应用程序和服务。 可以通过文件名、服务短名称或描述正在运行的应用程序 RM_UNIQUE_PROCESS 结构来标识资源。 RmRegisterResources 函数可由主安装程序或辅助安装程序使用。 @@ -20,7 +20,7 @@ public static class RstrtmgrLibrary /// 要注册的服务数。 /// 以 null 结尾的服务短名称字符串的数组。 如果 nServices 为 0,此参数可以为 NULL。 /// 这是收到的最新错误。 函数可以返回 Winerror.h 中定义的系统错误代码之一。 - [DllImport(Rstrtmgr, CharSet = CharSet.Unicode, EntryPoint = "RmRegisterResources", SetLastError = false, PreserveSig = true)] + [DllImport(Rstrtmgr, CharSet = CharSet.Unicode, EntryPoint = "RmRegisterResources", PreserveSig = true, SetLastError = false)] public static extern int RmRegisterResources(uint pSessionHandle, uint nFiles, [In, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr, SizeParamIndex = 0)] string[] rgsFilenames, uint nApplications, [In] RM_UNIQUE_PROCESS[] rgApplications, uint nServices, [In, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr, SizeParamIndex = 0)] string[] rgsServiceNames); /// @@ -30,7 +30,7 @@ public static class RstrtmgrLibrary /// 保留。 此参数应为 0。 /// 一个 以 null 结尾的字符串,其中包含新会话的会话密钥。 在调用 RmStartSession 函数之前,必须分配字符串。 /// 这是收到的最新错误。 函数可以返回 Winerror.h 中定义的系统错误代码之一。 - [DllImport(Rstrtmgr, CharSet = CharSet.Unicode, EntryPoint = "RmStartSession", SetLastError = false, PreserveSig = true)] + [DllImport(Rstrtmgr, CharSet = CharSet.Unicode, EntryPoint = "RmStartSession", PreserveSig = true, SetLastError = false)] public static extern int RmStartSession(out uint pSessionHandle, int dwSessionFlags, [MarshalAs(UnmanagedType.LPWStr)] string strSessionKey); /// @@ -38,7 +38,7 @@ public static class RstrtmgrLibrary /// /// 现有 Restart Manager 会话的句柄。 /// 这是收到的最新错误。 函数可以返回 Winerror.h 中定义的系统错误代码之一。 - [DllImport(Rstrtmgr, CharSet = CharSet.Unicode, EntryPoint = "RmEndSession", SetLastError = false, PreserveSig = true)] + [DllImport(Rstrtmgr, CharSet = CharSet.Unicode, EntryPoint = "RmEndSession", PreserveSig = true, SetLastError = false)] public static extern int RmEndSession(uint pSessionHandle); /// @@ -50,7 +50,7 @@ public static class RstrtmgrLibrary /// 一组RM_PROCESS_INFO结构,这些结构使用已注册到会话的资源列出应用程序和服务。 /// 指向位置的指针,该位置接收 RM_REBOOT_REASON 枚举的值,该枚举描述需要重启系统的原因。 /// 这是收到的最新错误。 函数可以返回 Winerror.h 中定义的系统错误代码之一。 - [DllImport(Rstrtmgr, CharSet = CharSet.Unicode, EntryPoint = "RmGetList", SetLastError = false, PreserveSig = true)] + [DllImport(Rstrtmgr, CharSet = CharSet.Unicode, EntryPoint = "RmGetList", PreserveSig = true, SetLastError = false)] public static extern int RmGetList(uint dwSessionHandle, out uint pnProcInfoNeeded, ref uint pnProcInfo, [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] RM_PROCESS_INFO[] rgAffectedApps, out uint lpdwRebootReasons); } } diff --git a/WindowsTools/WindowsAPI/PInvoke/Shell32/ABE.cs b/WindowsTools/WindowsAPI/PInvoke/Shell32/ABE.cs new file mode 100644 index 00000000..87cf9cee --- /dev/null +++ b/WindowsTools/WindowsAPI/PInvoke/Shell32/ABE.cs @@ -0,0 +1,10 @@ +namespace WindowsTools.WindowsAPI.PInvoke.Shell32 +{ + public enum ABE : uint + { + Left = 0, + Top = 1, + Right = 2, + Bottom = 3 + } +} diff --git a/WindowsTools/WindowsAPI/PInvoke/Shell32/ABM.cs b/WindowsTools/WindowsAPI/PInvoke/Shell32/ABM.cs new file mode 100644 index 00000000..b394e44c --- /dev/null +++ b/WindowsTools/WindowsAPI/PInvoke/Shell32/ABM.cs @@ -0,0 +1,73 @@ +namespace WindowsTools.WindowsAPI.PInvoke.Shell32 +{ + /// + /// 发送的应用栏消息值 + /// + public enum ABM : uint + { + /// + /// 注册新的应用栏,并指定系统应该用来向应用栏发送通知消息的消息标识符。 + /// + ABM_NEW = 0x00000000, + + /// + /// 注销应用栏,从系统的内部列表中删除该栏。 + /// + ABM_REMOVE = 0x00000001, + + /// + /// 请求应用栏的大小和屏幕位置。 + /// + ABM_QUERYPOS = 0x00000002, + + /// + /// 设置应用栏的大小和屏幕位置。 + /// + ABM_SETPOS = 0x00000003, + + /// + /// 检索 Windows 任务栏的自动隐藏和始终处于顶部状态。 + /// + ABM_GETSTATE = 0x00000004, + + /// + /// 检索 Windows 任务栏的边框。 请注意,这仅适用于系统任务栏。 其他对象(尤其是第三方软件提供的工具栏)也可以存在。 因此,用户可能无法看到 Windows 任务栏未覆盖的某些屏幕区域。 若要检索任务栏和其他应用栏未覆盖的屏幕区域(应用程序可用的工作区),请使用 GetMonitorInfo 函数。 + /// + ABM_GETTASKBARPOS = 0x00000005, + + /// + /// 通知系统激活或停用应用栏。 pData 指向的 APPBARDATA 的 lParam 成员设置为 TRUE 以激活,或设置为 FALSE 以停用。 + /// + ABM_ACTIVATE = 0x00000006, + + /// + /// 检索与屏幕的特定边缘关联的自动隐藏应用栏的句柄。 + /// + ABM_GETAUTOHIDEBAR = 0x00000007, + + /// + /// 注册或注销屏幕边缘的自动隐藏应用栏。 + /// + ABM_SETAUTOHIDEBAR = 0x00000008, + + /// + /// 当应用栏的位置发生更改时,通知系统。 + /// + ABM_WINDOWPOSCHANGED = 0x00000009, + + /// + /// Windows XP 及更高版本: 设置应用栏的自动隐藏和 Always-on-top 属性的状态。 + /// + ABM_SETSTATE = 0x0000000A, + + /// + /// Windows XP 及更高版本: 检索与特定监视器的特定边缘关联的自动隐藏应用栏的句柄。 + /// + ABM_GETAUTOHIDEBAREX = 0x0000000B, + + /// + /// Windows XP 及更高版本: 为特定监视器的边缘注册或注销自动隐藏应用栏。 + /// + ABM_SETAUTOHIDEBAREX = 0x0000000C + } +} diff --git a/WindowsTools/WindowsAPI/PInvoke/Shell32/APPBARDATA.cs b/WindowsTools/WindowsAPI/PInvoke/Shell32/APPBARDATA.cs new file mode 100644 index 00000000..fe10ef87 --- /dev/null +++ b/WindowsTools/WindowsAPI/PInvoke/Shell32/APPBARDATA.cs @@ -0,0 +1,45 @@ +using System; +using System.Runtime.InteropServices; +using WindowsTools.WindowsAPI.PInvoke.User32; + +namespace WindowsTools.WindowsAPI.PInvoke.Shell32 +{ + /// + /// 包含有关系统应用栏消息的信息。 + /// + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct APPBARDATA + { + /// + /// 结构大小(以字节为单位)。 + /// + public int cbSize; + + /// + /// 应用栏窗口的句柄。 并非所有邮件都使用此成员。 请参阅单个消息页,了解是否需要提供 hWind 值。 + /// + public IntPtr hWnd; + + /// + /// 应用程序定义的消息标识符。 应用程序对发送到 由 hWnd 成员标识的应用栏的通知消息使用指定的标识符。 发送 ABM_NEW 消息时使用此成员。 + /// + public uint uCallbackMessage; + + /// + /// 一个值,该值指定屏幕的边缘。 + /// + public ABE uEdge; + + /// + /// 其用法因消息而异的 RECT 结构: + /// ABM_GETTASKBARPOS、ABM_QUERYPOS、ABM_SETPOS:应用栏或 Windows 任务栏的屏幕坐标边框。 + /// ABM_GETAUTOHIDEBAREX,ABM_SETAUTOHIDEBAREX:正在其上执行操作的监视器。 可以通过 GetMonitorInfo 函数检索此信息。 + /// + public RECT rc; + + /// + /// 依赖于消息的值。 + /// + public int lParam; + } +} diff --git a/WindowsTools/WindowsAPI/PInvoke/Shell32/Shell32Library.cs b/WindowsTools/WindowsAPI/PInvoke/Shell32/Shell32Library.cs index 7bad5daf..7816f830 100644 --- a/WindowsTools/WindowsAPI/PInvoke/Shell32/Shell32Library.cs +++ b/WindowsTools/WindowsAPI/PInvoke/Shell32/Shell32Library.cs @@ -15,14 +15,14 @@ namespace WindowsTools.WindowsAPI.PInvoke.Shell32 /// public static class Shell32Library { - public const string Shell32 = "shell32.dll"; + private const string Shell32 = "shell32.dll"; /// /// 注册窗口是否接受已删除的文件。 /// /// 正在注册是否接受已删除文件的窗口的标识符。 /// 一个值,该值指示 hWnd 参数标识的窗口是否接受已删除的文件。 如果接受已删除的文件,则此值为 TRUE ;如果值为 FALSE ,则表示停止接受已删除的文件。 - [DllImport(Shell32, CharSet = CharSet.Unicode, EntryPoint = "DragAcceptFiles", SetLastError = false, PreserveSig = true)] + [DllImport(Shell32, CharSet = CharSet.Unicode, EntryPoint = "DragAcceptFiles", PreserveSig = true, SetLastError = false)] public static extern void DragAcceptFiles(IntPtr hwnd, bool fAccept); /// @@ -38,7 +38,7 @@ public static class Shell32Library /// 如果索引值为0xFFFFFFFF,则返回值是已删除文件的计数。 请注意,索引变量本身返回不变,因此保持0xFFFFFFFF。 /// 如果索引值介于零和已删除文件总数之间,并且 lpszFile 缓冲区地址为 NULL,则返回值是缓冲区所需的大小(以字符为单位), 不包括 终止 null 字符。 /// - [DllImport(Shell32, CharSet = CharSet.Unicode, EntryPoint = "DragQueryFileW", SetLastError = false, PreserveSig = true)] + [DllImport(Shell32, CharSet = CharSet.Unicode, EntryPoint = "DragQueryFileW", PreserveSig = true, SetLastError = false)] public static extern uint DragQueryFile(IntPtr hDrop, uint iFile, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder lpszFile, uint cch); /// @@ -47,7 +47,7 @@ public static class Shell32Library /// 述已删除文件的放置结构的句柄。 /// 指向 POINT 结构的指针,当此函数成功返回时,该结构接收删除文件时鼠标指针的坐标。 /// 如果删除发生在窗口的工作区中,则为 TRUE;否则为 FALSE。 - [DllImport(Shell32, CharSet = CharSet.Unicode, EntryPoint = "DragQueryPoint", SetLastError = false, PreserveSig = true)] + [DllImport(Shell32, CharSet = CharSet.Unicode, EntryPoint = "DragQueryPoint", PreserveSig = true, SetLastError = false)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool DragQueryPoint(IntPtr hDrop, out Point lppt); @@ -55,7 +55,7 @@ public static class Shell32Library /// 描述已删除的文件的结构的标识符。 此句柄是从WM_DROPFILES消息的 wParam 参数检索的。 /// /// 释放系统分配用于将文件名传输到应用程序的内存。 - [DllImport(Shell32, CharSet = CharSet.Unicode, EntryPoint = "DragFinish", SetLastError = false, PreserveSig = true)] + [DllImport(Shell32, CharSet = CharSet.Unicode, EntryPoint = "DragFinish", PreserveSig = true, SetLastError = false)] public static extern void DragFinish(IntPtr hDrop); /// @@ -63,22 +63,31 @@ public static class Shell32Library /// /// 指向包含路径的以 null 结尾的 Unicode 字符串的指针。 此字符串的长度应不超过 MAX_PATH 个字符,包括终止 null 字符。 /// 返回指向对应于路径的 ITEMIDLIST 结构的指针。 - [DllImport(Shell32, CharSet = CharSet.Unicode, EntryPoint = "ILCreateFromPathW", SetLastError = false, PreserveSig = true)] + [DllImport(Shell32, CharSet = CharSet.Unicode, EntryPoint = "ILCreateFromPathW", PreserveSig = true, SetLastError = false)] public static extern IntPtr ILCreateFromPath([MarshalAs(UnmanagedType.LPWStr)] string pszPath); /// /// 释放 Shell 分配的 ITEMIDLIST 结构。 /// /// 指向要释放的 ITEMIDLIST 结构的指针。 此参数可以为 NULL。 - [DllImport(Shell32, CharSet = CharSet.Unicode, EntryPoint = "ILFree", SetLastError = false, PreserveSig = true)] + [DllImport(Shell32, CharSet = CharSet.Unicode, EntryPoint = "ILFree", PreserveSig = true, SetLastError = false)] public static extern void ILFree(IntPtr pidl); + /// + /// 向系统发送应用栏消息。 + /// + /// 要发送的应用栏消息值。 + /// 指向 APPBARDATA 结构的指针。 进入和退出时结构的内容取决于 dwMessage 参数中设置的值。 + /// 此函数返回一个依赖于消息的值。 + [DllImport(Shell32, CharSet = CharSet.Unicode, EntryPoint = "SHAppBarMessage", PreserveSig = true, SetLastError = false)] + public static extern IntPtr SHAppBarMessage(ABM dwMessage, ref APPBARDATA pData); + /// /// 对指定文件执行操作。 /// /// 指向 SHELLEXECUTEINFO 结构的指针,该结构包含并接收有关正在执行的应用程序的信息。 /// 如果成功,则返回 TRUE ;否则为 FALSE。 调用 GetLastError 获取扩展错误信息。 - [DllImport(Shell32, CharSet = CharSet.Unicode, EntryPoint = "ShellExecuteExW", SetLastError = false, PreserveSig = true)] + [DllImport(Shell32, CharSet = CharSet.Unicode, EntryPoint = "ShellExecuteExW", PreserveSig = true, SetLastError = false)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool ShellExecuteEx(ref SHELLEXECUTEINFO lpExecInfo); @@ -98,7 +107,7 @@ public static class Shell32Library /// 对接口的 IID 的引用,以通过ppv(通常为IID_IShellItem或IID_IShellItem2)进行检索。 /// 此方法成功返回时,包含 riid 中请求的接口指针。这通常是IShellItem或IShellItem2。 /// 如果此函数成功,则返回 S_OK。 否则,将返回 HRESULT 错误代码。 - [DllImport(Shell32, CharSet = CharSet.Unicode, EntryPoint = "SHCreateItemFromParsingName", SetLastError = false, PreserveSig = true)] + [DllImport(Shell32, CharSet = CharSet.Unicode, EntryPoint = "SHCreateItemFromParsingName", PreserveSig = true, SetLastError = false)] public static extern int SHCreateItemFromParsingName([MarshalAs(UnmanagedType.LPWStr)] string pszPath, IBindCtx pbc, Guid riid, [MarshalAs(UnmanagedType.Interface)] out IShellItem ppv); /// @@ -115,7 +124,7 @@ public static class Shell32Library /// 此方法返回时,包含指向以 null 结尾的 Unicode 字符串的指针的地址,该字符串指定已知文件夹的路径。 调用进程负责通过调用 CoTaskMemFree 不再需要此资源后释放此资源,无论 SHGetKnownFolderPath 是否成功。 返回的路径不包括尾随反斜杠。 例如,返回“C:\Users”而不是“C:\Users\”。 /// /// 如果成功,则返回S_OK,否则返回错误值 - [DllImport(Shell32, CharSet = CharSet.Unicode, EntryPoint = "SHGetKnownFolderPath", SetLastError = false, PreserveSig = true)] + [DllImport(Shell32, CharSet = CharSet.Unicode, EntryPoint = "SHGetKnownFolderPath", PreserveSig = true, SetLastError = false)] public static extern int SHGetKnownFolderPath(Guid rfid, KNOWN_FOLDER_FLAG dwFlags, IntPtr hToken, [MarshalAs(UnmanagedType.LPWStr)] out string pszPath); /// diff --git a/WindowsTools/WindowsAPI/PInvoke/Shlwapi/ShlwapiLibrary.cs b/WindowsTools/WindowsAPI/PInvoke/Shlwapi/ShlwapiLibrary.cs index 913cc092..194c27f5 100644 --- a/WindowsTools/WindowsAPI/PInvoke/Shlwapi/ShlwapiLibrary.cs +++ b/WindowsTools/WindowsAPI/PInvoke/Shlwapi/ShlwapiLibrary.cs @@ -14,7 +14,7 @@ namespace WindowsTools.WindowsAPI.PInvoke.Shlwapi /// public static class ShlwapiLibrary { - public const string Shlwapi = "shlwapi.dll"; + private const string Shlwapi = "shlwapi.dll"; /// /// 打开或创建文件,并检索要读取或写入该文件的流。 diff --git a/WindowsTools/WindowsAPI/PInvoke/User32/HITTEST.cs b/WindowsTools/WindowsAPI/PInvoke/User32/HITTEST.cs new file mode 100644 index 00000000..deeaf3b8 --- /dev/null +++ b/WindowsTools/WindowsAPI/PInvoke/User32/HITTEST.cs @@ -0,0 +1,125 @@ +namespace WindowsTools.WindowsAPI.PInvoke.User32 +{ + /// + /// 指示光标热点的位置 + /// + public enum HITTEST + { + /// + /// 在屏幕背景上或窗口之间的分割线上(与 HTNOWHERE 相同,只是 DefWindowProc 函数会生成系统蜂鸣音以指示错误)。 + /// + HTERROR = -2, + + /// + /// 在同一线程当前由另一个窗口覆盖的窗口中(消息将发送到同一线程中的基础窗口,直到其中一个窗口返回不是 HTTRANSPARENT 的代码)。 + /// + HTTRANSPARENT = -1, + + /// + /// 在屏幕背景上,或在窗口之间的分隔线上。 + /// + HTNOWHERE = 0, + + /// + /// 在工作区中。 + /// + HTCLIENT = 1, + + /// + /// 在标题栏中。 + /// + HTCAPTION = 2, + + /// + /// 在窗口菜单或子窗口的关闭按钮中。 + /// + HTSYSMENU = 3, + + /// + /// 在大小框中(与 HTSIZE 相同)。 + /// + HTGROWBOX = 4, + + /// + /// 在菜单中。 + /// + HTMENU = 5, + + /// + /// 在水平滚动条中。 + /// + HTHSCROLL = 6, + + /// + /// 在垂直滚动条中。 + /// + HTVSCROLL = 7, + + /// + /// 在“最小化”按钮中。 + /// + HTMINBUTTON = 8, + + /// + /// 在“最大化”按钮中。 + /// + HTMAXBUTTON = 9, + + /// + /// 在可调整大小的窗口的左边框中(用户可以单击鼠标以水平调整窗口大小)。 + /// + HTLEFT = 10, + + /// + /// 在可调整大小的窗口的右左边框中(用户可以单击鼠标以水平调整窗口大小)。 + /// + HTRIGHT = 11, + + /// + /// 在窗口的上水平边框中。 + /// + HTTOP = 12, + + /// + /// 在窗口边框的左上角。 + /// + HTTOPLEFT = 13, + + /// + /// 在窗口边框的右上角。 + /// + HTTOPRIGHT = 14, + + /// + /// 在可调整大小的窗口的下水平边框中(用户可以单击鼠标以垂直调整窗口大小)。 + /// + HTBOTTOM = 15, + + /// + /// 在可调整大小的窗口的边框左下角(用户可以单击鼠标以对角线调整窗口大小)。 + /// + HTBOTTOMLEFT = 16, + + /// + /// 在可调整大小的窗口的边框右下角(用户可以单击鼠标以对角线调整窗口大小)。 + /// + HTBOTTOMRIGHT = 17, + + /// + /// 在没有大小调整边框的窗口边框中。 + /// + HTBORDER = 18, + + HTOBJECT = 19, + + /// + /// 在“关闭”按钮中。 + /// + HTCLOSE = 20, + + /// + /// 在“帮助”按钮中。 + /// + HTHELP = 21, + } +} diff --git a/WindowsTools/WindowsAPI/PInvoke/User32/HOOKPROC.cs b/WindowsTools/WindowsAPI/PInvoke/User32/HOOKPROC.cs index a6d6e6d4..5de8a19e 100644 --- a/WindowsTools/WindowsAPI/PInvoke/User32/HOOKPROC.cs +++ b/WindowsTools/WindowsAPI/PInvoke/User32/HOOKPROC.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.InteropServices; namespace WindowsTools.WindowsAPI.PInvoke.User32 { @@ -13,5 +14,6 @@ namespace WindowsTools.WindowsAPI.PInvoke.User32 /// 如果 nCode 小于零,则挂钩过程必须返回 CallNextHookEx 函数返回的值。 /// 如果 nCode 大于或等于零,强烈建议调用 CallNextHookEx 函数 并返回它返回的值; 否则,已安装 WH_CALLWNDPROCRET 挂钩的其他应用程序将不会收到挂钩通知,并可能因此行为不正确。 如果挂钩过程不调用 CallNextHookEx,则返回值应为零。 /// + [UnmanagedFunctionPointer(CallingConvention.Winapi)] public delegate IntPtr HOOKPROC(int nCode, UIntPtr wParam, IntPtr lParam); } diff --git a/WindowsTools/WindowsAPI/PInvoke/User32/LWA.cs b/WindowsTools/WindowsAPI/PInvoke/User32/LWA.cs new file mode 100644 index 00000000..dc432993 --- /dev/null +++ b/WindowsTools/WindowsAPI/PInvoke/User32/LWA.cs @@ -0,0 +1,18 @@ +namespace WindowsTools.WindowsAPI.PInvoke.User32 +{ + /// + /// 设置分层窗口的不透明度和透明度颜色键时对应的操作 + /// + public enum LWA + { + /// + /// 使用 crKey 作为透明度颜色。 + /// + LWA_COLORKEY = 0x1, + + /// + /// 使用 bAlpha 确定分层窗口的不透明度。 + /// + LWA_ALPHA = 0x2 + } +} diff --git a/WindowsTools/WindowsAPI/PInvoke/User32/NCCALCSIZE_PARAMS.cs b/WindowsTools/WindowsAPI/PInvoke/User32/NCCALCSIZE_PARAMS.cs new file mode 100644 index 00000000..274d6d11 --- /dev/null +++ b/WindowsTools/WindowsAPI/PInvoke/User32/NCCALCSIZE_PARAMS.cs @@ -0,0 +1,25 @@ +using System; +using System.Runtime.InteropServices; + +namespace WindowsTools.WindowsAPI.PInvoke.User32 +{ + /// + /// 包含应用程序在处理 WM_NCCALCSIZE 消息时可以使用的信息,以计算窗口工作区的大小、位置和有效内容。 + /// + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct NCCALCSIZE_PARAMS + { + /// + /// 矩形数组。 矩形数组的含义在处理 WM_NCCALCSIZE 消息期间发生更改。 + /// 当窗口过程收到 WM_NCCALCSIZE 消息时,第一个矩形包含已移动或调整大小的窗口的新坐标,即建议的新窗口坐标。 第二个包含移动或调整窗口大小之前窗口的坐标。 第三个包含移动或调整窗口大小之前窗口工作区的坐标。 如果窗口是子窗口,则坐标相对于父窗口的工作区。 如果窗口是顶级窗口,则坐标相对于屏幕原点。 + /// 当窗口过程返回时,第一个矩形包含移动或调整大小所产生的新客户端矩形的坐标。 第二个矩形包含有效的目标矩形,第三个矩形包含有效的源矩形。 最后两个矩形与 WM_NCCALCSIZE 消息的返回值结合使用,以确定要保留的窗口区域。 + /// + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public RECT[] rgrc; + + /// + /// 指向 WINDOWPOS 结构的指针,该结构包含移动窗口或调整窗口大小的操作中指定的大小和位置值。 + /// + public IntPtr lppos; + } +} diff --git a/WindowsTools/WindowsAPI/PInvoke/User32/PAINTSTRUCT.cs b/WindowsTools/WindowsAPI/PInvoke/User32/PAINTSTRUCT.cs new file mode 100644 index 00000000..44b815ea --- /dev/null +++ b/WindowsTools/WindowsAPI/PInvoke/User32/PAINTSTRUCT.cs @@ -0,0 +1,43 @@ +using System; +using System.Runtime.InteropServices; + +namespace WindowsTools.WindowsAPI.PInvoke.User32 +{ + /// + /// PAINTSTRUCT 结构包含应用程序的信息。 此信息可用于绘制该应用程序拥有的窗口的工作区。 + /// + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct PAINTSTRUCT + { + /// + /// 要用于绘制的显示 DC 的句柄。 + /// + public IntPtr hdc; + + /// + /// 指示是否必须擦除背景。 如果应用程序应擦除背景,则此值为非零值。 如果创建窗口类时没有背景画笔,则应用程序负责擦除背景。 有关详细信息,请参阅 WNDCLASS 结构的 hbrBackground 成员的说明。 + /// + public bool fErase; + + /// + /// RECT 结构,指定请求绘制的矩形的左上角和右下角,以相对于工作区左上角的设备单位表示。 + /// + public RECT rcPaint; + + /// + /// 保留;系统在内部使用。 + /// + public bool fRestore; + + /// + /// 保留;系统在内部使用。 + /// + public bool fIncUpdate; + + /// + /// 保留;系统在内部使用。 + /// + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public byte[] rgbReserved; + } +} diff --git a/WindowsTools/WindowsAPI/PInvoke/User32/RECT.cs b/WindowsTools/WindowsAPI/PInvoke/User32/RECT.cs new file mode 100644 index 00000000..dcba7551 --- /dev/null +++ b/WindowsTools/WindowsAPI/PInvoke/User32/RECT.cs @@ -0,0 +1,31 @@ +using System.Runtime.InteropServices; + +namespace WindowsTools.WindowsAPI.PInvoke.User32 +{ + /// + /// RECT 结构通过矩形的左上角和右下角的坐标来定义矩形。 + /// + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct RECT + { + /// + /// 指定矩形左上角的 x 坐标。 + /// + public int left; + + /// + /// 指定矩形左上角的 y 坐标。 + /// + public int top; + + /// + /// 指定矩形右下角的 x 坐标。 + /// + public int right; + + /// + /// 指定矩形右下角的 y 坐标。 + /// + public int bottom; + } +} diff --git a/WindowsTools/WindowsAPI/PInvoke/User32/SM.cs b/WindowsTools/WindowsAPI/PInvoke/User32/SM.cs new file mode 100644 index 00000000..91445f67 --- /dev/null +++ b/WindowsTools/WindowsAPI/PInvoke/User32/SM.cs @@ -0,0 +1,503 @@ +namespace WindowsTools.WindowsAPI.PInvoke.User32 +{ + /// + /// 要检索的系统指标或配置设置。 S + /// + public enum SM : int + { + /// + /// 指定系统如何排列最小化窗口的标志。 有关详细信息,请参阅本主题中的“备注”部分。 + /// + SM_ARRANGE = 56, + + /// + /// 指定系统启动方式的值: + /// 0 正常启动 + /// 1 故障安全启动 + /// 2 通过网络启动实现故障安全 + /// 故障安全启动 (也称为 SafeBoot、安全模式或干净启动) 会绕过用户启动文件。 + /// + SM_CLEANBOOT = 67, + + /// + /// 桌面上的显示监视器数。 有关详细信息,请参阅本主题中的“备注”部分。 + /// + SM_CMONITORS = 80, + + /// + /// 鼠标上的按钮数;如果未安装鼠标,则为零。 + /// + SM_CMOUSEBUTTONS = 43, + + /// + /// 反映笔记本电脑或平板模式的状态,0 表示板模式,否则为非零。 当此系统指标发生更改时,系统会通过 LPARAM 中带有“ConvertibleSlateMode” 的WM_SETTINGCHANGE 发送广播消息。 请注意,此系统指标不适用于台式电脑。 在这种情况下,请使用 GetAutoRotationState。 + /// + SM_CONVERTIBLESLATEMODE = 0x2003, + + /// + /// 窗口边框的宽度(以像素为单位)。 这等效于具有 3D 外观的窗口的 SM_CXEDGE 值。 + /// + SM_CXBORDER = 5, + + /// + /// 光标的标称宽度(以像素为单位)。 + /// + SM_CXCURSOR = 13, + + /// + /// 此值与 SM_CXFIXEDFRAME 相同。 + /// + SM_CXDLGFRAME = 7, + + /// + /// 矩形围绕双击序列中第一次单击的位置的宽度(以像素为单位)。 + /// 第二次单击必须在由 SM_CXDOUBLECLK 和 SM_CYDOUBLECLK 定义的矩形内发生,系统才能将两次单击视为双击。 两次单击也必须在指定时间内发生。 + /// 若要设置双击矩形的宽度,请使用SPI_SETDOUBLECLKWIDTH调用 SystemParametersInfo 。 + /// + SM_CXDOUBLECLK = 36, + + /// + /// 鼠标指针在拖动操作开始之前可以移动的鼠标向下点任一侧的像素数。 这允许用户轻松单击并释放鼠标按钮,而不会无意中启动拖动操作。 如果此值为负值,则从鼠标向下点的左侧减去该值,并将其添加到其右侧。 + /// + SM_CXDRAG = 68, + + /// + /// 三维边框的宽度(以像素为单位)。 此指标是SM_CXBORDER的三维对应指标。 + /// + SM_CXEDGE = 45, + + /// + /// 窗口周围具有描述文字但不是相当大的(以像素为单位)的框架的粗细。 SM_CXFIXEDFRAME是水平边框的高度,SM_CYFIXEDFRAME是垂直边框的宽度。 + /// 此值与 SM_CXDLGFRAME 相同。 + /// + SM_CXFIXEDFRAME = 7, + + /// + /// DrawFocusRect 绘制的焦点矩形的左边缘和右边缘的宽度。 此值以像素为单位。 + /// Windows 2000: 不支持此值。 + /// + SM_CXFOCUSBORDER = 83, + + /// + /// 此值与 SM_CXSIZEFRAME 相同。 + /// + SM_CXFRAME = 32, + + /// + /// 主显示器上全屏窗口的工作区宽度(以像素为单位)。 若要获取系统任务栏或应用程序桌面工具栏未遮挡的屏幕部分的坐标,请使用SPI_GETWORKAREA值调用 SystemParametersInfo 函数。 + /// + SM_CXFULLSCREEN = 16, + + /// + /// 水平滚动条上箭头位图的宽度(以像素为单位)。 + /// + SM_CXHSCROLL = 21, + + /// + /// 水平滚动条中拇指框的宽度(以像素为单位)。 + /// + SM_CXHTHUMB = 10, + + /// + /// 图标的系统大宽度(以像素为单位)。 LoadIcon 函数只能加载具有SM_CXICON和SM_CYICON指定尺寸的图标。 有关详细信息 ,请参阅图标大小 。 + /// + SM_CXICON = 11, + + /// + /// 大图标视图中项的网格单元格的宽度(以像素为单位)。 每个项都适合在排列时按SM_CYICONSPACING SM_CXICONSPACING大小的矩形。 此值始终大于或等于 SM_CXICON。 + /// + SM_CXICONSPACING = 38, + + /// + /// 主显示监视器上最大化的顶级窗口的默认宽度(以像素为单位)。 + /// + SM_CXMAXIMIZED = 61, + + /// + /// 具有描述文字和大小调整边框(以像素为单位)的窗口的默认最大宽度。 此指标是指整个桌面。 用户无法将窗口框架拖动到大于这些尺寸的大小。 窗口可以通过处理 WM_GETMINMAXINFO 消息来替代此值。 + /// + SM_CXMAXTRACK = 59, + + /// + /// 默认菜单的宽度检查标记位图(以像素为单位)。 + /// + SM_CXMENUCHECK = 71, + + /// + /// 菜单栏按钮的宽度,例如在多个文档界面中使用的子窗口关闭按钮(以像素为单位)。 + /// + SM_CXMENUSIZE = 54, + + /// + /// 窗口的最小宽度(以像素为单位)。 + /// + SM_CXMIN = 28, + + /// + /// 最小化窗口的宽度(以像素为单位)。 + /// + SM_CXMINIMIZED = 57, + + /// + /// 最小化窗口的网格单元格的宽度(以像素为单位)。 每个最小化窗口在排列时适合此大小的矩形。 此值始终大于或等于 SM_CXMINIMIZED。 + /// + SM_CXMINSPACING = 47, + + /// + /// 窗口的最小跟踪宽度(以像素为单位)。 用户无法将窗口框架拖动到小于这些尺寸的大小。 窗口可以通过处理 WM_GETMINMAXINFO 消息来替代此值。 + /// + SM_CXMINTRACK = 34, + + /// + /// 带字幕窗口的边框填充量(以像素为单位)。 + /// Windows XP/2000: 不支持此值。 + /// + SM_CXPADDEDBORDER = 92, + + /// + /// 主显示器的屏幕宽度(以像素为单位)。 这是通过调用 GetDeviceCaps 获取的相同值。 + /// + SM_CXSCREEN = 0, + + /// + /// 窗口中按钮的宽度描述文字或标题栏(以像素为单位)。 + /// + SM_CXSIZE = 30, + + /// + /// 可调整大小的窗口周边的大小边框的粗细(以像素为单位)。 SM_CXSIZEFRAME是水平边框的宽度,SM_CYSIZEFRAME是垂直边框的高度。 + /// 此值与 SM_CXFRAME 相同。 + /// + SM_CXSIZEFRAME = SM_CXFRAME, + + /// + /// 图标的系统小宽度(以像素为单位)。 小图标通常显示在窗口标题和小图标视图中。 有关详细信息 ,请参阅图标大小 。 + /// + SM_CXSMICON = 49, + + /// + /// 小描述文字按钮的宽度(以像素为单位)。 + /// + SM_CXSMSIZE = 52, + + /// + /// 虚拟屏幕的宽度(以像素为单位)。 虚拟屏幕是所有显示监视器的边框。 SM_XVIRTUALSCREEN指标是虚拟屏幕左侧的坐标。 + /// + SM_CXVIRTUALSCREEN = 78, + + /// + /// 垂直滚动条的宽度(以像素为单位)。 + /// + SM_CXVSCROLL = 2, + + /// + /// 窗口边框的高度(以像素为单位)。 这等效于具有 3D 外观的窗口的 SM_CYEDGE 值。 + /// + SM_CYBORDER = 6, + + /// + /// 描述文字区域的高度(以像素为单位)。 + /// + SM_CYCAPTION = 4, + + /// + /// 光标的标称高度(以像素为单位)。 + /// + SM_CYCURSOR = 14, + + /// + /// 此值与 SM_CYFIXEDFRAME 相同。 + /// + SM_CYDLGFRAME = 8, + + /// + /// 矩形围绕双击序列中第一次单击的位置的高度(以像素为单位)。 第二次单击必须在由 SM_CXDOUBLECLK 定义的矩形内发生,SM_CYDOUBLECLK系统会将两次单击视为双击。 两次单击也必须在指定时间内发生。 + /// 若要设置双击矩形的高度,请使用SPI_SETDOUBLECLKHEIGHT调用 SystemParametersInfo 。 + /// + SM_CYDOUBLECLK = 37, + + /// + /// 鼠标指针在拖动操作开始之前可以移动的鼠标向下点上方和下方的像素数。 这允许用户轻松单击并释放鼠标按钮,而不会无意中启动拖动操作。 如果此值为负值,则从鼠标向下点上方减去该值,并将其添加到其下方。 + /// + SM_CYDRAG = 69, + + /// + /// 三维边框的高度(以像素为单位)。 这是SM_CYBORDER的三维对应项。 + /// + SM_CYEDGE = 46, + + /// + /// 窗口周围具有描述文字但不是相当大的(以像素为单位)的框架的粗细。 SM_CXFIXEDFRAME是水平边框的高度,SM_CYFIXEDFRAME是垂直边框的宽度。 + /// 此值与 SM_CYDLGFRAME 相同。 + /// + SM_CYFIXEDFRAME = SM_CYDLGFRAME, + + /// + /// DrawFocusRect 绘制的焦点矩形的上边缘和下边缘的高度。 此值以像素为单位。 + /// Windows 2000: 不支持此值。 + /// + SM_CYFOCUSBORDER = 84, + + /// + /// 此值与 SM_CYSIZEFRAME 相同。 + /// + SM_CYFRAME = 33, + + /// + /// 主显示器上全屏窗口的工作区高度(以像素为单位)。 若要获取系统任务栏或应用程序桌面工具栏未遮挡的屏幕部分的坐标,请使用 SPI_GETWORKAREA 值调用 SystemParametersInfo 函数。 + /// + SM_CYFULLSCREEN = 17, + + /// + /// 水平滚动条的高度(以像素为单位)。 + /// + SM_CYHSCROLL = 3, + + /// + /// 图标的系统高度(以像素为单位)。 LoadIcon 函数只能加载具有SM_CXICON和SM_CYICON指定尺寸的图标。 + /// + SM_CYICON = 12, + + /// + /// 大图标视图中项的网格单元格的高度(以像素为单位)。 每个项都适合在排列时按SM_CYICONSPACING SM_CXICONSPACING大小的矩形。 此值始终大于或等于 SM_CYICON。 + /// + SM_CYICONSPACING = 39, + + /// + /// 对于系统的双字节字符集版本,这是屏幕底部的汉字窗口的高度(以像素为单位)。 + /// + SM_CYKANJIWINDOW = 18, + + /// + /// 主显示监视器上最大化的顶级窗口的默认高度(以像素为单位)。 + /// + SM_CYMAXIMIZED = 62, + + /// + /// 具有描述文字和大小调整边框的窗口的默认最大高度(以像素为单位)。 此指标是指整个桌面。 用户无法将窗口框架拖动到大于这些尺寸的大小。 窗口可以通过处理 WM_GETMINMAXINFO 消息来替代此值。 + /// + SM_CYMAXTRACK = 60, + + /// + /// 单行菜单栏的高度(以像素为单位)。 + /// + SM_CYMENU = 15, + + /// + /// 默认菜单的高度检查标记位图(以像素为单位)。 + /// + SM_CYMENUCHECK = 72, + + /// + /// 菜单栏按钮(例如在多个文档界面中使用的子窗口关闭按钮)的高度(以像素为单位)。 + /// + SM_CYMENUSIZE = 55, + + /// + /// 窗口的最小高度(以像素为单位)。 + /// + SM_CYMIN = 29, + + /// + /// 最小化窗口的高度(以像素为单位)。 + /// + SM_CYMINIMIZED = 58, + + /// + /// 最小化窗口的网格单元格的高度(以像素为单位)。 每个最小化窗口在排列时适合此大小的矩形。 此值始终大于或等于 SM_CYMINIMIZED。 + /// + SM_CYMINSPACING = 48, + + /// + /// 窗口的最小跟踪高度(以像素为单位)。 用户无法将窗口框架拖动到小于这些尺寸的大小。 窗口可以通过处理 WM_GETMINMAXINFO 消息来替代此值。 + /// + SM_CYMINTRACK = 35, + + /// + /// 主显示器的屏幕高度(以像素为单位)。 这是通过调用 GetDeviceCaps 获取的相同值。 + /// + SM_CYSCREEN = 1, + + /// + /// 窗口中按钮的高度描述文字或标题栏(以像素为单位)。 + /// + SM_CYSIZE = 31, + + /// + /// 可调整大小的窗口周边的大小边框的粗细(以像素为单位)。 SM_CXSIZEFRAME是水平边框的宽度,SM_CYSIZEFRAME是垂直边框的高度。 + /// 此值与 SM_CYFRAME 相同。 + /// + SM_CYSIZEFRAME = SM_CYFRAME, + + /// + /// 小描述文字的高度(以像素为单位)。 + /// + SM_CYSMCAPTION = 51, + + /// + /// 图标的系统小高度(以像素为单位)。 小图标通常显示在窗口标题和小图标视图中。 + /// + SM_CYSMICON = 50, + + /// + /// 小描述文字按钮的高度(以像素为单位)。 + /// + SM_CYSMSIZE = 53, + + /// + /// 虚拟屏幕的高度(以像素为单位)。 虚拟屏幕是所有显示监视器的边框。 SM_YVIRTUALSCREEN指标是虚拟屏幕顶部的坐标。 + /// + SM_CYVIRTUALSCREEN = 79, + + /// + /// 垂直滚动条上箭头位图的高度(以像素为单位)。 + /// + SM_CYVSCROLL = 20, + + /// + /// 垂直滚动条中拇指框的高度(以像素为单位)。 + /// + SM_CYVTHUMB = 9, + + /// + /// 如果 User32.dll 支持 DBCS,则为非零值;否则为 0。 + /// + SM_DBCSENABLED = 42, + + /// + /// 如果安装了 User.exe 的调试版本,则为非零;否则为 0。 + /// + SM_DEBUG = 22, + + /// + /// 如果当前操作系统是 Windows 7 或 Windows Server 2008 R2 并且平板电脑输入服务已启动,则为非零;否则为 0。 返回值是一个位掩码,用于指定设备支持的数字化器输入的类型。 + /// Windows Server 2008、Windows Vista 和 Windows XP/2000: 不支持此值。 + /// + SM_DIGITIZER = 94, + + /// + /// 如果启用了输入法管理器/输入法编辑器功能,则为非零值;否则为 0。 + /// SM_IMMENABLED指示系统是否已准备好在 Unicode 应用程序上使用基于 Unicode 的输入法。 若要确保依赖于语言的 IME 正常工作,检查 SM_DBCSENABLED 和系统 ANSI 代码页。 否则,ANSI 到 Unicode 的转换可能无法正确执行,或者某些组件(如字体或注册表设置)可能不存在。 + /// + SM_IMMENABLED = 82, + + /// + /// 如果系统中存在数字化器,则为非零;否则为 0。 + /// SM_MAXIMUMTOUCHES返回系统中每个数字化器支持的最大触点数的聚合最大值。 如果系统只有单点触控数字化器,则返回值为 1。 如果系统具有多点触控数字化器,则返回值是硬件可以提供的同时触点数。 + /// Windows Server 2008、Windows Vista 和 Windows XP/2000: 不支持此值。 + /// + SM_MAXIMUMTOUCHES = 95, + + /// + /// 如果当前操作系统为 Windows XP Media Center Edition,则为非零值;否则为 0。 + /// + SM_MEDIACENTER = 87, + + /// + /// 如果下拉菜单与相应的菜单栏项右对齐,则为非零;如果菜单左对齐,则为 0。 + /// + SM_MENUDROPALIGNMENT = 40, + + /// + /// 如果为希伯来语和阿拉伯语启用系统,则为非零值;否则为 0。 + /// + SM_MIDEASTENABLED = 74, + + /// + /// 如果安装了鼠标,则为非零;否则为 0。 此值很少为零,因为支持虚拟鼠标,并且某些系统检测到端口的存在,而不是鼠标的存在。 + /// + SM_MOUSEPRESENT = 19, + + /// + /// 如果安装了水平滚轮的鼠标,则为非零值;否则为 0。 + /// + SM_MOUSEHORIZONTALWHEELPRESENT = 91, + + /// + /// 如果安装了带垂直滚轮的鼠标,则为非零值;否则为 0。 + /// + SM_MOUSEWHEELPRESENT = 75, + + /// + /// 如果存在网络,则设置最小有效位;否则,会将其清除。 其他位保留供将来使用。 + /// + SM_NETWORK = 63, + + /// + /// 如果安装了 Microsoft Windows for Pen 计算扩展,则为非零;否则为零。 + /// + SM_PENWINDOWS = 41, + + /// + /// 此系统指标在终端服务环境中用于确定当前终端服务器会话是否受到远程控制。 如果当前会话是远程控制的,则其值为非零值;否则为 0。 + /// 可以使用终端服务管理工具(如终端服务管理器 (tsadmin.msc) 和 shadow.exe)来控制远程会话。 远程控制会话时,另一个用户可以查看该会话的内容,并可能与之交互。 + /// + SM_REMOTECONTROL = 0x2001, + + /// + /// 此系统指标在终端服务环境中使用。 如果调用进程与终端服务客户端会话相关联,则返回值为非零值。 如果调用进程与终端服务控制台会话相关联,则返回值为 0。 Windows Server 2003 和 Windows XP: 控制台会话不一定是物理主机。 + /// + SM_REMOTESESSION = 0x1000, + + /// + /// 如果所有显示监视器具有相同的颜色格式,则为非零值,否则为 0。 两个显示器可以具有相同的位深度,但颜色格式不同。 例如,红色、绿色和蓝色像素可以使用不同的位数进行编码,或者这些位可以位于像素颜色值的不同位置。 + /// + SM_SAMEDISPLAYFORMAT = 81, + + /// + /// 应忽略此系统指标;它始终返回 0。 + /// + SM_SECURE = 44, + + /// + /// 如果系统为 Windows Server 2003 R2,则为内部版本号;否则为 0。 + /// + SM_SERVERR2 = 89, + + /// + /// 如果用户要求应用程序在仅以有声形式呈现信息的情况下直观呈现信息,则为非零值;否则为 0。 + /// + SM_SHOWSOUNDS = 70, + + /// + /// 如果当前会话正在关闭,则为非零值;否则为 0。 + /// Windows 2000: 不支持此值。 + /// + SM_SHUTTINGDOWN = 0x2000, + + /// + /// 如果计算机具有低端 (慢速) 处理器,则为非零值;否则为 0。 + /// + SM_SLOWMACHINE = 73, + + /// + /// 如果当前操作系统为 Windows 7 简易版 Edition、Windows Vista 入门版 或 Windows XP Starter Edition,则为非零值;否则为 0。 + /// + SM_STARTER = 88, + + /// + /// 如果交换鼠标左右键的含义,则为非零值;否则为 0。 + /// + SM_SWAPBUTTON = 23, + + /// + /// 反映停靠模式的状态,0 表示未停靠模式,否则为非零。 当此系统指标发生更改时,系统会通过 LPARAM 中带有“SystemDockMode” 的WM_SETTINGCHANGE 发送广播消息。 + /// + SM_SYSTEMDOCKED = 0x2004, + + /// + /// 如果当前操作系统为 Windows XP Tablet PC 版本,或者当前操作系统为 Windows Vista 或 Windows 7 且平板电脑输入服务已启动,则为非零值;否则为 0。 SM_DIGITIZER设置指示运行 Windows 7 或 Windows Server 2008 R2 的设备支持的数字化器输入类型。 有关详细信息,请参阅“备注”。 + /// + SM_TABLETPC = 86, + + /// + /// 虚拟屏幕左侧的坐标。 虚拟屏幕是所有显示监视器的边框。 SM_CXVIRTUALSCREEN指标是虚拟屏幕的宽度。 + /// + SM_XVIRTUALSCREEN = 76, + + /// + /// 虚拟屏幕顶部的坐标。 虚拟屏幕是所有显示监视器的边框。 SM_CYVIRTUALSCREEN指标是虚拟屏幕的高度。 + /// + SM_YVIRTUALSCREEN = 77, + } +} diff --git a/WindowsTools/WindowsAPI/PInvoke/User32/TRACKMOUSEEVENT.cs b/WindowsTools/WindowsAPI/PInvoke/User32/TRACKMOUSEEVENT.cs new file mode 100644 index 00000000..c0b80e86 --- /dev/null +++ b/WindowsTools/WindowsAPI/PInvoke/User32/TRACKMOUSEEVENT.cs @@ -0,0 +1,32 @@ +using System; +using System.Runtime.InteropServices; + +namespace WindowsTools.WindowsAPI.PInvoke.User32 +{ + /// + /// 由 TrackMouseEvent 函数用来跟踪在指定的时间范围内,鼠标指针何时离开窗口或鼠标悬停在窗口上。 + /// + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public class TRACKMOUSEEVENT + { + /// + /// TRACKMOUSEEVENT 结构的大小(以字节为单位)。 + /// + public int cbSize; + + /// + /// 请求的服务。 + /// + public TRACKMOUSEEVENT_FLAGS dwFlags; + + /// + /// 要跟踪的窗口的句柄。 + /// + public IntPtr hwndTrack; + + /// + /// 如果在 dwFlags(以毫秒为单位)指定了TME_HOVER,则悬停超时。 可以 HOVER_DEFAULT,这意味着使用系统默认悬停超时。 + /// + public uint dwHoverTime; + } +} diff --git a/WindowsTools/WindowsAPI/PInvoke/User32/TRACKMOUSEEVENT_FLAGS.cs b/WindowsTools/WindowsAPI/PInvoke/User32/TRACKMOUSEEVENT_FLAGS.cs new file mode 100644 index 00000000..2dc6d3f4 --- /dev/null +++ b/WindowsTools/WindowsAPI/PInvoke/User32/TRACKMOUSEEVENT_FLAGS.cs @@ -0,0 +1,38 @@ +using System; + +namespace WindowsTools.WindowsAPI.PInvoke.User32 +{ + /// + /// 请求的服务。 + /// + [Flags] + public enum TRACKMOUSEEVENT_FLAGS : uint + { + /// + /// 调用方希望取消先前的跟踪请求。 调用方还应指定要取消的跟踪类型。 例如,若要取消悬停跟踪,调用方必须传递 TME_CANCEL 和 TME_HOVER 标志。 + /// + TME_CANCEL = 0x80000000, + + /// + /// 调用方需要悬停通知。 通知作为 WM_MOUSEHOVER 消息传递。 + /// 如果调用方请求悬停跟踪,而悬停跟踪已处于活动状态,则将重置悬停计时器。 + /// 如果鼠标指针不在指定的窗口或区域上,则忽略此标志。 + /// + TME_HOVER = 0x00000001, + + /// + /// 调用方想要离开通知。 通知作为 WM_MOUSELEAVE 消息传递。 如果鼠标未在指定的窗口或区域上,则会立即生成离开通知,并且不会执行进一步的跟踪。 + /// + TME_LEAVE = 0x00000002, + + /// + /// 调用方希望悬停并保留非工作区的通知。 通知以 WM_NCMOUSEHOVER 和 WM_NCMOUSELEAVE 消息的形式传递。 + /// + TME_NONCLIENT = 0x00000010, + + /// + /// 函数填充 结构,而不是将其视为跟踪请求。 结构已填充,如果结构已传递到 TrackMouseEvent,它将生成当前跟踪。 唯一的异常是,如果在原始 TrackMouseEvent 请求期间指定了HOVER_DEFAULT,则返回的悬停超时始终是实际超时,而不是HOVER_DEFAULT。 + /// + TME_QUERY = 0x40000000, + } +} diff --git a/WindowsTools/WindowsAPI/PInvoke/User32/User32Library.cs b/WindowsTools/WindowsAPI/PInvoke/User32/User32Library.cs index 753b9b46..a372bab1 100644 --- a/WindowsTools/WindowsAPI/PInvoke/User32/User32Library.cs +++ b/WindowsTools/WindowsAPI/PInvoke/User32/User32Library.cs @@ -1,4 +1,5 @@ using System; +using System.Drawing; using System.Runtime.InteropServices; using System.Windows.Forms; @@ -14,6 +15,19 @@ public static class User32Library { private const string User32 = "user32.dll"; + [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "AdjustWindowRectExForDpi", PreserveSig = true, SetLastError = false)] + public static extern bool AdjustWindowRectExForDpi(ref RECT lpRect, WindowStyle dwStyle, bool bMenu, uint dwExStyle, uint dpi); + + /// + /// BeginPaint 函数准备用于绘制的指定窗口,并使用有关绘制的信息填充 PAINTSTRUCT 结构。 + /// + /// 要重新绘制的窗口的句柄。 + /// 指向将接收绘制信息的 PAINTSTRUCT 结构的指针。 + /// 如果函数成功,则返回值是指定窗口的显示设备上下文的句柄。如果函数失败,则返回值为 NULL,指示没有可用的显示设备上下文。 + [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "BeginPaint", PreserveSig = true, SetLastError = false)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern IntPtr BeginPaint(IntPtr hwnd, out PAINTSTRUCT lpPaint); + /// /// 将挂钩信息传递给当前挂钩链中的下一个挂钩过程。 挂钩过程可以在处理挂钩信息之前或之后调用此函数。 /// @@ -22,7 +36,7 @@ public static class User32Library /// 传递给当前挂钩过程的 wParam 值。 此参数的含义取决于与当前挂钩链关联的挂钩类型。 /// 传递给当前挂钩过程的 lParam 值。 此参数的含义取决于与当前挂钩链关联的挂钩类型。 /// 此值由链中的下一个挂钩过程返回。 当前挂钩过程还必须返回此值。 返回值的含义取决于挂钩类型。 有关详细信息,请参阅各个挂钩过程的说明。 - [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "CallNextHookEx", SetLastError = false, PreserveSig = true)] + [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "CallNextHookEx", PreserveSig = true, SetLastError = false)] public static extern IntPtr CallNextHookEx(IntPtr idHook, int nCode, UIntPtr wParam, IntPtr lParam); /// @@ -33,28 +47,166 @@ public static class User32Library /// 要执行的操作,可以执行以下值 /// 指向 CHANGEFILTERSTRUCT 结构的可选指针。 /// 如果函数成功,则返回 TRUE;否则,它将返回 FALSE。 - [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "ChangeWindowMessageFilterEx", SetLastError = false, PreserveSig = true)] + [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "ChangeWindowMessageFilterEx", PreserveSig = true, SetLastError = false)] [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool ChangeWindowMessageFilterEx(IntPtr hWnd, WindowMessage message, ChangeFilterAction action, in CHANGEFILTERSTRUCT pChangeFilterStruct); + public static extern bool ChangeWindowMessageFilterEx(IntPtr hWnd, WindowMessage message, ChangeFilterAction action, CHANGEFILTERSTRUCT pChangeFilterStruct); + + /// + /// 创建具有扩展窗口样式的重叠、弹出窗口或子窗口;否则,此函数与 InitializeWindow 函数相同。 + /// 有关创建窗口以及 CreateWindowEx 的其他参数的完整说明的详细信息,请参阅 InitializeWindow。 + /// + /// 正在创建的窗口的扩展窗口样式。 + /// + /// 由上一次对 RegisterClass 或 RegisterClassEx 函数的调用创建的空终止字符串或类原子。 原子必须位于 的低序单词中;高序单词必须为零。 如果 是字符串,则指定窗口类名称。 类名可以是注册到 RegisterClass 或 RegisterClassEx 的任何名称,前提是注册该类的模块也是创建窗口的模块。 类名也可以是任何预定义的系统类名称。 + /// + /// + /// 窗口名称。 如果窗口样式指定标题栏,则 lpWindowName 指向的窗口标题将显示在标题栏中。 使用 InitializeWindow 创建控件(如按钮、复选框和静态控件)时,请使用 lpWindowName 指定控件的文本。 使用 SS_ICON 样式创建静态控件时,请使用 lpWindowName 指定图标名称或标识符。 若要指定标识符,请使用语法“#num”。 + /// + /// 正在创建的窗口的样式。 + /// + /// 窗口的初始水平位置。 对于重叠或弹出窗口, x 参数是窗口左上角的初始 x 坐标,以屏幕坐标表示。 + /// 对于子窗口, x 是窗口左上角相对于父窗口工作区左上角的 x 坐标。 如果 x 设置为 CW_USEDEFAULT,系统将选择窗口左上角的默认位置,并忽略 y 参数。 + /// CW_USEDEFAULT 仅适用于重叠窗口;如果为弹出窗口或子窗口指定, 则 x 和 y 参数设置为零。 + /// + /// + /// 窗口的初始垂直位置。 对于重叠或弹出窗口, y 参数是窗口左上角的初始 y 坐标,以屏幕坐标表示。 + /// 对于子窗口, y 是子窗口左上角相对于父窗口工作区左上角的初始 y 坐标。 对于列表框 y ,是列表框工作区左上角相对于父窗口工作区左上角的初始 y 坐标。 + /// + /// + /// 窗口的宽度(以设备单位为单位)。 对于重叠的窗口, nWidth 是窗口的宽度、屏幕坐标或 CW_USEDEFAULT。 + /// 如果 nWidth 是CW_USEDEFAULT,则系统会为窗口选择默认宽度和高度;默认宽度从初始 x 坐标扩展到屏幕的右边缘;默认高度从初始 y 坐标扩展到图标区域的顶部。 CW_USEDEFAULT 仅适用于重叠窗口;如果为弹出窗口或子窗口指定 了CW_USEDEFAULT , 则 nWidth 和 nHeight 参数设置为零。 + /// + /// + /// 窗口的高度(以设备单位为单位)。 对于重叠窗口, nHeight 是窗口的高度(以屏幕坐标为单位)。 + /// 如果 nWidth 参数设置为 CW_USEDEFAULT,则系统将忽略 nHeight。 + /// + /// + /// 正在创建的窗口的父窗口或所有者窗口的句柄。 若要创建子窗口或拥有的窗口,请提供有效的窗口句柄。 对于弹出窗口,此参数是可选的。 + /// + /// + /// 菜单的句柄,或指定子窗口标识符,具体取决于窗口样式。 对于重叠或弹出窗口, hMenu 标识要与窗口一起使用的菜单;如果使用类菜单,则为 NULL 。 + /// 对于子窗口, hMenu 指定子窗口标识符,即对话框控件用来通知其父级事件的整数值。 + /// 应用程序确定子窗口标识符;对于具有相同父窗口的所有子窗口,它必须是唯一的。 + /// + /// 要与窗口关联的模块实例的句柄。 + /// + /// 指向通过 CREATESTRUCT 结构传递给窗口的值的指针, (lpCreateParams 成员) WM_CREATE 消息的 lpParam 参数所指向的值。 此消息在返回之前由此函数发送到创建的窗口。 + /// + /// 如果函数成功,则返回值是新窗口的句柄。如果函数失败,则返回值为 NULL。 + [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "CreateWindowExW", PreserveSig = true, SetLastError = false)] + public static extern IntPtr CreateWindowEx(WindowExStyle dwExStyle, [MarshalAs(UnmanagedType.LPWStr)] string lpClassName, [MarshalAs(UnmanagedType.LPWStr)] string lpWindowName, WindowStyle dwStyle, int x, int y, int nWidth, int nHeight, IntPtr hWndParent, IntPtr hMenu, IntPtr hInstance, IntPtr lpParam); /// - /// 检索一个窗口的句柄,该窗口的类名和窗口名称与指定的字符串匹配。 该函数搜索子窗口,从指定子窗口后面的子窗口开始。 此函数不执行区分大小写的搜索。 + /// 调用默认窗口过程,为应用程序未处理的任何窗口消息提供默认处理。 此函数可确保处理每个消息。 DefWindowProc 使用窗口过程接收的相同参数调用。 /// - /// 要搜索其子窗口的父窗口的句柄。如果 hwndParent 为 NULL,则该函数使用桌面窗口作为父窗口。 函数在桌面的子窗口之间搜索。 如果 hwndParent 为HWND_MESSAGE,则函数将搜索所有 仅消息窗口。 - /// 子窗口的句柄。 搜索从 Z 顺序中的下一个子窗口开始。 子窗口必须是 hwndParent 的直接子窗口,而不仅仅是子窗口。 如果 hwndChildAfter 为 NULL,则搜索从 hwndParent 的第一个子窗口开始。请注意,如果 hwndParent 和 hwndChildAfter 均为 NULL,则该函数将搜索所有顶级窗口和仅消息窗口。 - /// 类名或上一次对 RegisterClass 或 RegisterClassEx 函数的调用创建的类名或类原子。 原子必须置于 lpszClass 的低序单词中;高阶单词必须为零。如果 lpszClass 是字符串,则指定窗口类名。 类名可以是注册到 RegisterClass 或 RegisterClassEx 的任何名称,也可以是预定义的控件类名称,也可以是 MAKEINTATOM(0x8000)。 在此后一种情况下,0x8000是菜单类的原子。 - /// 窗口名称 (窗口的标题) 。 如果此参数为 NULL,则所有窗口名称都匹配。 - /// 如果函数成功,则返回值是具有指定类和窗口名称的窗口的句柄。如果函数失败,则返回值为 NULL。 要获得更多的错误信息,请调用 GetLastError。 - [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "FindWindowExW", SetLastError = false, PreserveSig = true)] - public static extern IntPtr FindWindowEx(IntPtr hWndParent, IntPtr hWndChildAfter, [MarshalAs(UnmanagedType.LPWStr)] string lpszClass, [MarshalAs(UnmanagedType.LPWStr)] string lpszWindow); + /// 接收消息的窗口过程的句柄。 + /// 消息。 + /// 其他消息信息。 此参数的内容取决于 Msg 参数的值。 + /// 其他消息信息。 此参数的内容取决于 Msg 参数的值。 + /// 返回值是消息处理的结果,取决于消息。 + [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "DefWindowProcW", PreserveSig = true, SetLastError = false)] + public static extern IntPtr DefWindowProc(IntPtr hWnd, WindowMessage uMsg, UIntPtr wParam, IntPtr lParam); /// - /// 锁定工作站的显示器。 锁定工作站可防止未经授权的使用。 + /// EndPaint 函数在指定窗口中标记绘制的结束。 每次调用 BeginPaint 函数都需要此函数,但仅在绘制完成后。 /// - /// 如果该函数成功,则返回值为非零值。 由于函数以异步方式执行,因此非零返回值指示操作已启动。 它并不指示工作站是否已成功锁定。 - [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "LockWorkStation", SetLastError = false, PreserveSig = true)] + /// 已重新绘制的窗口的句柄。 + /// 指向 PAINTSTRUCT 结构的指针,该结构包含 BeginPaint 检索的绘画信息。 + /// 返回值始终为非零值。 + [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "EndPaint", PreserveSig = true, SetLastError = false)] + public static extern bool EndPaint(IntPtr hWnd, [In] ref PAINTSTRUCT lpPaint); + + /// + /// FillRect 函数使用指定的画笔填充矩形。 此函数包括左边框和上边框,但不包括矩形的右边框和下边框。 + /// + /// 设备上下文的句柄。 + /// 指向 RECT 结构的指针,该结构包含要填充的矩形的逻辑坐标。 + /// 用于填充矩形的画笔的句柄。 + /// 如果该函数成功,则返回值为非零值。如果函数失败,则返回值为零。 + [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "FillRect", PreserveSig = true, SetLastError = false)] + public static extern int FillRect(IntPtr hdc, RECT lprc, IntPtr hbr); + + /// + /// 检索窗口工作区的坐标。 客户端坐标指定工作区的左上角和右下角。 由于客户端坐标相对于窗口工作区的左上角,因此左上角的坐标 (0,0) 。 + /// + /// 要检索其客户端坐标的窗口的句柄。 + /// 指向接收客户端坐标的 RECT 结构的指针。 左侧成员和顶部成员为零。 右侧和底部成员包含窗口的宽度和高度。 + /// 如果该函数成功,则返回值为非零值。如果函数失败,则返回值为零。 + [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "GetClientRect", PreserveSig = true, SetLastError = false)] + public static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect); + + /// + /// 检索指定虚拟键的状态。 状态指定键是向上、向下还是切换, (打开、关闭—每次按下键时交替) 。 + /// + /// 虚拟密钥。 如果所需的虚拟键是字母或数字 (A 到 Z、a 到 z 或 0 到 9) ,则必须将 nVirtKey 设置为该字符的 ASCII 值。 对于其他密钥,它必须是虚拟密钥代码。 + /// 如果使用非英语键盘布局,则使用值在 ASCII A 到 Z 和 0 到 9 范围内的虚拟键来指定大多数字符键。 例如,对于德语键盘布局,ASCII O (0x4F) 值虚拟键是指“o”键,而VK_OEM_1表示“o with umlaut”键。 + /// + /// + /// 返回值指定指定虚拟密钥的状态,如下所示: + /// 如果高阶位为 1,则键关闭;否则,它已启动。 + /// 如果低序位为 1,则切换键。 如果某个键(如 CAPS LOCK 键)处于打开状态,则会将其切换。 如果低序位为 0,则键处于关闭状态并取消键。 切换键的指示灯(,如果键盘上的任何) 在切换键时将亮起,在取消切换键时处于关闭状态。 + /// + [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "GetKeyState", PreserveSig = true, SetLastError = false)] + public static extern short GetKeyState(Keys nVirtKey); + + /// + /// 检索指定的系统指标或系统配置设置,同时考虑提供的 DPI。 + /// + /// 要检索的系统指标或配置设置。 + /// 用于缩放指标的 DPI。 + /// 如果该函数成功,则返回值为非零值。如果函数失败,则返回值为零。 + [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "GetSystemMetricsForDpi", PreserveSig = true, SetLastError = false)] + public static extern int GetSystemMetricsForDpi(SM nIndex, int dpi); + + /// + /// 检索指定窗口的边框的尺寸。 尺寸以相对于屏幕左上角的屏幕坐标提供。 + /// + /// 窗口的句柄。 + /// 指向 RECT 结构的指针,该结构接收窗口左上角和右下角的屏幕坐标。 + /// 如果该函数成功,则返回值为非零值。如果函数失败,则返回值为零。 + [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "GetWindowRect", PreserveSig = true, SetLastError = false)] + public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect); + + /// + /// 检索有关指定窗口的信息。 该函数还会检索 32 位 (DWORD) 值,该值位于指定偏移量处,并进入额外的窗口内存。 + /// + /// 窗口的句柄,间接地是窗口所属的类。 + /// 要检索的值的从零开始的偏移量。 有效值在 0 到额外窗口内存的字节数中,减去 4 个;例如,如果指定了 12 个或更多字节的额外内存,则值 8 将是第三个 32 位整数的索引。 + /// + /// 如果函数成功,则返回值是请求的值。如果函数失败,则返回值为零。 + [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "GetWindowLongW", PreserveSig = true, SetLastError = false)] + public static extern int GetWindowLong(IntPtr hWnd, WindowLongIndexFlags nIndex); + + /// + /// 检索有关指定窗口的信息。 该函数还会检索 64 位 (DWORD) 值,该值位于指定偏移量处,并进入额外的窗口内存。 + /// + /// 窗口的句柄,间接地是窗口所属的类。 + /// 要检索的值的从零开始的偏移量。 有效值的范围为零到额外窗口内存的字节数,减去 LONG_PTR的大小。 + /// + /// 如果函数成功,则返回值是请求的值。如果函数失败,则返回值为零。 + [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "GetWindowLongPtrW", PreserveSig = true, SetLastError = false)] + public static extern int GetWindowLongPtr(IntPtr hWnd, WindowLongIndexFlags nIndex); + + /// + /// 确定指定窗口的可见性状态。 + /// + /// 要测试的窗口的句柄。 + /// + /// 如果指定的窗口、其父窗口、其父窗口等具有 WS_VISIBLE 样式,则返回值为非零。 否则返回值为零。 + /// 由于返回值指定窗口是否具有 WS_VISIBLE 样式,因此即使窗口被其他窗口完全遮挡,也可能为非零。 + /// + [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "IsWindowVisible", PreserveSig = true, SetLastError = false)] [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool LockWorkStation(); + public static extern bool IsWindowVisible(IntPtr hWnd); + + /// + /// 确定窗口是否最大化。 + /// + /// 要测试的窗口的句柄。 + /// 如果窗口已缩放,则返回值为非零值。如果未缩放窗口,则返回值为零。 + [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "IsZoomed", PreserveSig = true, SetLastError = false)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool IsZoomed(IntPtr hWnd); /// /// 合成键击。 系统可以使用这种合成的击键来生成 WM_KEYUP 或 WM_KEYDOWN 消息。 键盘驱动程序的中断处理程序调用 keybd_event 函数。 @@ -63,9 +215,65 @@ public static class User32Library /// 密钥的硬件扫描代码。 /// 控制函数操作的各个方面。 此参数可使用以下一个或多个值。 /// 与键笔划关联的附加值。 - [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "keybd_event", SetLastError = false, PreserveSig = true)] + [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "keybd_event", PreserveSig = true, SetLastError = false)] public static extern void keybd_event(Keys bVk, byte bScan, KEYEVENTFLAGS dwFlags, UIntPtr dwExtraInfo); + /// + /// 锁定工作站的显示器。 锁定工作站可防止未经授权的使用。 + /// + /// 如果该函数成功,则返回值为非零值。 由于函数以异步方式执行,因此非零返回值指示操作已启动。 它并不指示工作站是否已成功锁定。 + [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "LockWorkStation", PreserveSig = true, SetLastError = false)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool LockWorkStation(); + + /// + /// MapWindowPoints 函数将 (映射) 一组点从相对于一个窗口的坐标空间转换为相对于另一个窗口的坐标空间。 + /// + /// 从中转换点的窗口的句柄。 如果此参数为 NULL 或HWND_DESKTOP,则假定这些点位于屏幕坐标中。 + /// 指向要向其转换点的窗口的句柄。 如果此参数为 NULL 或HWND_DESKTOP,则点将转换为屏幕坐标。 + /// 指向 POINT 结构的数组的指针,该数组包含要转换的点集。 这些点以设备单位为单位。 此参数还可以指向 RECT 结构,在这种情况下, cPoints 参数应设置为 2。 + /// lpPoints 参数指向的数组中的 POINT 结构数。 + /// 如果函数成功,则返回值的低序字是添加到每个源点的水平坐标以计算每个目标点的水平坐标的像素数。 (除此之外,如果正对 hWndFrom 和 hWndTo 之一进行镜像,则每个生成的水平坐标乘以 -1.) 高序字是添加到每个源点垂直坐标的像素数,以便计算每个目标点的垂直坐标。 + /// 如果函数失败,则返回值为零。 在调用此方法之前调用 SetLastError ,以将错误返回值与合法的“0”返回值区分开来。 + [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "MapWindowPoints", PreserveSig = true, SetLastError = false)] + public static extern int MapWindowPoints(IntPtr hWndFrom, IntPtr hWndTo, ref Point lpPoints, uint cPoints); + + /// + /// 更改指定窗口的位置和尺寸。 对于顶级窗口,位置和尺寸是相对于屏幕左上角的。 对于子窗口,它们相对于父窗口工作区的左上角。 + /// + /// 窗口的句柄。 + /// 窗口左侧的新位置。 + /// 窗口顶部的新位置。 + /// 窗口的新宽度。 + /// 窗口的新高度。 + /// 指示是否重新绘制窗口。 如果此参数为 TRUE,则窗口将收到消息。 如果参数为 FALSE,则不会进行任何类型的重新绘制。 这适用于工作区、非工作区 (包括标题栏和滚动条) ,以及由于移动子窗口而发现父窗口的任何部分。 + /// 如果该函数成功,则返回值为非零值。如果函数失败,则返回值为零。 + [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "MoveWindow", PreserveSig = true, SetLastError = false)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool MoveWindow(IntPtr hWnd, int x, int y, int width, int height, bool bRepaint); + + /// + /// Places (在与创建指定窗口的线程关联的消息队列中发布) 消息,并在不等待线程处理消息的情况下返回 。 + /// 若要在与线程关联的消息队列中发布消息,请使用 PostThreadMessage 函数。 + /// + /// 窗口的句柄,窗口过程是接收消息。从 Windows Vista 开始,消息发布受 UIPI 的约束。 进程的线程只能将消息发布到完整性级别较低或相等的进程中线程的消息队列。 + /// 要发布的消息。 + /// 其他的消息特定信息。 + /// 其他的消息特定信息。 + /// 如果该函数成功,则返回值为非零值。如果函数失败,则返回值为零。 + [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "PostMessage", PreserveSig = true, SetLastError = false)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool PostMessage(IntPtr hWnd, WindowMessage Msg, UIntPtr wparam, IntPtr lparam); + + /// + /// PtInRect 函数确定指定的点是否位于指定的矩形内。 如果点位于左侧或顶部,或者位于所有四个边内,则点位于矩形内。 右侧或底部的点被视为矩形外部的点。 + /// + /// 指向包含指定矩形的 RECT 结构的指针。 + /// 包含指定点的 POINT 结构。 + /// 如果指定的点位于矩形内,则返回值为非零值。如果指定的点不在矩形内,则返回值为零。 + [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "PtInRect", PreserveSig = true, SetLastError = false)] + public static extern bool PtInRect(ref RECT lprc, Point pt); + /// /// 创建从指定文件中提取的图标的句柄数组。 /// @@ -80,22 +288,54 @@ public static class User32Library /// /// 如果 phicon 参数为 NULL 并且此函数成功,则返回值是文件中的图标数。 如果函数失败,则返回值为 0。如果 phicon 参数不为 NULL 且函数成功,则返回值是提取的图标数。 否则,如果未找到该文件,则返回值0xFFFFFFFF。 /// - [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "PrivateExtractIconsW", SetLastError = false, PreserveSig = true)] + [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "PrivateExtractIconsW", PreserveSig = true, SetLastError = false)] public static extern int PrivateExtractIcons([MarshalAs(UnmanagedType.LPWStr)] string lpszFile, int nIconIndex, int cxIcon, int cyIcon, [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] IntPtr[] phicon, [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] int[] piconid, int nIcons, int flags); + /// + /// 注册一个窗口类,以便在调用 CreateWindow 或 CreateWindowEx 函数时使用。 + /// + /// 指向 WNDCLASSEX 结构的指针。 在将结构传递给函数之前,必须用相应的类属性填充结构。 + /// + /// 如果函数成功,则返回值为唯一标识要注册的类的类原子。 此原子只能由 CreateWindow、CreateWindowEx、GetClassInfo、GetClassInfoEx使用, FindWindow、FindWindowEx和 UnregisterClass 函数和 IActiveIMMap::FilterClientWindows 方法。 + /// 如果函数失败,则返回值为零。 + /// + [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "RegisterClassExW", PreserveSig = true, SetLastError = false)] + public static extern int RegisterClassEx(ref WNDCLASSEX lpwcx); + + /// + /// 从当前线程中的窗口释放鼠标捕获,并还原正常鼠标输入处理。 捕获鼠标的窗口接收所有鼠标输入,而不考虑光标的位置,但当光标热点位于另一个线程的窗口中时单击鼠标按钮除外。 + /// + /// 如果该函数成功,则返回值为非零值。如果函数失败,则返回值为零。 + [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "ReleaseCapture", PreserveSig = true, SetLastError = false)] + public static extern bool ReleaseCapture(); + /// /// 注册应用程序以接收特定电源设置事件的电源设置通知。 /// - /// 指示电源设置通知的发送位置的句柄。 对于交互式应用程序, Flags 参数应为零, hRecipient 参数应为窗口句柄。 对于服务,Flags 参数应为 1,hRecipient 参数应为从 RegisterServiceCtrlHandlerEx 返回的SERVICE_STATUS_HANDLE。 + /// 指示电源设置通知的发送位置的句柄。 对于交互式应用程序, dwFlags 参数应为零, hRecipient 参数应为窗口句柄。 对于服务,dwFlags 参数应为 1,hRecipient 参数应为从 RegisterServiceCtrlHandlerEx 返回的SERVICE_STATUS_HANDLE。 /// 要为其发送通知的电源设置的 GUID 。 /// /// DEVICE_NOTIFY_WINDOW_HANDLE:使用 wParam 参数为 PBT_POWERSETTINGCHANGE 的WM_POWERBROADCAST消息发送通知。 /// DEVICE_NOTIFY_SERVICE_HANDLE:通知发送到 HandlerEx 回调函数,其中 dwControl 参数 为 SERVICE_CONTROL_POWEREVENT , dwEventType为 PBT_POWERSETTINGCHANGE。 /// /// 返回用于取消注册电源通知的通知句柄。 如果函数失败,则返回值为 NULL。 - [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "RegisterPowerSettingNotification", SetLastError = false, PreserveSig = true)] + [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "RegisterPowerSettingNotification", PreserveSig = true, SetLastError = false)] public static extern IntPtr RegisterPowerSettingNotification(IntPtr hRecipient, in Guid PowerSettingGuid, uint Flags); + /// + /// 设置分层窗口的不透明度和透明度颜色键。 + /// + /// + /// 分层窗口的句柄。 通过使用 CreateWindowEx 函数创建窗口时指定WS_EX_LAYERED,或者在创建窗口后通过 SetWindowLong 设置WS_EX_LAYERED来创建分层窗口。 + /// Windows 8:顶级窗口和子窗口支持WS_EX_LAYERED样式。 以前的 Windows 版本仅对顶级窗口支持 WS_EX_LAYERED 。 + /// + /// COLORREF 结构,指定组合分层窗口时要使用的透明度颜色键。 窗口以这种颜色绘制的所有像素都是透明的。 若要生成 COLORREF,请使用 RGB 宏。 + /// 用于描述分层窗口的不透明度的 Alpha 值。 类似于 BLENDFUNCTION 结构的 SourceConstantAlpha 成员。 当 bAlpha 为 0 时,窗口是完全透明的。 当 bAlpha 为 255 时,窗口是不透明的。 + /// 要执行的操作。 + /// 如果该函数成功,则返回值为非零值。如果函数失败,则返回值为零。 要获得更多的错误信息,请调用 GetLastError。 + [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "SetLayeredWindowAttributes", PreserveSig = true, SetLastError = false)] + public static extern bool SetLayeredWindowAttributes(IntPtr hWnd, int crKey, int alpha, LWA dwFlags); + /// /// 将指定的消息发送到窗口或窗口。SendMessage 函数调用指定窗口的窗口过程,在窗口过程处理消息之前不会返回。 /// @@ -108,9 +348,38 @@ public static class User32Library /// 其他的消息特定信息。 /// 其他的消息特定信息。 /// 返回值指定消息处理的结果;这取决于发送的消息。 - [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "SendMessageW", SetLastError = false, PreserveSig = true)] + [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "SendMessageW", PreserveSig = true, SetLastError = false)] public static extern IntPtr SendMessage(IntPtr hWnd, WindowMessage wMsg, UIntPtr wParam, IntPtr lParam); + /// + /// 将鼠标捕获设置为属于当前线程的指定窗口。 当鼠标悬停在捕获窗口上时,或者在鼠标悬停在捕获窗口上且按钮仍然向下的情况下按下鼠标按钮时,SetCapture 将捕获鼠标输入。 一次只会有一个窗口捕获鼠标。 + /// 如果鼠标光标位于另一个线程创建的窗口上,则仅当鼠标按钮按下时,系统才会将鼠标输入定向到指定的窗口。 + /// + /// 当前线程中窗口的句柄,用于捕获鼠标。 + /// 返回值是以前捕获了鼠标的窗口的句柄。 如果没有此类窗口,则返回值为 NULL。 + [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "SetCapture", PreserveSig = true, SetLastError = false)] + public static extern IntPtr SetCapture(IntPtr hWnd); + + /// + /// 更改指定窗口的属性。 该函数还将指定偏移量处的32位(long类型)值设置到额外的窗口内存中。 + /// + /// 窗口的句柄,间接地是窗口所属的类 + /// 要设置的值的从零开始的偏移量。 有效值的范围为零到额外窗口内存的字节数,减去整数的大小。 + /// 新事件处理函数(回调函数) + /// 如果函数成功,则返回值是指定 32 位整数的上一个值。如果函数失败,则返回值为零。 + [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "SetWindowLongW", PreserveSig = true, SetLastError = false)] + public static extern IntPtr SetWindowLong(IntPtr hWnd, WindowLongIndexFlags nIndex, IntPtr dwNewLong); + + /// + /// 更改指定窗口的属性。 该函数还将指定偏移量处的64位(long类型)值设置到额外的窗口内存中。 + /// + /// 窗口的句柄,间接地是窗口所属的类 + /// 要设置的值的从零开始的偏移量。 有效值的范围为零到额外窗口内存的字节数,减去整数的大小。 + /// 新事件处理函数(回调函数) + /// 如果函数成功,则返回值是指定偏移量的上一个值。如果函数失败,则返回值为零。 + [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "SetWindowLongPtrW", PreserveSig = true, SetLastError = false)] + public static extern IntPtr SetWindowLongPtr(IntPtr hWnd, WindowLongIndexFlags nIndex, IntPtr dwNewLong); + /// /// 更改子窗口、弹出窗口或顶级窗口的大小、位置和 Z 顺序。 这些窗口根据屏幕上的外观进行排序。 最上面的窗口接收最高排名,是 Z 顺序中的第一个窗口。 /// @@ -122,7 +391,7 @@ public static class User32Library /// 窗口的新高度(以像素为单位)。 /// 窗口大小调整和定位标志。 /// 如果该函数成功,则返回值为非零值。如果函数失败,则返回值为零。 - [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "SetWindowPos", SetLastError = false, PreserveSig = true)] + [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "SetWindowPos", PreserveSig = true, SetLastError = false)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, SetWindowPosFlags uFlags); @@ -134,24 +403,40 @@ public static class User32Library /// DLL 的句柄,其中包含 lpfn 参数指向的挂钩过程。 如果 dwThreadId 参数指定当前进程创建的线程,并且挂钩过程位于与当前进程关联的代码中,则必须将 hMod 参数设置为 NULL。 /// 要与挂钩过程关联的线程的标识符。 对于桌面应用,如果此参数为零,则挂钩过程与调用线程在同一桌面中运行的所有现有线程相关联。 对于 Windows 应用商店应用,请参阅“备注”部分。 /// 如果函数成功,则返回值是挂钩过程的句柄。如果函数失败,则返回值为 NULL。 - [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "SetWindowsHookExW", SetLastError = false, PreserveSig = true)] + [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "SetWindowsHookExW", PreserveSig = true, SetLastError = false)] public static extern IntPtr SetWindowsHookEx(HOOKTYPE idHook, HOOKPROC lpfn, IntPtr hMod, int dwThreadId); + /// + /// 当在指定时间内鼠标指针离开窗口或将鼠标悬停在窗口上时,发布消息。 + /// + /// 指向包含跟踪信息的 TRACKMOUSEEVENT 结构的指针。 + /// 如果函数成功,则返回值为非零 。如果函数失败,则返回值为零。 + [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "TrackMouseEvent", PreserveSig = true, SetLastError = false)] + public static extern bool TrackMouseEvent(TRACKMOUSEEVENT lpEventTrack); + /// /// 删除 SetWindowsHookEx 函数安装在挂钩链中的挂钩过程。 /// /// 要移除的挂钩的句柄。 此参数是由先前调用 SetWindowsHookEx 获取的挂钩句柄。 /// 如果该函数成功,则返回值为非零值。如果函数失败,则返回值为零。 - [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "UnhookWindowsHookEx", SetLastError = false, PreserveSig = true)] + [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "UnhookWindowsHookEx", PreserveSig = true, SetLastError = false)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool UnhookWindowsHookEx(IntPtr idHook); + /// + /// 检索包含指定点的窗口的句柄。 + /// + /// 要检查的点。 + /// 返回值是包含点的窗口的句柄。 如果给定点不存在窗口,则返回值为 NULL。 如果点位于静态文本控件上,则返回值是静态文本控件下窗口的句柄。 + [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "WindowFromPoint", PreserveSig = true, SetLastError = false)] + public static extern IntPtr WindowFromPoint(Point Point); + /// /// 取消注册电源设置通知。 /// /// 从 RegisterPowerSettingNotification 函数返回的句柄。 /// 如果该函数成功,则返回值为非零值。如果函数失败,则返回值为零。 - [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "UnregisterPowerSettingNotification", SetLastError = false, PreserveSig = true)] + [DllImport(User32, CharSet = CharSet.Unicode, EntryPoint = "UnregisterPowerSettingNotification", PreserveSig = true, SetLastError = false)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool UnregisterPowerSettingNotification(IntPtr handle); } diff --git a/WindowsTools/WindowsAPI/PInvoke/User32/WNDCLASSEX.cs b/WindowsTools/WindowsAPI/PInvoke/User32/WNDCLASSEX.cs new file mode 100644 index 00000000..93be79fa --- /dev/null +++ b/WindowsTools/WindowsAPI/PInvoke/User32/WNDCLASSEX.cs @@ -0,0 +1,74 @@ +using System; +using System.Runtime.InteropServices; + +namespace WindowsTools.WindowsAPI.PInvoke.User32 +{ + /// + /// 包含窗口类信息。 它与 RegisterClassEx 和 GetClassInfoEx 函数一起使用。 + /// + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct WNDCLASSEX + { + /// + /// 此结构的大小(以字节为单位)。 将此成员设置为 sizeof(WNDCLASSEX)。 在调用 GetClassInfoEx 函数之前,请务必设置此成员。 + /// + public int cbSize; + + /// + /// 类样式。 此成员可以是类样式的任意组合。 + /// + public WNDCLASS_STYLES style; + + /// + /// 指向窗口过程的指针。 必须使用 CallWindowProc 函数来调用窗口过程。 有关详细信息,请参阅 WindowProc。 + /// + public WNDPROC lpfnWndProc; + + /// + /// 要按照窗口类结构分配的额外字节数。 系统将字节初始化为零。 + /// + public int cbClsExtra; + + /// + /// 在窗口实例之后分配的额外字节数。 系统将字节初始化为零。 如果应用程序使用 WNDCLASSEX 注册通过使用资源文件中的 CLASS 指令创建的对话框,则必须将此成员设置为 DLGWINDOWEXTRA。 + /// + public int cbWndExtra; + + /// + /// 包含类的窗口过程的实例的句柄。 + /// + public IntPtr hInstance; + + /// + /// 类图标的句柄。 此成员必须是图标资源的句柄。 如果此成员 NULL,则系统提供默认图标。 + /// + public IntPtr hIcon; + + /// + /// 类游标的句柄。 此成员必须是游标资源的句柄。 如果此成员 NULL,则每当鼠标移动到应用程序的窗口中时,应用程序都必须显式设置光标形状。 + /// + public IntPtr hCursor; + + /// + /// 类背景画笔的句柄。 此成员可以是用于绘制背景的画笔的句柄,也可以是颜色值。 颜色值必须是以下标准系统颜色之一(值 1 必须添加到所选颜色中)。 + /// + public IntPtr hbrBackground; + + /// + /// 指向以 null 结尾的字符串的指针,该字符串指定类菜单的资源名称,因为名称显示在资源文件中。 如果使用整数来标识菜单,请使用 MAKEINTRESOURCE 宏。 如果此成员 NULL,则属于此类的窗口没有默认菜单。 + /// + public string lpszMenuName; + + /// + /// 指向以 null 结尾的字符串或原子的指针。 如果此参数是 atom,则它必须是上一次调用 RegisterClass 或 RegisterClassEx 函数创建的类 atom。 原子必须位于 lpszClassName的低序单词中;高序单词必须为零。 + /// 如果 lpszClassName 是字符串,则指定窗口类名。 类名称可以是注册到 RegisterClass 或 RegisterClassEx的任何名称,也可以是预定义的控件类名称。 + /// lpszClassName 的最大长度为 256。 如果 lpszClassName 大于最大长度,则 RegisterClassEx 函数将失败。 + /// + public string lpszClassName; + + /// + /// 与窗口类关联的小图标的句柄。 如果此成员 NULL,系统将搜索由 hIcon 成员指定的图标资源,以获取要用作小图标的相应大小的图标。 + /// + public IntPtr hIconSm; + } +} diff --git a/WindowsTools/WindowsAPI/PInvoke/User32/WNDCLASS_STYLES.cs b/WindowsTools/WindowsAPI/PInvoke/User32/WNDCLASS_STYLES.cs new file mode 100644 index 00000000..7446e82b --- /dev/null +++ b/WindowsTools/WindowsAPI/PInvoke/User32/WNDCLASS_STYLES.cs @@ -0,0 +1,74 @@ +using System; + +namespace WindowsTools.WindowsAPI.PInvoke.User32 +{ + /// + /// 下面是窗口类样式。 + /// + [Flags] + public enum WNDCLASS_STYLES : uint + { + /// + /// 如果移动或大小调整更改了工作区的高度,则重新绘制整个窗口。 + /// + CS_VREDRAW = 1, + + /// + /// 如果移动或大小调整更改了工作区的宽度,则重绘整个窗口。 + /// + CS_HREDRAW = 2, + + /// + /// 当用户在光标位于属于 类的窗口中时双击鼠标时,将双击消息发送到窗口过程。 + /// + CS_DBLCLKS = 8, + + /// + /// 为类中的每个窗口分配唯一的设备上下文。 + /// + CS_OWNDC = 32, + + /// + /// 分配一个设备上下文,以便类中的所有窗口共享。 由于窗口类特定于进程,因此应用程序的多个线程可以创建同一类的窗口。 线程还可以尝试同时使用设备上下文。 发生这种情况时,系统仅允许一个线程成功完成其绘制操作。 + /// + CS_CLASSDC = 64, + + /// + /// 将子窗口的剪裁矩形设置为父窗口的剪裁矩形,以便子窗口可以在父窗口上绘制。 具有 CS_PARENTDC 样式位的窗口从系统的设备上下文缓存接收常规设备上下文。 它不会为子级提供父级的设备上下文或设备上下文设置。 指定 CS_PARENTDC 可增强应用程序的性能。 + /// + CS_PARENTDC = 128, + + /// + /// 在窗口菜单上禁用 “关闭 ”。 + /// + CS_NOCLOSE = 512, + + /// + /// 保存此类窗口遮盖的屏幕图像部分作为位图。 删除窗口时,系统会使用保存的位图还原屏幕图像,包括被遮盖的其他窗口。 因此,如果位图使用的内存尚未丢弃,并且其他屏幕操作未使存储的图像失效,则系统不会将 WM_PAINT 消息发送到被遮盖的窗口。 + /// 此样式适用于小型窗口 (例如菜单或对话框) ,这些菜单或对话框在发生其他屏幕活动之前会短暂显示,然后删除。 此样式会增加显示窗口所需的时间,因为系统必须先分配内存来存储位图。 + /// + CS_SAVEBITS = 2048, + + /// + /// 将窗口的工作区与 x 方向) 的字节边界 (对齐。 此样式会影响窗口的宽度及其在显示器上的水平位置。 + /// + CS_BYTEALIGNCLIENT = 4096, + + /// + /// 使窗口在字节边界 (沿 x 方向) 对齐。 此样式会影响窗口的宽度及其在显示器上的水平位置。 + /// + CS_BYTEALIGNWINDOW = 8192, + + /// + /// 指示窗口类是应用程序全局类。 有关详细信息,请参阅 关于窗口类的“应用程序全局类”部分。 + /// + CS_GLOBALCLASS = 16384, + + CS_IME = 65536, + + /// + /// 在窗口上启用投影效果。 通过 SPI_SETDROPSHADOW打开和关闭效果。 通常,对于小型、生存期较短的窗口(如菜单)启用此功能,以强调其与其他窗口的 Z 顺序关系。 从具有此样式的类创建的 Windows 必须是顶级窗口;它们可能不是子窗口。 + /// + CS_DROPSHADOW = 131072, + } +} diff --git a/WindowsTools/WindowsAPI/PInvoke/User32/WNDPROC.cs b/WindowsTools/WindowsAPI/PInvoke/User32/WNDPROC.cs new file mode 100644 index 00000000..3a4c0833 --- /dev/null +++ b/WindowsTools/WindowsAPI/PInvoke/User32/WNDPROC.cs @@ -0,0 +1,16 @@ +using System; +using System.Runtime.InteropServices; + +namespace WindowsTools.WindowsAPI.PInvoke.User32 +{ + /// + /// 一个回调函数,可在应用程序中定义,用于处理发送到窗口的消息。 WNDPROC 类型定义指向此回调函数的指针。 WndProc 名称是应用程序中定义的函数名称的占位符。 + /// + /// 窗口的句柄。 此参数通常名为 hWnd。 + /// 消息。 此参数通常命名为 uMsg。 + /// 其他消息信息。 此参数通常名为 wParam。wParam 参数的内容取决于 uMsg 参数的值。 + /// 其他消息信息。 此参数通常名为 lParam。lParam 参数的内容取决于 uMsg 参数的值。 + /// 返回值是消息处理的结果,取决于发送的消息。 + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + public delegate IntPtr WNDPROC(IntPtr hWnd, WindowMessage uMsg, UIntPtr wParam, IntPtr lParam); +} diff --git a/WindowsTools/WindowsAPI/PInvoke/User32/WindowExStyle.cs b/WindowsTools/WindowsAPI/PInvoke/User32/WindowExStyle.cs new file mode 100644 index 00000000..a78d6e54 --- /dev/null +++ b/WindowsTools/WindowsAPI/PInvoke/User32/WindowExStyle.cs @@ -0,0 +1,154 @@ +using System; + +namespace WindowsTools.WindowsAPI.PInvoke.User32 +{ + /// + /// 扩展窗口样式。 + /// + [Flags] + public enum WindowExStyle : uint + { + /// + /// 窗口接受拖放文件。 + /// + WS_EX_ACCEPTFILES = 0x00000010, + + /// + /// 当窗口可见时,将顶级窗口强制到任务栏上。 + /// + WS_EX_APPWINDOW = 0x00040000, + + /// + /// 窗口有一个边框,带有沉没边缘。 + /// + WS_EX_CLIENTEDGE = 0x00000200, + + /// + /// 使用双缓冲按从下到上绘制顺序绘制窗口的所有后代。 从下到上绘制顺序允许后代窗口具有半透明 (alpha) 和透明度 (颜色键) 效果,但前提是后代窗口还设置了 WS_EX_TRANSPARENT 位。 双缓冲允许不闪烁地绘制窗口及其后代。 如果窗口的 类样式 为 CS_OWNDC 或 CS_CLASSDC,则不能使用此样式。 + /// Windows 2000:不支持此样式。 + /// + WS_EX_COMPOSITED = 0x02000000, + + /// + /// 窗口的标题栏包含问号。 当用户单击问号时,光标将更改为带有指针的问号。 如果用户单击子窗口,子窗口将收到 WindowMessage.WM_HELP 消息。 子窗口应将消息传递给父窗口过程,该过程应使用 HELP_WM_HELP 命令调用 WinHelp 函数。 帮助应用程序会显示一个弹出窗口,该窗口通常包含子窗口的帮助。 + /// WS_EX_CONTEXTHELP 不能与 WindowStyles.WS_MAXIMIZEBOX 或 WindowStyles.WS_MINIMIZEBOX 样式一起使用 + /// + WS_EX_CONTEXTHELP = 0x00000400, + + /// + /// 窗口本身包含应参与对话框导航的子窗口。 如果指定了此样式,则执行导航操作(例如处理 TAB 键、箭头键或键盘助记键)时,对话框管理器将递归到此窗口的子级。 + /// + WS_EX_CONTROLPARENT = 0x00010000, + + /// + /// 窗口具有双边框;可以选择使用标题栏创建窗口,方法是在 dwStyle 参数中指定 WindowStyles.WS_CAPTION 样式。 + /// + WS_EX_DLGMODALFRAME = 0x00000001, + + /// + /// 窗口是分层 窗口。 如果窗口的 类样式 为 CS_OWNDC 或 CS_CLASSDC,则不能使用此样式。 + /// Windows 8:顶级窗口和子窗口支持 WS_EX_LAYERED 样式。 以前的Windows版本仅支持顶级窗口 WS_EX_LAYERED。 + /// + WS_EX_LAYERED = 0x00080000, + + /// + /// 如果 shell 语言是希伯来语、阿拉伯语或支持阅读顺序对齐的另一种语言,则窗口的水平原点位于右边缘。 将水平值增大到左侧。 + /// + WS_EX_LAYOUTRTL = 0x00400000, + + /// + /// 该窗口具有泛型左对齐属性。 这是默认值。 + /// + WS_EX_LEFT = 0x00000000, + + /// + /// 如果 shell 语言是希伯来语、阿拉伯语或支持阅读顺序对齐的另一种语言,则垂直滚动条 (如果存在) 位于工作区左侧。 对于其他语言,将忽略该样式。 + /// + WS_EX_LEFTSCROLLBAR = 0x00004000, + + /// + /// 窗口文本使用从左到右的阅读顺序属性显示。 这是默认值。 + /// + WS_EX_LTRREADING = WS_EX_LEFT, + + /// + /// 窗口是 MDI 子窗口。 + /// + WS_EX_MDICHILD = 0x00000040, + + /// + /// 当用户单击该样式时,使用此样式创建的顶级窗口不会成为前台窗口。 当用户最小化或关闭前台窗口时,系统不会将此窗口引入前台。 + /// 不应通过编程访问或通过辅助技术(如讲述人)通过键盘导航激活窗口。 + /// 若要激活窗口,请使用 SetActiveWindow 或 User32Library.SetForegroundWindow 函数。 + /// 默认情况下,该窗口不会显示在任务栏上。 若要强制窗口显示在任务栏上,请使用 WS_EX_APPWINDOW 样式。 + /// + WS_EX_NOACTIVATE = 0x08000000, + + /// + /// 该窗口不将其窗口布局传递给其子窗口。 + /// + WS_EX_NOINHERITLAYOUT = 0x00100000, + + /// + /// 使用此样式创建的子窗口不会在创建或销毁时将 WindowMessage.WM_PARENTNOTIFY 消息发送到其父窗口。 + /// + WS_EX_NOPARENTNOTIFY = 0x00000004, + + /// + /// 窗口不会呈现到重定向图面。 这是对于没有可见内容的窗口,或者使用表面以外的机制来提供视觉对象。 + /// + WS_EX_NOREDIRECTIONBITMAP = 0x00200000, + + /// + /// 窗口是重叠的窗口。 + /// + WS_EX_OVERLAPPEDWINDOW = WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE, + + /// + /// 窗口是调色板窗口,它是一个无模式对话框,用于显示命令数组。 + /// + WS_EX_PALETTEWINDOW = WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST, + + /// + /// 该窗口具有泛型“右对齐”属性。 这依赖于窗口类。 仅当 shell 语言为希伯来语、阿拉伯语或支持阅读顺序对齐的另一种语言时,此样式才有效;否则,将忽略样式。 + /// 对静态控件或编辑控件使用 WS_EX_RIGHT 样式的效果与分别使用 SS_RIGHT 或 ES_RIGHT 样式的效果相同。 将此样式与按钮控件结合使用的效果与使用 BS_RIGHT 和 BS_RIGHTBUTTON 样式的效果相同。 + /// + WS_EX_RIGHT = 0x00001000, + + /// + /// 如果存在) 位于工作区右侧,则垂直滚动条 (。 这是默认值。 + /// + WS_EX_RIGHTSCROLLBAR = WS_EX_LEFT, + + /// + /// 如果 shell 语言是希伯来语、阿拉伯语或支持阅读顺序对齐的另一种语言,则使用从右到左的阅读顺序属性显示窗口文本。 对于其他语言,将忽略该样式。 + /// + WS_EX_RTLREADING = 0x00002000, + + /// + /// 该窗口具有一个三维边框样式,用于不接受用户输入的项目。 + /// + WS_EX_STATICEDGE = 0x00020000, + + /// + /// 该窗口旨在用作浮动工具栏。 工具窗口具有短于普通标题栏的标题栏和使用较小的字体绘制的窗口标题。 工具窗口不会显示在任务栏中,也不会显示在用户按下 Alt+TAB 时出现的对话框中。 如果工具窗口有系统菜单,则其图标不会显示在标题栏上。 但是,可以通过右键单击或键入 Alt+SPACE 来显示系统菜单。 + /// + WS_EX_TOOLWINDOW = 0x00000080, + + /// + /// 该窗口应放置在所有非最顶层窗口上方,并且应保持其上方,即使窗口已停用也是如此。 若要添加或删除此样式,请使用 SetWindowPos 函数。 + /// + WS_EX_TOPMOST = 0x00000008, + + /// + /// 在绘制同一线程) 创建的窗口下方的同级 (之前,不应绘制窗口。 窗口显示为透明,因为已绘制基础同级窗口的位。 + /// 若要在不使用这些限制的情况下实现透明度,请使用 SetWindowRgn 函数。 + /// + WS_EX_TRANSPARENT = 0x00000020, + + /// + /// 窗口具有带有凸起边缘的边框。 + /// + WS_EX_WINDOWEDGE = 0x00000100, + } +} diff --git a/WindowsTools/WindowsAPI/PInvoke/User32/WindowLongIndexFlags.cs b/WindowsTools/WindowsAPI/PInvoke/User32/WindowLongIndexFlags.cs new file mode 100644 index 00000000..295ee223 --- /dev/null +++ b/WindowsTools/WindowsAPI/PInvoke/User32/WindowLongIndexFlags.cs @@ -0,0 +1,66 @@ +using System; + +namespace WindowsTools.WindowsAPI.PInvoke.User32 +{ + /// + /// 对应的窗口属性 + /// + [Flags] + public enum WindowLongIndexFlags : int + { + /// + /// 检索扩展窗口样式。 + /// + GWL_EXSTYLE = -20, + + /// + /// 检索应用程序实例的句柄。 + /// + GWLP_HINSTANCE = -6, + + /// + /// 检索父窗口的句柄(如果有)。 + /// + GWLP_HWNDPARENT = -8, + + /// + /// 设置新的应用程序实例句柄。 + /// + GWL_ID = -12, + + /// + /// 检索窗口的标识符。 + /// + GWLP_ID = GWL_ID, + + /// + /// 检索窗口样式。 + /// + GWL_STYLE = -16, + + /// + /// 检索与窗口关联的用户数据。 此数据供创建窗口的应用程序使用。 其值最初为零。 + /// + GWL_USERDATA = -21, + + /// + /// 检索窗口过程的地址,或表示窗口过程的地址的句柄。 必须使用 CallWindowProc 函数调用窗口过程。 + /// + GWL_WNDPROC = -4, + + /// + /// 检索应用程序专用的额外信息,例如句柄或指针。 + /// + DWLP_USER = 0x8, + + /// + /// 检索对话框中处理的消息的返回值。 + /// + DWLP_MSGRESULT = 0x0, + + /// + /// 检索对话框过程的地址,或表示对话框过程的地址的句柄。 必须使用 CallWindowProc 函数调用对话框过程。 + /// + DWLP_DLGPROC = 0x4, + } +} diff --git a/WindowsTools/WindowsAPI/PInvoke/User32/WindowStyle.cs b/WindowsTools/WindowsAPI/PInvoke/User32/WindowStyle.cs new file mode 100644 index 00000000..6dd77c3a --- /dev/null +++ b/WindowsTools/WindowsAPI/PInvoke/User32/WindowStyle.cs @@ -0,0 +1,143 @@ +using System; + +namespace WindowsTools.WindowsAPI.PInvoke.User32 +{ + /// + /// 主窗口风格。只要需要窗口样式,就可以指定以下样式。 + /// 创建控件后,不能修改这些样式,除非有说明。 + /// + [Flags] + public enum WindowStyle : uint + { + /// + /// 窗口具有细线边框 + /// + WS_BORDER = 0x800000, + + /// + /// 窗口具有标题栏 (包括 WS_BORDER 样式) 。 + /// + WS_CAPTION = 0xc00000, + + /// + /// 窗口是子窗口。 具有此样式的窗口不能有菜单栏。 + /// 此样式不能与 WS_POPUP 样式一 起使用。 + /// + WS_CHILD = 0x40000000, + + /// + /// 与 WS_CHILD 样式相同。 + /// + WS_CHILDWINDOW = WS_CHILD, + + /// + /// 在父窗口中绘制时,排除子窗口占用的区域。 创建父窗口时会使用此样式。 + /// + WS_CLIPCHILDREN = 0x2000000, + + /// + /// 将子窗口相对于彼此剪裁;也就是说,当特定子窗口收到 WindowMessage.WM_PAINT 消息时, WS_CLIPSIBLINGS 样式会将所有其他重叠子窗口剪辑到要更新的子窗口区域。 如果未指定 WS_CLIPSIBLINGS 并且子窗口重叠,则当在子窗口的工作区内绘制时,可以在相邻子窗口的工作区内绘制。 + /// + WS_CLIPSIBLINGS = 0x4000000, + + /// + /// 窗口最初处于禁用状态。 禁用的窗口无法从用户接收输入。 若要在创建窗口后进行更改,请使用 EnableWindow 函数。 + /// + WS_DISABLED = 0x8000000, + + /// + /// 窗口具有通常与对话框一起使用的样式边框。 具有此样式的窗口不能有标题栏。 + /// + WS_DLGFRAME = 0x400000, + + /// + /// 窗口是一组控件的第一个控件。 该组包含此第一个控件及其之后定义的所有控件,最多包含 WS_GROUP 样式的下一个控件。 每个组中的第一个控件通常具有 WS_TABSTOP 样式,以便用户可以从组移动到组。 用户随后可以使用方向键将组中的一个控件中的键盘焦点更改为组中的下一个控件。可以打开和关闭此样式以更改对话框导航。 若要在创建窗口后更改此样式,请使用 SetWindowLong 函数。 + /// + WS_GROUP = 0x20000, + + /// + /// 窗口具有水平滚动条。 + /// + WS_HSCROLL = 0x100000, + + /// + /// 窗口最初最小化。 与 WS_MINIMIZE 样式相同。 + /// + WS_ICONIC = 0x20000000, + + /// + /// 窗口最初最大化。 + /// + WS_MAXIMIZE = 0x1000000, + + /// + /// 窗口具有最大化按钮。 不能与 WindowStyleEx.WS_EX_CONTEXTHELP 样式组合。 还必须指定 WS_SYSMENU 样式。 + /// + WS_MAXIMIZEBOX = 0x10000, + + /// + /// 窗口最初最小化。 与 WS_ICONIC 样式相同。 + /// + WS_MINIMIZE = WS_ICONIC, + + /// + /// 窗口具有最小化按钮。 不能与 WindowStyleEx.WS_EX_CONTEXTHELP 样式组合。 还必须指定 WS_SYSMENU 样式。 + /// + WS_MINIMIZEBOX = WS_GROUP, + + /// + /// 窗口是重叠的窗口。 + /// + WS_OVERLAPPED = 0x0, + + /// + /// 窗口是重叠的窗口。 + /// + WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_SIZEFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX, + + /// + /// 窗口是弹出窗口。 此样式不能与 WS_CHILD 样式一 起使用。 + /// + WS_POPUP = 0x80000000u, + + /// + /// 窗口是弹出窗口。 必须组合 WS_CAPTION 和 WS_POPUPWINDOW 样式以使窗口菜单可见。 + /// + WS_POPUPWINDOW = WS_POPUP | WS_BORDER | WS_SYSMENU, + + /// + /// 窗口具有大小调整边框。 + /// + WS_SIZEFRAME = 0x40000, + + /// + /// 窗口的标题栏上有一个窗口菜单。 还必须指定 WS_CAPTION 样式。 + /// + WS_SYSMENU = 0x80000, + + /// + /// 窗口是一个控件,当用户按下 TAB 键时,可以接收键盘焦点。 按 Tab 键会将键盘焦点更改为 具有 WS_TABSTOP 样式的下一个控件。可以打开和关闭此样式以更改对话框导航。 若要在创建窗口后更改此样式,请使用 SetWindowLong 函数。 若要使用户创建的窗口和无模式对话框使用制表位,请更改消息循环以调用 IsDialogMessage 函数。 + /// + WS_TABSTOP = WS_MAXIMIZEBOX, + + /// + /// 窗口具有大小调整边框。 + /// + WS_THICKFRAME = WS_SIZEFRAME, + + /// + /// 窗口是重叠的窗口。 重叠窗口具有标题栏和边框。 + /// + WS_TILED = WS_OVERLAPPED, + + /// + /// 窗口最初可见。 可以使用 User32Library.ShowWindow 或 SetWindowPos 函数打开和关闭此样式。 + /// + WS_VISIBLE = 0x10000000, + + /// + /// 该窗口具有垂直滚动条。 + /// + WS_VSCROLL = 0x200000, + } +} diff --git a/WindowsTools/WindowsAPI/PInvoke/Uxtheme/BPPF.cs b/WindowsTools/WindowsAPI/PInvoke/Uxtheme/BPPF.cs new file mode 100644 index 00000000..d4b35768 --- /dev/null +++ b/WindowsTools/WindowsAPI/PInvoke/Uxtheme/BPPF.cs @@ -0,0 +1,23 @@ +using System; + +namespace WindowsTools.WindowsAPI.PInvoke.Uxtheme +{ + [Flags] + public enum BPPF : uint + { + /// + /// 在 BeginBufferedPaint 期间,将缓冲区初始化为 ARGB = {0, 0, 0, 0}。 这会擦除缓冲区的先前内容。 + /// + BPPF_ERASE = 0x0001, + + /// + /// 不要将目标 DC 的剪辑区域应用于双缓冲区。 如果未设置此标志,并且目标 DC 是窗口 DC,则由于重叠窗口而进行剪裁将应用于双缓冲区。 + /// + BPPF_NOCLIP = 0x0002, + + /// + /// 正在使用非客户端 DC。 + /// + BPPF_NONCLIENT = 0x0004 + } +} diff --git a/WindowsTools/WindowsAPI/PInvoke/Uxtheme/BP_BUFFERFORMAT.cs b/WindowsTools/WindowsAPI/PInvoke/Uxtheme/BP_BUFFERFORMAT.cs new file mode 100644 index 00000000..18349cfe --- /dev/null +++ b/WindowsTools/WindowsAPI/PInvoke/Uxtheme/BP_BUFFERFORMAT.cs @@ -0,0 +1,28 @@ +namespace WindowsTools.WindowsAPI.PInvoke.Uxtheme +{ + /// + /// 指定缓冲区的格式。 由 BeginBufferedAnimation 和 BeginBufferedPaint 使用。 + /// + public enum BP_BUFFERFORMAT + { + /// + /// 兼容的位图。 每像素的位数基于与使用 BeginBufferedPaint 或 BeginBufferedAnimation 指定的 HDC 关联的设备的颜色格式,通常,这是显示设备。 + /// + BPBF_COMPATIBLEBITMAP, + + /// + /// 自下而上与设备无关的位图。 位图的原点为左下角。 使用每像素 32 位。 + /// + BPBF_DIB, + + /// + /// 自上而下与设备无关的位图。 位图的原点是左上角。 使用每像素 32 位。 + /// + BPBF_TOPDOWNDIB, + + /// + /// 自上而下、单色、与设备无关的位图。 使用每像素 1 位。 + /// + BPBF_TOPDOWNMONODIB + } +} diff --git a/WindowsTools/WindowsAPI/PInvoke/Uxtheme/UxthemeLibrary.cs b/WindowsTools/WindowsAPI/PInvoke/Uxtheme/UxthemeLibrary.cs index 904b9931..8c3169da 100644 --- a/WindowsTools/WindowsAPI/PInvoke/Uxtheme/UxthemeLibrary.cs +++ b/WindowsTools/WindowsAPI/PInvoke/Uxtheme/UxthemeLibrary.cs @@ -1,4 +1,7 @@ -using System.Runtime.InteropServices; +using System; +using System.Runtime.InteropServices; +using WindowsTools.WindowsAPI.PInvoke.Gdi32; +using WindowsTools.WindowsAPI.PInvoke.User32; // 抑制 CA1401 警告 #pragma warning disable CA1401 @@ -12,17 +15,70 @@ public static class UxthemeLibrary { private const string Uxtheme = "uxtheme.dll"; + /// + /// 开始缓冲绘制操作。 + /// + /// 将绘制缓冲区的目标 DC 的句柄。 + /// 指向 RECT 结构的指针,该结构指定要在其中绘制的目标 DC 的区域。 + /// 指定缓冲区格式的 BP_BUFFERFORMAT 枚举的成员。 + /// 指向定义绘制操作参数 的BP_PAINTPARAMS 结构的指针。 此值可以为 NULL。 + /// 当此函数返回时, 指向新设备上下文的句柄。 + /// + /// 缓冲的绘制上下文的句柄。 如果此函数失败,则返回值为 NULL, phdc 为 NULL。 + /// 调用 EndBufferedPaint 时释放返回的句柄。 + /// 在调用 BeginBufferedPaint 之前,应用程序应在调用线程上调用 BufferedPaintInit,并在线程终止之前调用 BufferedPaintUnInit。 调用 BufferedPaintInit 失败可能会导致性能下降,因为每个缓冲的绘制操作的内部数据都会被初始化和销毁。 + /// + [DllImport(Uxtheme, CharSet = CharSet.Unicode, EntryPoint = "BeginBufferedPaint", PreserveSig = true, SetLastError = false)] + public static extern unsafe IntPtr BeginBufferedPaint(IntPtr hdcTarget, ref RECT prcTarget, BP_BUFFERFORMAT dwFormat, ref BP_PAINTPARAMS pPaintParams, out IntPtr phdc); + + /// + /// 初始化当前线程的缓冲绘制。 + /// + /// 如果此函数成功,则返回 S_OK。 否则,将返回 HRESULT 错误代码。 + [DllImport(Uxtheme, CharSet = CharSet.Unicode, EntryPoint = "BufferedPaintInit", PreserveSig = true, SetLastError = false)] + public static extern IntPtr BufferedPaintInit(); + + /// + /// 将 alpha 设置为给定矩形中的指定值。 alpha 控制与缓冲区混合到目标设备上下文 (DC) 时应用的透明度。 + /// + /// 缓冲的绘制上下文的句柄,通过 BeginBufferedPaint 获取。 + /// 指向 RECT 结构的指针,该结构指定要在其中设置 alpha 的矩形。 将此参数设置为 NULL 可指定整个缓冲区。 + /// 要设置的 alpha 值。 alpha 值的范围可以是零 (完全透明) 到 255 (完全不透明) 。 + /// 如果此函数成功,则返回 S_OK。 否则,将返回 HRESULT 错误代码。 + [DllImport(Uxtheme, CharSet = CharSet.Unicode, EntryPoint = "BufferedPaintInit", PreserveSig = true, SetLastError = false)] + public static extern int BufferedPaintSetAlpha(IntPtr hBufferedPaint, ref RECT prc, byte alpha); + + /// + /// 完成缓冲绘制操作并释放关联的缓冲绘制句柄。 + /// + /// 缓冲的绘制上下文的句柄,通过 BeginBufferedPaint 获取。 + /// 若要将缓冲区复制到目标 DC,则为 TRUE。 + /// 如果此函数成功,则返回 S_OK。 否则,将返回 HRESULT 错误代码。 + [DllImport(Uxtheme, CharSet = CharSet.Unicode, EntryPoint = "EndBufferedPaint", PreserveSig = true, SetLastError = false)] + public static extern int EndBufferedPaint(IntPtr hBufferedPaint, bool fUpdateTarget); + /// /// 设置 win32 右键菜单的样式 /// /// 菜单样式 - [DllImport(Uxtheme, CharSet = CharSet.Unicode, EntryPoint = "#135", SetLastError = false, PreserveSig = true)] + [DllImport(Uxtheme, CharSet = CharSet.Unicode, EntryPoint = "#135", PreserveSig = true, SetLastError = false)] public static extern int SetPreferredAppMode(PreferredAppMode preferredAppMode); + /// + /// 设置属性以控制如何将视觉样式应用于指定窗口。 + /// + /// 要向其应用更改的窗口句柄。 + /// 指定要设置的属性的类型。 此参数的值确定应在 pvAttribute 参数中传递的数据类型。 + /// 一个指针,指定要设置的属性。 类型由 eAttribute 值的值确定。 + /// 指定 pvAttribute指向的数据的大小(以字节为单位)。 + /// 如果此函数成功,则返回 S_OK。 否则,它将返回 HRESULT 错误代码。 + [DllImport(Uxtheme, CharSet = CharSet.Unicode, EntryPoint = "SetWindowThemeAttribute", PreserveSig = true, SetLastError = false)] + public static extern int SetWindowThemeAttribute(IntPtr hWnd, WINDOWTHEMEATTRIBUTETYPE eAttribute, ref WTA_OPTIONS pvAttribute, uint cbAttribute); + /// /// 刷新右键菜单样式 /// - [DllImport(Uxtheme, CharSet = CharSet.Unicode, EntryPoint = "#136", SetLastError = false, PreserveSig = true)] + [DllImport(Uxtheme, CharSet = CharSet.Unicode, EntryPoint = "#136", PreserveSig = true, SetLastError = false)] public static extern int FlushMenuThemes(); } } diff --git a/WindowsTools/WindowsAPI/PInvoke/Uxtheme/WINDOWTHEMEATTRIBUTETYPE.cs b/WindowsTools/WindowsAPI/PInvoke/Uxtheme/WINDOWTHEMEATTRIBUTETYPE.cs new file mode 100644 index 00000000..d10e2fd8 --- /dev/null +++ b/WindowsTools/WindowsAPI/PInvoke/Uxtheme/WINDOWTHEMEATTRIBUTETYPE.cs @@ -0,0 +1,13 @@ +namespace WindowsTools.WindowsAPI.PInvoke.Uxtheme +{ + /// + /// 指定要在窗口上设置的视觉样式属性的类型。 + /// + public enum WINDOWTHEMEATTRIBUTETYPE : int + { + /// + /// 指定非客户端相关属性。 pvAttribute 必须是 WTA_OPTIONS类型的指针。 + /// + WTA_NONCLIENT = 1 + } +} diff --git a/WindowsTools/WindowsAPI/PInvoke/Uxtheme/WTA_OPTIONS.cs b/WindowsTools/WindowsAPI/PInvoke/Uxtheme/WTA_OPTIONS.cs new file mode 100644 index 00000000..894e5554 --- /dev/null +++ b/WindowsTools/WindowsAPI/PInvoke/Uxtheme/WTA_OPTIONS.cs @@ -0,0 +1,21 @@ +using System.Runtime.InteropServices; + +namespace WindowsTools.WindowsAPI.PInvoke.Uxtheme +{ + /// + /// 定义用于设置窗口视觉样式属性的选项。 + /// + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct WTA_OPTIONS + { + /// + /// 修改窗口视觉样式属性的标志的组合。 可以是 WTNCA 常量的组合。 + /// + public WTNCA dwFlags; + + /// + /// 一个位掩码,描述应如何应用 dwFlags 中指定的值。 如果 与 dwFlags 中的值对应的位为 0,则将删除该标志。 如果位为 1,则将添加 标志。 + /// + public uint dwMask; + } +} diff --git a/WindowsTools/WindowsAPI/PInvoke/Uxtheme/WTNCA.cs b/WindowsTools/WindowsAPI/PInvoke/Uxtheme/WTNCA.cs new file mode 100644 index 00000000..22deeb1b --- /dev/null +++ b/WindowsTools/WindowsAPI/PInvoke/Uxtheme/WTNCA.cs @@ -0,0 +1,36 @@ +using System; + +namespace WindowsTools.WindowsAPI.PInvoke.Uxtheme +{ + /// + /// 指定修改窗口视觉样式属性的标志。 使用以下值之一或按位组合。 + /// + [Flags] + public enum WTNCA + { + /// + /// 防止绘制窗口描述文字。 + /// + WTNCA_NODRAWCAPTION = 0x00000001, + + /// + /// 阻止绘制系统图标。 + /// + WTNCA_NODRAWICON = 0x00000002, + + /// + /// 防止显示系统图标菜单。 + /// + WTNCA_NOSYSMENU = 0x00000004, + + /// + /// 即使在从右到左 (RTL) 布局中,也会阻止问号的镜像。 + /// + WTNCA_NOMIRRORHELP = 0x00000008, + + /// + /// 包含所有有效位的掩码。 + /// + WTNCA_VALIDBITS = 0x0000000F + } +} diff --git a/WindowsTools/WindowsTools.csproj b/WindowsTools/WindowsTools.csproj index 70e5dffc..e881e4d4 100644 --- a/WindowsTools/WindowsTools.csproj +++ b/WindowsTools/WindowsTools.csproj @@ -28,8 +28,6 @@ true false true - false - @@ -242,6 +240,7 @@ + @@ -281,10 +280,10 @@ - + @@ -327,15 +326,11 @@ - - - - diff --git a/WindowsToolsPackage/Package.appxmanifest b/WindowsToolsPackage/Package.appxmanifest index bb3a072e..ab08db0a 100644 --- a/WindowsToolsPackage/Package.appxmanifest +++ b/WindowsToolsPackage/Package.appxmanifest @@ -13,7 +13,7 @@ + Version="3.2.1204.0" /> ms-resource:PackageDisplayName diff --git a/WindowsToolsShellExtension/Properties/AssemblyInfo.cs b/WindowsToolsShellExtension/Properties/AssemblyInfo.cs index 40ba9cff..3e08659a 100644 --- a/WindowsToolsShellExtension/Properties/AssemblyInfo.cs +++ b/WindowsToolsShellExtension/Properties/AssemblyInfo.cs @@ -7,11 +7,11 @@ [assembly: AssemblyCompany("高怡飞")] [assembly: AssemblyCopyright("Copyright ©2024 高怡飞, All Rights Reserved.")] [assembly: AssemblyDescription("Windows 工具箱 右键菜单扩展")] -[assembly: AssemblyFileVersion("3.2.1128.0")] -[assembly: AssemblyInformationalVersion("3.2.1128.0")] +[assembly: AssemblyFileVersion("3.2.1204.0")] +[assembly: AssemblyInformationalVersion("3.2.1204.0")] [assembly: AssemblyProduct("Windows 工具箱 右键菜单扩展")] [assembly: AssemblyTitle("Windows 工具箱 右键菜单扩展")] -[assembly: AssemblyVersion("3.2.1128.0")] +[assembly: AssemblyVersion("3.2.1204.0")] // 应用程序默认区域性的资源控制器设置 [assembly: NeutralResourcesLanguage("en-us")] diff --git a/WindowsToolsShellExtension/WindowsAPI/PInvoke/Shell32/Shell32Library.cs b/WindowsToolsShellExtension/WindowsAPI/PInvoke/Shell32/Shell32Library.cs index 2cf0e263..ae362f7a 100644 --- a/WindowsToolsShellExtension/WindowsAPI/PInvoke/Shell32/Shell32Library.cs +++ b/WindowsToolsShellExtension/WindowsAPI/PInvoke/Shell32/Shell32Library.cs @@ -8,7 +8,7 @@ namespace WindowsToolsShellExtension.WindowsAPI.PInvoke.Shell32 /// public static partial class Shell32Library { - public const string Shell32 = "shell32.dll"; + private const string Shell32 = "shell32.dll"; /// /// 对指定文件执行操作。 diff --git a/WindowsToolsShellExtension/WindowsAPI/PInvoke/Shlwapi/ShlwapiLibrary.cs b/WindowsToolsShellExtension/WindowsAPI/PInvoke/Shlwapi/ShlwapiLibrary.cs index e3cca280..8989051f 100644 --- a/WindowsToolsShellExtension/WindowsAPI/PInvoke/Shlwapi/ShlwapiLibrary.cs +++ b/WindowsToolsShellExtension/WindowsAPI/PInvoke/Shlwapi/ShlwapiLibrary.cs @@ -8,7 +8,7 @@ namespace WindowsToolsShellExtension.WindowsAPI.PInvoke.Shlwapi /// public static partial class ShlwapiLibrary { - public const string Shlwapi = "shlwapi.dll"; + private const string Shlwapi = "shlwapi.dll"; /// /// 尝试通过查询具有 GetWindow 方法的各种接口,从组件对象模型 (COM) 对象检索窗口句柄。