Refactored for new C# language optimizations

master
David Hall 2024-06-06 11:19:40 -06:00
parent dcdfe3f215
commit 05c6710389
25 changed files with 122 additions and 207 deletions

View File

@ -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)
{

View File

@ -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<uint>((int)lpen.elpNumEntries) ?? new uint[0];
uint[] styleArray = lpen.elpStyleEntry.ToArray<uint>((int)lpen.elpNumEntries) ?? [];
pen.DashPattern = Array.ConvertAll(styleArray, i => (float)i);
}
break;
@ -213,18 +213,11 @@ public static class GdiObjExtensions2
/// <summary>A self-releasing pattern for IDeviceContext.GetHdc and ReleaseHdc.</summary>
/// <seealso cref="System.IDisposable"/>
public class SafeTempHDC : IDisposable, IGraphicsObjectHandle
/// <remarks>Initializes a new instance of the <see cref="SafeTempHDC"/> class with an <see cref="IDeviceContext"/>.</remarks>
/// <param name="dc">The <see cref="IDeviceContext"/> instance.</param>
public class SafeTempHDC(IDeviceContext? dc) : IDisposable, IGraphicsObjectHandle
{
private readonly IDeviceContext? dc;
private readonly IntPtr hdc;
/// <summary>Initializes a new instance of the <see cref="SafeTempHDC"/> class with an <see cref="IDeviceContext"/>.</summary>
/// <param name="dc">The <see cref="IDeviceContext"/> instance.</param>
public SafeTempHDC(IDeviceContext? dc)
{
this.dc = dc;
hdc = dc?.GetHdc() ?? default;
}
private readonly IntPtr hdc = dc?.GetHdc() ?? default;
/// <summary>Gets a value indicating whether this instance has a NULL handle.</summary>
/// <value><see langword="true"/> if this has a NULL handle; otherwise, <see langword="false"/>.</value>
@ -239,5 +232,9 @@ public class SafeTempHDC : IDisposable, IGraphicsObjectHandle
public IntPtr DangerousGetHandle() => hdc;
/// <summary>Releases claimed HDC.</summary>
public void Dispose() => dc?.ReleaseHdc();
public void Dispose()
{
dc?.ReleaseHdc();
GC.SuppressFinalize(this);
}
}

View File

@ -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<Color>
{
private SmartBitmapLock bmpLock;
private readonly SmartBitmapLock bmpLock;
private int idx = -1;
internal Enumertor(SmartBitmapLock bmp) => bmpLock = bmp;

View File

@ -161,7 +161,7 @@ public class BindContext : IDisposable, IBindCtxV, IBindCtx
/// in a return value of E_NOTIMPL.
/// </para>
/// </remarks>
public IEnumerable<string> EnumObjectParam() => ((IBindCtxV)this).EnumObjectParam(out var ppenum).Succeeded ? ppenum.Enum().ToArray() : new string[0];
public IEnumerable<string> EnumObjectParam() => ((IBindCtxV)this).EnumObjectParam(out var ppenum).Succeeded ? ppenum.Enum().ToArray() : [];
/// <summary>
/// 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)]

View File

@ -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);

View File

@ -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;

View File

@ -62,7 +62,7 @@ public static class RecycleBin
/// The <see cref="ShellItem"/> of the deleted item in the Recycle Bin. This cannot be a reference to an undeleted shell item.
/// </param>
/// <param name="hideUI"><see langword="true"/> to hide all user interface interactions; <see langword="false"/> to allow them.</param>
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);
/// <summary>Restores the specified deleted items to their original location.</summary>
/// <param name="deletedItems">

View File

@ -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)
{
}
}
}

View File

@ -25,7 +25,7 @@ public class CommandVerbDictionary : RegBasedDictionary<CommandVerb>
/// <summary>Get the filtered list of keys under the base.</summary>
[Browsable(false)]
public override IEnumerable<string> Keys => key?.GetSubKeyNames() ?? new string[0];
public override IEnumerable<string> Keys => key?.GetSubKeyNames() ?? [];
/// <summary>Gets or sets the order of the command verbs.</summary>
/// <value>The ordered list of command verbs.</value>

View File

@ -10,25 +10,19 @@ namespace Vanara.Windows.Shell;
/// <summary>A virtual dictionary that is based on values in the Windows Registry.</summary>
/// <typeparam name="T">Type used to capture multiple values within the registry.</typeparam>
/// <seealso cref="VirtualReadOnlyDictionary{TKey, TValue}"/>
public abstract class RegBasedDictionary<T> : VirtualReadOnlyDictionary<string, T>
/// <remarks>Initializes a new instance of the <see cref="RegBasedDictionary{T}"/> class.</remarks>
/// <param name="baseKey">The base registry key.</param>
/// <param name="readOnly">if set to <see langword="true"/> render this dictionary read-only.</param>
public abstract class RegBasedDictionary<T>(RegistryKey? baseKey, bool readOnly) : VirtualReadOnlyDictionary<string, T>
{
/// <summary>Read-only flag.</summary>
protected readonly bool readOnly;
protected readonly bool readOnly = readOnly;
/// <summary>The base registry key for this dictionary.</summary>
protected RegistryKey? key;
/// <summary>Initializes a new instance of the <see cref="RegBasedDictionary{T}"/> class.</summary>
/// <param name="baseKey">The base registry key.</param>
/// <param name="readOnly">if set to <see langword="true"/> render this dictionary read-only.</param>
protected RegBasedDictionary(RegistryKey? baseKey, bool readOnly)
{
key = baseKey;
this.readOnly = readOnly;
}
protected RegistryKey? key = baseKey;
/// <summary>Get the filtered list of keys under the base.</summary>
public override IEnumerable<string> Keys => key?.GetSubKeyNames().Where(SubKeyFilter) ?? new string[0];
public override IEnumerable<string> Keys => key?.GetSubKeyNames().Where(SubKeyFilter) ?? [];
/// <summary>Determines if a specified key is in the filtered list of keys under the base.</summary>
/// <param name="key">The name of the key to check.</param>
@ -43,12 +37,8 @@ public abstract class RegBasedDictionary<T> : VirtualReadOnlyDictionary<string,
protected virtual bool SubKeyFilter(string keyName) => true;
}
internal class AppDictionary : RegBasedDictionary<AppRegistration>
internal class AppDictionary(bool readOnly) : RegBasedDictionary<AppRegistration>(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<AppRegistration>
}
}
internal class FileTypeDictionary : RegBasedDictionary<FileTypeAssociation>
internal class FileTypeDictionary(bool readOnly) : RegBasedDictionary<FileTypeAssociation>(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<FileTypeAssociation>
protected override bool SubKeyFilter(string keyName) => keyName.StartsWith(".");
}
internal class ProgIdDictionary : RegBasedDictionary<ProgId>
internal class ProgIdDictionary(bool readOnly) : RegBasedDictionary<ProgId>(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<ProgId>
!keyName.StartsWith("Kind.") && Array.BinarySearch(badKeys, keyName, StringComparer.OrdinalIgnoreCase) < 0;
}
internal class ShellAssociationDictionary : RegBasedDictionary<ShellAssociation>
internal class ShellAssociationDictionary(bool readOnly) : RegBasedDictionary<ShellAssociation>(Registry.ClassesRoot, readOnly)
{
public ShellAssociationDictionary(bool readOnly) : base(Registry.ClassesRoot, readOnly)
{
}
public override bool TryGetValue(string key, [MaybeNullWhen(false)] out ShellAssociation value)
{
value = null;

View File

@ -51,7 +51,7 @@ internal class RegBasedKeyCollection : ICollection<string>, IDisposable
/// <summary>Gets the enumerator.</summary>
/// <returns></returns>
public IEnumerator<string> GetEnumerator() => (key?.GetValueNames().Cast<string>() ?? new string[0]).GetEnumerator();
public IEnumerator<string> GetEnumerator() => (key?.GetValueNames().Cast<string>() ?? []).GetEnumerator();
/// <summary>Removes the specified item.</summary>
/// <param name="item">The item.</param>

View File

@ -3,26 +3,20 @@
namespace Vanara.Windows.Shell;
/// <summary>Base class for registry based settings.</summary>
public abstract class RegBasedSettings : IDisposable, IEquatable<RegBasedSettings>, IComparable<RegBasedSettings>
/// <remarks>Initializes a new instance of the <see cref="RegBasedSettings"/> class.</remarks>
/// <param name="key">The key to use as the base key for queries.</param>
/// <param name="readOnly">if set to <c>true</c> the supplied <paramref name="key"/> was opened read-only.</param>
public abstract class RegBasedSettings(RegistryKey key, bool readOnly) : IDisposable, IEquatable<RegBasedSettings>, IComparable<RegBasedSettings>
{
/// <summary>The base key from which to perform all queries.</summary>
protected internal RegistryKey key;
/// <summary>Initializes a new instance of the <see cref="RegBasedSettings"/> class.</summary>
/// <param name="key">The key to use as the base key for queries.</param>
/// <param name="readOnly">if set to <c>true</c> the supplied <paramref name="key"/> was opened read-only.</param>
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));
/// <summary>Gets a value indicating whether this instance is system wide.</summary>
/// <value><see langword="true"/> if this instance is system wide; otherwise, <see langword="false"/>.</value>
public bool IsSystemWide => !key.Name.StartsWith("HKEY_CURRENT_USER");
/// <summary>Gets or sets a value indicating whether these settings are read-only.</summary>
public bool ReadOnly { get; }
public bool ReadOnly { get; } = readOnly;
/// <summary>Gets the absolute (qualified) name of the key.</summary>
public string RegPath => key.Name;

View File

@ -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.
/// </remarks>
public class DragEventArgs : EventArgs
/// <remarks>Initializes a new instance of the <see cref="DragEventArgs"/> class.</remarks>
/// <param name="data">The data associated with this event.</param>
/// <param name="keyState">The current state of the SHIFT, CTRL, and ALT keys.</param>
/// <param name="x">The x-coordinate of the mouse cursor in pixels.</param>
/// <param name="y">The y-coordinate of the mouse cursor in pixels.</param>
/// <param name="allowedEffect">One of the DROPEFFECT values.</param>
/// <param name="lastEffect">One of the DROPEFFECT values.</param>
public class DragEventArgs(IDataObject? data, MouseButtonState keyState, int x, int y, Ole32.DROPEFFECT allowedEffect, Ole32.DROPEFFECT lastEffect) : EventArgs
{
/// <summary>Initializes a new instance of the <see cref="DragEventArgs"/> class.</summary>
/// <param name="data">The data associated with this event.</param>
/// <param name="keyState">The current state of the SHIFT, CTRL, and ALT keys.</param>
/// <param name="x">The x-coordinate of the mouse cursor in pixels.</param>
/// <param name="y">The y-coordinate of the mouse cursor in pixels.</param>
/// <param name="allowedEffect">One of the DROPEFFECT values.</param>
/// <param name="lastEffect">One of the DROPEFFECT values.</param>
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;
}
/// <summary>Gets which drag-and-drop operations are allowed by the originator (or source) of the drag event.</summary>
public DROPEFFECT AllowedEffect { get; }
public DROPEFFECT AllowedEffect { get; } = allowedEffect;
/// <summary>Gets the IDataObject that contains the data associated with this event.</summary>
public IDataObject? Data { get; }
public IDataObject? Data { get; } = data;
/// <summary>Gets or sets the target drop effect in a drag-and-drop operation.</summary>
public DROPEFFECT Effect { get; set; }
public DROPEFFECT Effect { get; set; } = lastEffect;
/// <summary>Gets the current state of the SHIFT, CTRL, and ALT keys, as well as the state of the mouse buttons.</summary>
public MouseButtonState KeyState { get; }
public MouseButtonState KeyState { get; } = keyState;
/// <summary>Gets the x-coordinate of the mouse pointer, in screen coordinates.</summary>
public int X { get; }
public int X { get; } = x;
/// <summary>Gets the y-coordinate of the mouse pointer, in screen coordinates.</summary>
public int Y { get; }
public int Y { get; } = y;
}
/// <summary>

View File

@ -461,52 +461,48 @@ public partial class ShellFileOperations : IDisposable
private ShellItemArray GetSHArray(IEnumerable<ShellItem> 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)
{

View File

@ -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

View File

@ -8,14 +8,12 @@ namespace Vanara.Windows.Shell;
/// <seealso cref="IEquatable{T}"/>
/// <seealso cref="IEquatable{T}"/>
/// <seealso cref="INotifyPropertyChanged"/>
public abstract class ComObjWrapper<TObj, TComType> : IDisposable, IEquatable<TComType>, INotifyPropertyChanged where TObj : ComObjWrapper<TObj, TComType> where TComType : class
/// <remarks>Initializes a new instance of the <see cref="ComObjWrapper{TObj, TComType}"/> class.</remarks>
/// <param name="baseInterface">The base interface.</param>
public abstract class ComObjWrapper<TObj, TComType>(TComType baseInterface) : IDisposable, IEquatable<TComType>, INotifyPropertyChanged where TObj : ComObjWrapper<TObj, TComType> where TComType : class
{
/// <summary>The internal reference to the COM object.</summary>
protected TComType iObj;
/// <summary>Initializes a new instance of the <see cref="ComObjWrapper{TObj, TComType}"/> class.</summary>
/// <param name="baseInterface">The base interface.</param>
protected ComObjWrapper(TComType baseInterface) => iObj = baseInterface ?? throw new ArgumentNullException(nameof(baseInterface));
protected TComType iObj = baseInterface ?? throw new ArgumentNullException(nameof(baseInterface));
/// <summary>Occurs when a property value changes.</summary>
public virtual event PropertyChangedEventHandler? PropertyChanged;

View File

@ -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++)

View File

@ -516,7 +516,7 @@ public class ShellItem : IComparable<ShellItem>, IDisposable, IEquatable<IShellI
/// <para>Gets the property store for the item.</para>
/// <note>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
/// <see cref="ShellItemPropertyStore.ReadOnly"/> value to <see langword="false"/>, all subsequent calls to <see
/// <see cref="ReadOnlyPropertyStore.ReadOnly"/> value to <see langword="false"/>, all subsequent calls to <see
/// cref="Properties"/> will access the read-write property store. If this <see cref="ShellItem"/> 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.</note>
@ -785,7 +785,7 @@ public class ShellItem : IComparable<ShellItem>, IDisposable, IEquatable<IShellI
if (IsFolder)
SHOpenFolderAndSelectItems(PIDL, 0, null, OFASI.OFASI_NONE);
else
SHOpenFolderAndSelectItems(Parent!.PIDL, 1, new IntPtr[] { (IntPtr)PIDL }, OFASI.OFASI_NONE);
SHOpenFolderAndSelectItems(Parent!.PIDL, 1, [(IntPtr)PIDL], OFASI.OFASI_NONE);
}
/// <summary>
@ -877,14 +877,12 @@ public class ShellItem : IComparable<ShellItem>, IDisposable, IEquatable<IShellI
/// <summary>Local implementation of IShellItem.</summary>
/// <seealso cref="IDisposable"/>
/// <seealso cref="Shell32.IShellItem"/>
protected class ShellItemImpl : IDisposable, IShellItem
/// <remarks>Initializes a new instance of the <see cref="ShellItemImpl"/> class.</remarks>
/// <param name="pidl">The pidl.</param>
/// <param name="owner">if set to <see langword="true"/> [owner].</param>
protected class ShellItemImpl(Shell32.PIDL pidl, bool owner) : IDisposable, IShellItem
{
/// <summary>Initializes a new instance of the <see cref="ShellItemImpl"/> class.</summary>
/// <param name="pidl">The pidl.</param>
/// <param name="owner">if set to <see langword="true"/> [owner].</param>
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);
/// <summary>Binds to a handler for an item as specified by the handler ID value (BHID).</summary>
/// <param name="pbc">
@ -941,7 +939,7 @@ public class ShellItem : IComparable<ShellItem>, IDisposable, IEquatable<IShellI
{
var parentFolder = InternalGetParent().GetIShellFolder();
var result = sfgaoMask;
parentFolder.GetAttributesOf(1, new[] { (IntPtr)PIDL.LastId }, ref result).ThrowIfFailed();
parentFolder.GetAttributesOf(1, [(IntPtr)PIDL.LastId], ref result).ThrowIfFailed();
return result & sfgaoMask;
}
@ -1013,7 +1011,7 @@ internal class ShellItemTypeConverter : TypeConverter
}
else if (destinationType == typeof(InstanceDescriptor))
{
return new InstanceDescriptor(typeof(ShellItem).GetMethod("Open", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(string) }, null), new object[] { uri.ToString() });
return new InstanceDescriptor(typeof(ShellItem).GetMethod("Open", BindingFlags.Public | BindingFlags.Static, null, [typeof(string)], null), new object[] { uri.ToString() });
}
}
return base.ConvertTo(context, culture, value, destinationType);

View File

@ -8,13 +8,10 @@ using static Vanara.PInvoke.User32;
namespace Vanara.Windows.Shell;
/// <summary>Exposes methods that get images related to shell items.</summary>
public class ShellItemImages
/// <remarks>Initializes a new instance of the <see cref="ShellItemImages"/> class.</remarks>
/// <param name="shellItem">The <see cref="ShellItem"/> instance.</param>
public class ShellItemImages(ShellItem shellItem)
{
private readonly ShellItem shellItem;
/// <summary>Initializes a new instance of the <see cref="ShellItemImages"/> class.</summary>
/// <param name="shellItem">The <see cref="ShellItem"/> instance.</param>
public ShellItemImages(ShellItem shellItem) => this.shellItem = shellItem;
/// <summary>
/// Gets an image that represents this item. The default behavior is to load a thumbnail. If there is no thumbnail for the current

View File

@ -99,7 +99,7 @@ public class ShellLibrary : ShellFolder
/// <summary>Gets the set of child folders that are contained in the library.</summary>
/// <value>A <see cref="ShellItemArray"/> containing the child folders.</value>
public ShellLibraryFolders Folders => folders ?? (folders = GetFilteredFolders());
public ShellLibraryFolders Folders => folders ??= GetFilteredFolders();
/// <summary>
/// Gets or sets a string that describes the location of the default icon. The string must be formatted as

View File

@ -97,7 +97,7 @@ public class SearchCondition : ICloneable, IDisposable
/// <returns>The new <see cref="SearchCondition"/> node.</returns>
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<IQueryParser>(systemCatalog, (uint)cultureInfo.LCID));
qm.Item.InitializeOptions(false, true, qp.Item);

View File

@ -3,14 +3,12 @@
namespace Vanara.Windows.Shell;
/// <summary>Encapsulates an <see cref="IPropertyBag"/> instance.</summary>
public class PropertyBag
/// <remarks>Initializes a new instance of the <see cref="PropertyBag"/> class.</remarks>
/// <param name="ppb">The property bag.</param>
public class PropertyBag(PInvoke.OleAut32.IPropertyBag ppb)
{
/// <summary>The IPropertyBag instance.</summary>
protected readonly IPropertyBag ibag;
/// <summary>Initializes a new instance of the <see cref="PropertyBag"/> class.</summary>
/// <param name="ppb">The property bag.</param>
public PropertyBag(IPropertyBag ppb) => ibag = ppb;
protected readonly IPropertyBag ibag = ppb;
/// <summary>Gets or sets the <see cref="object"/> with the specified property name.</summary>
/// <value>The <see cref="object"/>.</value>

View File

@ -5,7 +5,12 @@ using static Vanara.PInvoke.User32;
namespace Vanara.Windows.Shell;
/// <summary>Represents a standard system icon.</summary>
public class StockIcon : IDisposable
/// <remarks>Creates a new StockIcon instance with the specified identifer and options.</remarks>
/// <param name="id">A value that identifies the icon represented by this instance.</param>
/// <param name="size">A value that indicates the size of the stock icon.</param>
/// <param name="isLinkOverlay">A bool value that indicates whether the icon has a link overlay.</param>
/// <param name="isSelected">A bool value that indicates whether the icon is in a selected state.</param>
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);
/// <summary>Creates a new StockIcon instance with the specified identifer and options.</summary>
/// <param name="id">A value that identifies the icon represented by this instance.</param>
/// <param name="size">A value that indicates the size of the stock icon.</param>
/// <param name="isLinkOverlay">A bool value that indicates whether the icon has a link overlay.</param>
/// <param name="isSelected">A bool value that indicates whether the icon is in a selected state.</param>
public StockIcon(SHSTOCKICONID id, ShellIconType size = ShellIconType.Large, bool isLinkOverlay = false, bool isSelected = false)
{
Identifier = id;
LinkOverlay = isLinkOverlay;
Selected = isSelected;
Size = size;
}
/// <summary>Finalizes an instance of the <see cref="StockIcon"/> class.</summary>
~StockIcon()
{
@ -39,11 +31,11 @@ public class StockIcon : IDisposable
public HICON IconHandle => hIcon;
/// <summary>Gets or sets the Stock Icon identifier associated with this icon.</summary>
public SHSTOCKICONID Identifier { get; set; }
public SHSTOCKICONID Identifier { get; set; } = id;
/// <summary>Gets or sets a value that cotrols whether to put a link overlay on the icon.</summary>
/// <value>A <see cref="bool"/> value.</value>
public bool LinkOverlay { get; set; }
public bool LinkOverlay { get; set; } = isLinkOverlay;
/// <summary>Gets the icon location, composed of a resource path and the icon's index.</summary>
/// <value>The icon location.</value>
@ -51,11 +43,11 @@ public class StockIcon : IDisposable
/// <summary>Gets or sets a value indicating whether the icon appears selected.</summary>
/// <value>A <see cref="bool"/> value.</value>
public bool Selected { get; set; }
public bool Selected { get; set; } = isSelected;
/// <summary>Gets or sets a value that controls the size of the Stock Icon.</summary>
/// <value>A <see cref="ShellIconType"/> value.</value>
public ShellIconType Size { get; set; }
public ShellIconType Size { get; set; } = size;
/// <summary>Gets the index of the image in the system icon cache.</summary>
/// <value>The index of the system image.</value>

View File

@ -266,18 +266,11 @@ public class JumpListSeparator : JumpListItem, IJumpListItem
/// <summary>A task for a jumplist.</summary>
/// <seealso cref="JumpListItem"/>
public class JumpListTask : JumpListItem, IJumpListItem
/// <remarks>Initializes a new instance of the <see cref="JumpListTask"/> class.</remarks>
public class JumpListTask(string? title, string applicationPath) : JumpListItem, IJumpListItem
{
private int iconResIdx = -1;
private string path;
private string? title, description, args, dir, iconPath, appUserModelID;
/// <summary>Initializes a new instance of the <see cref="JumpListTask"/> class.</summary>
public JumpListTask(string? title, string applicationPath)
{
this.title = title;
path = applicationPath;
}
private string? description, args, dir, iconPath, appUserModelID;
/// <summary>Gets or sets the application path.</summary>
/// <value>The application path.</value>
@ -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<T> : 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);

View File

@ -21,10 +21,13 @@ ChangeFilters, DialogStatus, ExecutableType, FileUsageType, FolderItemFilter, Li
</PropertyGroup>
<ItemGroup Condition=" $(TargetFramework.StartsWith('net4')) ">
<Reference Include="System.Configuration" />
<Reference Include="System.Drawing" />
</ItemGroup>
<ItemGroup Condition=" !$(TargetFramework.StartsWith('net4')) ">
<PackageReference Include="System.Configuration.ConfigurationManager" Version="6.0.1" Condition=" '$(TargetFramework)' == 'netcoreapp3.1' Or '$(TargetFramework)' == 'net5.0' " />
<PackageReference Include="System.Configuration.ConfigurationManager" Version="8.0.0" Condition=" '$(TargetFramework)' != 'netcoreapp3.1' And '$(TargetFramework)' != 'net5.0' " />
<PackageReference Include="System.Drawing.Common" Version="6.0.0" Condition=" $(TargetFramework.StartsWith('netcore')) Or $(TargetFramework.StartsWith('net5')) " />
<PackageReference Include="System.Drawing.Common" Version="8.0.5" Condition=" !$(TargetFramework.StartsWith('netcore')) And !$(TargetFramework.StartsWith('net5')) " />
<PackageReference Include="System.IO.FileSystem.AccessControl" Version="5.0.0" Condition=" $(TargetFramework.StartsWith('netcore')) Or $(TargetFramework.StartsWith('netstandard')) Or $(TargetFramework.StartsWith('net5')) Or $(TargetFramework.StartsWith('net6')) Or $(TargetFramework.StartsWith('net7')) " />
<PackageReference Include="System.Security.Permissions" Version="6.0.0" Condition=" '$(TargetFramework)' == 'netcoreapp3.1' Or '$(TargetFramework)' == 'net5.0' " />
<PackageReference Include="System.Security.Permissions" Version="8.0.0" Condition=" '$(TargetFramework)' != 'netcoreapp3.1' And '$(TargetFramework)' != 'net5.0' " />
@ -35,6 +38,7 @@ ChangeFilters, DialogStatus, ExecutableType, FileUsageType, FolderItemFilter, Li
<ProjectReference Include="..\PInvoke\Ole\Vanara.PInvoke.Ole.csproj" />
<ProjectReference Include="..\PInvoke\Shell32\Vanara.PInvoke.Shell32.csproj" />
<ProjectReference Include="..\PInvoke\SearchApi\Vanara.PInvoke.SearchApi.csproj" />
<ProjectReference Include="..\Windows.Extensions\Vanara.Windows.Extensions.csproj" />
</ItemGroup>
<ItemGroup>
<None Remove="~AppBar.cs" />