My application (the Endless OS installer for Windows) uses methods on IShellDispatch (provided by Shell32.dll), to extract a Zip file (example). In various modes of operation, this file may be downloaded from the internet to a fixed disk; on an exFAT filesystem on a USB stick; or in an ISO 9660 (with Joliet extensions) image which may be mounted as a virtual drive, or written to a DVD. In all cases but the last, extracting the Zip file works; but when the Zip file is on a DVD, all that's created in the target directory is the directory structure (EFI\BOOT\) for the first file in the archive (EFI\BOOT\bootx64.efi); neither that file, nor any other files in that directory or any other directory, are extracted. With exactly the same Zip file on any other medium — including inserting the ISO into a VirtualBox virtual optical drive — the problem disappears.

The original C++ code where I first saw this problem is here. It looks like this (with error handling removed, since all methods return a successful HRESULT in my testing, and the FOF_NO_UI also removed in case that was masking an error message):

void UnpackZip(const CComBSTR source, const CComBSTR dest) {
    CComPtr<IShellDispatch>    pISD;
    CComPtr<Folder>            pToFolder, pFromFolder;
    CComPtr<FolderItems>       folderItems;

    CoCreateInstance(CLSID_Shell, NULL, CLSCTX_INPROC_SERVER, IID_IShellDispatch,
        (void **)&pISD);
    pISD->NameSpace(CComVariant(dest), &pToFolder);
    pISD->NameSpace(CComVariant(source), &pFromFolder);
    pFromFolder->Items(&folderItems);
    pToFolder->CopyHere(CComVariant(folderItems), CComVariant(0));
}

I can reproduce this problem both by attempting to unpack the Zip file from Windows Explorer GUI (which does not report any errors), and by running the following in PowerShell, so I am reasonably sure it's not my application code that's at fault:

PS> $shell = new-object -ComObject shell.application
PS> $zip = $shell.NameSpace("D:\endless\eos-eos3.3-amd64-amd64.171122-232702.base.boot.zip")
PS> $target = $shell.NameSpace("C:\test")
PS> $target.CopyHere($zip.items())

If I explicitly iterate over the top-level folders in the Zip file, as follows, then some but not all files from each folder are extracted (and still none in EFI\BOOT\):

PS> foreach ($item in $zip.items()) { $target.CopyHere($item) }

If I explicitly select that first file which is not unpacked, no error is raised:

PS> $item = $zip.items().Item(0).GetFolder().Items().Item(0).GetFolder().Items().item(0);
PS> $item
Application  : System.__ComObject
Parent       : System.__ComObject
Name         : bootx64.efi
Path         :
GetLink      :
GetFolder    :
IsLink       : False
IsFolder     : False
IsFileSystem : False
IsBrowsable  : False
ModifyDate   : 22/11/2017 23:33:56
Size         : 1157984
Type         : EFI File
PS> $target.CopyHere($item)

But it's still not unpacked to $target. The DVD drive does not even spin up!

If I copy exactly the same Zip file to a fixed drive – or mount the DVD ISO as a virtual disk, whether within Windows, or from the outside via the VirtualBox VM Windows is running in – everything works correctly. The problem only occurs when the archive is really on a physical DVD. I've received many reports of this problem from users with various hardware, so it's not my DVD drive or laptop. I personally have only tested and reproduced it on Windows 10 (build 14393 and 15063, at least); I'm not sure whether it can be reproduced on older Windows versions but since Windows 10 is the most commonly-used version by users of this application, it's a moot point whether this worked on older versions.

The files which are not unpacked are all those ending .efi (EFI executables) and those ending .mod (legacy BIOS GRUB modules). This is totally deterministic. But I'm stumped as to why the shell would take such a disliking to certain files, only when the archive is on a DVD.

My application can work around this problem by copying the Zip file to the hard disk before extracting it. But the question remains: why is this happening? And at a higher level: short of stepping through the compiled Shell32.dll code in a debugger, how could I diagnose what's going wrong?

0

There are 0 best solutions below