From 05c6710389c3d7eb0e6ceeea03d1885dc3583883 Mon Sep 17 00:00:00 2001 From: David Hall Date: Thu, 6 Jun 2024 11:19:40 -0600 Subject: [PATCH] Refactored for new C# language optimizations --- PInvoke/Shell32/ShObjIdl.ShellUtil.cs | 10 ++--- Windows.Extensions/GdiObjExtensions.cs | 23 +++++------ Windows.Extensions/GraphicsExtension.cs | 4 +- Windows.Shell.Common/BindContext.cs | 10 ++--- Windows.Shell.Common/IconLocation.cs | 2 +- Windows.Shell.Common/MRUManager.cs | 6 +-- Windows.Shell.Common/RecycleBin.cs | 2 +- .../Registration/AppRegistration.cs | 5 +-- .../Registration/CommandVerbDictionary.cs | 2 +- .../Registration/RegBasedDictionary.cs | 48 ++++++---------------- .../Registration/RegBasedKeyCollection.cs | 2 +- .../Registration/RegBasedSettings.cs | 18 +++----- .../ShellExtensions/ShellDropTarget.cs | 37 +++++++---------- .../ShellFileOperations/ShellFileOperations.cs | 32 +++++++-------- Windows.Shell.Common/ShellItemChangeWatcher.cs | 20 +++------ Windows.Shell.Common/ShellObjects/ComObjWrapper.cs | 10 ++--- .../ShellObjects/ShellContextMenu.cs | 2 +- Windows.Shell.Common/ShellObjects/ShellItem.cs | 20 ++++----- .../ShellObjects/ShellItemImages.cs | 9 ++-- Windows.Shell.Common/ShellObjects/ShellLibrary.cs | 2 +- .../ShellObjects/ShellSearchConditions.cs | 2 +- .../ShellProperties/PropertyBag.cs | 10 ++--- Windows.Shell.Common/StockIcon.cs | 28 +++++-------- Windows.Shell.Common/TaskBar/JumpList.cs | 21 ++++------ .../Vanara.Windows.Shell.Common.csproj | 4 ++ 25 files changed, 122 insertions(+), 207 deletions(-) diff --git a/PInvoke/Shell32/ShObjIdl.ShellUtil.cs b/PInvoke/Shell32/ShObjIdl.ShellUtil.cs index 57fb676c..51e14499 100644 --- a/PInvoke/Shell32/ShObjIdl.ShellUtil.cs +++ b/PInvoke/Shell32/ShObjIdl.ShellUtil.cs @@ -1,5 +1,4 @@ -#pragma warning disable IL2050 // Correctness of COM interop cannot be guaranteed after trimming. Interfaces and interface members might be removed. -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices.ComTypes; using System.Security; @@ -459,7 +458,7 @@ public static partial class Shell32 private class ManualParentAndItem : IParentAndItem, IDisposable { private readonly PIDL pChild; - private IShellFolder psf; + private readonly IShellFolder psf; public ManualParentAndItem(IShellItem psi) { @@ -469,10 +468,7 @@ public static partial class Shell32 pItem.Dispose(); } - void IDisposable.Dispose() - { - pChild.Dispose(); - } + void IDisposable.Dispose() => pChild.Dispose(); HRESULT IParentAndItem.GetParentAndItem(out PIDL ppidlParent, out IShellFolder ppsf, out PIDL ppidlChild) { diff --git a/Windows.Extensions/GdiObjExtensions.cs b/Windows.Extensions/GdiObjExtensions.cs index e6f6daa3..cd70b9f8 100644 --- a/Windows.Extensions/GdiObjExtensions.cs +++ b/Windows.Extensions/GdiObjExtensions.cs @@ -115,7 +115,7 @@ public static class GdiObjExtensions2 pen = new Pen(lpen.elpColor) { DashStyle = (DashStyle)lpen.Style }; if (pen.DashStyle == DashStyle.Custom && lpen.elpNumEntries > 0) { - uint[] styleArray = lpen.elpStyleEntry.ToArray((int)lpen.elpNumEntries) ?? new uint[0]; + uint[] styleArray = lpen.elpStyleEntry.ToArray((int)lpen.elpNumEntries) ?? []; pen.DashPattern = Array.ConvertAll(styleArray, i => (float)i); } break; @@ -213,18 +213,11 @@ public static class GdiObjExtensions2 /// A self-releasing pattern for IDeviceContext.GetHdc and ReleaseHdc. /// -public class SafeTempHDC : IDisposable, IGraphicsObjectHandle +/// Initializes a new instance of the class with an . +/// The instance. +public class SafeTempHDC(IDeviceContext? dc) : IDisposable, IGraphicsObjectHandle { - private readonly IDeviceContext? dc; - private readonly IntPtr hdc; - - /// Initializes a new instance of the class with an . - /// The instance. - public SafeTempHDC(IDeviceContext? dc) - { - this.dc = dc; - hdc = dc?.GetHdc() ?? default; - } + private readonly IntPtr hdc = dc?.GetHdc() ?? default; /// Gets a value indicating whether this instance has a NULL handle. /// if this has a NULL handle; otherwise, . @@ -239,5 +232,9 @@ public class SafeTempHDC : IDisposable, IGraphicsObjectHandle public IntPtr DangerousGetHandle() => hdc; /// Releases claimed HDC. - public void Dispose() => dc?.ReleaseHdc(); + public void Dispose() + { + dc?.ReleaseHdc(); + GC.SuppressFinalize(this); + } } \ No newline at end of file diff --git a/Windows.Extensions/GraphicsExtension.cs b/Windows.Extensions/GraphicsExtension.cs index 695db025..dfc0cc53 100644 --- a/Windows.Extensions/GraphicsExtension.cs +++ b/Windows.Extensions/GraphicsExtension.cs @@ -87,7 +87,7 @@ public static partial class GdiExtension throw new ArgumentNullException(nameof(destination)); if (source.IsEmpty) throw new ArgumentNullException(nameof(source)); - if (transparency < 0 || transparency > 1.0f) + if (transparency is < 0 or > 1.0f) throw new ArgumentNullException(nameof(transparency)); var imageRectangle = GetRectangleFromAlignment(alignment, destination, source.Size); @@ -333,7 +333,7 @@ public static partial class GdiExtension private class Enumertor : IEnumerator { - private SmartBitmapLock bmpLock; + private readonly SmartBitmapLock bmpLock; private int idx = -1; internal Enumertor(SmartBitmapLock bmp) => bmpLock = bmp; diff --git a/Windows.Shell.Common/BindContext.cs b/Windows.Shell.Common/BindContext.cs index 90a3b6b4..cfd7557e 100644 --- a/Windows.Shell.Common/BindContext.cs +++ b/Windows.Shell.Common/BindContext.cs @@ -161,7 +161,7 @@ public class BindContext : IDisposable, IBindCtxV, IBindCtx /// in a return value of E_NOTIMPL. /// /// - public IEnumerable EnumObjectParam() => ((IBindCtxV)this).EnumObjectParam(out var ppenum).Succeeded ? ppenum.Enum().ToArray() : new string[0]; + public IEnumerable EnumObjectParam() => ((IBindCtxV)this).EnumObjectParam(out var ppenum).Succeeded ? ppenum.Enum().ToArray() : []; /// /// Retrieves an interface pointer to the object associated with the specified key in the bind context's string-keyed table of pointers. @@ -892,13 +892,9 @@ public class BindContext : IDisposable, IBindCtxV, IBindCtx } [ComVisible(true)] - private class CDummyUnknown : IPersist + private class CDummyUnknown(Guid clsid) : IPersist { - private readonly Guid _clsid; - - public CDummyUnknown(in Guid clsid) => _clsid = clsid; - - public Guid GetClassID() => _clsid; + public Guid GetClassID() => clsid; } [ComVisible(true)] diff --git a/Windows.Shell.Common/IconLocation.cs b/Windows.Shell.Common/IconLocation.cs index 052ce3f8..8cc2da7e 100644 --- a/Windows.Shell.Common/IconLocation.cs +++ b/Windows.Shell.Common/IconLocation.cs @@ -71,7 +71,7 @@ internal class IconLocationTypeConverter : ExpandableObjectConverter public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? info, object? value, Type destType) { if (destType == typeof(InstanceDescriptor)) - return new InstanceDescriptor(typeof(IconLocation).GetConstructor(new Type[0]), null, false); + return new InstanceDescriptor(typeof(IconLocation).GetConstructor([]), null, false); if (destType == typeof(string) && value is IconLocation s) return s.ToString(); return base.ConvertTo(context, info, value, destType); diff --git a/Windows.Shell.Common/MRUManager.cs b/Windows.Shell.Common/MRUManager.cs index 06158922..b980bf21 100644 --- a/Windows.Shell.Common/MRUManager.cs +++ b/Windows.Shell.Common/MRUManager.cs @@ -450,11 +450,9 @@ public class MRUManager : Component private SafeRegKey GetKey() => new(Registry.CurrentUser, SubKeyName ?? throw new InvalidOperationException("Invalid assembly name."), RegistryKeyPermissionCheck.ReadWriteSubTree); - private class SafeRegKey : IDisposable + private class SafeRegKey(RegistryKey root, string subKeyName, RegistryKeyPermissionCheck opt) : IDisposable { - public readonly RegistryKey Key; - - public SafeRegKey(RegistryKey root, string subKeyName, RegistryKeyPermissionCheck opt) => Key = root.CreateSubKey(subKeyName, opt); + public readonly RegistryKey Key = root.CreateSubKey(subKeyName, opt); public static implicit operator RegistryKey(SafeRegKey k) => k.Key; diff --git a/Windows.Shell.Common/RecycleBin.cs b/Windows.Shell.Common/RecycleBin.cs index 5021a75f..9c55c955 100644 --- a/Windows.Shell.Common/RecycleBin.cs +++ b/Windows.Shell.Common/RecycleBin.cs @@ -62,7 +62,7 @@ public static class RecycleBin /// The of the deleted item in the Recycle Bin. This cannot be a reference to an undeleted shell item. /// /// to hide all user interface interactions; to allow them. - public static void Restore(ShellItem deletedItem, bool hideUI = false) => Restore(new[] { deletedItem }, hideUI); + public static void Restore(ShellItem deletedItem, bool hideUI = false) => Restore([deletedItem], hideUI); /// Restores the specified deleted items to their original location. /// diff --git a/Windows.Shell.Common/Registration/AppRegistration.cs b/Windows.Shell.Common/Registration/AppRegistration.cs index 68b8575f..e2597a51 100644 --- a/Windows.Shell.Common/Registration/AppRegistration.cs +++ b/Windows.Shell.Common/Registration/AppRegistration.cs @@ -347,10 +347,7 @@ public class AppRegistration : RegBasedSettings return new AppRegistration(ska, sk, readOnly); } - private class PathSubKey : RegBasedSettings + private class PathSubKey(RegistryKey key, bool readOnly) : RegBasedSettings(key, readOnly) { - public PathSubKey(RegistryKey key, bool readOnly) : base(key, readOnly) - { - } } } \ No newline at end of file diff --git a/Windows.Shell.Common/Registration/CommandVerbDictionary.cs b/Windows.Shell.Common/Registration/CommandVerbDictionary.cs index db47ae40..09338c7c 100644 --- a/Windows.Shell.Common/Registration/CommandVerbDictionary.cs +++ b/Windows.Shell.Common/Registration/CommandVerbDictionary.cs @@ -25,7 +25,7 @@ public class CommandVerbDictionary : RegBasedDictionary /// Get the filtered list of keys under the base. [Browsable(false)] - public override IEnumerable Keys => key?.GetSubKeyNames() ?? new string[0]; + public override IEnumerable Keys => key?.GetSubKeyNames() ?? []; /// Gets or sets the order of the command verbs. /// The ordered list of command verbs. diff --git a/Windows.Shell.Common/Registration/RegBasedDictionary.cs b/Windows.Shell.Common/Registration/RegBasedDictionary.cs index ffd383b1..800da851 100644 --- a/Windows.Shell.Common/Registration/RegBasedDictionary.cs +++ b/Windows.Shell.Common/Registration/RegBasedDictionary.cs @@ -10,25 +10,19 @@ namespace Vanara.Windows.Shell; /// A virtual dictionary that is based on values in the Windows Registry. /// Type used to capture multiple values within the registry. /// -public abstract class RegBasedDictionary : VirtualReadOnlyDictionary +/// Initializes a new instance of the class. +/// The base registry key. +/// if set to render this dictionary read-only. +public abstract class RegBasedDictionary(RegistryKey? baseKey, bool readOnly) : VirtualReadOnlyDictionary { /// Read-only flag. - protected readonly bool readOnly; + protected readonly bool readOnly = readOnly; /// The base registry key for this dictionary. - protected RegistryKey? key; - - /// Initializes a new instance of the class. - /// The base registry key. - /// if set to render this dictionary read-only. - protected RegBasedDictionary(RegistryKey? baseKey, bool readOnly) - { - key = baseKey; - this.readOnly = readOnly; - } + protected RegistryKey? key = baseKey; /// Get the filtered list of keys under the base. - public override IEnumerable Keys => key?.GetSubKeyNames().Where(SubKeyFilter) ?? new string[0]; + public override IEnumerable Keys => key?.GetSubKeyNames().Where(SubKeyFilter) ?? []; /// Determines if a specified key is in the filtered list of keys under the base. /// The name of the key to check. @@ -43,12 +37,8 @@ public abstract class RegBasedDictionary : VirtualReadOnlyDictionary true; } -internal class AppDictionary : RegBasedDictionary +internal class AppDictionary(bool readOnly) : RegBasedDictionary(Registry.ClassesRoot.OpenSubKey(AppRegistration.appsSubKey, !readOnly), readOnly) { - public AppDictionary(bool readOnly) : base(Registry.ClassesRoot.OpenSubKey(AppRegistration.appsSubKey, !readOnly), readOnly) - { - } - public override bool TryGetValue(string key, [MaybeNullWhen(false)] out AppRegistration value) { value = null; @@ -58,12 +48,8 @@ internal class AppDictionary : RegBasedDictionary } } -internal class FileTypeDictionary : RegBasedDictionary +internal class FileTypeDictionary(bool readOnly) : RegBasedDictionary(Registry.ClassesRoot, readOnly) { - public FileTypeDictionary(bool readOnly) : base(Registry.ClassesRoot, readOnly) - { - } - public override bool TryGetValue(string key, [MaybeNullWhen(false)] out FileTypeAssociation value) { value = null; @@ -75,21 +61,17 @@ internal class FileTypeDictionary : RegBasedDictionary protected override bool SubKeyFilter(string keyName) => keyName.StartsWith("."); } -internal class ProgIdDictionary : RegBasedDictionary +internal class ProgIdDictionary(bool readOnly) : RegBasedDictionary(Registry.ClassesRoot, readOnly) { private static readonly string[] badKeys = - { + [ "*", "AllFileSystemObjects", "AppID", "Applications", "AudioCD", "Briefcase", "CID", "CID.Local", "CLSID", "CompressedFolder", "ConflictFolder", "DVD", "DVDFile", "DesktopBackground", "DirectShow", "Directory", "Drive", "ExplorerCLSIDFlags", "Folder", "Interface", "LibraryFolder", "Local Settings", "MIME", "Media Servers", "Media Type", "MediaFoundation", "NetServer", "NetShare", "Network", "Printers", "Stack", "SystemFileAssociations", "TypeLib", "Unknown", "UserLibraryFolder", "VideoClipContainers", "VirtualStore" - }; - - public ProgIdDictionary(bool readOnly) : base(Registry.ClassesRoot, readOnly) - { - } + ]; public override bool TryGetValue(string key, [MaybeNullWhen(false)] out ProgId value) { @@ -103,12 +85,8 @@ internal class ProgIdDictionary : RegBasedDictionary !keyName.StartsWith("Kind.") && Array.BinarySearch(badKeys, keyName, StringComparer.OrdinalIgnoreCase) < 0; } -internal class ShellAssociationDictionary : RegBasedDictionary +internal class ShellAssociationDictionary(bool readOnly) : RegBasedDictionary(Registry.ClassesRoot, readOnly) { - public ShellAssociationDictionary(bool readOnly) : base(Registry.ClassesRoot, readOnly) - { - } - public override bool TryGetValue(string key, [MaybeNullWhen(false)] out ShellAssociation value) { value = null; diff --git a/Windows.Shell.Common/Registration/RegBasedKeyCollection.cs b/Windows.Shell.Common/Registration/RegBasedKeyCollection.cs index 6e6c1bcb..6ecf1c78 100644 --- a/Windows.Shell.Common/Registration/RegBasedKeyCollection.cs +++ b/Windows.Shell.Common/Registration/RegBasedKeyCollection.cs @@ -51,7 +51,7 @@ internal class RegBasedKeyCollection : ICollection, IDisposable /// Gets the enumerator. /// - public IEnumerator GetEnumerator() => (key?.GetValueNames().Cast() ?? new string[0]).GetEnumerator(); + public IEnumerator GetEnumerator() => (key?.GetValueNames().Cast() ?? []).GetEnumerator(); /// Removes the specified item. /// The item. diff --git a/Windows.Shell.Common/Registration/RegBasedSettings.cs b/Windows.Shell.Common/Registration/RegBasedSettings.cs index 42378fab..ed1bd94f 100644 --- a/Windows.Shell.Common/Registration/RegBasedSettings.cs +++ b/Windows.Shell.Common/Registration/RegBasedSettings.cs @@ -3,26 +3,20 @@ namespace Vanara.Windows.Shell; /// Base class for registry based settings. -public abstract class RegBasedSettings : IDisposable, IEquatable, IComparable +/// Initializes a new instance of the class. +/// The key to use as the base key for queries. +/// if set to true the supplied was opened read-only. +public abstract class RegBasedSettings(RegistryKey key, bool readOnly) : IDisposable, IEquatable, IComparable { /// The base key from which to perform all queries. - protected internal RegistryKey key; - - /// Initializes a new instance of the class. - /// The key to use as the base key for queries. - /// if set to true the supplied was opened read-only. - protected RegBasedSettings(RegistryKey key, bool readOnly) - { - this.key = key ?? throw new ArgumentNullException(nameof(key)); - ReadOnly = readOnly; - } + protected internal RegistryKey key = key ?? throw new ArgumentNullException(nameof(key)); /// Gets a value indicating whether this instance is system wide. /// if this instance is system wide; otherwise, . public bool IsSystemWide => !key.Name.StartsWith("HKEY_CURRENT_USER"); /// Gets or sets a value indicating whether these settings are read-only. - public bool ReadOnly { get; } + public bool ReadOnly { get; } = readOnly; /// Gets the absolute (qualified) name of the key. public string RegPath => key.Name; diff --git a/Windows.Shell.Common/ShellExtensions/ShellDropTarget.cs b/Windows.Shell.Common/ShellExtensions/ShellDropTarget.cs index 49a790c5..8f48b596 100644 --- a/Windows.Shell.Common/ShellExtensions/ShellDropTarget.cs +++ b/Windows.Shell.Common/ShellExtensions/ShellDropTarget.cs @@ -14,42 +14,33 @@ namespace Vanara.Windows.Shell; /// A DragEventArgs object specifies any data associated with drag/drop events; the current state of the SHIFT, CTRL, and ALT keys; the /// location of the mouse pointer; and the drag-and-drop effects allowed by the source and target of the drag event. /// -public class DragEventArgs : EventArgs +/// Initializes a new instance of the class. +/// The data associated with this event. +/// The current state of the SHIFT, CTRL, and ALT keys. +/// The x-coordinate of the mouse cursor in pixels. +/// The y-coordinate of the mouse cursor in pixels. +/// One of the DROPEFFECT values. +/// One of the DROPEFFECT values. +public class DragEventArgs(IDataObject? data, MouseButtonState keyState, int x, int y, Ole32.DROPEFFECT allowedEffect, Ole32.DROPEFFECT lastEffect) : EventArgs { - /// Initializes a new instance of the class. - /// The data associated with this event. - /// The current state of the SHIFT, CTRL, and ALT keys. - /// The x-coordinate of the mouse cursor in pixels. - /// The y-coordinate of the mouse cursor in pixels. - /// One of the DROPEFFECT values. - /// One of the DROPEFFECT values. - public DragEventArgs(IDataObject? data, MouseButtonState keyState, int x, int y, DROPEFFECT allowedEffect, DROPEFFECT lastEffect) - { - Data = data; - KeyState = keyState; - X = x; - Y = y; - AllowedEffect = allowedEffect; - Effect = lastEffect; - } /// Gets which drag-and-drop operations are allowed by the originator (or source) of the drag event. - public DROPEFFECT AllowedEffect { get; } + public DROPEFFECT AllowedEffect { get; } = allowedEffect; /// Gets the IDataObject that contains the data associated with this event. - public IDataObject? Data { get; } + public IDataObject? Data { get; } = data; /// Gets or sets the target drop effect in a drag-and-drop operation. - public DROPEFFECT Effect { get; set; } + public DROPEFFECT Effect { get; set; } = lastEffect; /// Gets the current state of the SHIFT, CTRL, and ALT keys, as well as the state of the mouse buttons. - public MouseButtonState KeyState { get; } + public MouseButtonState KeyState { get; } = keyState; /// Gets the x-coordinate of the mouse pointer, in screen coordinates. - public int X { get; } + public int X { get; } = x; /// Gets the y-coordinate of the mouse pointer, in screen coordinates. - public int Y { get; } + public int Y { get; } = y; } /// diff --git a/Windows.Shell.Common/ShellFileOperations/ShellFileOperations.cs b/Windows.Shell.Common/ShellFileOperations/ShellFileOperations.cs index c36fa2f7..23ac76f6 100644 --- a/Windows.Shell.Common/ShellFileOperations/ShellFileOperations.cs +++ b/Windows.Shell.Common/ShellFileOperations/ShellFileOperations.cs @@ -461,52 +461,48 @@ public partial class ShellFileOperations : IDisposable private ShellItemArray GetSHArray(IEnumerable items) => items is ShellItemArray a ? a : new ShellItemArray(items); - private class OpSink : IFileOperationProgressSink + private class OpSink(ShellFileOperations ops) : IFileOperationProgressSink { - private readonly ShellFileOperations parent; - - public OpSink(ShellFileOperations ops) => parent = ops; - - public HRESULT FinishOperations(HRESULT hrResult) => CallChkErr(() => parent.FinishOperations?.Invoke(parent, new ShellFileOpEventArgs(0, null, null, null, null, hrResult))); + public HRESULT FinishOperations(HRESULT hrResult) => CallChkErr(() => ops.FinishOperations?.Invoke(ops, new ShellFileOpEventArgs(0, null, null, null, null, hrResult))); public HRESULT PauseTimer() => HRESULT.E_NOTIMPL; public HRESULT PostCopyItem(TRANSFER_SOURCE_FLAGS dwFlags, IShellItem psiItem, IShellItem psiDestinationFolder, [MarshalAs(UnmanagedType.LPWStr)] string pszNewName, HRESULT hrCopy, IShellItem psiNewlyCreated) => - CallChkErr(() => parent.PostCopyItem?.Invoke(parent, new ShellFileOpEventArgs(dwFlags, psiItem, psiDestinationFolder, psiNewlyCreated, pszNewName, hrCopy))); + CallChkErr(() => ops.PostCopyItem?.Invoke(ops, new ShellFileOpEventArgs(dwFlags, psiItem, psiDestinationFolder, psiNewlyCreated, pszNewName, hrCopy))); public HRESULT PostDeleteItem(TRANSFER_SOURCE_FLAGS dwFlags, IShellItem psiItem, HRESULT hrDelete, IShellItem? psiNewlyCreated) => - CallChkErr(() => parent.PostDeleteItem?.Invoke(parent, new ShellFileOpEventArgs(dwFlags, psiItem, null, psiNewlyCreated, null, hrDelete))); + CallChkErr(() => ops.PostDeleteItem?.Invoke(ops, new ShellFileOpEventArgs(dwFlags, psiItem, null, psiNewlyCreated, null, hrDelete))); public HRESULT PostMoveItem(TRANSFER_SOURCE_FLAGS dwFlags, IShellItem psiItem, IShellItem psiDestinationFolder, [MarshalAs(UnmanagedType.LPWStr)] string pszNewName, HRESULT hrMove, IShellItem psiNewlyCreated) => - CallChkErr(() => parent.PostMoveItem?.Invoke(parent, new ShellFileOpEventArgs(dwFlags, psiItem, psiDestinationFolder, psiNewlyCreated, pszNewName, hrMove))); + CallChkErr(() => ops.PostMoveItem?.Invoke(ops, new ShellFileOpEventArgs(dwFlags, psiItem, psiDestinationFolder, psiNewlyCreated, pszNewName, hrMove))); public HRESULT PostNewItem(TRANSFER_SOURCE_FLAGS dwFlags, IShellItem psiDestinationFolder, [MarshalAs(UnmanagedType.LPWStr)] string pszNewName, [MarshalAs(UnmanagedType.LPWStr)] string? pszTemplateName, uint dwFileAttributes, HRESULT hrNew, IShellItem psiNewItem) => - CallChkErr(() => parent.PostNewItem?.Invoke(parent, new ShellFileNewOpEventArgs(dwFlags, null, psiDestinationFolder, psiNewItem, pszNewName, hrNew, pszTemplateName, dwFileAttributes))); + CallChkErr(() => ops.PostNewItem?.Invoke(ops, new ShellFileNewOpEventArgs(dwFlags, null, psiDestinationFolder, psiNewItem, pszNewName, hrNew, pszTemplateName, dwFileAttributes))); public HRESULT PostRenameItem(TRANSFER_SOURCE_FLAGS dwFlags, IShellItem psiItem, [MarshalAs(UnmanagedType.LPWStr)] string pszNewName, HRESULT hrRename, IShellItem psiNewlyCreated) => - CallChkErr(() => parent.PostRenameItem?.Invoke(parent, new ShellFileOpEventArgs(dwFlags, psiItem, null, psiNewlyCreated, pszNewName, hrRename))); + CallChkErr(() => ops.PostRenameItem?.Invoke(ops, new ShellFileOpEventArgs(dwFlags, psiItem, null, psiNewlyCreated, pszNewName, hrRename))); public HRESULT PreCopyItem(TRANSFER_SOURCE_FLAGS dwFlags, IShellItem psiItem, IShellItem psiDestinationFolder, [MarshalAs(UnmanagedType.LPWStr)] string? pszNewName) => - CallChkErr(() => parent.PreCopyItem?.Invoke(parent, new ShellFileOpEventArgs(dwFlags, psiItem, psiDestinationFolder, null, pszNewName))); + CallChkErr(() => ops.PreCopyItem?.Invoke(ops, new ShellFileOpEventArgs(dwFlags, psiItem, psiDestinationFolder, null, pszNewName))); public HRESULT PreDeleteItem(TRANSFER_SOURCE_FLAGS dwFlags, IShellItem psiItem) => - CallChkErr(() => parent.PreDeleteItem?.Invoke(parent, new ShellFileOpEventArgs(dwFlags, psiItem))); + CallChkErr(() => ops.PreDeleteItem?.Invoke(ops, new ShellFileOpEventArgs(dwFlags, psiItem))); public HRESULT PreMoveItem(TRANSFER_SOURCE_FLAGS dwFlags, IShellItem psiItem, IShellItem psiDestinationFolder, [MarshalAs(UnmanagedType.LPWStr)] string? pszNewName) => - CallChkErr(() => parent.PreMoveItem?.Invoke(parent, new ShellFileOpEventArgs(dwFlags, psiItem, psiDestinationFolder, null, pszNewName))); + CallChkErr(() => ops.PreMoveItem?.Invoke(ops, new ShellFileOpEventArgs(dwFlags, psiItem, psiDestinationFolder, null, pszNewName))); public HRESULT PreNewItem(TRANSFER_SOURCE_FLAGS dwFlags, IShellItem psiDestinationFolder, [MarshalAs(UnmanagedType.LPWStr)] string pszNewName) => - CallChkErr(() => parent.PreNewItem?.Invoke(parent, new ShellFileOpEventArgs(dwFlags, null, psiDestinationFolder, null, pszNewName))); + CallChkErr(() => ops.PreNewItem?.Invoke(ops, new ShellFileOpEventArgs(dwFlags, null, psiDestinationFolder, null, pszNewName))); - public HRESULT PreRenameItem(TRANSFER_SOURCE_FLAGS dwFlags, IShellItem psiItem, [MarshalAs(UnmanagedType.LPWStr)] string pszNewName) => CallChkErr(() => parent.PreRenameItem?.Invoke(parent, new ShellFileOpEventArgs(dwFlags, psiItem, null, null, pszNewName))); + public HRESULT PreRenameItem(TRANSFER_SOURCE_FLAGS dwFlags, IShellItem psiItem, [MarshalAs(UnmanagedType.LPWStr)] string pszNewName) => CallChkErr(() => ops.PreRenameItem?.Invoke(ops, new ShellFileOpEventArgs(dwFlags, psiItem, null, null, pszNewName))); public HRESULT ResetTimer() => HRESULT.E_NOTIMPL; public HRESULT ResumeTimer() => HRESULT.E_NOTIMPL; - public HRESULT StartOperations() => CallChkErr(() => parent.StartOperations?.Invoke(parent, EventArgs.Empty)); + public HRESULT StartOperations() => CallChkErr(() => ops.StartOperations?.Invoke(ops, EventArgs.Empty)); - public HRESULT UpdateProgress(uint iWorkTotal, uint iWorkSoFar) => CallChkErr(() => parent.UpdateProgress?.Invoke(parent, new ProgressChangedEventArgs(iWorkTotal == 0 ? 0 : (int)(iWorkSoFar * 100 / iWorkTotal), null))); + public HRESULT UpdateProgress(uint iWorkTotal, uint iWorkSoFar) => CallChkErr(() => ops.UpdateProgress?.Invoke(ops, new ProgressChangedEventArgs(iWorkTotal == 0 ? 0 : (int)(iWorkSoFar * 100 / iWorkTotal), null))); private HRESULT CallChkErr(Action action) { diff --git a/Windows.Shell.Common/ShellItemChangeWatcher.cs b/Windows.Shell.Common/ShellItemChangeWatcher.cs index fc9b9661..3122f08a 100644 --- a/Windows.Shell.Common/ShellItemChangeWatcher.cs +++ b/Windows.Shell.Common/ShellItemChangeWatcher.cs @@ -315,7 +315,7 @@ public class ShellItemChangeWatcher : Component, ISupportInitialize enabled = true; if (IsSuspended) return; SHGetIDListFromObject(Item.IShellItem, out PIDL pidlWatch).ThrowIfFailed(); - SHChangeNotifyEntry[] entries = { new SHChangeNotifyEntry { pidl = pidlWatch.DangerousGetHandle(), fRecursive = IncludeChildren } }; + SHChangeNotifyEntry[] entries = [new SHChangeNotifyEntry { pidl = pidlWatch.DangerousGetHandle(), fRecursive = IncludeChildren }]; ulRegister = SHChangeNotifyRegister(hPump.MessageWindowHandle, sources, (SHCNE)NotifyFilter, hPump.MessageId, entries.Length, entries); if (ulRegister == 0) throw new InvalidOperationException("Unable to register shell notifications."); } @@ -349,28 +349,20 @@ public class ShellItemChangeWatcher : Component, ISupportInitialize public ChangeFilters ChangeType { get; } } - private class WatcherNativeWindow : SystemEventHandler + private class WatcherNativeWindow(ShellItemChangeWatcher parent) : SystemEventHandler() { - private readonly ShellItemChangeWatcher p; - - public WatcherNativeWindow(ShellItemChangeWatcher parent) : base() - { - MessageId = RegisterWindowMessage($"{parent.GetType()}{DateTime.Now.Ticks}"); - p = parent; - } - - public uint MessageId { get; set; } + public uint MessageId { get; set; } = RegisterWindowMessage($"{parent.GetType()}{DateTime.Now.Ticks}"); protected override bool MessageFilter(HWND hwnd, uint msg, IntPtr wParam, IntPtr lParam, out IntPtr lReturn) { lReturn = default; - if (msg == MessageId && p.enabled && !p.IsSuspended) + if (msg == MessageId && parent.enabled && !parent.IsSuspended) { HLOCK hNotifyLock = default; try { hNotifyLock = SHChangeNotification_Lock(wParam, (uint)lParam.ToInt32(), out IntPtr rgpidl, out SHCNE lEvent); - if (hNotifyLock != IntPtr.Zero && rgpidl != IntPtr.Zero && p.NotifyFilter.IsFlagSet((ChangeFilters)lEvent)) + if (hNotifyLock != IntPtr.Zero && rgpidl != IntPtr.Zero && parent.NotifyFilter.IsFlagSet((ChangeFilters)lEvent)) { ShellItemChangeEventArgs args; if (NoParamEvent.IsFlagSet(lEvent)) @@ -379,7 +371,7 @@ public class ShellItemChangeWatcher : Component, ISupportInitialize args = new ShellItemChangeEventArgs(lEvent, Marshal.ReadIntPtr(rgpidl, 0), Marshal.ReadIntPtr(rgpidl, IntPtr.Size)); else args = new ShellItemChangeEventArgs(lEvent, Marshal.ReadIntPtr(rgpidl, 0)); - p.OnChanged(args); + parent.OnChanged(args); } } finally diff --git a/Windows.Shell.Common/ShellObjects/ComObjWrapper.cs b/Windows.Shell.Common/ShellObjects/ComObjWrapper.cs index 4002c253..2c93a1d4 100644 --- a/Windows.Shell.Common/ShellObjects/ComObjWrapper.cs +++ b/Windows.Shell.Common/ShellObjects/ComObjWrapper.cs @@ -8,14 +8,12 @@ namespace Vanara.Windows.Shell; /// /// /// -public abstract class ComObjWrapper : IDisposable, IEquatable, INotifyPropertyChanged where TObj : ComObjWrapper where TComType : class +/// Initializes a new instance of the class. +/// The base interface. +public abstract class ComObjWrapper(TComType baseInterface) : IDisposable, IEquatable, INotifyPropertyChanged where TObj : ComObjWrapper where TComType : class { /// The internal reference to the COM object. - protected TComType iObj; - - /// Initializes a new instance of the class. - /// The base interface. - protected ComObjWrapper(TComType baseInterface) => iObj = baseInterface ?? throw new ArgumentNullException(nameof(baseInterface)); + protected TComType iObj = baseInterface ?? throw new ArgumentNullException(nameof(baseInterface)); /// Occurs when a property value changes. public virtual event PropertyChangedEventHandler? PropertyChanged; diff --git a/Windows.Shell.Common/ShellObjects/ShellContextMenu.cs b/Windows.Shell.Common/ShellObjects/ShellContextMenu.cs index 97aa4fc7..58bf13ea 100644 --- a/Windows.Shell.Common/ShellObjects/ShellContextMenu.cs +++ b/Windows.Shell.Common/ShellObjects/ShellContextMenu.cs @@ -462,7 +462,7 @@ public class ShellContextMenu : IDisposable internal static MenuItemInfo[] GetMenuItems(HMENU hMenu, ShellContextMenu? scm) { if (hMenu.IsNull) - return new MenuItemInfo[0]; + return []; var SubMenus = new MenuItemInfo[GetMenuItemCount(hMenu)]; for (uint i = 0; i < SubMenus.Length; i++) diff --git a/Windows.Shell.Common/ShellObjects/ShellItem.cs b/Windows.Shell.Common/ShellObjects/ShellItem.cs index 553362cf..dafc459c 100644 --- a/Windows.Shell.Common/ShellObjects/ShellItem.cs +++ b/Windows.Shell.Common/ShellObjects/ShellItem.cs @@ -516,7 +516,7 @@ public class ShellItem : IComparable, IDisposable, IEquatableGets the property store for the item. /// Initially, this property store is the read-only store (change from R/W in v3.2.9) and should always have properties. /// However, setting any of the properties of this value change the function of all subsequent uses. For example, if you set the - /// value to , all subsequent calls to value to , all subsequent calls to will access the read-write property store. If this does not support /// properties directly, your use of this property will fail. It is important that you check for exceptions when changing the /// properties of this value to prevent unexpected failures. @@ -785,7 +785,7 @@ public class ShellItem : IComparable, IDisposable, IEquatable @@ -877,14 +877,12 @@ public class ShellItem : IComparable, IDisposable, IEquatableLocal implementation of IShellItem. /// /// - protected class ShellItemImpl : IDisposable, IShellItem + /// Initializes a new instance of the class. + /// The pidl. + /// if set to [owner]. + protected class ShellItemImpl(Shell32.PIDL pidl, bool owner) : IDisposable, IShellItem { - /// Initializes a new instance of the class. - /// The pidl. - /// if set to [owner]. - public ShellItemImpl(PIDL pidl, bool owner) => PIDL = owner ? pidl : new PIDL(pidl); - - private PIDL PIDL { get; set; } + private PIDL PIDL { get; set; } = owner ? pidl : new PIDL(pidl); /// Binds to a handler for an item as specified by the handler ID value (BHID). /// @@ -941,7 +939,7 @@ public class ShellItem : IComparable, IDisposable, IEquatableExposes methods that get images related to shell items. -public class ShellItemImages +/// Initializes a new instance of the class. +/// The instance. +public class ShellItemImages(ShellItem shellItem) { - private readonly ShellItem shellItem; - - /// Initializes a new instance of the class. - /// The instance. - public ShellItemImages(ShellItem shellItem) => this.shellItem = shellItem; /// /// Gets an image that represents this item. The default behavior is to load a thumbnail. If there is no thumbnail for the current diff --git a/Windows.Shell.Common/ShellObjects/ShellLibrary.cs b/Windows.Shell.Common/ShellObjects/ShellLibrary.cs index df6b3cc0..f533dee4 100644 --- a/Windows.Shell.Common/ShellObjects/ShellLibrary.cs +++ b/Windows.Shell.Common/ShellObjects/ShellLibrary.cs @@ -99,7 +99,7 @@ public class ShellLibrary : ShellFolder /// Gets the set of child folders that are contained in the library. /// A containing the child folders. - public ShellLibraryFolders Folders => folders ?? (folders = GetFilteredFolders()); + public ShellLibraryFolders Folders => folders ??= GetFilteredFolders(); /// /// Gets or sets a string that describes the location of the default icon. The string must be formatted as diff --git a/Windows.Shell.Common/ShellObjects/ShellSearchConditions.cs b/Windows.Shell.Common/ShellObjects/ShellSearchConditions.cs index ed1f58b7..bd9d1843 100644 --- a/Windows.Shell.Common/ShellObjects/ShellSearchConditions.cs +++ b/Windows.Shell.Common/ShellObjects/ShellSearchConditions.cs @@ -97,7 +97,7 @@ public class SearchCondition : ICloneable, IDisposable /// The new node. public static SearchCondition CreateFromStructuredQuery(string query, CultureInfo? cultureInfo = null) { - if (cultureInfo is null) cultureInfo = CultureInfo.CurrentUICulture; + cultureInfo ??= CultureInfo.CurrentUICulture; using var qm = ComReleaserFactory.Create(new IQueryParserManager()); using var qp = ComReleaserFactory.Create(qm.Item.CreateLoadedParser(systemCatalog, (uint)cultureInfo.LCID)); qm.Item.InitializeOptions(false, true, qp.Item); diff --git a/Windows.Shell.Common/ShellProperties/PropertyBag.cs b/Windows.Shell.Common/ShellProperties/PropertyBag.cs index 93a3d4ef..dbe0a56d 100644 --- a/Windows.Shell.Common/ShellProperties/PropertyBag.cs +++ b/Windows.Shell.Common/ShellProperties/PropertyBag.cs @@ -3,14 +3,12 @@ namespace Vanara.Windows.Shell; /// Encapsulates an instance. -public class PropertyBag +/// Initializes a new instance of the class. +/// The property bag. +public class PropertyBag(PInvoke.OleAut32.IPropertyBag ppb) { /// The IPropertyBag instance. - protected readonly IPropertyBag ibag; - - /// Initializes a new instance of the class. - /// The property bag. - public PropertyBag(IPropertyBag ppb) => ibag = ppb; + protected readonly IPropertyBag ibag = ppb; /// Gets or sets the with the specified property name. /// The . diff --git a/Windows.Shell.Common/StockIcon.cs b/Windows.Shell.Common/StockIcon.cs index 3b91f5b4..abbb7830 100644 --- a/Windows.Shell.Common/StockIcon.cs +++ b/Windows.Shell.Common/StockIcon.cs @@ -5,7 +5,12 @@ using static Vanara.PInvoke.User32; namespace Vanara.Windows.Shell; /// Represents a standard system icon. -public class StockIcon : IDisposable +/// Creates a new StockIcon instance with the specified identifer and options. +/// A value that identifies the icon represented by this instance. +/// A value that indicates the size of the stock icon. +/// A bool value that indicates whether the icon has a link overlay. +/// A bool value that indicates whether the icon is in a selected state. +public class StockIcon(Shell32.SHSTOCKICONID id, ShellIconType size = ShellIconType.Large, bool isLinkOverlay = false, bool isSelected = false) : IDisposable { private SHGSI curFlags; private SHSTOCKICONID curId; @@ -15,19 +20,6 @@ public class StockIcon : IDisposable static StockIcon() => FileIconInit(false); - /// Creates a new StockIcon instance with the specified identifer and options. - /// A value that identifies the icon represented by this instance. - /// A value that indicates the size of the stock icon. - /// A bool value that indicates whether the icon has a link overlay. - /// A bool value that indicates whether the icon is in a selected state. - public StockIcon(SHSTOCKICONID id, ShellIconType size = ShellIconType.Large, bool isLinkOverlay = false, bool isSelected = false) - { - Identifier = id; - LinkOverlay = isLinkOverlay; - Selected = isSelected; - Size = size; - } - /// Finalizes an instance of the class. ~StockIcon() { @@ -39,11 +31,11 @@ public class StockIcon : IDisposable public HICON IconHandle => hIcon; /// Gets or sets the Stock Icon identifier associated with this icon. - public SHSTOCKICONID Identifier { get; set; } + public SHSTOCKICONID Identifier { get; set; } = id; /// Gets or sets a value that cotrols whether to put a link overlay on the icon. /// A value. - public bool LinkOverlay { get; set; } + public bool LinkOverlay { get; set; } = isLinkOverlay; /// Gets the icon location, composed of a resource path and the icon's index. /// The icon location. @@ -51,11 +43,11 @@ public class StockIcon : IDisposable /// Gets or sets a value indicating whether the icon appears selected. /// A value. - public bool Selected { get; set; } + public bool Selected { get; set; } = isSelected; /// Gets or sets a value that controls the size of the Stock Icon. /// A value. - public ShellIconType Size { get; set; } + public ShellIconType Size { get; set; } = size; /// Gets the index of the image in the system icon cache. /// The index of the system image. diff --git a/Windows.Shell.Common/TaskBar/JumpList.cs b/Windows.Shell.Common/TaskBar/JumpList.cs index c6a679a4..d6a8b772 100644 --- a/Windows.Shell.Common/TaskBar/JumpList.cs +++ b/Windows.Shell.Common/TaskBar/JumpList.cs @@ -266,18 +266,11 @@ public class JumpListSeparator : JumpListItem, IJumpListItem /// A task for a jumplist. /// -public class JumpListTask : JumpListItem, IJumpListItem +/// Initializes a new instance of the class. +public class JumpListTask(string? title, string applicationPath) : JumpListItem, IJumpListItem { private int iconResIdx = -1; - private string path; - private string? title, description, args, dir, iconPath, appUserModelID; - - /// Initializes a new instance of the class. - public JumpListTask(string? title, string applicationPath) - { - this.title = title; - path = applicationPath; - } + private string? description, args, dir, iconPath, appUserModelID; /// Gets or sets the application path. /// The application path. @@ -286,12 +279,12 @@ public class JumpListTask : JumpListItem, IJumpListItem [Editor("System.Windows.Forms.Design.FileNameEditor, System.Design", "System.Drawing.Design.UITypeEditor, System.Drawing")] public string ApplicationPath { - get => path; + get => applicationPath; set { if (value is null) throw new ArgumentNullException(nameof(ApplicationPath)); - if (path == value) return; - path = value; + if (applicationPath == value) return; + applicationPath = value; OnPropertyChanged(); } } @@ -460,7 +453,7 @@ internal class GenericExpandableObjectConverter : ExpandableObjectConverter public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? info, object? value, Type destType) { if (destType == typeof(InstanceDescriptor)) - return new InstanceDescriptor(typeof(T).GetConstructor(new Type[0]), null, false); + return new InstanceDescriptor(typeof(T).GetConstructor([]), null, false); if (destType == typeof(string)) return ""; return base.ConvertTo(context, info, value, destType); diff --git a/Windows.Shell.Common/Vanara.Windows.Shell.Common.csproj b/Windows.Shell.Common/Vanara.Windows.Shell.Common.csproj index c1066af2..41f0377a 100644 --- a/Windows.Shell.Common/Vanara.Windows.Shell.Common.csproj +++ b/Windows.Shell.Common/Vanara.Windows.Shell.Common.csproj @@ -21,10 +21,13 @@ ChangeFilters, DialogStatus, ExecutableType, FileUsageType, FolderItemFilter, Li + + + @@ -35,6 +38,7 @@ ChangeFilters, DialogStatus, ExecutableType, FileUsageType, FolderItemFilter, Li +