using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Vanara.Collections;
using Vanara.InteropServices;
using Vanara.PInvoke;
using static Vanara.PInvoke.Ole32;
using static Vanara.PInvoke.PropSys;
using static Vanara.PInvoke.Shell32;
namespace Vanara.Windows.Shell
{
/// Queued and static file operations using the Shell.
///
public class ShellFileOperations : IDisposable
{
private const OperationFlags defaultOptions = OperationFlags.AllowUndo | OperationFlags.NoConfirmMkDir;
private bool disposedValue = false;
private IFileOperation op;
private OperationFlags opFlags = defaultOptions;
private IFileOperationProgressSink sink;
private uint sinkCookie;
/// Initializes a new instance of the class.
/// The window that owns the modal dialog. This value can be .
public ShellFileOperations(System.Windows.Forms.IWin32Window owner = null)
{
op = new IFileOperation();
if (owner != null) op.SetOwnerWindow(owner.Handle);
sink = new OpSink(this);
sinkCookie = op.Advise(sink);
}
/// Finalizes an instance of the class.
~ShellFileOperations()
{
Dispose(false);
}
/// Performs caller-implemented actions after the last operation performed by the call to IFileOperation is complete.
///
/// The return value of the final operation. Note that this is not the HRESULT returned by one of the IFileOperation methods, which simply queue the
/// operations. Instead, this is the result of the actual operation, such as copy, delete, or move.
///
public event EventHandler FinishOperations;
/// Performs caller-implemented actions after the copy process for each item is complete.
///
/// bitwise value that contains flags that were used during the copy operation. Some values can be set or changed during the copy operation. See
/// TRANSFER_SOURCE_FLAGS for flag descriptions.
///
/// Pointer to an IShellItem that specifies the source item.
/// Pointer to an IShellItem that specifies the destination folder to which the item was copied.
///
/// Pointer to the new name that was given to the item after it was copied. This is a null-terminated Unicode string. Note that this might not be the
/// name that you asked for, given collisions and other naming rules.
///
///
/// The return value of the copy operation. Note that this is not the HRESULT returned by CopyItem, which simply queues the copy operation. Instead, this
/// is the result of the actual copy.
///
/// Pointer to an IShellItem that represents the new copy of the item.
public event EventHandler PostCopyItem;
/// Performs caller-implemented actions after the delete process for each item is complete.
///
/// bitwise value that contains flags that were used during the delete operation. Some values can be set or changed during the delete operation. See
/// TRANSFER_SOURCE_FLAGS for flag descriptions.
///
/// Pointer to an IShellItem that specifies the item that was deleted.
///
/// The return value of the delete operation. Note that this is not the HRESULT returned by DeleteItem, which simply queues the delete operation.
/// Instead, this is the result of the actual deletion.
///
///
/// A pointer to an IShellItem that specifies the deleted item, now in the Recycle Bin. If the item was fully deleted, this value is NULL.
///
public event EventHandler PostDeleteItem;
/// Performs caller-implemented actions after the move process for each item is complete.
///
/// bitwise value that contains flags that were used during the move operation. Some values can be set or changed during the move operation. See
/// TRANSFER_SOURCE_FLAGS for flag descriptions.
///
/// Pointer to an IShellItem that specifies the source item.
/// Pointer to an IShellItem that specifies the destination folder that contains the moved item.
///
/// Pointer to the name that was given to the item after it was moved. This is a null-terminated Unicode string. Note that this might not be the name
/// that you asked for, given collisions and other naming rules.
///
///
/// The return value of the move operation. Note that this is not the HRESULT returned by MoveItem, which simply queues the move operation. Instead, this
/// is the result of the actual move.
///
/// Pointer to an IShellItem that represents the moved item in its new location.
public event EventHandler PostMoveItem;
/// Performs caller-implemented actions after the new item is created.
///
/// bitwise value that contains flags that were used during the creation operation. Some values can be set or changed during the creation operation. See
/// TRANSFER_SOURCE_FLAGS for flag descriptions.
///
/// Pointer to an IShellItem that specifies the destination folder to which the new item was added.
/// Pointer to the file name of the new item, for instance Newfile.txt. This is a null-terminated, Unicode string.
///
/// Pointer to the name of the template file (for example Excel9.xls) that the new item is based on, stored in one of the following locations:
///
///
/// CSIDL_COMMON_TEMPLATES. The default path for this folder is %ALLUSERSPROFILE%\Templates.
///
///
/// CSIDL_TEMPLATES. The default path for this folder is %USERPROFILE%\Templates.
///
///
/// %SystemRoot%\shellnew
///
///
///
/// This is a null-terminated, Unicode 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.
///
/// This parameter is normally NULL to specify a new, blank file.
///
/// The file attributes applied to the new item. One or more of the values found at GetFileAttributes.
///
/// The return value of the creation operation. Note that this is not the HRESULT returned by NewItem, which simply queues the creation operation.
/// Instead, this is the result of the actual creation.
///
/// Pointer to an IShellItem that represents the new item.
public event EventHandler PostNewItem;
/// Performs caller-implemented actions after the rename process for each item is complete.
///
/// bitwise value that contains flags that were used during the rename operation. Some values can be set or changed during the rename operation. See
/// TRANSFER_SOURCE_FLAGS for flag descriptions.
///
/// Pointer to an IShellItem that specifies the item before it was renamed.
///
/// Pointer to the new display name of the item. This is a null-terminated, Unicode string. Note that this might not be the name that you asked for,
/// given collisions and other naming rules.
///
///
/// The return value of the rename operation. Note that this is not the HRESULT returned by RenameItem, which simply queues the rename operation.
/// Instead, this is the result of the actual rename operation.
///
/// Pointer to an IShellItem that represents the item with its new name.
public event EventHandler PostRenameItem;
/// Performs caller-implemented actions before the copy process for each item begins.
/// bitwise value that contains flags that control the operation. See TRANSFER_SOURCE_FLAGS for flag descriptions.
/// Pointer to an IShellItem that specifies the source item.
/// Pointer to an IShellItem that specifies the destination folder to contain the copy of the item.
///
/// Pointer to a new name for the item after it has been copied. This is a null-terminated Unicode string and can be NULL. If NULL, the name of the
/// destination item is the same as the source.
///
public event EventHandler PreCopyItem;
/// Performs caller-implemented actions before the delete process for each item begins.
/// bitwise value that contains flags that control the operation. See TRANSFER_SOURCE_FLAGS for flag descriptions.
/// Pointer to an IShellItem that specifies the item to be deleted.
public event EventHandler PreDeleteItem;
/// Performs caller-implemented actions before the move process for each item begins.
/// bitwise value that contains flags that control the operation. See TRANSFER_SOURCE_FLAGS for flag descriptions.
/// Pointer to an IShellItem that specifies the item to be moved.
/// Pointer to an IShellItem that specifies the destination folder to contain the moved item.
///
/// Pointer to a new name for the item in its new location. This is a null-terminated Unicode string and can be NULL. If NULL, the name of the
/// destination item is the same as the source.
///
public event EventHandler PreMoveItem;
/// Performs caller-implemented actions before the process to create a new item begins.
/// bitwise value that contains flags that control the operation. See TRANSFER_SOURCE_FLAGS for flag descriptions.
/// Pointer to an IShellItem that specifies the destination folder that will contain the new item.
/// Pointer to the file name of the new item, for instance Newfile.txt. This is a null-terminated, Unicode string.
public event EventHandler PreNewItem;
/// Performs caller-implemented actions before the rename process for each item begins.
/// bitwise value that contains flags that control the operation. See TRANSFER_SOURCE_FLAGS for flag descriptions.
/// Pointer to an IShellItem that specifies the item to be renamed.
/// Pointer to the new display name of the item. This is a null-terminated, Unicode string.
public event EventHandler PreRenameItem;
/// Performs caller-implemented actions before any specific file operations are performed.
public event EventHandler StartOperations;
/// Updates the progress.
/// The i work total.
/// The i work so far.
public event System.ComponentModel.ProgressChangedEventHandler UpdateProgress;
/// Flags that control the file operation.
[Flags]
public enum OperationFlags : uint
{
///
/// The pTo member specifies multiple destination files (one for each source file in pFrom) rather than one directory where all source files are to
/// be deposited.
///
MultiDestFiles = FILEOP_FLAGS.FOF_MULTIDESTFILES,
/// Do not display a progress dialog box.
Silent = FILEOP_FLAGS.FOF_SILENT,
/// Give the item being operated on a new name in a move, copy, or rename operation if an item with the target name already exists.
RenameOnCollision = FILEOP_FLAGS.FOF_RENAMEONCOLLISION,
/// Respond with Yes to All for any dialog box that is displayed.
NoConfirmation = FILEOP_FLAGS.FOF_NOCONFIRMATION,
///
/// If FOF_RENAMEONCOLLISION is specified and any files were renamed, assign a name mapping object that contains their old and new names to the
/// hNameMappings member. This object must be freed using SHFreeNameMappings when it is no longer needed.
///
WantMappingHandle = FILEOP_FLAGS.FOF_WANTMAPPINGHANDLE,
///
/// Preserve undo information, if possible.
/// Prior to Windows Vista, operations could be undone only from the same process that performed the original operation.
///
/// In Windows Vista and later systems, the scope of the undo is a user session. Any process running in the user session can undo another operation.
/// The undo state is held in the Explorer.exe process, and as long as that process is running, it can coordinate the undo functions.
///
/// If the source file parameter does not contain fully qualified path and file names, this flag is ignored.
///
AllowUndo = FILEOP_FLAGS.FOF_ALLOWUNDO,
/// Perform the operation only on files (not on folders) if a wildcard file name (*.*) is specified.
FilesOnly = FILEOP_FLAGS.FOF_FILESONLY,
/// Display a progress dialog box but do not show individual file names as they are operated on.
SimpleProgress = FILEOP_FLAGS.FOF_SIMPLEPROGRESS,
/// Do not confirm the creation of a new folder if the operation requires one to be created.
NoConfirmMkDir = FILEOP_FLAGS.FOF_NOCONFIRMMKDIR,
///
/// Do not display a message to the user if an error occurs. If this flag is set without FOFX_EARLYFAILURE, any error is treated as if the user had
/// chosen Ignore or Continue in a dialog box. It halts the current action, sets a flag to indicate that an action was aborted, and proceeds with the
/// rest of the operation.
///
NoErrorUI = FILEOP_FLAGS.FOF_NOERRORUI,
/// Do not copy the security attributes of the item.
NoCopySecurityAttribs = FILEOP_FLAGS.FOF_NOCOPYSECURITYATTRIBS,
/// Only operate in the local folder. Do not operate recursively into subdirectories.
NoRecursion = FILEOP_FLAGS.FOF_NORECURSION,
/// Do not move connected items as a group. Only move the specified files.
NoConnectedElements = FILEOP_FLAGS.FOF_NO_CONNECTED_ELEMENTS,
///
/// Send a warning if a file or folder is being destroyed during a delete operation rather than recycled. This flag partially overrides FOF_NOCONFIRMATION.
///
WantNukeWarning = FILEOP_FLAGS.FOF_WANTNUKEWARNING,
///
/// Walk into Shell namespace junctions. By default, junctions are not entered. For more information on junctions, see Specifying a Namespace
/// Extension's Location.
///
NoSkipJunctions = FILEOP_FLAGS.FOFX_NOSKIPJUNCTIONS,
/// If possible, create a hard link rather than a new instance of the file in the destination.
PreferHardLink = FILEOP_FLAGS.FOFX_PREFERHARDLINK,
/// If an operation requires elevated rights and the FOF_NOERRORUI flag is set to disable error UI, display a UAC UI prompt nonetheless.
ShowElevationPrompt = FILEOP_FLAGS.FOFX_SHOWELEVATIONPROMPT,
///
/// If FOFX_EARLYFAILURE is set together with FOF_NOERRORUI, the entire set of operations is stopped upon encountering any error in any operation.
/// This flag is valid only when FOF_NOERRORUI is set.
///
EarlyFailure = FILEOP_FLAGS.FOFX_EARLYFAILURE,
///
/// Rename collisions in such a way as to preserve file name extensions. This flag is valid only when FOF_RENAMEONCOLLISION is also set.
///
PreserveFileExtensions = FILEOP_FLAGS.FOFX_PRESERVEFILEEXTENSIONS,
///
/// Keep the newer file or folder, based on the Date Modified property, if a collision occurs. This is done automatically with no prompt UI presented
/// to the user.
///
KeepNewerFile = FILEOP_FLAGS.FOFX_KEEPNEWERFILE,
/// Do not use copy hooks.
NoCopyHooks = FILEOP_FLAGS.FOFX_NOCOPYHOOKS,
/// Do not allow the progress dialog to be minimized.
NoMinimizeBox = FILEOP_FLAGS.FOFX_NOMINIMIZEBOX,
///
/// Copy the security attributes of the source item to the destination item when performing a cross-volume move operation. Without this flag, the
/// destination item receives the security attributes of its new folder.
///
MoveACLsAcrossVolumes = FILEOP_FLAGS.FOFX_MOVEACLSACROSSVOLUMES,
/// Do not display the path of the source item in the progress dialog.
DontDisplaySourcePath = FILEOP_FLAGS.FOFX_DONTDISPLAYSOURCEPATH,
/// Do not display the path of the destination item in the progress dialog.
DontDisplayDestPath = FILEOP_FLAGS.FOFX_DONTDISPLAYDESTPATH,
///
/// Introduced in Windows Vista SP1. The user expects a requirement for rights elevation, so do not display a dialog box asking for a confirmation of
/// the elevation.
///
RequireElevation = FILEOP_FLAGS.FOFX_REQUIREELEVATION,
/// Introduced in Windows 8. The file operation was user-invoked and should be placed on the undo stack. This flag is preferred to FOF_ALLOWUNDO.
AddUndoRecord = FILEOP_FLAGS.FOFX_ADDUNDORECORD,
/// Introduced in Windows 7. Display a Downloading instead of Copying message in the progress dialog.
CopyAsDownload = FILEOP_FLAGS.FOFX_COPYASDOWNLOAD,
/// Introduced in Windows 7. Do not display the location line in the progress dialog.
DontDisplayLocations = FILEOP_FLAGS.FOFX_DONTDISPLAYLOCATIONS,
}
/// Used by methods of the ITransferSource and ITransferDestination interfaces to control their file operations.
[Flags]
public enum TransferFlags
{
/// Fail if the destination already exists, unless TSF_OVERWRITE_EXIST is specified. This is a default behavior.
Normal = TRANSFER_SOURCE_FLAGS.TSF_NORMAL,
/// Fail if the destination already exists, unless TSF_OVERWRITE_EXIST is specified. This is a default behavior
FailExist = TRANSFER_SOURCE_FLAGS.TSF_FAIL_EXIST,
/// Rename with auto-name generation if the destination already exists.
RenameExist = TRANSFER_SOURCE_FLAGS.TSF_RENAME_EXIST,
/// Overwrite or merge with the destination.
OverwriteExist = TRANSFER_SOURCE_FLAGS.TSF_OVERWRITE_EXIST,
/// Allow creation of a decrypted destination.
AllowDecryption = TRANSFER_SOURCE_FLAGS.TSF_ALLOW_DECRYPTION,
/// No discretionary access control list (DACL), system access control list (SACL), or owner.
NoSecurity = TRANSFER_SOURCE_FLAGS.TSF_NO_SECURITY,
///
/// Copy the creation time as part of the copy. This can be useful for a move operation that is being used as a copy and delete operation (TSF_MOVE_AS_COPY_DELETE).
///
CopyCreationTime = TRANSFER_SOURCE_FLAGS.TSF_COPY_CREATION_TIME,
/// Copy the last write time as part of the copy.
CopyWriteTime = TRANSFER_SOURCE_FLAGS.TSF_COPY_WRITE_TIME,
/// Assign write, read, and delete permissions as share mode.
UseFullAccess = TRANSFER_SOURCE_FLAGS.TSF_USE_FULL_ACCESS,
/// Recycle on file delete, if possible.
DeleteRecycleIfPossible = TRANSFER_SOURCE_FLAGS.TSF_DELETE_RECYCLE_IF_POSSIBLE,
/// Hard link to the desired source (not required). This avoids a normal copy operation.
CopyHardLink = TRANSFER_SOURCE_FLAGS.TSF_COPY_HARD_LINK,
/// Copy the localized name.
CopyLocalizedName = TRANSFER_SOURCE_FLAGS.TSF_COPY_LOCALIZED_NAME,
/// Move as a copy and delete operation.
MoveAsCopyDelete = TRANSFER_SOURCE_FLAGS.TSF_MOVE_AS_COPY_DELETE,
/// Suspend Shell events.
SuspendShellEvents = TRANSFER_SOURCE_FLAGS.TSF_SUSPEND_SHELLEVENTS,
}
/// Gets or sets options that control file operations.
public OperationFlags Options
{
get => opFlags;
set { if (value == opFlags) return; op.SetOperationFlags((FILEOP_FLAGS)value); opFlags = value; }
}
public int QueuedOperations { get; protected set; }
// TODO: public Form CustomProgressDialog { get; set; }
/// Copies a single item to a specified destination using the Shell to provide progress and error dialogs.
/// A that specifies the source item.
/// A that specifies the destination folder to contain the copy of the item.
///
/// An optional new name for the item after it has been copied. This can be . If , the name of the
/// destination item is the same as the source.
///
/// Options that control file operations.
public static void Copy(ShellItem source, ShellFolder dest, string newName = null, OperationFlags options = defaultOptions)
{
using (var sop = new ShellFileOperations())
{
sop.Options = options;
HRESULT hr = HRESULT.S_OK;
sop.PostCopyItem += OnPost;
try
{
sop.QueueCopyOperation(source, dest, newName);
sop.PerformOperations();
hr.ThrowIfFailed();
}
finally
{
sop.PostCopyItem -= OnPost;
}
void OnPost(object sender, ShellFileOpEventArgs e) => hr = e.Result;
}
}
/// Copies a set of items to a specified destination using the Shell to provide progress and error dialogs.
/// An of instances that represent the group of items to be copied.
/// A that specifies the destination folder to contain the copy of the items.
/// Options that control file operations.
public static void Copy(IEnumerable sourceItems, ShellFolder dest, OperationFlags options = defaultOptions)
{
using (var sop = new ShellFileOperations())
{
sop.Options = options;
HRESULT hr = HRESULT.S_OK;
sop.PostCopyItem += OnPost;
try
{
sop.QueueCopyOperation(sourceItems, dest);
sop.PerformOperations();
hr.ThrowIfFailed();
}
finally
{
sop.PostCopyItem -= OnPost;
}
void OnPost(object sender, ShellFileOpEventArgs e) => hr = e.Result;
}
}
/// Deletes a single item using the Shell to provide progress and error dialogs.
/// A that specifies the item to be deleted.
/// Options that control file operations.
public static void Delete(ShellItem source, OperationFlags options = defaultOptions)
{
using (var sop = new ShellFileOperations())
{
sop.Options = options;
HRESULT hr = HRESULT.S_OK;
sop.PostDeleteItem += OnPost;
try
{
sop.QueueDeleteOperation(source);
sop.PerformOperations();
hr.ThrowIfFailed();
}
finally
{
sop.PostDeleteItem -= OnPost;
}
void OnPost(object sender, ShellFileOpEventArgs e) => hr = e.Result;
}
}
/// Deletes a set of items using the Shell to provide progress and error dialogs.
/// An of instances which represents the group of items to be deleted.
/// Options that control file operations.
public static void Delete(IEnumerable sourceItems, OperationFlags options = defaultOptions)
{
using (var sop = new ShellFileOperations())
{
sop.Options = options;
HRESULT hr = HRESULT.S_OK;
sop.PostDeleteItem += OnPost;
try
{
sop.QueueDeleteOperation(sourceItems);
sop.PerformOperations();
hr.ThrowIfFailed();
}
finally
{
sop.PostDeleteItem -= OnPost;
}
void OnPost(object sender, ShellFileOpEventArgs e) => hr = e.Result;
}
}
/// Moves a single item to a specified destination using the Shell to provide progress and error dialogs.
/// A that specifies the source item.
/// A that specifies the destination folder to contain the moved item.
///
/// An optional new name for the item in its new location. This can be . If , the name of the destination
/// item is the same as the source.
///
/// Options that control file operations.
public static void Move(ShellItem source, ShellFolder dest, string newName = null, OperationFlags options = defaultOptions)
{
using (var sop = new ShellFileOperations())
{
sop.Options = options;
HRESULT hr = HRESULT.S_OK;
sop.PostMoveItem += OnPost;
try
{
sop.QueueMoveOperation(source, dest, newName);
sop.PerformOperations();
hr.ThrowIfFailed();
}
finally
{
sop.PostMoveItem -= OnPost;
}
void OnPost(object sender, ShellFileOpEventArgs e) => hr = e.Result;
}
}
/// Moves a set of items to a specified destination using the Shell to provide progress and error dialogs.
/// An of instances which represents the group of items to be moved.
/// A that specifies the destination folder to contain the moved items.
/// Options that control file operations.
public static void Move(IEnumerable sourceItems, ShellFolder dest, OperationFlags options = defaultOptions)
{
using (var sop = new ShellFileOperations())
{
sop.Options = options;
HRESULT hr = HRESULT.S_OK;
sop.PostMoveItem += OnPost;
try
{
sop.QueueMoveOperation(sourceItems, dest);
sop.PerformOperations();
hr.ThrowIfFailed();
}
finally
{
sop.PostMoveItem -= OnPost;
}
void OnPost(object sender, ShellFileOpEventArgs e) => hr = e.Result;
}
}
/// Creates a new item in a specified location using the Shell to provide progress and error dialogs.
/// A that specifies the destination folder that will contain the new item.
/// The file name of the new item, for instance Newfile.txt.
/// A value that specifies the file system attributes for the file or folder.
///
/// The name of the template file (for example Excel9.xls) that the new item is based on, stored in one of the following locations:
///
/// CSIDL_COMMON_TEMPLATES. The default path for this folder is %ALLUSERSPROFILE%\Templates.
/// CSIDL_TEMPLATES. The default path for this folder is %USERPROFILE%\Templates.
/// %SystemRoot%\shellnew
///
///
/// 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.
///
/// This parameter is normally to specify a new, blank file.
///
/// Options that control file operations.
public static void NewItem(ShellFolder dest, string name, System.IO.FileAttributes attr = System.IO.FileAttributes.Normal, string template = null, OperationFlags options = defaultOptions)
{
using (var sop = new ShellFileOperations())
{
sop.Options = options;
HRESULT hr = HRESULT.S_OK;
sop.PostNewItem += OnPost;
try
{
sop.QueueNewItemOperation(dest, name, attr, template);
sop.PerformOperations();
hr.ThrowIfFailed();
}
finally
{
sop.PostRenameItem -= OnPost;
}
void OnPost(object sender, ShellFileOpEventArgs e) => hr = e.Result;
}
}
/// Renames a single item to a new display name using the Shell to provide progress and error dialogs.
/// A that specifies the source item.
/// The new display name of the item.
/// Options that control file operations.
public static void Rename(ShellItem source, string newName = null, OperationFlags options = defaultOptions)
{
using (var sop = new ShellFileOperations())
{
sop.Options = options;
HRESULT hr = HRESULT.S_OK;
sop.PostRenameItem += OnPost;
try
{
sop.QueueRenameOperation(source, newName);
sop.PerformOperations();
hr.ThrowIfFailed();
}
finally
{
sop.PostRenameItem -= OnPost;
}
void OnPost(object sender, ShellFileOpEventArgs e) => hr = e.Result;
}
}
///
/// 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.
///
/// An of instances which represents the group of items to be renamed.
/// The new display name of the items.
/// Options that control file operations.
public static void Rename(IEnumerable sourceItems, string newName, OperationFlags options = defaultOptions)
{
using (var sop = new ShellFileOperations())
{
sop.Options = options;
HRESULT hr = HRESULT.S_OK;
sop.PostRenameItem += OnPost;
try
{
sop.QueueRenameOperation(sourceItems, newName);
sop.PerformOperations();
hr.ThrowIfFailed();
}
finally
{
sop.PostRenameItem -= OnPost;
}
void OnPost(object sender, ShellFileOpEventArgs e) => hr = e.Result;
}
}
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// Executes all selected operations.
///
/// This method is called last to execute those actions that have been specified earlier by calling their individual methods. For instance,
/// does not rename the item, it simply sets the parameters. The actual renaming is done when you
/// call PerformOperations.
///
public void PerformOperations()
{
op.PerformOperations();
QueuedOperations = 0;
}
/// Declares a set of properties and values to be set on an item.
/// The item to receive the new property values.
///
/// An , which contains a dictionary of objects that specify the properties to be set and their new values.
///
public void QueueApplyPropertiesOperation(ShellItem item, ShellItemPropertyUpdates props)
{
op.SetProperties(props.IPropertyChangeArray);
op.ApplyPropertiesToItem(item.IShellItem);
QueuedOperations++;
}
/// Declares a set of properties and values to be set on a set of items.
///
/// An of instances that represent the group of items to which to apply the properties.
///
///
/// An , which contains a dictionary of objects that specify the properties to be set and their new values.
///
public void QueueApplyPropertiesOperation(IEnumerable items, ShellItemPropertyUpdates props)
{
op.SetProperties(props.IPropertyChangeArray);
op.ApplyPropertiesToItems(new ShellItemArray(items).IShellItemArray);
QueuedOperations++;
}
/// Declares a single item that is to be copied to a specified destination.
/// A that specifies the source item.
/// A that specifies the destination folder to contain the copy of the item.
///
/// An optional new name for the item after it has been copied. This can be . If , the name of the
/// destination item is the same as the source.
///
public void QueueCopyOperation(ShellItem source, ShellFolder dest, string newName = null)
{
op.CopyItem(source.IShellItem, dest.IShellItem, newName, null);
QueuedOperations++;
}
/// Declares a set of items that are to be copied to a specified destination.
/// An of instances that represent the group of items to be copied.
/// A that specifies the destination folder to contain the copy of the items.
public void QueueCopyOperation(IEnumerable sourceItems, ShellFolder dest)
{
op.CopyItems(new ShellItemArray(sourceItems).IShellItemArray, dest.IShellItem);
QueuedOperations++;
}
/// Declares a single item that is to be deleted.
/// >A that specifies the item to be deleted.
public void QueueDeleteOperation(ShellItem item)
{
op.DeleteItem(item.IShellItem, null);
QueuedOperations++;
}
/// Declares a set of items that are to be deleted.
/// An of instances which represents the group of items to be deleted.
public void QueueDeleteOperation(IEnumerable items)
{
op.DeleteItems(new ShellItemArray(items).IShellItemArray);
QueuedOperations++;
}
/// Declares a single item that is to be moved to a specified destination.
/// A that specifies the source item.
/// A that specifies the destination folder to contain the moved item.
///
/// An optional new name for the item in its new location. This can be . If , the name of the destination
/// item is the same as the source.
///
public void QueueMoveOperation(ShellItem source, ShellFolder dest, string newName = null)
{
op.MoveItem(source.IShellItem, dest.IShellItem, newName, null);
QueuedOperations++;
}
/// Declares a set of items that are to be moved to a specified destination.
/// An of instances which represents the group of items to be moved.
/// A that specifies the destination folder to contain the moved items.
public void QueueMoveOperation(IEnumerable sourceItems, ShellFolder dest)
{
op.MoveItems(new ShellItemArray(sourceItems).IShellItemArray, dest.IShellItem);
QueuedOperations++;
}
/// Declares a new item that is to be created in a specified location.
/// A that specifies the destination folder that will contain the new item.
/// The file name of the new item, for instance Newfile.txt.
/// A value that specifies the file system attributes for the file or folder.
///
/// The name of the template file (for example Excel9.xls) that the new item is based on, stored in one of the following locations:
///
/// CSIDL_COMMON_TEMPLATES. The default path for this folder is %ALLUSERSPROFILE%\Templates.
/// CSIDL_TEMPLATES. The default path for this folder is %USERPROFILE%\Templates.
/// %SystemRoot%\shellnew
///
///
/// 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.
///
/// This parameter is normally to specify a new, blank file.
///
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++;
}
/// Declares a single item that is to be given a new display name.
/// A that specifies the source item.
/// The new display name of the item.
public void QueueRenameOperation(ShellItem source, string newName)
{
op.RenameItem(source.IShellItem, newName, null);
QueuedOperations++;
}
/// Declares a set of items that are to be given a new display name. All items are given the same name.
/// An of instances which represents the group of items to be renamed.
/// The new display name of the items.
public void QueueRenameOperation(IEnumerable sourceItems, string newName)
{
op.RenameItems(new ShellItemArray(sourceItems).IShellItemArray, newName);
QueuedOperations++;
}
/// Releases unmanaged and - optionally - managed resources.
/// true to release both managed and unmanaged resources; false to release only unmanaged resources.
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
// TODO: dispose managed state (managed objects).
}
if (sink != null) op.Unadvise(sinkCookie);
if (op != null)
{
Marshal.FinalReleaseComObject(op);
op = null;
}
disposedValue = true;
}
}
/// Arguments supplied to the event.
///
public class ShellFileNewOpEventArgs : ShellFileOpEventArgs
{
internal ShellFileNewOpEventArgs(TRANSFER_SOURCE_FLAGS flags, IShellItem source, IShellItem folder, IShellItem dest, string name, HRESULT hr, string templ, uint attr) :
base(flags, source, folder, dest, name, hr)
{
TemplateName = templ;
FileAttributes = (System.IO.FileAttributes)attr;
}
/// Gets the name of the template.
/// The name of the template.
public string TemplateName { get; protected set; }
/// Gets the file attributes.
/// The file attributes.
public System.IO.FileAttributes FileAttributes { get; protected set; }
}
/// Arguments supplied to events from . Depending on the event, some properties may not be set.
///
public class ShellFileOpEventArgs : EventArgs
{
internal ShellFileOpEventArgs(TRANSFER_SOURCE_FLAGS flags, IShellItem source, IShellItem folder = null, IShellItem dest = null, string name = null, HRESULT hr = default(HRESULT))
{
Flags = (TransferFlags)flags;
if (source != null) SourceItem = ShellItem.Open(source);
if (folder != null) DestFolder = ShellItem.Open(folder);
if (dest != null) DestItem = ShellItem.Open(dest);
Name = name;
Result = hr;
}
/// Gets the destination folder.
/// The destination folder.
public ShellItem DestFolder { get; protected set; }
/// Gets the destination item.
/// The destination item.
public ShellItem DestItem { get; protected set; }
/// Gets the tranfer flag values.
/// The flags.
public TransferFlags Flags { get; protected set; }
/// Gets the name of the item.
/// The item name.
public string Name { get; protected set; }
/// Gets the result of the operation.
/// The result.
public HRESULT Result { get; protected set; }
/// Gets the source item.
/// The source item.
public ShellItem SourceItem { get; protected set; }
/// Returns a that represents this instance.
/// A that represents this instance.
public override string ToString() => $"HR:{Result};Src:{SourceItem};DFld:{DestFolder};Dst:{DestItem};Name:{Name}";
}
private class OpSink : IFileOperationProgressSink
{
private readonly ShellFileOperations parent;
public OpSink(ShellFileOperations ops) { parent = ops; }
public void FinishOperations(HRESULT hrResult) => parent.FinishOperations?.Invoke(parent, new ShellFileOpEventArgs(0, null, null, null, null, hrResult));
public void PauseTimer() { }
public void PostCopyItem(TRANSFER_SOURCE_FLAGS dwFlags, IShellItem psiItem, IShellItem psiDestinationFolder, [MarshalAs(UnmanagedType.LPWStr)] string pszNewName, HRESULT hrCopy, IShellItem psiNewlyCreated) =>
parent.PostCopyItem?.Invoke(parent, new ShellFileOpEventArgs(dwFlags, psiItem, psiDestinationFolder, psiNewlyCreated, pszNewName, hrCopy));
public void PostDeleteItem(TRANSFER_SOURCE_FLAGS dwFlags, IShellItem psiItem, HRESULT hrDelete, IShellItem psiNewlyCreated) =>
parent.PostDeleteItem?.Invoke(parent, new ShellFileOpEventArgs(dwFlags, psiItem, null, psiNewlyCreated, null, hrDelete));
public void PostMoveItem(TRANSFER_SOURCE_FLAGS dwFlags, IShellItem psiItem, IShellItem psiDestinationFolder, [MarshalAs(UnmanagedType.LPWStr)] string pszNewName, HRESULT hrMove, IShellItem psiNewlyCreated) =>
parent.PostMoveItem?.Invoke(parent, new ShellFileOpEventArgs(dwFlags, psiItem, psiDestinationFolder, psiNewlyCreated, pszNewName, hrMove));
public void PostNewItem(TRANSFER_SOURCE_FLAGS dwFlags, IShellItem psiDestinationFolder, [MarshalAs(UnmanagedType.LPWStr)] string pszNewName, [MarshalAs(UnmanagedType.LPWStr)] string pszTemplateName, uint dwFileAttributes, HRESULT hrNew, IShellItem psiNewItem) =>
parent.PostNewItem?.Invoke(parent, new ShellFileNewOpEventArgs(dwFlags, null, psiDestinationFolder, psiNewItem, pszNewName, hrNew, pszTemplateName, dwFileAttributes));
public void PostRenameItem(TRANSFER_SOURCE_FLAGS dwFlags, IShellItem psiItem, [MarshalAs(UnmanagedType.LPWStr)] string pszNewName, HRESULT hrRename, IShellItem psiNewlyCreated) =>
parent.PostRenameItem?.Invoke(parent, new ShellFileOpEventArgs(dwFlags, psiItem, null, psiNewlyCreated, pszNewName, hrRename));
public void PreCopyItem(TRANSFER_SOURCE_FLAGS dwFlags, IShellItem psiItem, IShellItem psiDestinationFolder, [MarshalAs(UnmanagedType.LPWStr)] string pszNewName) =>
parent.PreCopyItem?.Invoke(parent, new ShellFileOpEventArgs(dwFlags, psiItem, psiDestinationFolder, null, pszNewName));
public void PreDeleteItem(TRANSFER_SOURCE_FLAGS dwFlags, IShellItem psiItem) =>
parent.PreDeleteItem?.Invoke(parent, new ShellFileOpEventArgs(dwFlags, psiItem));
public void PreMoveItem(TRANSFER_SOURCE_FLAGS dwFlags, IShellItem psiItem, IShellItem psiDestinationFolder, [MarshalAs(UnmanagedType.LPWStr)] string pszNewName) =>
parent.PreMoveItem?.Invoke(parent, new ShellFileOpEventArgs(dwFlags, psiItem, psiDestinationFolder, null, pszNewName));
public void PreNewItem(TRANSFER_SOURCE_FLAGS dwFlags, IShellItem psiDestinationFolder, [MarshalAs(UnmanagedType.LPWStr)] string pszNewName) =>
parent.PreNewItem?.Invoke(parent, new ShellFileOpEventArgs(dwFlags, null, psiDestinationFolder, null, pszNewName));
public void PreRenameItem(TRANSFER_SOURCE_FLAGS dwFlags, IShellItem psiItem, [MarshalAs(UnmanagedType.LPWStr)] string pszNewName) => parent.PreRenameItem?.Invoke(parent, new ShellFileOpEventArgs(dwFlags, psiItem, null, null, pszNewName));
public void ResetTimer() { }
public void ResumeTimer() { }
public void StartOperations() => parent.StartOperations?.Invoke(parent, EventArgs.Empty);
public void UpdateProgress(uint iWorkTotal, uint iWorkSoFar) => parent.UpdateProgress?.Invoke(parent, new System.ComponentModel.ProgressChangedEventArgs((int)(iWorkSoFar * 100 / iWorkTotal), null));
}
}
///
/// A dictionary of properties that can be used to set or update property values on Shell items via the
/// method. This class wraps the
/// COM interface.
///
///
///
public class ShellItemPropertyUpdates : IDictionary, IDisposable
{
private IPropertyChangeArray changes;
/// Initializes a new instance of the class.
public ShellItemPropertyUpdates()
{
PSCreatePropertyChangeArray(null, null, null, 0, typeof(IPropertyChangeArray).GUID, out changes).ThrowIfFailed();
}
/// Gets the number of elements contained in the .
public int Count => (int)changes.GetCount();
/// Gets the COM interface for .
/// The value.
public IPropertyChangeArray IPropertyChangeArray => changes;
/// Gets an containing the keys of the .
public ICollection Keys
{
get
{
var l = new List(Count);
for (uint i = 0; i < Count; i++)
{
using (var p = new ComReleaser(changes.GetAt(i, typeof(IPropertyChange).GUID)))
l.Add(p.Item.GetPropertyKey());
}
return l;
}
}
/// Gets an containing the values in the .
public ICollection