我以前使用过MiniZip(zlib包装器)来解压缩档案。 MiniZip不能用于Metro应用程序,因为它在“ iowin32.c”中使用了已弃用的API-CreateFile()和SetFilePointer()。
我认为这将是一个简单的修复程序,并用CreateFile()和SetFilePointer()替换为CreateFile2()和SetFilePointerEx()创建了“ iowinrt.c”。通过这种方式,我获得了仅使用经过批准的Win8 API的MiniZip版本,但事实证明它仍然没有用-我忘记了沙盒。如果我使用FileOpenPicker()选择一个文件并将其路径传递给修改后的MiniZip,我仍然无法打开它-CreateFile2()将失败,并显示“访问被拒绝”。信息。
因此,似乎旧的C API用于文件访问(如果现在几乎没有用);据我了解,为了解决此问题,我需要使用新的异步文件访问权限在C ++ / CX中重新实现我的“ iowinrt”。还有其他选择吗?我想我在某个地方看到WinRT确实具有压缩/解压缩功能,但是它仅适用于单个文件,而不适用于存档。
我需要它在内存中工作的其他要求。
有一阵子我以为我通过.NET Framework 4.5有一个解决方案:
我发现了有关如何创建可从C ++ / CX使用的.NET类的信息:
http://social.msdn.microsoft.com/Forums/en-US/winappswithnativecode/thread/3ff383d0-0c9f-4a30-8987-ff2b23957f01
.NET Framework 4.5在System.IO.Compression中包含ZipArchive和ZipArchiveEntry类:
http://msdn.microsoft.com/en-us/library/system.io.compression.ziparchive%28v=vs.110%29.aspx#Y0
http://msdn.microsoft.com/zh-cn/library/system.io.compression.ziparchiveentry%28v=vs.110%29.aspx#Y0
我以为可以用WinMD输出类型创建C#Metro类库,并公开ZipArchive和ZipArchiveEntry,然后在我的C ++ / CX项目中使用它。但是,即使它起作用了,也不会在内存中起作用。看来ZipArchive和ZipArchiveEntry仅适用于文件。
最佳答案
从归档工作中读取信息。下面的解释和代码,但实际上只是一个hack,以查看是否有可能。我一直在不断修改东西,直到有东西起作用为止。这只是一个可行的示例,绝不是生产质量代码(它不能重新开始)。毫无疑问,有很多事情是不好的/不必要的/ wtf,因此请随时使用注释来帮助进行清理。
如前所述,将路径传递到库已不再足够-除非文件位于KnownFolders(文档,家庭,媒体,音乐,图片,可移动或视频)之一中,否则最终会收到“拒绝访问”消息。相反,库必须能够接受从FileOpenPicker返回的StorageFile ^。至少我没有找到其他方法可以这样做,也许有人知道吗?
MiniZip通过iowin32.h / .c为zlib提供Windows文件系统访问层。对于旧版应用程序,它仍然可以在桌面模式下使用,但对于Metro应用程序,则不能使用,因为它使用了已弃用的API并且依赖于路径。要使MiniZip在Windows 8上运行,需要完全重写iowin32。
为了使一切恢复正常,首先要找到一种方法,将StorageFile ^一直传递到iowinrt(Windows 8替代iowin32)。幸运的是,这不是问题,因为MiniZip提供了两种样式的打开文件功能-一种接受char指针,另一种接受void指针。由于^仍然只是一个指针,因此将StorageFile ^强制转换为void *,然后将其强制转换为StorageFile ^是可以的。
现在我已经能够将StorageFile ^传递给新的iowinrt,下一个问题是如何使新的异步C ++文件访问API与Zlib一起使用。为了支持非常老的C编译器,Zlib是用老的K&R样式C编写的。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目前仅实现Write,尚未使用过。
这是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)
{
[...]