在Windows 8上解压缩存档

时间:2012-06-07 06:30:42

标签: zip microsoft-metro zlib c++-cx

我之前使用MiniZip(zlib包装器)来解压缩档案。 MiniZip不能用于Metro应用程序,因为它在" iowin32.c"中使用了已弃用的API。 - CreateFile()和SetFilePointer()。

我认为这将是一个简单的修复并创建" iowinrt.c"将CreateFile()和SetFilePointer()替换为CreateFile2()和SetFilePointerEx()。虽然这样我获得了一个只使用经过批准的Win8 API的MiniZip版本,但它仍然没用 - 我忘记了沙盒。如果我使用FileOpenPicker()选择一个文件并将其路径传递给我修改过的MiniZip,我仍然无法打开它 - CreateFile2()将失败并且#34;访问被拒绝。"消息。

所以看来旧的C API用于文件访问,如果现在几乎无用;我的理解是,为了解决这个问题,我需要重新实现我的" iowinrt"在C ++ / CX中使用新的异步文件访问。还有其他选择吗?我想我看到某个地方WinRT确实有压缩/解压缩功能,但它只适用于单个文件,而不适用于档案。

我需要在内存中工作的附加要求。

我认为我有一个通过.NET Framework 4.5的解决方案:

  1. 我发现了一条关于如何创建可以在C ++ / CX中使用的.NET类的信息: http://social.msdn.microsoft.com/Forums/en-US/winappswithnativecode/thread/3ff383d0-0c9f-4a30-8987-ff2b23957f01

  2. .NET Framework 4.5在System.IO.Compression中包含ZipArchive和ZipArchiveEntry类: http://msdn.microsoft.com/en-us/library/system.io.compression.ziparchive%28v=vs.110%29.aspx#Y0 http://msdn.microsoft.com/en-us/library/system.io.compression.ziparchiveentry%28v=vs.110%29.aspx#Y0

  3. 我以为我可以使用WinMD输出类型创建C#Metro类库,公开ZipArchive和ZipArchiveEntry,然后在我的C ++ / CX项目中使用它。但是,即使它起作用,也不会在内存中起作用; ZipArchive和ZipArchiveEntry似乎只适用于文件。

1 个答案:

答案 0 :(得分:5)

从存档工作中读取。下面的解释和代码,但在这一点上真的只是一个黑客,看看它是否可能。我一直在修改东西,直到我得到了一些工作;这只是一个有效的例子,绝不是一个生产质量代码(它不能重新开始)。毫无疑问,很多事情是糟糕的/不必要的/ wtf,所以请随意使用评论来帮助清理。

如前所述,将路径传递到库已经不够了 - 除非文件位于KnownFolders(文档,家庭,媒体,音乐,图片,可移动或视频)中,否则最终会导致访问被拒绝“ 信息。相反,库必须能够接受从FileOpenPicker返回的StorageFile ^。至少我没有找到任何其他方法去做,也许有人知道更好吗?

MiniZip通过iowin32.h / .c为zlib提供Windows文件系统访问层。这仍然适用于旧式应用程序的桌面模式,但不适用于Metro应用程序,因为它使用已弃用的API并依赖于路径。要让MiniZip在Windows 8上运行,需要完全重写iowin32。

为了让事情再次发挥作用,首先要找到一种方法将StorageFile ^一直传递到iowinrt(Windows 8替换为iowin32)。幸运的是,这不是问题,因为MiniZip提供了两种样式的打开文件函数 - 一种接受指向char的指针,另一种接受指向void的指针。由于^仍然只是一个指针,因此将StorageFile ^转换为void *而不是返回StorageFile ^可以正常工作。

现在我能够将StorageFile ^传递给我的新iowinrt,接下来的问题是如何使用Zlib使新的异步C ++文件访问API工作。为了支持非常古老的C编译器,Zlib是用旧的K& R样式C编写的.VisualStudio编译器将拒绝将其编译为C ++,它必须编译为C,新的iowinrt当然必须编译为C ++ - 在创建项目时请记住这一点。关于VS项目的其他注意事项是我将它作为Visual C ++ Windows Metro样式静态库做了虽然DLL也应该可以工作但是你还必须定义宏来导出MiniZip API(我没试过这个,不知道哪个宏你必须使用)。我想我还必须设置“消耗Windows运行时扩展”(/ ZW),设置“不使用预编译标头”并将_CRT_SECURE_NO_WARNINGS和_CRT_NONSTDC_NO_WARNINGS添加到预处理器定义。

至于iowinrt本身,我将它分成两个文件。一个人拥有两个密封的参考类 - 读者和作者对象;他们接受StorageFile ^。 Reader实现了Read,Tell,SeekFromBeginning,SeekFromCurrent和SeekFromEnd(3 Seek方法的原因是因为ref密封类必须坚持使用RT类型,而且显然不包括枚举,所以我只采取了简单的方法)。 Writer现在只执行Write,还没有使用它。

这是FileReader代码:

    #include "pch.h"
    #include "FileAccess.h"  // FileReader and FileWriter

    using namespace Concurrency;
    using namespace Windows::Security::Cryptography;
    using namespace CFileAccess;

    FileReader::FileReader(StorageFile^ archive)
    {
        if (nullptr != archive)
        {
            create_task(archive->OpenReadAsync()).then([this](IRandomAccessStreamWithContentType^ archiveStream)
            {
                if (nullptr != archiveStream)
                {
                    _readStream = archiveStream;
                }
            }).wait();
        }
    } // end of constructor

    int32 FileReader::Read(WriteOnlyArray<byte>^ fileData)
    {
        int32 bytesRead = 0;

        if ((nullptr != _readStream) && (fileData->Length > 0))
        {
            try
            {
                auto inputStreamReader = ref new DataReader(_readStream);
                create_task(inputStreamReader->LoadAsync(fileData->Length)).then([&](task<unsigned int> dataRead)
                {
                    try
                    {
                        bytesRead = dataRead.get();
                        if (bytesRead)
                        {
                            inputStreamReader->ReadBytes(fileData);
                        }
                    }
                    catch (Exception^ e)
                    {
                        bytesRead = -1;
                    }

                    inputStreamReader->DetachStream();
                }).wait();
            }
            catch (Exception^ e)
            {
                bytesRead = -1;
            }
        }

        return (bytesRead);
    } // end of method Read()

    int64 FileReader::Tell(void)
    {
        int64 ret = -1;

        if (nullptr != _readStream)
        {
            ret = _readStream->Position;
        }

        return (ret);
    } // end of method Tell()

    int64 FileReader::SeekFromBeginning(uint64 offset)
    {
        int64 ret = -1;

        if ((nullptr != _readStream) && (offset < _readStream->Size))
        {
            _readStream->Seek(offset);
            ret = 0;
        }

        return (ret);
    } // end of method SeekFromBeginning()

    int64 FileReader::SeekFromCurrent(uint64 offset)
    {
        int64 ret = -1;

        if ((nullptr != _readStream) && ((_readStream->Position + offset) < _readStream->Size))
        {
            _readStream->Seek(_readStream->Position + offset);
            ret = 0;
        }

        return (ret);
    } // end of method SeekFromCurrent()

    int64 FileReader::SeekFromEnd(uint64 offset)
    {
        int64 ret = -1;

        if ((nullptr != _readStream) && ((_readStream->Size - offset) >= 0))
        {
            _readStream->Seek(_readStream->Size - offset);
            ret = 0;
        }

        return (ret);
    } // end of method SeekFromEnd()

iowinrt位于MiniZip和FileReader(以及FileWriter)之间。在这里提供所有内容太长了,但这应该足以重建其余部分,因为它主要只是更多相同的不同函数名称,加上一堆显而易见的fill_winRT_filefuncxxx():

    #include "zlib.h"
    #include "ioapi.h"
    #include "iowinrt.h"
    #include "FileAccess.h"

    using namespace Windows::Security::Cryptography;
    using namespace Platform;
    using namespace CFileAccess;

    static FileReader^ g_fileReader = nullptr;
    static FileWriter^ g_fileWriter = nullptr;
    static StorageFile^ g_storageFile = nullptr;

    [...]

    static voidpf winRT_translate_open_mode(int mode)
    {
        if (nullptr != g_storageFile)
        {
            if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ)
            {
                g_fileWriter = nullptr;
                g_fileReader = ref new FileReader(g_storageFile);
            }
            else if (mode & ZLIB_FILEFUNC_MODE_EXISTING)
            {
                g_fileReader = nullptr;
                g_fileWriter = ref new FileWriter(g_storageFile);
            }
            else if (mode & ZLIB_FILEFUNC_MODE_CREATE)
            {
                g_fileReader = nullptr;
                g_fileWriter = ref new FileWriter(g_storageFile);
            }
        }
        return (nullptr != g_fileReader ? reinterpret_cast<voidpf>(g_fileReader) : reinterpret_cast<voidpf>(g_fileWriter));
    }


    voidpf ZCALLBACK winRT_open64_file_func (voidpf opaque,const void* storageFile,int mode)
    {
        g_storageFile = reinterpret_cast<StorageFile^>(const_cast<void*>(storageFile));
        return (winRT_translate_open_mode(mode));
    }

    [...]

    Long ZCALLBACK winRT_read_file_func (voidpf opaque, voidpf stream, void* buf,uLong size)
    {
        uLong bytesRead = 0;
        if (nullptr != g_fileReader)
        {
            auto fileData = ref new Platform::Array<byte>(size);
            bytesRead = g_fileReader->Read(fileData);
            memcpy(buf, fileData->Data, fileData->Length);
        }
        return (bytesRead);
    }


    uLong ZCALLBACK winRT_write_file_func (voidpf opaque,voidpf stream,const void* buf,uLong size)
    {
        uLong bytesWritten = 0;
        if (nullptr != g_fileWriter)
        {
            auto bytes = ref new Array<uint8>(reinterpret_cast<uint8*>(const_cast<void*>(buf)), size);
            IBuffer ^writeBuffer = CryptographicBuffer::CreateFromByteArray(bytes);
            bytesWritten = g_fileWriter->Write(writeBuffer);
        }
        return (bytesWritten);
    }

    long ZCALLBACK winRT_tell_file_func (voidpf opaque,voidpf stream)
    {
        long long ret = 0;
        if (nullptr != g_fileReader)
        {
            ret = g_fileReader->Tell();
        }
        return (static_cast<long>(ret));
    }

    ZPOS64_T ZCALLBACK winRT_tell64_file_func (voidpf opaque, voidpf stream)
    {
        ZPOS64_T ret = 0;
        if (nullptr != g_fileReader)
        {
            ret = g_fileReader->Tell();
        }
        return (ret);
    }

    [...]

    long ZCALLBACK winRT_seek64_file_func (voidpf opaque, voidpf stream,ZPOS64_T offset,int origin)
    {
        long long ret = -1;
        if (nullptr != g_fileReader)
        {
            switch (origin)
            {
            case ZLIB_FILEFUNC_SEEK_CUR :
                ret = g_fileReader->SeekFromCurrent(offset);
                break;
            case ZLIB_FILEFUNC_SEEK_END :
                ret = g_fileReader->SeekFromEnd(offset);
                break;
            case ZLIB_FILEFUNC_SEEK_SET :
                ret = g_fileReader->SeekFromBeginning(offset);
                break;
            default:
                // should never happen!
                ret = -1;
                break;
            }
        }
        return (static_cast<long>(ret));
    }

    int ZCALLBACK winRT_close_file_func (voidpf opaque, voidpf stream)
    {
        g_fileWriter = nullptr;
        g_fileReader = nullptr;
        return (0);
    }

    int ZCALLBACK winRT_error_file_func (voidpf opaque,voidpf stream)
    {
        /// @todo Get errors from FileAccess
        return (0);
    }

这足以让MiniZip继续运行(至少是为了阅读),但是你必须注意你如何调用MiniZip函数 - 因为Metro是关于异步的,阻塞UI线程最终会出现异常,你必须将访问权限包含在内任务:

    FileOpenPicker^ openPicker = ref new FileOpenPicker();
    openPicker->ViewMode = PickerViewMode::List;
    openPicker->SuggestedStartLocation = PickerLocationId::ComputerFolder;
    openPicker->FileTypeFilter->Append(".zip");
    task<IVectorView<StorageFile^>^>(openPicker->PickMultipleFilesAsync()).then([this](IVectorView<StorageFile^>^ files)
    {
        if (files->Size > 0)
        {
            std::for_each(begin(files), end(files), [this](StorageFile ^file)
            {   // open selected zip archives
                create_task([this, file]()
                {
                    OpenArchive(file);
                    [...]
                });
            });
        }
        else
        {
            rootPage->NotifyUserBackgroundThread("No files were returned.", NotifyType::ErrorMessage);
        }
    });

    [...]

    bool OpenArchive(StorageFile^ archive)
    {
        bool isArchiveOpened = false;

        if (nullptr != archive)
        { // open ZIP archive
            zlib_filefunc64_def ffunc;
            fill_winRT_filefunc64(&ffunc); 

            unzFile archiveObject = NULL;
            create_task([this, &ffunc, archive]()
            {
                archiveObject = unzOpen2_64(reinterpret_cast<const void*>(archive), &ffunc);
            }).wait();

            if (NULL != archiveObject)
            {
                [...]