Welcome to shell: revealed Sign in | Join | Help
in Search

Shell Blog

IFileOperation – Part 1: Introduction

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. 

Published Monday, April 16, 2007 4:17 PM by chrdavis

Comments

 

dannyres said:

Please show us the recommended .NET declaration of this interface in a future post!  Many people would find this very usful, it's alot of work to convert an interface like this to .NET style and I'm sure someone in MS has already acomplished this.

April 16, 2007 7:32 PM
 

Shell Blog said:

In the first part of my posts for the IFileOperation interface , I wanted to give an overview of what

April 23, 2007 1:30 PM
 

chrdavis said:

dannyres - there is an article in MSDN magazine online that covers how to use IFileOperation from managed code.

http://msdn.microsoft.com/msdnmag/issues/07/12/NETMatters/

November 27, 2007 12:25 AM
 

iunknown21 said:

I'm trying to use the sample code but the file doesn't seem to be copied.

here are my attempts:

 IFileOperationCopyItem(L"C:\\temp\\Dell_Badge.bmp\0", L"C:\\test\0", L"guff.bmp\0");

 IFileOperationCopyItem(L"C:\\temp\\Dell_Badge.bmp\0", L"C:\\test\\\0", L"guff.bmp\0");

 IFileOperationCopyItem(L"C:\\temp\\Dell_Badge.bmp\0", L"C:\\test\\\0", L"C:\\test\\guff.bmp\0");

all 3 of them have HR's of S_OK but yet the file doesn't get copied.

any ideas?

May 6, 2008 11:20 PM
 

iunknown21 said:

never mind.  I forgot that I had spoofed that directory....d'oh!

May 7, 2008 9:35 AM
 

Kenny Kerr said:

I found a bug in IFileOperation. There’s an access violation at “shell32.dll!CRecursiveFolderOperation::Do() + 0x149b1d bytes” inside PerformOperations when the NewItem operation is included and the FOF_ALLOWUNDO flag is set (it’s set by default). Looks like a null pointer being dereferenced.

“76213D42 mov ecx,dword ptr [eax]” where eax is 0.

August 4, 2008 5:55 AM
Anonymous comments are disabled
Powered by Community Server, by Telligent Systems © 2006 Microsoft Corporation. All rights reserved. Terms of Use | Trademarks | Privacy Statement.