[VB6] Use IFileOperation to replace SHFileOperation for modern Copy/Move box/prompts
cFileOperation 0.1
Display the latest version of the copy/move/delete progress dialog and the related prompts.
SHFileOperation has been superseded by IFileOperation on Windows Vista and above. At least the basic parts of it are easy to access in VB6- showing the standard Windows dialog/progress boxes to Move, Copy, or Delete files. While the class handles it, this function also requires us to bring in the IShellItem interface and its relatives into VB, so take a look at the class module code if you ever wanted to use other functions that required this.
PROJECT UPDATE 30 Apr 2016: Cancelling through code
I'm updating the project to show how to cancel operations through code. While there doesn't seem to be built-in way to cancel e.g. in the middle of a large file, you can cancel between items by using the VTable-swap technique to replace the subs in the callback class with functions that can return an error in the HRESULT (I use E_ABORT). This technique has been implemented for Multiple Files copy in the demo project. Also note that the project now references the latest version of oleexp.tlb, while the previous version still used a 3.x version, so if you haven't downloaded that yet you'll either need to, or (not recommended but possible) change the references back..
Project Update - 17 Apr 2015
The project now includes a template class implementing the IFileOperationProgressSink, so you can get feedback while the dialog is running. Examples of what to do with that information are included, like getting the final name, or passing progress back to the application.
Note:
If there's an error in a function called from the events class, the operation will not be performed but the result will say that it was. So if everything else looks right but an operation isn't happening, check that. Took me a while to catch such a problem.
Use of IShellItem to identify items rather than string paths. SHFileOperation required path and destination strings to terminate in two null characters rather than the standard single null character, which itself was used to delimit multiple paths in the string. Identifying an item through IShellItem is more robust and less prone to programming errors. It also allows you to access non-file system items such as virtual folders. Multiple items in one operation can be passed as an IShellItemArray, IDataObject, or a collection accessed through IEnumShellItems rather than as a string.
More accurate error reporting through HRESULT values in conjunction with an API such as FormatMessage. Return codes from SHFileOperation could be misleading or inaccurate.
Extensibility. As a Component Object Model (COM) interface, IFileOperation can have its capabilities extended by a third-party to meet their specific needs, although this should be a very rare case. Windows provides a default implementation of IFileOperation that should meet the needs of most users.
Better progress feedback. Detailed operation progress, including notifications when specific operations begin and end on individual items as well as the overall progress, can be received during the operation. While SHFileOperation did provide progress UI, it was not as detailed.
More functionality. In addition to the copy, delete, move, and rename functionality provided by SHFileOperation, IFileOperation allows you to apply property values and create new items.
More control over the operation. In addition to the operation flags recognized by SHFileOperation, new flags are recognized in IFileOperation::SetOperationFlags that specify extended operation options.
Different operations can be performed in one call. For instance, you can move a set of files, copy others, rename a folder, and apply properties to yet another item all in one operation. SHFileOperation could only do one operation—copy, move, rename, or delete—at a time.
Requirements
-IFileOperation is only available on Windows Vista and higher; this project will not work on XP.
-Requires oleexp.tlb v4.0 or higher
Usage Summary
Using the class is fairly straight forward;
-Make sure the project's reference to oleexp.tlb is correct
-Add the class module to the project and go nuts.
-Sample project included to show how the class is called.
The Class
Once you've added oleexp, you're ready to start using cFileOperation. Since this calls the native methods, everything functions the same as in Explorer, including prompts about overwriting, confirmation deletion, etc. No extra code is needed to handle that.
Here are the currently supported calls:
.ParentWindow - Specify the parent window (e.g. Form1.hWnd) to keep the dialogs on top of it. .SingleFile - For performing operations on a single file. .SetFileList - For multiple files, specify an array containing a single full path to a file in each item. .FileList - Retrieve the current file list. .DestFolder - The destination folder; don't need to set for Delete. .Flags - Set flags for the operation; uses the standard FileOperationFlags enum (see below). .CopyFile - Copies the single file. .CopyFiles - Copies the file list. .MoveFile - Moves the single file. .MoveFiles - Moves the file list. .DeleteFile - Deletes the single file. .DeleteFiles - Deletes the file list.
-------
All bug reports, comments, criticisms, and suggestions welcome.
PLEASE NOTE: I don't have access to multiple test systems; everything works on Win7 x64, and everything should work from Vista through 10, but please let met know if there's an issue.
--------------------------
Project update 24 Nov 2016: Updated to reference oleexp v4.0 or higher
Last edited by fafalone; Nov 24th, 2016 at 04:51 PM.
Reason: Attached project updated to reference oleexp.tlb 4.0 or higher
Re: [VB6] Using the new IFileOperation interface to replace SHFileOperation on Vista+
Appendix
Here's how the interface is added in as VB-accessable:
Code:
[
odl,
uuid(947aab5f-0a5c-4c13-b4d6-4bf7836fc9f8),
]
interface IFileOperation : stdole.IUnknown
{
// 1) (Optional) Set up your event sink.
[helpstring("Not implemented. Needs IFileOperationProgressSink class module implementation.")]
HRESULT Advise([in] void *pfops, [out] LONG *pdwCookie);
HRESULT Unadvise([in] LONG dwCookie);
// 2) Set operation state
// FOF_ flags (defined in shellapi.h) and FOFX_ flags are passed here
// if not specified the default flags are FOF_ALLOWUNDO | FOF_NOCONFIRMMKDIR
HRESULT SetOperationFlags([in] FILEOP_FLAGS dwOperationFlags);
HRESULT SetProgressMessage([in] LPCWSTR pszMessage);
HRESULT SetProgressDialog([in] IOperationsProgressDialog *popd);
HRESULT SetProperties([in] IPropertyChangeArray *pproparray);
HRESULT SetOwnerWindow([in] LONG hwndOwner);
// 3) Specify operations to take on given items.
// FooItem takes an IShellItem*.
// FooItems takes an IShellItem*, an IEnumShellItems* or an IDataObject*.
HRESULT ApplyPropertiesToItem([in] IShellItem *psiItem);
HRESULT ApplyPropertiesToItems([in] IUnknown *punkItems);
HRESULT RenameItem(
[in] IShellItem *psiItem,
[in] LPCWSTR pszNewName,
[in] void *pfopsItem); //IFileOperationProgressSink
[helpstring("punkItems is either IShellItemArray, IEnumShellItems, or IDataObject")]
HRESULT RenameItems(
[in] IUnknown *pUnkItems,
[in] LPCWSTR pszNewName);
HRESULT MoveItem(
[in] IShellItem *psiItem,
[in] IShellItem *psiDestinationFolder,
[in] LPCWSTR pszNewName,
[in] void *pfopsItem); //IFileOperationProgressSink
[helpstring("punkItems is either IShellItemArray, IEnumShellItems, or IDataObject")]
HRESULT MoveItems(
[in] IUnknown *punkItems,
[in] IShellItem *psiDestinationFolder);
HRESULT CopyItem(
[in] IShellItem *psiItem,
[in] IShellItem *psiDestinationFolder,
[in] LPCWSTR pszCopyName,
[in] void *pfopsItem);
[helpstring("punkItems is either IShellItemArray, IEnumShellItems, or IDataObject")]
HRESULT CopyItems(
[in] IUnknown *punkItems,
[in] IShellItem *psiDestinationFolder);
HRESULT DeleteItem(
[in] IShellItem *psiItem,
[in] void *pfopsItem);
[helpstring("punkItems is either IShellItemArray, IEnumShellItems, or IDataObject")]
HRESULT DeleteItems([in] IUnknown *punkItems);
HRESULT NewItem(
[in] IShellItem *psiDestinationFolder,
[in] LONG dwFileAttributes,
[in] LPCWSTR pszName,
[in] LPCWSTR pszTemplateName,
[in] void *pfopsItem);
// 4) Perform operations.
HRESULT PerformOperations();
// 5) Were any operations aborted?
HRESULT GetAnyOperationsAborted([out] LONG *pfAnyOperationsAborted);
}
Interfaces that aren't mandatory and not yet implemented by me are left as void *, which translates to As Any in VB. Types are converted into ones that are understood by VB.
Last edited by fafalone; Oct 21st, 2014 at 11:48 AM.
Re: [VB6] Use IFileOperation to replace SHFileOperation for modern Copy/Move box/prom
I had changed it since I was having a very difficult time getting it working in VB (still can't get it).. I can switch it back, give me a couple hours.
Edit: Ok, here's it as a function again and the corresponding editing source if you wanted to compile it yourself.
If you wouldn't mind sharing how you got IDataObject working from the TLB I'd appreciate it.
I'm going to look into splitting this off... there's just so many things that would have to be duplicated though.
Edit2: Here's a better solution. I've split off all the new interfaces. The original olelib still needs to be replaced with this one; read changes.txt for more information, but it's absolutely identical to the original olelib except for having the IShellFolder/IShellFolder2/IEnumIDList interfaces corrected (they were wrong in original olelib, many things needed to be functions and not subs) and ITaskbarList coclass removed to avoid a conflict with ITaskbarList3/4 (backwards compatible tho).
tl_ole.zip is the way it was with just the IDataObject change, oleexp.zip is the new version that I'll be using henceforth in all updates and new projects.
Last edited by fafalone; Dec 31st, 2015 at 02:29 PM.
Reason: Attachments Removed: Please use latest version of oleexp instead (see sig)
Re: [VB6] Use IFileOperation to replace SHFileOperation for modern Copy/Move box/prom
Thanks, works great now! I will use the non-split way for now (less changes to my code). I will later let you know how IFileOperation is working (testing under Win8.1)...
Re: [VB6] Use IFileOperation to replace SHFileOperation for modern Copy/Move box/prom
In case you're interested, the changes to those are based on being able to receive the HRESULT for things; like being able to use If SUCCEEDED(isdParent.EnumObjects(hWndOwner, shflag, ieidl)) Then instead of having to call it as a sub, and then check if IEnumIDList has been set, and similar such error checking.
Re: [VB6] Use IFileOperation to replace SHFileOperation for modern Copy/Move box/prom
I successfully renamed a file using your class under Win8.1. Very good! So I assume the other functions will work as well. Well done, thank you very much!!!
Re: [VB6] Use IFileOperation to replace SHFileOperation for modern Copy/Move box/prom
Project Updated with IFileOperationProgressSink
-Class implementation of IFileOperationProgressSink allows you to get feedback once the operation has started. Events are sent Pre- and Post- operation, and there's also an UpdateProgress event that allows you to pass the current progress back to your program.
Re: [VB6] Use IFileOperation to replace SHFileOperation for modern Copy/Move box/prom
Thank you!
I noted a possible issue with your sample code: On copying or moving a file to a destination where a file of the same name already exists, you get the expected overwrite prompt only the first time (within the project session). Subsequently, even when copying other files, there is no more overwrite prompt, and the target file is overwritten without asking. Apparently something is not reset...
Re: [VB6] Use IFileOperation to replace SHFileOperation for modern Copy/Move box/prom
Since that's not something stored by the user program, I expect that it could only be cleared by Set cFO = Nothing and then Set cFO = New cFileOperation again. Is it after you check the 'keep doing this' box right?
If it's straight up overwriting things and not sending them to the recycle bin, you could specify the FOF_WANTNUKEWARNING option to get a confirm dialog.
Re: [VB6] Use IFileOperation to replace SHFileOperation for modern Copy/Move box/prom
Good idea, that works, thanks!
No, there isn't any 'keep doing this' box in that overwrite prompt.
* * *
I have another issue: When I move a non-empty folder from a portable device (a tablet in my case) to a local drive, only the folder is moved but the contents disappear to nirvana. This is 100% reproducible. There is an error message showing apparently error E_UNEXPECTED (I have it only in German, sorry):
Note that *copying* non-empty folders in any direction works just fine, and moving non-empty folders in the reverse direction also works fine, and moving empty folders in any direction is as well fine.
Note that files on portable devices need to be referenced using "USB paths" (e.g. ::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\\?\usb#vid_18d1&pid_4ee1#r32ca05dakr#{6ac27878-a6fa-4155-ba85-f98f491d4f33}\SID-{10001,,14049177600}\{00000006-0001-0001-0000-000000000000}\{00000C07-0001-0001-0000-000000000000}\{00000C59-0001-0001-0000-000000000000}).
Re: [VB6] Use IFileOperation to replace SHFileOperation for modern Copy/Move box/prom
*pv is the return value, not the HRESULT.
For example,
Code:
For ic = 0 To 3
Call isdParent.MapColumnToSCID(ic, pst)
DebugAppend "FillEx,GetDetailsEx(" & ic & ")=" & isdParent.GetDetailsEx(pidlRelChild, pst)
Next ic
In any case, things like that were why I started rewriting that and IShellFolder; it's a holdover from the original olelib shell.inc
I've really been thinking about just redefining HRESULT as a long and turning everything into a function like it should be... but it could have far reaching implications. Not sure why all HRESULT's were set up in a way that made everything a sub anyway.
Last edited by fafalone; Jan 20th, 2015 at 12:00 PM.
Re: [VB6] Use IFileOperation to replace SHFileOperation for modern Copy/Move box/prom
Back to GetDetailsEx: I wonder why you did it that way. The original HRESULT was a useful return value since it allowed to catch invalid calls to GetDetailsEx. Now, for example, when I call for PID_STG_SIZE on a folder (which is not a valid call) I get an automation error.
Re: [VB6] Use IFileOperation to replace SHFileOperation for modern Copy/Move box/prom
I didn't intentionally do it that way I just hadn't changed it yet from the original olelib definition.
Here, now everything will return an hresult and nothing has an [out] as a retval.
Code:
[
odl,
helpstring("IShellFolder2"),
uuid(93F2F68C-1D1B-11d3-A30E-00C04F79ABD1),
]
interface IShellFolder2 : stdole.IUnknown {
long ParseDisplayName(
[in] long hwndOwner,
[in] long pbcReserved,
[in] long lpszDisplayName,
[in, out] long* pchEaten,
[in, out] long* ppidl,
[in, out] long* pdwAttributes);
long EnumObjects(
[in] long hwndOwner,
[in] SHCONTF grfFlags,
[in, out] IEnumIDList **ppenumIDList);
long BindToObject(
[in] long pidl,
[in] long pbcReserved,
[in, out] UUID *riid,
[in, out] LPVOID ppvOut);
long BindToStorage(
[in] long pidl,
[in] long pbcReserved,
[in, out] UUID *riid,
[out] IStorage **ppvObj);
long CompareIDs(
[in] long lparam,
[in] long pidl1,
[in] long pidl2);
long CreateViewObject(
[in] long hwndOwner,
[in, out] UUID* riid,
[out] long *ppvOut);
long GetAttributesOf(
[in] long cidl,
[in, out] long* apidl,
[in, out] long* rgfInOut);
long GetUIObjectOf(
[in] long hwndOwner,
[in] long cidl,
[in, out] long *apidl,
[in, out] UUID *riid,
[in, out] long *prgfInOut,
[out] long *ppvOut);
long GetDisplayNameOf(
[in] long pidl,
[in] SHGNO_Flags uFlags,
[in, out] STRRET* lpName);
long SetNameOf(
[in] long hwndOwner,
[in] long pidl,
[in] long lpszName,
[in] SHGNO_Flags uFlags,
[out] long* ppidlOut);
// Returns the guid of the search that is to be invoked when user clicks
// on the search toolbar button
long GetDefaultSearchGUID(
[out] GUID *pguid);
// gives an enumerator of the searches to be added to the search menu
long EnumSearches(
[out] IEnumExtraSearch **ppenum);
long GetDefaultColumn(
[in] LONG dwRes,
[out] LONG *pSort,
[out] LONG *pDisplay);
// return SHCOLSTATE_ values
long GetDefaultColumnState(
[in] INT iColumn,
[out] SHCOLSTATE *pcsFlags);
long GetDetailsEx(
[in] LONG pidl,
[in] SHCOLUMNID *pscid,
[out] VARIANT *pv);
long GetDetailsOf(
[in] LONG pidl,
[in] INT iColumn,
[out] SHELLDETAILS *psd);
long MapColumnToSCID(
[in] INT iColumn,
[in] SHCOLUMNID *pscid);
};
Last edited by fafalone; Apr 30th, 2016 at 06:28 PM.
Reason: Removed ancient oleexp version that was attached
Re: [VB6] Use IFileOperation to replace SHFileOperation for modern Copy/Move box/prom
Project has been updated showing a technique to cancel a multi-file operation between files using code; useful for example if you want to set it to silent so the regular progress dialog isn't shown and still want the user to be able to cancel; or any circumstance where you want the user/your program to cancel through code instead of relying on the cancel button on the progress dialog.
This is done with the v-table swap method to return E_ABORT as an HRESULT (anything other than S_OK cancels though).
Re: [VB6] Use IFileOperation to replace SHFileOperation for modern Copy/Move box/prom
Hi fafalone,
I'd like to know whether if I use SHFileOperation or IFileOperation for copying folders and its sub-directories in silent mode, displaying only progress (if I want...) I'm gonna get rid of app freezing (Not Responding)??
Re: [VB6] Use IFileOperation to replace SHFileOperation for modern Copy/Move box/prom
You can use the progress sink (.Advise) for all operations and have DoEvents in the IFileOperationProgressSink_UpdateProgress sub like the sample project does for the copy multiple files part; it fires frequently enough the lag isn't really noticable unless you're doing something intense. DoEvents on a progress callback is the best you can get without multithreading or just shelling a command prompt (no way of knowing progress or many other details, plus the bad user experience).
Last edited by fafalone; May 5th, 2016 at 10:36 PM.
Re: [VB6] Use IFileOperation to replace SHFileOperation for modern Copy/Move box/prom
Your code work great !!!
even if it doesn't abort the copy operation of a more than 5 GO file, ...but less than 2 GO it DOES abort perfectly !!!
it works PERFECT nevertheless !
Anyway, even SuperCopier or Windows Explorer Copy also crash sometimes if you try to cancel a big file like over 3 GO.
===
I'd like know what to do if I want to set it to silent ?? (Hide the windows progress bar)
Re: [VB6] Use IFileOperation to replace SHFileOperation for modern Copy/Move box/prom
It accepts all the flags that SHFileOperation does + extra ones; use .Flags = IFO_SILENT
For clarity I made a new enum instead of expanding the old one; so all the new flags (and old) are in IFO_Flags
Re: [VB6] Use IFileOperation to replace SHFileOperation for modern Copy/Move box/prom
Hi fafalone.
I tried to do that but I couldn't locate where to put that ".Flags = IFO_SILENT" to hide the progress bar.
Can you please tell me exactly what to do?? (in which module, which function, which line)???
I'd like to use your application in replacement of the current one for Windows;
as I tested it and realized that it's faster than windows explorer copy. (FANTASTIC !!!)
Re: [VB6] Use IFileOperation to replace SHFileOperation for modern Copy/Move box/prom
If you're using the class from the demo project you set it along with all the other things you're setting for the class... cFO.Flags = IFO_SILENT (obviously before starting the action with cFO.CopyFiles and others)...
If you're using the FileOperation object directly, it's .SetOperationFlags IFO_SILENT, before .PerformOperations
It's odd that it would be faster than Explorer since it essentially *is* Explorer's dialog.. it's the same thing Explorer calls.
Re: [VB6] Use IFileOperation to replace SHFileOperation for modern Copy/Move box/prom
Hi fafalone,
Your app was working perfectly until it crashes when trying to copy.
I even downloaded a new Zip folder to check maybe... but still behaving the same.
when I click on "Copy With Events" or "Copy" button, I got an error saying :
Code:
Runntime : '91'
Object Variable or Block Variable with undefined
The error is located in the below function.
Public Sub IFileOperationProgressSink_PreCopyItem(ByVal dwFlags As Long, ByVal psiItem As IShellItem, ByVal psiDestinationFolder As IShellItem, ByVal pszNewName As Long)
Dim lPtr As Long
psiDestinationFolder.GetDisplayName SIGDN_FILESYSPATH, lPtr
Debug.Print "cFileOperationProgressSink.IFileOperationProgressSink_PreCopyItem.destfolder=" & BStrFromLPWStr(lPtr, True)
DoEvents
End Sub
The debugger highlights the below line.
psiDestinationFolder.GetDisplayName SIGDN_FILESYSPATH, lPtr
Re: [VB6] Use IFileOperation to replace SHFileOperation for modern Copy/Move box/prom
That's odd... you set a destination folder right? You can't copy an item without having a destination. Also I'm assuming the error number is right because 91 is 'not set', not 'undefined'. Although I can't reproduce the error by leaving the path blank on my system, or by specifying a path that doesn't exist. And what do you mean 'zip folder'... are you trying to copy out of or into a .zip?
So I'm pretty sure it's got something to do with the destination not being valid, but you can always either delete that line or do a sanity check (as any production app should do anyway; thorough error checking isn't traditionally included in demos) with If (psiDestinationFolder Is Nothing) = False Then
Re: [VB6] Use IFileOperation to replace SHFileOperation for modern Copy/Move box/prom
It's solved. Seems like it was due to my OWN mistake when entering the destination folder and was nothing to do with your code. I was entering a file name with an extension instead of a folder name.
And what do you mean 'zip folder'... are you trying to copy out of or into a .zip?
I meant I downloaded the new zip file (cFileOperationNew.zip) from your first post to check... but it was also behaving the same way. Anyway, it's okay now!!!
===
If you're using the class from the demo project you set it along with all the other things you're setting for the class...
cFO.Flags = IFO_SILENT (obviously before starting the action with cFO.CopyFiles and others)...
it's cFO.Flags = FOF_SILENT instead.
===
Question
Is it possible to make the copy operation PAUSE, and obviously CONTINUE ???
if you may implement those two functionalities, it'll be fantastic.
Re: [VB6] Use IFileOperation to replace SHFileOperation for modern Copy/Move box/prom
I'm sure I noted it somewhere... but for some compatibility reason I don't recall too well (this decision was nearly 1.5 years ago in the first version of oleexp), I left the FILEOP_Flags enum alone (the one that contains FOF_SILENT) so that SHFileOperation users didn't get confused/think they could use all the new FOFX_ flags that only apply to IFileOperation. So for IFileOperation I made a new enum, IFO_Flags, and just changed FOF_ and FOFX_ to IFO_ and IFOX_. So FOF_SILENT is the same as IFO_SILENT, and the IFO_ enum is in fact the one that applies to IFileOperation (and thus cFO.Flags).
Pause/Continue can't really be done well here... remember this is an interface to Window's functions, it's not the VB project or typelib that contains the code that performs the operation. The only things you have to work with are the callback events, but I believe even if you put a function in that didn't return, you'd need a DoEvents somewhere to have a responsive UI to continue, and that would just let the op continue.
What you can do is fall back to the lower level CopyFileEx and related APIs, which do offer such functionality.
Last edited by fafalone; May 11th, 2016 at 08:27 AM.
Re: [VB6] Use IFileOperation to replace SHFileOperation for modern Copy/Move box/prom
The TLB itself will work, but only if the interface being called is available, and few of the interfaces added by oleexp are available on XP, and none before.
Re: [VB6] Use IFileOperation to replace SHFileOperation for modern Copy/Move box/prom
Hi fafalone,
I tried to add a menu "File" and a sub menu "exit" in your App.
but curiously while a copy operation is in progress, when I click on "File" menu... the copy operation stops itself, and I have to click on the main form to make it continue !!!
I also put a "Command button" on the Form displaying a Msgbox,
but while a copy is in progress, when I click on that Cmd button to display the Msgbox, the copy stops as well !!!
My main concern is I don't want the copy operation to stop when I click some where else.
Do I only need multi-threading ???
How can I get rid of that ???
Last edited by freesix; May 16th, 2016 at 08:05 AM.
Re: [VB6] Use IFileOperation to replace SHFileOperation for modern Copy/Move box/prom
That's a limit of the DoEvents statement; DoEvents just processes other events, so execution won't resume if one of those other events (messagebox, etc) hasn't returned. You'd have to stick the operation in another thread to avoid that yes; the same issue would occur with any other method too; FSO, CopyFileEx.. none of them are asynchronous so all would have to be placed in another thread.
Re: [VB6] Use IFileOperation to replace SHFileOperation for modern Copy/Move box/prom
Thanks Fafalone for clarifying me.
But The problem is I'm still learning multi-threading, I mean I don't have very clear idea of what I'm doing,
and I'm not sure to make a stable application with that knowledge after a couple of weeks ONLY !!!