Got a reading from the archive. The explanation and code are below, but it’s actually just hacking this point to see if this is possible at all. I just kept changing things until I managed to work; this is just an example of what works and by no means a product quality code (it does not start to run). There are undoubtedly many things that are bad / unnecessary / wtf, so feel free to use comments to help with the cleanup.
As mentioned earlier, it’s not enough to go to the library - if the file is not in one of the KnownFolders (documents, home, media, music, pictures, removable or video), you get a “denied access” message. Instead, the library should be able to accept StorageFile ^ returned from FileOpenPicker. At least I have not found another way to do this, maybe someone knows better?
MiniZip provides a level of access to the Windows file system for zlib through iowin32.h / .c. This still works in the desktop mode for old-style applications, but does not work for Metro applications, as it uses outdated APIs and relies on paths. In order for MiniZip to work on Windows 8, a complete redesign of iowin32 is required.
In order for everything to work again, first of all, it was necessary to find a way to transfer StorageFile ^ up to iowinrt (replacement of Windows 8 for iowin32). Fortunately, this is not a problem, because MiniZip provides two styles of open file functions - those that accept a pointer to char, and the others that accept a pointer to void. Since ^ is still just a pointer, different from StorageFile ^ to void * and back to StorageFile ^ works fine.
Now that I was able to transfer StorageFile ^ to the new iowinrt, the next problem was creating a new Cync async C ++ file access API with Zlib. To support very old C compilers, Zlib is written with the old K & R C style. The VisualStudio compiler refuses to compile it as C ++, it needs to be compiled as C, and the new iowinrt must be compiled as C ++, remember this when creating your project . Other things to note about the VS project is that I made it like a Visual C ++ Windows Metro Static Library, although the DLL should also work, but then you should also define a macro to export the MiniZip API (I have not tried this, I don’t know which macro you should use). I think I also needed to install the "Windows Runtime Extension Consumption" (/ ZW), set the "Don't use pre-compressed headers" and add _CRT_SECURE_NO_WARNINGS and _CRT_NONSTDC_NO_WARNINGS to the preprocessor definitions.
As for iowinrt itself, I split it into two files. One holds two locked ref classes — reader and writer objects; they accept StorageFile ^. Reader implements Read, Tell, SeekFromBeginning, SeekFromCurrent and SeekFromEnd (the reason for the three search methods is that ref-sealed classes should adhere to RT types and apparently exclude enumerations, so I just took the easy way). Writer implements only Write at the moment, has not used it yet.
This is the filereader code:
#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 is between MiniZip and FileReader (and FileWriter). It is too long to give everything here, but it should be enough to restore the rest, since it is basically just more, with different function names, plus a bunch of fill_winRT_filefuncxxx (), which are obvious:
#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); }
This is enough to get MiniZip (at least for reading), but you need to take care of what you call MiniZip functions - since Metro is all about asynchronous mode and blocking the user interface thread ends with an exception, you have to wrap access to tasks:
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) { [...]