2012-06-07 22 views
8

Użyłem poprzednio programu MiniZip (zlib wrapper) do rozpakowania archiwów. MiniZip nie może być używany w aplikacjach Metro, ponieważ używa nieaktualnych API w "iowin32.c" - CreateFile() i SetFilePointer().Rozpakowywanie archiwum w systemie Windows 8

Pomyślałem, że będzie to łatwa łatka i utworzone "iowinrt.c" z CreateFile() i SetFilePointer() zastąpione przez CreateFile2() i SetFilePointerEx(). W ten sposób otrzymałem wersję MiniZip, która używa tylko zatwierdzonych API Win8, ale i tak okazała się bezużyteczna - zapomniałem o piaskownicy. Jeśli wybiorę plik przy użyciu FileOpenPicker() i przekazuję jego ścieżkę do zmodyfikowanego pliku MiniZip, nadal nie mogę go otworzyć - CreateFile2() zakończy się niepowodzeniem z "Odmowa dostępu". wiadomość.

Wygląda na to, że stare C API do dostępu do plików, jeśli teraz w większości przypadków są bezużyteczne; Rozumiem, że aby to naprawić, muszę ponownie zaimplementować mój "iowinrt" w C++/CX, korzystając z nowego dostępu do plików asynchronicznych. Czy są jakieś inne opcje? Myślę, że widziałem gdzieś, że WinRT ma funkcję kompresji/dekompresji, ale działa tylko na pojedynczych plikach, a nie na archiwach.

Dodatkowe wymagania, które są mi potrzebne do pracy w pamięci.

Przez chwilę myślałem, że rozwiązanie poprzez .NET Framework 4.5:

  1. znalazłem ten kawałek informacji na temat sposobu tworzenia klas .NET, które mogą być używane z C++/CX: http://social.msdn.microsoft.com/Forums/en-US/winappswithnativecode/thread/3ff383d0-0c9f-4a30-8987-ff2b23957f01

  2. .NET Framework 4.5 zawiera klasy ZipArchive i ZipArchiveEntry w 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

Pomyślałem, że mogę utworzyć bibliotekę klasy Metro z typem pliku wyjściowego WinMD eksponując ZipArchive i ZipArchiveEntry, a następnie użyj tego w moim projekcie C++/CX. Jednak nawet gdyby zadziałało, nie działałoby w pamięci; wygląda na to, że ZipArchive i ZipArchiveEntry działają tylko z plikami.

+0

Twoje podejście jest poprawne i proste, jeśli chodzi o bibliotekę minizip. Przekazujemy ścieżkę do minizip, a następnie wywołania zwrotne I/O odtwarzają wewnętrznie obiekt StorageFile. Czy sprawdziłeś w Monitorze procesu i sprawdziłeś połączenia I/O i związane z nimi błędy? –

+0

@Nathan Dzięki za sugestie - nie próbowałem tego, dasz mu szansę. Jednak w zasadzie zrezygnowałem z chwili obecnej na Win8 C++. Dopóki dokumentacja WinRT C++ nie poradzi sobie z dokumentacją C#/JS, dalsza praca nad programowaniem WinRT C++ to strata czasu. Ponieważ MS nie uważa dokumentacji C++ za ważną (patrz komentarze tutaj: http://social.msdn.microsoft.com/Forums/en-US/winappswithnativecode/thread/04cbe02b-700f-4be5-b6e9-fe98f3f2cd2e/) Myślę, że " Czekam przez rok lub dwa, zanim jeszcze raz pójdę. –

+0

To jest zły człowiek. Wygląda na to, że masz większość drogi. –

Odpowiedz

5

Czytanie z archiwum działa. Wyjaśnienie i kod poniżej, ale tak naprawdę tylko włamanie w tym momencie, aby sprawdzić, czy jest to w ogóle możliwe. Po prostu ciągle modyfikowałem rzeczy, dopóki nie uzyskałem czegoś działającego; jest to tylko przykład tego, co działa i nie jest to kod jakości produkcji (nie jest to ponowne wprowadzenie na początek). Istnieje niewątpliwie wiele rzeczy, które są złe/niepotrzebne/wtf, więc zachęcamy do korzystania z komentarzy, aby pomóc w sprzątaniu.

Jak już wcześniej wspomniano, nie wystarczy już przekazać ścieżkę do biblioteki - chyba że plik znajduje się w jednym ze znanych folderów (dokumenty, dom, multimedia, muzyka, zdjęcia, filmy lub filmy), a kończy się na "dostęp jest odmowa "wiadomości. Zamiast tego biblioteka musi być w stanie zaakceptować StorageFile ^, jako zwrócony z FileOpenPicker. Przynajmniej nie znalazłem innego sposobu, żeby to zrobić, może ktoś wie lepiej?

MiniZip zapewnia warstwę dostępu do systemu plików Windows dla zlib za pośrednictwem iowin32.h/.c. Wciąż działa w trybie pulpitu dla aplikacji w starym stylu, ale nie działa w przypadku aplikacji Metro, ponieważ używa nieaktualnych interfejsów API i opiera się na ścieżkach. Aby MiniZip działał w systemie Windows 8, wymagana jest pełna wersja iowin32.

Aby wszystko działało ponownie, pierwszą rzeczą było znalezienie sposobu na przesłanie StorageFile^aż do iowinrt (zamiennik Windows 8 dla iowin32). Na szczęście nie stanowiło to problemu, ponieważ MiniZip udostępnia dwa style funkcji otwartego pliku - te, które akceptują wskaźnik do znaku, a pozostałe akceptują wskaźnik do unieważnienia.Ponieważ^jest nadal tylko wskaźnikiem, rzucając StorageFile^do unieważnienia *, a następnie z powrotem do StorageFile^działa dobrze.

Teraz, gdy udało mi się przekazać StorageFile^do mojego nowego iowinrt, następnym problemem było to, jak zrobić nowy asynchroniczny interfejs API dostępu do plików C++ z Zlib. Aby obsługiwać bardzo stare kompilatory języka C, Zlib jest napisany starym K & C. Kompilator VisualStudio nie chce skompilować tego jako C++, musi być skompilowany jako C, a nowy iowinrt musi być skompilowany jako C++ oczywiście - miej to na uwadze podczas tworzenia projektu. Inną rzeczą wartą odnotowania w projekcie VS jest to, że zrobiłem to jako Biblioteka statyczna Visual Metro w stylu Windows Metro, chociaż DLL powinien również działać, ale musisz także zdefiniować makro do eksportu MiniZip API (nie próbowałem tego, nie wiem, które makro musisz użyć). Wydaje mi się, że musiałem również ustawić "Zużyj Windows Runtime Extension" (/ ZW), ustaw "Nie używając prekompilowanych nagłówków" i dodaj _CRT_SECURE_NO_WARNINGS i _CRT_NONSTDC_NO_WARNINGS do definicji preprocesora.

Co do samego iowinrt, podzieliłem go na dwa pliki. Jeden posiada dwie zapieczętowane klasy ref - obiekty czytelnika i pisarza; akceptują StorageFile ^. Czytnik implementuje Read, Tell, SeekFromBeginning, SeekFromCurrent i SeekFromEnd (powodem 3 metod Seek jest to, że ref zamknięte klasy muszą trzymać się typów RT i to najwyraźniej wyklucza wyliczenia, więc po prostu wziąłem łatwą trasę). Writer zaimplementował właśnie Write w tej chwili, jeszcze go nie używał.

to kod 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 znajduje się pomiędzy MiniZip i FileReader (i FileWriter). To zbyt długo, aby dać z siebie wszystko tutaj, ale to powinno być wystarczające, aby zrekonstruować reszty, ponieważ jest to najczęściej tylko więcej tego samego z różnych nazw funkcji, a także grono fill_winRT_filefuncxxx(), które są oczywiste:

#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); 
    } 

To wystarczy Aby uruchomić MiniZip (przynajmniej do czytania), ale musisz zadbać o to, jak wywołujesz funkcje MiniZip - ponieważ Metro jest o asynchronizmie, a blokowanie wątku UI zakończy się wyjątkiem, musisz zawinąć dostęp w zadaniach:

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

I dziękuję. Jesteś pierwszą osobą, która zachęciła mój pomysł do odwagi przekształcenia istniejącego projektu * nix OSS, zlib, w Windows Store. Rozszerzyłem twój pomysł i ostatecznie mogłem skompilować zlibstat.lib dla Windows Store x86, x64 i ARM archtectures] (http://stackoverflow.com/q/13900749/1712065). Jeśli chcesz, możemy przejść przez kolejne kroki, aby udoskonalić proces konwersji. – Annie

Powiązane problemy