Vanara/Windows.Shell.Common/ShellFileOperations/ShellFileOperations.cs

519 lines
27 KiB
C#

using System.Collections.Generic;
using System.ComponentModel;
using Vanara.PInvoke;
using static Vanara.PInvoke.Shell32;
namespace Vanara.Windows.Shell;
/// <summary>Queued and static file operations using the Shell.</summary>
/// <seealso cref="IDisposable"/>
public partial class ShellFileOperations : IDisposable
{
private const OperationFlags defaultOptions = OperationFlags.AllowUndo | OperationFlags.NoConfirmMkDir;
private readonly IFileOperationProgressSink sink;
private readonly uint sinkCookie;
private ShellFileOperationDialog? customProgressDialog;
private bool disposedValue = false;
private IFileOperation op;
private OperationFlags opFlags = defaultOptions;
private HWND owner;
/// <summary>Initializes a new instance of the <see cref="ShellFileOperations"/> class.</summary>
/// <param name="owner">The window that owns the modal dialog. This value can be <see langword="null"/>.</param>
public ShellFileOperations(HWND owner = default)
{
op = new IFileOperation();
if (owner != default)
{
op.SetOwnerWindow(owner);
}
sink = new OpSink(this);
sinkCookie = op.Advise(sink);
}
/// <summary>Initializes a new instance of the <see cref="ShellFileOperations"/> class.</summary>
/// <param name="operation">An existing <see cref="IFileOperation"/> instance.</param>
public ShellFileOperations(IFileOperation operation)
{
op = operation;
sink = new OpSink(this);
sinkCookie = op.Advise(sink);
}
/// <summary>Finalizes an instance of the <see cref="ShellFileOperations"/> class.</summary>
~ShellFileOperations()
{
Dispose(false);
}
/// <summary>Performs caller-implemented actions after the last operation performed by the call to IFileOperation is complete.</summary>
public event EventHandler<ShellFileOpEventArgs>? FinishOperations;
/// <summary>Performs caller-implemented actions after the copy process for each item is complete.</summary>
public event EventHandler<ShellFileOpEventArgs>? PostCopyItem;
/// <summary>Performs caller-implemented actions after the delete process for each item is complete.</summary>
public event EventHandler<ShellFileOpEventArgs>? PostDeleteItem;
/// <summary>Performs caller-implemented actions after the move process for each item is complete.</summary>
public event EventHandler<ShellFileOpEventArgs>? PostMoveItem;
/// <summary>Performs caller-implemented actions after the new item is created.</summary>
public event EventHandler<ShellFileNewOpEventArgs>? PostNewItem;
/// <summary>Performs caller-implemented actions after the rename process for each item is complete.</summary>
public event EventHandler<ShellFileOpEventArgs>? PostRenameItem;
/// <summary>Performs caller-implemented actions before the copy process for each item begins.</summary>
public event EventHandler<ShellFileOpEventArgs>? PreCopyItem;
/// <summary>Performs caller-implemented actions before the delete process for each item begins.</summary>
public event EventHandler<ShellFileOpEventArgs>? PreDeleteItem;
/// <summary>Performs caller-implemented actions before the move process for each item begins.</summary>
public event EventHandler<ShellFileOpEventArgs>? PreMoveItem;
/// <summary>Performs caller-implemented actions before the process to create a new item begins.</summary>
public event EventHandler<ShellFileOpEventArgs>? PreNewItem;
/// <summary>Performs caller-implemented actions before the rename process for each item begins.</summary>
public event EventHandler<ShellFileOpEventArgs>? PreRenameItem;
/// <summary>Performs caller-implemented actions before any specific file operations are performed.</summary>
public event EventHandler? StartOperations;
/// <summary>Occurs when a progress update is received.</summary>
public event ProgressChangedEventHandler? UpdateProgress;
/// <summary>
/// Gets a value that states whether any file operations initiated by a call to <see cref="PerformOperations"/> were stopped before they
/// were complete. The operations could be stopped either by user action or silently by the system.
/// </summary>
/// <value><see langword="true"/> if any file operations were aborted before they were complete; otherwise, <see langword="false"/>.</value>
public bool AnyOperationsAborted => op.GetAnyOperationsAborted();
/// <summary>Specifies a dialog box used to display the progress of the operation.</summary>
/// <value>A ShellFileOperationDialog object that represents the dialog box.</value>
public ShellFileOperationDialog? CustomProgressDialog
{
get => customProgressDialog;
set => op.SetProgressDialog((customProgressDialog = value)?.iProgressDialog);
}
/// <summary>Gets or sets options that control file operations.</summary>
public OperationFlags Options
{
get => opFlags;
set { if (value == opFlags) { return; } op.SetOperationFlags((FILEOP_FLAGS)(opFlags = value)); }
}
/// <summary>Gets or sets the parent or owner window for progress and dialog windows.</summary>
/// <value>The owner window of the operation. This window will receive error messages.</value>
public HWND OwnerWindow
{
get => owner;
set => op.SetOwnerWindow(owner = value);
}
/// <summary>Gets the number of queued operations.</summary>
public int QueuedOperations { get; protected set; }
/// <summary>Copies a single item to a specified destination using the Shell to provide progress and error dialogs.</summary>
/// <param name="source">A string that specifies the source item's full file path.</param>
/// <param name="dest">A string that specifies the full path of the destination folder to contain the copy of the item.</param>
/// <param name="newName">
/// An optional new name for the item after it has been copied. This can be <see langword="null"/>. If <see langword="null"/>, the name
/// of the destination item is the same as the source.
/// </param>
/// <param name="options">Options that control file operations.</param>
public static void Copy(string source, string dest, string? newName = null, OperationFlags options = defaultOptions)
{
using ShellItem shfile = new(source);
using ShellFolder shfld = new(dest);
Copy(shfile, shfld, newName, options);
}
/// <summary>Copies a single item to a specified destination using the Shell to provide progress and error dialogs.</summary>
/// <param name="source">A <see cref="ShellItem"/> that specifies the source item.</param>
/// <param name="dest">A <see cref="ShellFolder"/> that specifies the destination folder to contain the copy of the item.</param>
/// <param name="newName">
/// An optional new name for the item after it has been copied. This can be <see langword="null"/>. If <see langword="null"/>, the name
/// of the destination item is the same as the source.
/// </param>
/// <param name="options">Options that control file operations.</param>
public static void Copy(ShellItem source, ShellFolder dest, string? newName = null, OperationFlags options = defaultOptions) =>
SHOp(options, s => s.PostCopyItem, s => s.QueueCopyOperation(source, dest, newName));
/// <summary>Copies a set of items to a specified destination using the Shell to provide progress and error dialogs.</summary>
/// <param name="sourceItems">
/// An <see cref="IEnumerable{T}"/> of <see cref="ShellItem"/> instances that represent the group of items to be copied.
/// </param>
/// <param name="dest">A <see cref="ShellFolder"/> that specifies the destination folder to contain the copy of the items.</param>
/// <param name="options">Options that control file operations.</param>
public static void Copy(IEnumerable<ShellItem> sourceItems, ShellFolder dest, OperationFlags options = defaultOptions) =>
SHOp(options, s => s.PostCopyItem, s => s.QueueCopyOperation(sourceItems, dest));
/// <summary>Deletes a single item using the Shell to provide progress and error dialogs.</summary>
/// <param name="source">A string that specifies the full path of the item to be deleted.</param>
/// <param name="options">Options that control file operations.</param>
public static void Delete(string source, OperationFlags options = defaultOptions)
{
using ShellItem shfile = new(source);
Delete(shfile, options);
}
/// <summary>Deletes a single item using the Shell to provide progress and error dialogs.</summary>
/// <param name="source">A <see cref="ShellItem"/> that specifies the item to be deleted.</param>
/// <param name="options">Options that control file operations.</param>
public static void Delete(ShellItem source, OperationFlags options = defaultOptions) =>
SHOp(options, s => s.PostDeleteItem, s => s.QueueDeleteOperation(source));
/// <summary>Deletes a set of items using the Shell to provide progress and error dialogs.</summary>
/// <param name="sourceItems">
/// An <see cref="IEnumerable{T}"/> of <see cref="ShellItem"/> instances which represents the group of items to be deleted.
/// </param>
/// <param name="options">Options that control file operations.</param>
public static void Delete(IEnumerable<ShellItem> sourceItems, OperationFlags options = defaultOptions) =>
SHOp(options, s => s.PostDeleteItem, s => s.QueueDeleteOperation(sourceItems));
/// <summary>Moves a single item to a specified destination using the Shell to provide progress and error dialogs.</summary>
/// <param name="source">A string that specifies the source item's full file path.</param>
/// <param name="dest">A string that specifies the full path of the destination folder to contain the copy of the item.</param>
/// <param name="newName">
/// An optional new name for the item in its new location. This can be <see langword="null"/>. If <see langword="null"/>, the name of the
/// destination item is the same as the source.
/// </param>
/// <param name="options">Options that control file operations.</param>
public static void Move(string source, string dest, string? newName = null, OperationFlags options = defaultOptions)
{
using ShellItem shfile = new(source);
using ShellFolder shfld = new(dest);
Move(shfile, shfld, newName, options);
}
/// <summary>Moves a single item to a specified destination using the Shell to provide progress and error dialogs.</summary>
/// <param name="source">A <see cref="ShellItem"/> that specifies the source item.</param>
/// <param name="dest">A <see cref="ShellFolder"/> that specifies the destination folder to contain the moved item.</param>
/// <param name="newName">
/// An optional new name for the item in its new location. This can be <see langword="null"/>. If <see langword="null"/>, the name of the
/// destination item is the same as the source.
/// </param>
/// <param name="options">Options that control file operations.</param>
public static void Move(ShellItem source, ShellFolder dest, string? newName = null, OperationFlags options = defaultOptions) =>
SHOp(options, s => s.PostMoveItem, s => s.QueueMoveOperation(source, dest, newName));
/// <summary>Moves a set of items to a specified destination using the Shell to provide progress and error dialogs.</summary>
/// <param name="sourceItems">
/// An <see cref="IEnumerable{T}"/> of <see cref="ShellItem"/> instances which represents the group of items to be moved.
/// </param>
/// <param name="dest">A <see cref="ShellFolder"/> that specifies the destination folder to contain the moved items.</param>
/// <param name="options">Options that control file operations.</param>
public static void Move(IEnumerable<ShellItem> sourceItems, ShellFolder dest, OperationFlags options = defaultOptions) =>
SHOp(options, s => s.PostMoveItem, s => s.QueueMoveOperation(sourceItems, dest));
/// <summary>Creates a new item in a specified location using the Shell to provide progress and error dialogs.</summary>
/// <param name="dest">A <see cref="ShellItem"/> that specifies the destination folder that will contain the new item.</param>
/// <param name="name">The file name of the new item, for instance Newfile.txt.</param>
/// <param name="attr">A value that specifies the file system attributes for the file or folder.</param>
/// <param name="template">
/// The name of the template file (for example Excel9.xls) that the new item is based on, stored in one of the following locations:
/// <list type="bullet">
/// <item>
/// <description>CSIDL_COMMON_TEMPLATES. The default path for this folder is %ALLUSERSPROFILE%\Templates.</description>
/// </item>
/// <item>
/// <description>CSIDL_TEMPLATES. The default path for this folder is %USERPROFILE%\Templates.</description>
/// </item>
/// <item>
/// <description>%SystemRoot%\shellnew</description>
/// </item>
/// </list>
/// <para>
/// This is a string used to specify an existing file of the same type as the new file, containing the minimal content that an
/// application wants to include in any new file.
/// </para>
/// <para>This parameter is normally <see langword="null"/> to specify a new, blank file.</para>
/// </param>
/// <param name="options">Options that control file operations.</param>
public static void NewItem(ShellFolder dest, string name, System.IO.FileAttributes attr = System.IO.FileAttributes.Normal, string? template = null, OperationFlags options = defaultOptions) =>
SHOp(options, s => s.PostNewItem, s => s.QueueNewItemOperation(dest, name, attr, template));
/// <summary>Renames a single item to a new display name using the Shell to provide progress and error dialogs.</summary>
/// <param name="source">A <see cref="ShellItem"/> that specifies the source item.</param>
/// <param name="newName">The new display name of the item.</param>
/// <param name="options">Options that control file operations.</param>
public static void Rename(ShellItem source, string newName, OperationFlags options = defaultOptions) =>
SHOp(options, s => s.PostRenameItem, s => s.QueueRenameOperation(source, newName));
/// <summary>
/// Renames a set of items that are to be given a new display name using the Shell to provide progress and error dialogs. All items are
/// given the same name.
/// </summary>
/// <param name="sourceItems">
/// An <see cref="IEnumerable{T}"/> of <see cref="ShellItem"/> instances which represents the group of items to be renamed.
/// </param>
/// <param name="newName">The new display name of the items.</param>
/// <param name="options">Options that control file operations.</param>
public static void Rename(IEnumerable<ShellItem> sourceItems, string newName, OperationFlags options = defaultOptions) =>
SHOp(options, s => s.PostRenameItem, s => s.QueueRenameOperation(sourceItems, newName));
/// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>Executes all selected operations.</summary>
/// <remarks>
/// This method is called last to execute those actions that have been specified earlier by calling their individual methods. For
/// instance, <see cref="QueueRenameOperation(ShellItem, string)"/> does not rename the item, it simply sets the parameters. The actual
/// renaming is done when you call PerformOperations.
/// </remarks>
public void PerformOperations()
{
op.PerformOperations();
QueuedOperations = 0;
}
/// <summary>Declares a set of properties and values to be set on an item.</summary>
/// <param name="item">The item to receive the new property values.</param>
/// <param name="props">
/// An <see cref="ShellItemPropertyUpdates"/>, which contains a dictionary of objects that specify the properties to be set and their new values.
/// </param>
public void QueueApplyPropertiesOperation(ShellItem item, ShellItemPropertyUpdates props)
{
op.SetProperties(props.IPropertyChangeArray);
op.ApplyPropertiesToItem(item.IShellItem);
QueuedOperations++;
}
/// <summary>Declares a set of properties and values to be set on a set of items.</summary>
/// <param name="items">
/// An <see cref="IEnumerable{T}"/> of <see cref="ShellItem"/> instances that represent the group of items to which to apply the properties.
/// </param>
/// <param name="props">
/// An <see cref="ShellItemPropertyUpdates"/>, which contains a dictionary of objects that specify the properties to be set and their new values.
/// </param>
public void QueueApplyPropertiesOperation(IEnumerable<ShellItem> items, ShellItemPropertyUpdates props)
{
op.SetProperties(props.IPropertyChangeArray);
op.ApplyPropertiesToItems(GetSHArray(items).IShellItemArray!);
QueuedOperations++;
}
/// <summary>Declares a single item that is to be copied to a specified destination.</summary>
/// <param name="source">A <see cref="ShellItem"/> that specifies the source item.</param>
/// <param name="dest">A <see cref="ShellFolder"/> that specifies the destination folder to contain the copy of the item.</param>
/// <param name="newName">
/// An optional new name for the item after it has been copied. This can be <see langword="null"/>. If <see langword="null"/>, the name
/// of the destination item is the same as the source.
/// </param>
public void QueueCopyOperation(ShellItem source, ShellFolder dest, string? newName = null)
{
op.CopyItem(source.IShellItem, dest.IShellItem, newName, null);
QueuedOperations++;
}
/// <summary>Declares a set of items that are to be copied to a specified destination.</summary>
/// <param name="sourceItems">
/// An <see cref="IEnumerable{T}"/> of <see cref="ShellItem"/> instances that represent the group of items to be copied.
/// </param>
/// <param name="dest">A <see cref="ShellFolder"/> that specifies the destination folder to contain the copy of the items.</param>
public void QueueCopyOperation(IEnumerable<ShellItem> sourceItems, ShellFolder dest)
{
op.CopyItems(GetSHArray(sourceItems).IShellItemArray!, dest.IShellItem);
QueuedOperations++;
}
/// <summary>Declares a single item that is to be deleted.</summary>
/// <param name="item">&gt;A <see cref="ShellItem"/> that specifies the item to be deleted.</param>
public void QueueDeleteOperation(ShellItem item)
{
op.DeleteItem(item.IShellItem, null);
QueuedOperations++;
}
/// <summary>Declares a set of items that are to be deleted.</summary>
/// <param name="items">
/// An <see cref="IEnumerable{T}"/> of <see cref="ShellItem"/> instances which represents the group of items to be deleted.
/// </param>
public void QueueDeleteOperation(IEnumerable<ShellItem> items)
{
op.DeleteItems(GetSHArray(items).IShellItemArray!);
QueuedOperations++;
}
/// <summary>Declares a single item that is to be moved to a specified destination.</summary>
/// <param name="source">A <see cref="ShellItem"/> that specifies the source item.</param>
/// <param name="dest">A <see cref="ShellFolder"/> that specifies the destination folder to contain the moved item.</param>
/// <param name="newName">
/// An optional new name for the item in its new location. This can be <see langword="null"/>. If <see langword="null"/>, the name of the
/// destination item is the same as the source.
/// </param>
public void QueueMoveOperation(ShellItem source, ShellFolder dest, string? newName = null)
{
op.MoveItem(source.IShellItem, dest.IShellItem, newName, null);
QueuedOperations++;
}
/// <summary>Declares a set of items that are to be moved to a specified destination.</summary>
/// <param name="sourceItems">
/// An <see cref="IEnumerable{T}"/> of <see cref="ShellItem"/> instances which represents the group of items to be moved.
/// </param>
/// <param name="dest">A <see cref="ShellFolder"/> that specifies the destination folder to contain the moved items.</param>
public void QueueMoveOperation(IEnumerable<ShellItem> sourceItems, ShellFolder dest)
{
op.MoveItems(GetSHArray(sourceItems).IShellItemArray!, dest.IShellItem);
QueuedOperations++;
}
/// <summary>Declares a new item that is to be created in a specified location.</summary>
/// <param name="dest">A <see cref="ShellItem"/> that specifies the destination folder that will contain the new item.</param>
/// <param name="name">The file name of the new item, for instance Newfile.txt.</param>
/// <param name="attr">A value that specifies the file system attributes for the file or folder.</param>
/// <param name="template">
/// The name of the template file (for example Excel9.xls) that the new item is based on, stored in one of the following locations:
/// <list type="bullet">
/// <item>
/// <description>CSIDL_COMMON_TEMPLATES. The default path for this folder is %ALLUSERSPROFILE%\Templates.</description>
/// </item>
/// <item>
/// <description>CSIDL_TEMPLATES. The default path for this folder is %USERPROFILE%\Templates.</description>
/// </item>
/// <item>
/// <description>%SystemRoot%\shellnew</description>
/// </item>
/// </list>
/// <para>
/// This is a string used to specify an existing file of the same type as the new file, containing the minimal content that an
/// application wants to include in any new file.
/// </para>
/// <para>This parameter is normally <see langword="null"/> to specify a new, blank file.</para>
/// </param>
public void QueueNewItemOperation(ShellFolder dest, string name, System.IO.FileAttributes attr = System.IO.FileAttributes.Normal, string? template = null)
{
op.NewItem(dest.IShellItem, attr, name, template, null);
QueuedOperations++;
}
/// <summary>Declares a single item that is to be given a new display name.</summary>
/// <param name="source">A <see cref="ShellItem"/> that specifies the source item.</param>
/// <param name="newName">The new display name of the item.</param>
public void QueueRenameOperation(ShellItem source, string newName)
{
op.RenameItem(source.IShellItem, newName, null);
QueuedOperations++;
}
/// <summary>Declares a set of items that are to be given a new display name. All items are given the same name.</summary>
/// <param name="sourceItems">
/// An <see cref="IEnumerable{T}"/> of <see cref="ShellItem"/> instances which represents the group of items to be renamed.
/// </param>
/// <param name="newName">The new display name of the items.</param>
public void QueueRenameOperation(IEnumerable<ShellItem> sourceItems, string newName)
{
op.RenameItems(GetSHArray(sourceItems).IShellItemArray!, newName);
QueuedOperations++;
}
/// <summary>Releases unmanaged and - optionally - managed resources.</summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
// Dispose managed state (managed objects).
}
if (sink != null)
{
op.Unadvise(sinkCookie);
}
disposedValue = true;
}
}
private static void SHOp<T>(OperationFlags options, Func<ShellFileOperations, EventHandler<T>?> evt, Action<ShellFileOperations> action)
where T : ShellFileOpEventArgs
{
using ShellFileOperations sop = new() { Options = options };
HRESULT hr = HRESULT.S_OK;
EventHandler<T>? eh = evt(sop);
eh += OnPost;
try
{
action(sop);
sop.PerformOperations();
hr.ThrowIfFailed();
}
finally
{
eh -= OnPost;
}
void OnPost(object? sender, T e) => hr = e.Result;
}
private ShellItemArray GetSHArray(IEnumerable<ShellItem> items) => items is ShellItemArray a ? a : new ShellItemArray(items);
private class OpSink(ShellFileOperations ops) : IFileOperationProgressSink
{
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(() => 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(() => 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(() => 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(() => 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(() => 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(() => ops.PreCopyItem?.Invoke(ops, new ShellFileOpEventArgs(dwFlags, psiItem, psiDestinationFolder, null, pszNewName)));
public HRESULT PreDeleteItem(TRANSFER_SOURCE_FLAGS dwFlags, IShellItem 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(() => 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(() => 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(() => 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(() => ops.StartOperations?.Invoke(ops, EventArgs.Empty));
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)
{
try { action(); }
catch (COMException comex) { return comex.ErrorCode; }
catch (Win32Exception w32ex) { return new Win32Error(unchecked((uint)w32ex.NativeErrorCode)).ToHRESULT(); }
catch (Exception e)
{
return e.HResult;
}
return HRESULT.S_OK;
}
}
}