Search code examples
windowsbatch-fileunzipwindows-shell

Does Window's Shell CopyHere method support redirection?


I use Window's Shell CopyHere method to unzip archives in some batches. It generally works, as shown in the script example below, but gives an error in some cases. In particular, I found it gives an error when unzipping an archive to a special system folder like Downloads, if it was moved by a user to a new location, despite the folder symlink was left by OS in its default location in the user profile, and the user has write permissions for the new location.

Is there a way to work around this limitation or modify the code below without changing its overall batch hybrid approach?

<!-- : Begin batch script
@echo off
set "dir=%temp%" & set "file=%USERPROFILE%\Backup\archive.zip\"
cscript //nologo "%~f0?.wsf" "%dir%" "%file%"
exit /b

----- Begin wsf script --->
<job><script language="VBScript">
 set fso = CreateObject("Scripting.FileSystemObject")
 Set Ag=Wscript.Arguments
 If NOT fso.FolderExists(Ag(0)) Then
 fso.CreateFolder(Ag(0))
 End If
 set objShell = CreateObject("Shell.Application")
 set FilesInZip = objShell.NameSpace(Ag(1)).items
 objShell.NameSpace(Ag(0)).CopyHere(FilesInZip)
 set fso = Nothing
 set objShell = Nothing
</script></job>

:: Error if user profile's Downloads folder was moved from default location

\test.bat?.wsf(15, 3) Microsoft VBScript runtime error: Object required: 'objShell.NameSpace(...)'

Solution

  • It appears CopyHere method doesn't support special folder path redirection. If a user changed location of a special Windows folder listed in the Enumerating Special Folders document like My Documents, a simple VBS construct would allow to find current folder and file path:

    set objShell = CreateObject("Shell.Application")
    set objFolder = objShell.Namespace(&H5&)
    set FilesInZip = objShell.NameSpace(Ag(1)).items
    objShell.NameSpace(objFolder).CopyHere(FilesInZip)
    

    Otherwise, VBS doesn't offer any method to find current special folder path. The only way I found to get it in a hybrid batch & VBS script is by querying the Registry key HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders. Current .zip file path is then sent as cscript argument using script in question.

    For example, if archive.zip to be unzipped to current Downloads folder:

    @echo off
    setlocal EnableDelayedExpansion
    set "key=HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
    for /f "tokens=* Skip=2" %%g in ('reg query "%key%" 2^>nul') do (
        echo %%g | find /i "Downloads" >nul && for %%o in (%%g) do (
            set "disk=%%o" & if exist "!disk:~0,3!" set dir=%%o ))
    echo !dir!
    
    K:\Downloads