Many developers who have used Shell APIs have had at one time or another used SHFileOperation in their code. SHFileOperation is used to perform copy, move, rename and delete operations on items in the file system. It is an entry point into the Shell’s copy engine and is the same API used by Windows Explorer to perform file operations. Callers also leverage its various UI elements such as confirmation, error and progress dialogs. This API evolved from code in versions of Windows earlier than Windows 95 and is clearly showing its age. I won’t go into too much detail about SHFileOperation in this post, but you can read more about common issues with this API in my past posts on this subject:
Common Questions Concerning the SHFileOperation API: Part 1
Common Questions Concerning the SHFileOperation API: Part 2
In Vista, a new entry point into the copy engine was created to allow callers to perform operations on more than just files and folders – but virtual items such as namespace elements and search results. This is one of the many purposes for the creation of the IFileOperation interface. Below is a summary of the new features and benefits of this interface over the old SHFileOperation API:
No More String Paths!
One of the first things developers will notice about the IFileOperation interface is that they do not specify their source and destination of operations using string-based paths. Instead it uses IShellItem instances. Thus, callers are responsible for creating their own IShellItem instances, but at the tradeoff of not having to deal with the ambiguity that the copy engine encounters when passed string buffers (wildcards, relative/invalid paths, multiple null-delimited paths, etc). The caller also has the option of specifying multiple source items for operations by using an IShellItemArray, IEnumShellItems or IDataObject.
Better Error Handling
One of the greatest benefits of using IFileOperation over SHFileOperation is in the HRESULT return codes from its methods. As explained in my first post about SHFileOperation – the return codes from that API can be misleading and even incorrect. With IFileOperation, the HRESULTs returned can be used in conjunction with APIs such as FormatMessage to get accurate descriptions of their meaning. It is also worth mentioning here that you should check the BOOL returned from the GetAnyOperationsAborted method (see below) as well to determine if the operation was cancelled by the user.
COM
As a COM interface, IFileOperation aligns better with how the Windows Shell is designed. This also makes it easier for developers to extend the abilities of this interface within their own code.
Operation Feedback
A common request of developers was the ability to receive detailed operation progress while the operation is being performed. This was not possible with the SHFileOperation API. With the Advise and Unadvise methods of IFileOperation, the caller can specifiy an IFileOperationProgressSink instance to be notified when operations on individual items begin and end as well as overall progress. I will go into more detail in later posts on how to implement and use this interface with IFileOperation.
New Operation Types
This interface not only allows callers to perform standard operations such as copy, move, rename and delete. It also provides the ability to apply properties to items and create new items. We will delve into these further in later posts.
More Flags
Some elements of the interface will be familiar. For example, the interface accepts all of the operation flags that were available through SHFileOperation (ex: FOF_SILENT, FOF_NOERRORUI, etc). Yet, many new flags are available. These flags are prefixed with FOFX_ and can only be used through the IFileOperation interface (these are ignored if passed to SHFileOperation through the SHFILEOPSTRUCT). These new flags give more control to developers and are based off of feedback we have received over the years.
Flag | Description |
FOFX_NOSKIPJUNCTIONS | By default, the copy engine does not walk into junction points. This flag enables this functionality. |
FOFX_PREFERHARDLINK | Create a hard link to the file if possible, rather than creating a new instance of the file in the destination. |
FOFX_SHOWELEVATIONPROMPT | If the operation requires elevation, show the UAC prompt. |
FOFX_EARLYFAILURE | If the caller specifies FOF_NOERRORUI with this flag, the operation ends on the first failure occurrence instead of ignoring the error and continuing the operation. If FOF_NOERRORUI is not included with this flag, it has no effect. |
FOFX_PRESERVEFILEEXTENSIONS | If the operation will cause a file extension to be changed, this flag will cause the original extension to remain the same while changing the name component of the file. |
FOFX_KEEPNEWERFILE | When a file is encountered in the destination with the same name as that in the source, this flag sets the default to choosing the newer file (based on Date Modified). The user will not be prompted with a confirmation dialog. |
FOFX_NOCOPYHOOKS | Turns off all copy hooks. |
FOFX_NOMINIMIZEBOX | Removes the minimize button from the progress dialog for the operation. |
FOFX_MOVEACLSACROSSVOLUMES | In cross volume moves, the security attributes of the parent folder in the destination are applied to the items by default. This flag causes security attributes to persist when items are moved across volumes. |
FOFX_DONTDISPLAYSOURCEPATH | Don't display the path of source file in progress dialog |
FOFX_DONTDISPLAYDESTPATH | Don't display the path of destination file in progress dialog |
Documentation Corrections
At the time of this writing the documentation for this interface on MSDN was not accurate. The documentation writers have been notified and are making the necessary corrections.
The below methods are NOT INCLUDED in the IFileOperation interface:
LinkItem
LinkItems
The below method was left out of the documentation:
HRESULT GetAnyOperationsAborted([out] BOOL *pfAnyOperationsAborted);
This method allows users to determine if an operation was cancelled by the user. It is important that this is checked since the call to PerformOperations could return a successful HRESULT even if the operation was cancelled. This provides the same functionality as the fAnyOperationsAborted member of the SHFILEOPSTRUCT.
Example Usage
The below code shows how to perform a simple operation with the IFileOperation interface. This function takes as parameters the path to a source file, the path to a destination, and an optional new name for the file in the destination. It should be noted here that IFileOperation can only be applied in a STA COM apartment. It will not work for MTA. For MTA apartment, SHFileOperation is still the only option.
HRESULT CopyItem(__in PCWSTR pszSrcItem, __in PCWSTR pszDest, PCWSTR pszNewName)
{
//
// Initialize COM as STA
//
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
if (SUCCEEDED(hr))
{
IFileOperation *pfo;
//
// Create the IFileOperation interface
//
hr = CoCreateInstance(CLSID_FileOperation,
NULL,
CLSCTX_ALL,
IID_PPV_ARGS(&pfo));
if (SUCCEEDED(hr))
{
//
// Set the operation flags. Turn off all UI
// from being shown to the user during the
// operation. This includes error, confirmation
// and progress dialogs.
//
hr = pfo->SetOperationFlags(FOF_NO_UI);
if (SUCCEEDED(hr))
{
//
// Create an IShellItem from the supplied source path
//
IShellItem *psiFrom = NULL;
hr = SHCreateItemFromParsingName(pszSrcItem,
NULL,
IID_PPV_ARGS(&psiFrom));
if (SUCCEEDED(hr))
{
IShellItem *psiTo = NULL;
if (NULL != pszDest)
{
//
// Create an IShellItem from the supplied
// destination path
//
hr = SHCreateItemFromParsingName(pszDest,
NULL,
IID_PPV_ARGS(&psiTo));
}
if (SUCCEEDED(hr))
{
//
// Add the operation
//
hr = pfo->CopyItem(psiFrom, psiTo, pszNewName, NULL);
if (NULL != psiTo)
{
psiTo->Release();
}
}
psiFrom->Release();
}
if (SUCCEEDED(hr))
{
//
// Perform the operation to copy the file
//
hr = pfo->PerformOperations();
}
}
//
// Release the IFileOperation interface
//
pfo->Release();
}
CoUninitialize();
}
return hr;
}
Some of you may be thinking: “That is a lot more code than simply calling SHFileOperation! Why would I want to use this?” Aside from resolving the common issues with SHFileOperation, I can give you yet another good reason. Have you ever tried to perform a complex operation with SHFileOperation in one call? This involves placing multiple paths in a double null terminated buffer and it can only be with the same operation type. With IFileOperation, you can add as many operations as you want before calling PerformOperations. Thus, you can easily batch multiple operations of various types together into the same call. See the full sample from the SDK for an example of doing this.
SDK Sample Code
If you download the Windows SDK, there is a sample which shows how to use the IFileOperation interface to perform operations. It is located under the …\Samples\winui\Shell\AppPlatform\FileOperations directory of your sdk installation.
This post was only an introduction to this interface. There are many other great features that will require posts of their own.