2012-06-07 153 views
8

我以前使用MiniZip(zlib wrapper)来解压压缩文件。 MiniZip不能用于Metro应用程序,因为它在“iowin32.c”中使用了不推荐使用的API - CreateFile()和SetFilePointer()。在Windows上解压压缩文件8

我认为这将是一个简单的解决方案,并用CreateFile2()和SetFilePointerEx()替换CreateFile()和SetFilePointer()来创建“iowinrt.c”。虽然这样我获得了只使用经认可的Win8 API的MiniZip版本,但它仍然是无用的 - 我忘记了沙盒。如果我使用FileOpenPicker()选择一个文件并将其路径传递给我的修改后的MiniZip,我仍然无法打开它 - CreateFile2()将失败,并且“访问被拒绝”。信息。

因此,看起来旧的C API用于文件访问,如果现在大部分是无用的;这是我的理解,为了解决这个问题,我需要在C++/CX中使用新的异步文件访问来重新实现我的“iowinrt”。还有其他选择吗?我想我看到了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中包含ZipArchive和ZipArchiveEntry班System.IO.Compression: 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

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

+0

就Minizip库而言,您的方法是正确和直接的。您将路径传递给minizip,然后使用I/O回调在内部重新创建StorageFile对象。你看过Process Monitor并检查了I/O调用和相关的错误吗? –

+0

@Nathan谢谢你的建议 - 没有尝试过,会放弃它。不过,我现在基本上放弃了Win8 C++。在WinRT C++文档赶上C#/ JS文档之前,将进一步的努力投入到WinRT C++编程中是浪费时间。由于MS不认为C++文档很重要(请参阅这里的评论:http://social.msdn.microsoft.com/Forums/en-US/winappswithnativecode/thread/04cbe02b-700f-4be5-b6e9-fe98f3f2cd2e/)我想我'等一两年后再给它一次。 –

+0

这太糟糕了。它看起来像你在那里的大部分。 –

回答

5

从档案工作中获得阅读。下面的解释和代码,但在这一点上真的只是一个破解,看看它是否可能。我只是不断修改东西,直到我找到工作。这仅仅是一个有效的例子,决不是一个生产质量代码(它不能重新开始)。毫无疑问,很多事情是不好的/不必要的/跆拳道,所以随时用评论来帮助清理。

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

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

为了重新开始工作,首先要找到一种方法将StorageFile ^一直传递到iowinrt(Windows 8 for iowin32的替代品)。幸运的是,这不是一个问题,因为MiniZip提供了两种类型的开放文件函数 - 接受指向char的指针,其他接受指向void的指针。由于^仍然只是一个指针,因此将StorageFile ^转换为void *并返回到StorageFile ^工作正常。

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

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

这是的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) 
      { 
       [...] 
+0

谢谢。您是第一个鼓励我敢于将现有* nix OSS项目zlib转换为Windows应用商店的人。我扩展了你的想法,并最终能够[编译用于Windows Store x86,x64和ARM架构的zlibstat.lib](http://stackoverflow.com/q/13900749/1712065)。如果您愿意,我们可以逐步完成转换过程。 – Annie