For my Software PhotoTagStudio I implemented a new feature to copy jpg-files from a memory card to the disk. I wanted to use that fancy Windows XP explorer copy dialog with the papers flying from one folder to another. And where I get the (more or less accurate) time estimation for free.
Searching the web I found the shell32.dll and the SHFileOperation function. This function does exactly what I need and on www.codeproject.com you can find a great article by arikp on how to address system32.dll from .net. arikp included sourcecode so you find everything you need to call the system32.dll functions from .net. Unfortunately two little features didn’t not work as expected:
hNameMappings is rarely used and can help only if I’m interesting in the new names the user had to give during the operation…
(but did not work in the ShellApi.cs)
…and finally lpszProgressTitle, If we set the flag FOF_SIMPLEPROGRESS, the progress dialog box does not present the file names and is supposed to present the text of this parameter. When I test this function I couldn’t get this parameter to show, it didn’t show the file names with the SIMPLEPROGRESS flag but it did not show the title parameter. What can I say, strange.
(did not work as well)
But I have to know the new names and thus had to get the NameMappings-thing working all by myself (I could not find a solution anywhere in the web). So I asked a good friend of mine, Wolfram Bernhardt, who subsequently did most of the following work:
He found a problem in the SHFILEOPSTRUCT
:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct SHFILEOPSTRUCT
{
public IntPtr hwnd;
public UInt32 wFunc;
public IntPtr pFrom;
public IntPtr pTo;
public UInt16 fFlags;
public Int32 fAnyOperationsAborted;
public IntPtr hNameMappings;
[MarshalAs(UnmanagedType.LPWStr)]
public String lpszProgressTitle;
}
When marshalling this from .net to unsafe/native each element of the struct is stored at an address that is an multiple of 4. (This is the default for .net on Win32 for performance reasons). This leads to some unused bytes – i.e. fFlags
is only 16 bit width and the next parameter should not leave a space of two byte.
The functions of shell32.dll are old fashion and do not waste any space. So they read wrong values after fFlags
. The fAnyOperationsAborted
wasn’t harmed that much and it’s value wasn’t checked too carefully in my application. But the hNameMappings
-Pointer went totally wrong.
To change this behaviour you just have to add the Pack=2
parameter to the attribute:
[StructLayout(LayoutKind.Sequential, Pack = 2, CharSet = CharSet.Unicode)]
After this little change hNameMappings
is marshalled back correctly and points to a mapping-structue. As a “side-effect” the lpszProgressTitle
is now shown as expected – at least on Win XP, Vista seems to ignore this string. Obviously MS has changed this function of shell32.dll. But the more important functions work as desired.
To get the new filenames you have to set the Flags FOF_WANTMAPPINGHANDLE
and FOF_RENAMEONCOLLISION
. The latter allows the function to (automatically) rename files during copying. If a filename already exists, the new copy is renamed to something like “Copy of [filename]”. On a Windows Vista machine you don’t need the FOF_RENAMEONCOLLISION
flag, because the user can choose the new option “Keep both the original file and the copy� in the dialog that asks to override existing files. In both cases the hNameMappings
is filled with a pointer to a list of Mapping objects that contain the original and the new filenames.
To get the list from the hNameMappings
Pointer you have to implement two more structs and Wolfram did it, too. He added a new property NameMappings
to the ShellFileOperation
Class. (For details see the source code of the changes ShellLib.)
With the following download we provide an updated Version of the ShellLib with the described changes. For more information please refer to the original article on The Code Project and take a look on the demo code over there.
March 5th, 2008 at 12:10 am
[…] I know. The first time he appeared here in September last year on an article regarding the Win32 shell. He will start to write more articles worth reading here soon. I think 2008 will be interesting for […]
January 10th, 2010 at 4:38 pm
[…] time ago we blogged about a nifty little .net assembly that enables you to access the basic Windows shell operations in shell32.dll . You may want to do so especially in Win7 because your file operations integrate […]