Could someone tell me if it is possible to use SHFileOperation() function to
copy files to a ZIP file?
I have seen a C# program which uses Shell32.Folder CopyHere() function to do
this successfully and I am trying to port this to C++ using standard Shell
functions. I have read that CopyHere is a wrapper around SHFileOperation so I
am trying to use that.
My problem is that SHFileOperation returns success, but the source file is
not copied to the destination zip file. Instead a copy of the source file is
created in the same folder as the source file.
I have set the wFunc field of the SHFILEOPSTRUCT structure that I pass into
SHFileOperation to FO_COPY and the fFlags field has been set to
FOF_NOCONFIRMATION | FOF_SILENT | FOF_RENAMEONCOLLISION | FOF_NOCONFIRMMKDIR
| FOF_NOERRORUI.
Can I use SHFileOperation() to add files to a zip folder? If so, what is the
right way to do this? If not, is there another method to accomplish this? Any
information is greatly appreciated.
Thanks,
Priya
No, SHFileOperation only deals with filesystem files, not virtual namespace objects.
> I have seen a C# program which uses Shell32.Folder CopyHere()
> function to do this successfully and I am trying to port this to C++
> using standard Shell functions. I have read that CopyHere is a
> wrapper around SHFileOperation so I am trying to use that.
CopyHere uses SHFileOperation only if the parameter is a string containing a wildcard path. Otherwise, it simulates a drag-drop operation. You can code this directly, but it's probably easier just to use the shell folder object (it is a COM object that can be used in C++ - it does not require C#).
--
Jim Barry, MVP (Windows SDK)
Thank you for your response.
I have not worked with Shell objects before, so could you please give me
some more information about using the Shell folder object in C++? Should I be
calling CoCreateInstance to create the object? What CLSID and IID should I
pass to CoCreateInstance?
I could not find the relevant information in the "Shell Objects for C++"
section of MSDN.
Thanks,
Priya
The CLSID is {13709620-C279-11CE-A49E-444553540000} (CLSID_Shell). The interface is IShellDispatch, IID {D8F015C0-C278-11CE-A49E-444553540000}. The definitions are in ShlDisp.idl/ShlDisp.h.
> I could not find the relevant information in the "Shell Objects for
> C++" section of MSDN.
It's in the "Shell Objects for Scripting and Microsoft Visual Basic" section:
http://msdn2.microsoft.com/en-us/library/bb774094.aspx
Thanks again for the information. I wrote a sample program to test this out,
but could not get it to work properly. The program compiles and runs fine,
but the source file is not copied to the destination compressed folder. When
I step through the code I see that CopyHere() returns S_OK even though the
file is not copied to the ZIP file. I tried replacing the compressed folder
with a regular folder and CopyHere() worked as expected. Here is my sample
code:
int _tmain(int argc, _TCHAR* argv[])
{
DWORD strlen = 0;
char szFrom[] = "C:\\Test.log",
szTo[] = "C:\\Sample.zip";
HRESULT hResult;
IShellDispatch *pISD;
Folder *pToFolder = NULL;
VARIANT vDir, vFile, vOpt;
BSTR strptr1, strptr2;
CoInitialize(NULL);
hResult = CoCreateInstance(CLSID_Shell, NULL, CLSCTX_INPROC_SERVER,
IID_IShellDispatch, (void **)&pISD);
if (SUCCEEDED(hResult))
{
strlen = MultiByteToWideChar(CP_ACP, 0, szTo, -1, 0, 0);
strptr1 = SysAllocStringLen(0, strlen);
MultiByteToWideChar(CP_ACP, 0, szTo, -1, strptr1, strlen);
VariantInit(&vDir);
vDir.vt = VT_BSTR;
vDir.bstrVal = strptr1;
hResult = pISD->NameSpace(vDir, &pToFolder);
if (SUCCEEDED(hResult))
{
strlen = MultiByteToWideChar(CP_ACP, 0, szFrom, -1, 0, 0);
strptr2 = SysAllocStringLen(0, strlen);
MultiByteToWideChar(CP_ACP, 0, szFrom, -1, strptr2, strlen);
VariantInit(&vFile);
vFile.vt = VT_BSTR;
vFile.bstrVal = strptr2;
VariantInit(&vOpt);
vOpt.vt = VT_I4;
vOpt.lVal = 4; // Do not display a progress dialog box
hResult = pToFolder->CopyHere(vFile, vOpt);
SysFreeString(strptr2);
pToFolder->Release();
}
SysFreeString(strptr1);
pISD->Release();
}
CoUninitialize();
return 0;
}
Is this the proper way to do it or am I missing some steps? If I wanted to
pass in FolderItem or FolderItems to CopyHere() function what is the variable
type to use?
Please excuse the very basic questions. Normally I look up things like this
online, but there seems to surprisingly little information about this on MSDN
or the other developer sites like CodeProject. Or maybe I am not looking in
the right places. Thanks again for taking the time to answer my questions.
Regards,
Priya
I think the problem here is that the copy operation is scheduled on a background thread, and your process exits before the thread has had a chance to run. I guess you need to look out for a new thread being created and wait for it to finish before exiting the process.
Yes, that was the problem. I have got it working now. Thanks for all your
help.
Regards,
Priya
And what if the thread list differences cannot be reliably detected
(for example when having a DLL that creates threads independent of the
applicaction)? Is there no reliable way to ask the shell object? It
would mean it's pretty useless as you can never be sure all files are
packed into the ZIP file.
Periodically checking by opeining the file READWRITE, DENY_ALL does
not work - possibly the file is not opened by the thread after some
CopyHere() calls.
Do you have any idea?
Christian
The correct way is to use SHSetInstanceExplorer, but not all context menu handlers conform to this protocol. There is a good discussion of the issue here:
http://www.eluent.com/runmenu.htm
"Jim Barry" wrote:
Can you refer me to some sample code showing how to look out for a new
thread being created and waiting for it to finish? I need to detect when
CopyHere completes. Thanks.
No sample code, but the idea is basically this:
1) Enumerate current threads in the process using Thread32First/Thread32Next
2) Start the operation
3) Enumerate the threads again
4) Wait for any new threads using WaitForMultipleObjects
Of course, if the operation creates any new threads that don't exit, then you have a problem.
--
Jim Barry, Microsoft MVP