Чтение из архива. Объяснение и код ниже, но на самом деле просто взломать этот момент, чтобы увидеть, возможно ли это вообще. Я просто продолжал изменять вещи, пока у меня не получилось что-то работать; это всего лишь пример того, что работает и отнюдь не является кодом качества продукции (для его запуска не требуется повторный вход). Несомненно, есть много вещей, которые являются плохими/ненужными/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)
{
[...]
Ваш подход правилен и прямолинейен до тех пор, пока идет библиотека minizip. Вы проходите путь до minizip, после чего обратные вызовы ввода-вывода воссоздают объект StorageFile внутри. Вы смотрели в Process Monitor и изучали вызовы ввода/вывода и связанные с ними ошибки? –
@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/) Я думаю, Подождите несколько лет, прежде чем дать ему еще один шаг. –
Это очень плохой человек. Похоже, вы получили большую часть пути. –