2012-06-07 199 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