2012-06-07 3 views
8

Я ранее использовал MiniZip (zlib wrapper) для распаковки архивов. MiniZip нельзя использовать для приложений Metro, поскольку использует устаревшие API в «iowin32.c» - CreateFile() и SetFilePointer().Распакованный архив на Windows 8

Я думал, что это будет легко исправить и создать «iowinrt.c» с помощью CreateFile() и SetFilePointer(), замененных CreateFile2() и SetFilePointerEx(). Хотя таким образом я получил версию MiniZip, которая использует только одобренные API Win8, она по-прежнему оказалась бесполезной - я забыл о песочнице. Если я выберу файл с помощью FileOpenPicker() и передам его путь к моему модифицированному MiniZip, я все равно не могу его открыть - CreateFile2() завершится неудачей с «Доступ запрещен». сообщение.

Таким образом, кажется, что старый C API для доступа к файлам, если теперь в основном бесполезен; я понимаю, что для исправления этого мне нужно будет переопределить мой «iowinrt» в C++/CX, используя новый доступ к async-файлу. Есть ли другие варианты? Я думаю, что я где-то видел, что у WinRT есть функции сжатия/разблокировки, но он работает только с отдельными файлами, а не с архивами.

Дополнительные требования, которые мне нужны для работы в памяти.

На мгновение я думал, что решение с помощью .NET Framework 4.5:

  1. Я нашел этот кусок информации о том, как создавать классы .NET, которые могут быть использованы с C++/CX: 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

Я думал, что смогу создать C# Metro Class Library с WinMD. Однако, даже если он сработает, он не будет работать в памяти; похоже, что ZipArchive и ZipArchiveEntry работают только с файлами.

+0

Ваш подход правилен и прямолинейен до тех пор, пока идет библиотека minizip. Вы проходите путь до minizip, после чего обратные вызовы ввода-вывода воссоздают объект StorageFile внутри. Вы смотрели в Process Monitor и изучали вызовы ввода/вывода и связанные с ними ошибки? –

+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

Чтение из архива. Объяснение и код ниже, но на самом деле просто взломать этот момент, чтобы увидеть, возможно ли это вообще. Я просто продолжал изменять вещи, пока у меня не получилось что-то работать; это всего лишь пример того, что работает и отнюдь не является кодом качества продукции (для его запуска не требуется повторный вход). Несомненно, есть много вещей, которые являются плохими/ненужными/wtf, поэтому не стесняйтесь использовать комментарии, чтобы помочь с очисткой.

Как уже упоминалось ранее, недостаточно пройти путь к библиотеке - если файл не находится в одном из KnownFolders (документы, домашний, мультимедийный, музыкальный, съемный или видеоролик), вы получаете «доступ отклонено ". Вместо этого библиотека должна иметь возможность принимать StorageFile ^, возвращенные из FileOpenPicker. По крайней мере, я не нашел другого способа сделать это, может быть, кто-то знает лучше?

MiniZip обеспечивает уровень доступа к файловой системе Windows для zlib через iowin32.h/.c. Это все еще работает в настольном режиме для приложений старого стиля, но не работает для приложений Metro, поскольку использует устаревшие API и опирается на пути. Чтобы MiniZip работал в Windows 8, требуется полная переработка iowin32.

Чтобы все снова работало, прежде всего нужно было найти способ передать StorageFile^вплоть до iowinrt (замена Windows 8 для iowin32). К счастью, это не проблема, поскольку MiniZip предоставляет два стиля открытых файловых функций - те, которые принимают указатель на char, а остальные принимают указатель на void.Так как^все еще просто указатель, то отличное от StorageFile^до void * и обратно в StorageFile^отлично работает.

Теперь, когда я смог передать StorageFile^на мой новый iowinrt, следующей проблемой было создание нового API доступа к файлам async C++ с Zlib. Для поддержки очень старых компиляторов C Zlib написан со старым K & R. Стиль C. Компилятор VisualStudio откажется компилировать это как C++, его нужно скомпилировать как C, а новый iowinrt должен быть скомпилирован как C++, помните об этом при создании своего проекта. Другие вещи, чтобы отметить проект VS, это то, что я сделал это как Visual C++ Windows Metro Static Library, хотя DLL также должна работать, но тогда вы также должны определить макрос для экспорта MiniZip API (я этого не пробовал, не знаю, какой макрос вы должны использовать). Я думаю, что мне также пришлось установить «Расходуйте Windows Runtime Extension» (/ ZW), установите «Не использовать предварительно сжатые заголовки» и добавьте _CRT_SECURE_NO_WARNINGS и _CRT_NONSTDC_NO_WARNINGS в определения препроцессора.

Что касается самого iowinrt, я разделил его на два файла. Один держит два запертых класса ref - объекты читателя и писателя; они принимают StorageFile ^. Reader реализует Read, Tell, SeekFromBeginning, SeekFromCurrent и SeekFromEnd (причина для трех методов поиска заключается в том, что ref-sealed классы должны придерживаться типов RT и, по-видимому, исключают перечисления, поэтому я просто взял легкий путь). Писатель реализует только пишут в данный момент, еще не использовал его.

Это 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 все об асинхронном режиме и блокирует поток пользовательского интерфейса, в конечном итоге будет исключение, вы должны обернуть доступ к задачам:

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 Store. Я расширил вашу идею и, наконец, смог [скомпилировать zlibstat.lib для Windows Store x86, x64 и ARM archtectures] (http://stackoverflow.com/q/13900749/1712065). Если вы хотите, мы можем пройти через шаги, чтобы уточнить процесс преобразования. – Annie

Смежные вопросы