8

以前、MiniZip (zlib ラッパー) を使用してアーカイブを解凍しました。MiniZip は、"iowin32.c" で非推奨の API (CreateFile() および SetFilePointer()) を使用するため、Metro アプリケーションには使用できません。

簡単に修正できると思い、CreateFile() と SetFilePointer() を CreateFile2() と SetFilePointerEx() に置き換えた "iowinrt.c" を作成しました。このようにして、承認された Win8 API のみを使用するバージョンの MiniZip を入手しましたが、それでも役に立たないことが判明しました。サンドボックス化のことを忘れていました。FileOpenPicker() を使用してファイルを選択し、そのパスを変更した MiniZip に渡すと、まだ開くことができません。CreateFile2() は「アクセスが拒否されました」で失敗します。メッセージ。

そのため、ファイル アクセス用の古い C API は、今ではほとんど役に立たないようです。これを修正するには、新しい非同期ファイル アクセスを使用して C++/CX で「iowinrt」を再実装する必要があることを理解しています。他のオプションはありますか?WinRT には圧縮/解凍機能があるが、アーカイブではなく個々のファイルでのみ機能することをどこかで見たと思います。

追加の要件は、これがメモリ内で機能する必要があることです。

しばらくの間、.NET Framework 4.5 による解決策があると思いました。

  1. C++/CX から使用できる .NET クラスを作成する方法に関するこの情報を見つけました: http://social.msdn.microsoft.com/Forums/en-US/winappswithnativecode/thread/3ff383d0-0c9f-4a30- 8987-ff2b23957f01

  2. .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/en-us/library/system.io.compression.ziparchiveentry%28v=vs.110%29.aspx#Y0

ZipArchive と ZipArchiveEntry を公開する WinMD 出力タイプを使用して C# Metro クラス ライブラリを作成し、それを C++/CX プロジェクトで使用できると考えました。ただし、機能したとしても、メモリ内では機能しません。ZipArchive と ZipArchiveEntry はファイルでのみ機能するようです。

4

1 に答える 1

5

アーカイブ作業から読み取りました。以下の説明とコードですが、実際にはこの時点でハックして、それが可能かどうかを確認してください。何かが機能するようになるまで、物事を変更し続けました。これは機能するものの単なる例であり、決して製品品質のコードではありません (開始時に再入可能ではありません)。間違いなく悪い/不必要な/wtfなことがたくさんあるので、コメントを使ってクリーンアップを手伝ってください.

前述のように、ライブラリへのパスを渡すだけでは不十分です。ファイルが KnownFolders (ドキュメント、ホーム、メディア、音楽、写真、リムーバブル、またはビデオ) のいずれかにない限り、「アクセスが拒否されました」というメッセージが表示されます。代わりに、ライブラリは、FileOpenPicker から返される StorageFile^ を受け入れることができなければなりません。少なくとも私はそれを行う他の方法を見つけていません。多分誰かがもっとよく知っていますか?

MiniZip は、iowin32.h/.c を介して zlib 用の Windows ファイルシステム アクセス レイヤーを提供します。これは古いスタイルのアプリのデスクトップ モードでは引き続き機能しますが、非推奨の API を使用し、パスに依存するため、Metro アプリでは機能しません。Windows 8 で MiniZip を使用するには、iowin32 を完全に書き直す必要があります。

物事を再び機能させるには、まず StorageFile^ を iowinrt (iowin32 の Windows 8 代替) に渡す方法を見つける必要がありました。幸いなことに、MiniZip には 2 つのスタイルのファイルを開く関数 (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)、「プリコンパイル済みヘッダーを使用しない」も設定する必要があったと思います。

iowinrt 自体については、2 つのファイルに分割しました。1 つは、2 つのシールされた 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)
            {
                [...]
于 2012-06-14T23:44:56.380 に答える