SHFILEOPSTRUCT and the SHFileOperation

Environment: Windows 9x/NT/2K, Version 4.00 (or later) of Shell32.dll

Introduction

The function
SHFileOperation, enables you to copy, move or delete a file system object.
As you can see, this function takes as its sole parameter a pointer to a SHFILEOPSTRUCT
structure (also shown below).

// SHFileOperation syntax
WINSHELLAPI int WINAPI SHFileOperation(LPSHFILEOPSTRUCT lpFileOp);

// SHFILEOPSTRUCT layout
typedef struct _SHFILEOPSTRUCT{
 HWND		hwnd;
 UINT		wFunc;
 LPCSTR		pFrom;
 LPCSTR		pTo;
 FILEOP_FLAGS	fFlags;
 BOOL		fAnyOperationsAborted;
 LPVOID		hNameMappings;
 LPCSTR		lpszProgressTitle;
} SHFILEOPSTRUCT, FAR *LPSHFILEOPSTRUCT;

Unfortunately, despite this function’s obvious usefulness,
the Microsoft documentation is very ambiguous regarding
the SHFILEOPSTRUCT structure and how to fill it
out. In fact, there are several pitfalls when using this
structure. Therefore, in this article, I’ll attempt to address
these issues so that those of you using this
function for the first time can benefit from my experience as to how to
take advantage of this very handy function.

Name Collision Resolution and the hNameMappings member

hNameMappings is (almost always) an input-only member
of SHFILEOPSTRUCT

In fact, after the call to SHFileOperation has returned successfully, this
value will be actually be set to NULL. The only time that this value will be
changed by the SHFileOperation function is if you have specified the flags
FOF_RENAMEONCOLLISION | FOF_WANTMAPPINGSHANDLE in the
FILEOP_FLAGS member. Then if there were files that had to be renamed
due to a name collision, assign a name mapping object will containing their old and
new names to the hNameMappings member.

This member can be treated as a void pointer to a continuous memory block.
This memory block in turn can be treated as some kind of the structure with
int as a first member. Value of this integer indicates how many SHNAMEMAPPING
structures follows. This way you can retrieve needed info.

File Name Retrieval

Another pitfall that you need to know about deals with retrieving the names of files.
pszOldPath and pszNewPath members are declared
as LPSTR. However, they are laid out as DWORD; not
char! Therefore, any string copying function available fails to
successfully copy the path strings.

Sample Code

Here is a code snippet
illustrating these issues. It can be done differently and is not
posted for the purpose of code review. Treat it rather as a suggestion.

CString OldPath;
CString NewPath;
SHFILEOPSTRUCT shFileOp;

//after filling other members:
shFileOp.fFlags = FOF_WANTMAPPINGHANDLE
                | FOF_RENAMEONCOLLISION;

shFileOp.lpszProgressTitle = "Test";
shFileOp.fAnyOperationsAborted = FALSE;

SHFileOperation(&shFileOp);

if(!shFileOp.hNameMappings)
 return;	//there is nothing further to do, no file collision

struct TMPMAP
{
int Indx;
SHNAMEMAPPING *pMapping;
};

//this will give you a  pointer to the beginning of the 
//array of SHNAMEMAPPING structures
TMPMAP *pTmpMap = (TMPMAP*)shFileOp.hNameMappings;

for(int in = 0; in < pTmpMap->Indx; in++)
{
 SHNAMEMAPPING *pMap = &pTmpMap->pMapping[in];

 //do the same thing for csOldPath
 /*********************************************************/
 char *buf = csNewPath.GetBufferSetLength(pMap->cchNewPath);

strcpy(buf, (char*)pMap->pszOldPath); //see the result of
this,

for(int dw = 0 ; dw < 2 * pMap->cchNewPath - 1 ; dw+=2)
{
*buf = (pMap->pszNewPath[dw]);
buf++;
}
buf = 0;
csNewPath.ReleaseBuffer();  //and see the result of this.
/**********************************************************/
}

//always free it if requested
SHFreeNameMappings(shFileOp.hNameMappings);

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read