Visual Studio 2019のC#にて、直接バイナリを叩いて指定ファイルをzipにアーカブする。コードのサンプルです。

## 概要
表題の通り
Visual Studio 2019のC#にて、
Visual Studio 2019のC#にて、直接バイナリを叩いて指定ファイルをzipに無圧縮アーカブする。
コードのサンプルでございます。

特段、NuGetなどで他のプラグインや
別途外部dllを必要としないようにしています。

原理的なことは、以下のニコニコ動画にて公開しております。
[『自力でzipしてみた』
https://www.nicovideo.jp/watch/sm27938612](https://www.nicovideo.jp/watch/sm27938612)

要は、バイナリを以下のように、直接叩いています。
<b>1.</b>zipのヘッダーを書き込む。
<b>2.</b>ファイルのバイナリを読み込み。
<b>3.</b>そのままファイルバイナリを横流して書き込み。
<b>4.</b>zipの中間の事項と、フッターを書き込む。

Javaが無い環境下などで、
とにかく無圧縮でもいいからzipを作成したい場合があり、

それに依存しないで、zipをファイルに書き出すことにしたため
作成いたしました。

このQiitaに投稿している、
[Visual Studio 2019のC#にて、中のxmlを直接叩いてODSを出力する。コードのサンプルです。](https://qiita.com/oyk3865b/items/7c89f81f2c4e17e78281)
でも、
ODSは、実質中身は、zipファイルなので
今回の内容と重複する部分がございます。

## 参考サイト
[zip内部に関しての参考サイト]
・[ APPNOTE.TXT - .ZIP File Format Specification](https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT)
・[ZIP書庫ファイル フォーマット - 略して仮。](http://www.tvg.ne.jp/menyukko/cauldron/dtzipformat.html)
・[The structure of a PKZip file](https://users.cs.jmu.edu/buchhofp/forensics/formats/pkzip.html)
・[自力でzipしてみた - ニコニコ動画](https://www.nicovideo.jp/watch/sm27938612)
　※└→私が勝手にまとめた動画です。

[コードに関しての参考サイト]
・[[VBA]CRC-32の計算 言語: VB.NET](https://code.msdn.microsoft.com/office/VBACRC-32-dad7d087)
・[C#实现crc32函数 - 时间是把杀刀猪 - CSDN博客](https://blog.csdn.net/ciaos/article/details/12490911)
・[A CRC32 implementation in C# - Sanity Free Coding - C&#35;, .NET, PHP](http://sanity-free.org/12/crc32_implementation_in_csharp.html)


## 内部コード

```c#:clsZIP.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

public class clsZIP
{
    //無圧縮でZIPにアーカイブするコード
    //→とにかく無圧縮でいいから、Javaなどがない環境下で、ファイルをZIPにアーカイブしたい。という際での使用を想定。

    //偉大な参考URL様
    //[zipの構造に関して]
    //https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT
    //http://www.tvg.ne.jp/menyukko/cauldron/dtzipformat.html
    //https://users.cs.jmu.edu/buchhofp/forensics/formats/pkzip.html
    //他、コード中のURLは、参考サイト様です。


    //■Local file header関係+++++++++++++++++++++++++++++++++++++++++++++++
    //Local file headerの識別子 4bytes(0x04034b50)
    readonly byte[] local_file_header_signature = BitConverter.GetBytes(Convert.ToInt32(0x4034B50));

    //解凍に最低必要なバージョン 2bytes 今回は、ver.1.0で、無圧縮を指定(Default value)→0x000A 
    readonly byte[] version_needed_to_extract = BitConverter.GetBytes(Convert.ToInt16(0xA));

    //オプションフラグと、圧縮方法 2+2bytes{今回はオプションもなく、圧縮もしないので、0を指定共に0x0000}
    readonly byte[] general_purpose_and_compression_method = BitConverter.GetBytes(Convert.ToInt32(0x0));


    //CRC-32 4bytes{アーカイブ前の生ファイルから生成するCRC32}
    byte[] crc_32;
    List<byte[]> ary_crc_32s = null; //zip内の各ファイルの、CRC-32を格納。

    //圧縮後のファイルサイズ 4bytes{今回は無圧縮なので圧縮前の値と同値}
    //圧縮前のファイルサイズ 4bytes
    byte[] compressed_size;
    List<byte[]> ary_uncompressed_sizes = null; //zip内の各ファイルの、圧縮前のファイルサイズを格納。

    //アーカイブ前のファイル名のサイズを格納する 2bytes
    string file_name; //ファイル名
    byte[] file_name_ary; // ファイル名のバイナリ
    List<byte[]> ary_file_name_arys = null; //zip内の各ファイルの、ファイル名のバイナリを格納。
    byte[] file_name_length; //ファイル名のバイナリサイズ

    //拡張フィールドのサイズ 2bytes{今回は、指定しないので0x0000}
    readonly byte[] extra_field_length = BitConverter.GetBytes(Convert.ToInt16(0x0));


    //■Central file header関係+++++++++++++++++++++++++++++++++++++++++++++++
    //Central file headerの識別子 4bytes(0x02014b50)
    readonly byte[] central_file_header_signature = BitConverter.GetBytes(Convert.ToInt32(0x2014B50));

    //作成したバージョン　1+1bytes{今回は、ver.1.0で、無圧縮を指定(Default value)→0x000A }
    readonly byte[] version_made_by = BitConverter.GetBytes(Convert.ToInt16(0xA));

    //ファイルの属性などの情報　2+2+2+4bytes　→　今回は無視するのですべて0
    readonly byte[] file_attributes_etc = new byte[10] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

    //対応するファイルのlocal file header信号のある位置　4bytes
    //出力zipの各local_file_header signatureの、バイト開始位置を示す4バイトのバイナリ配列を格納。
    List<Int32> relative_offset_of_local_header = null;


    //■End of central directory record関係+++++++++++++++++++++++++++++++++++++++++++++++
    //End of central directory record 4bytes(0x06054b50)
    readonly byte[] end_of_central_dir_signature = BitConverter.GetBytes(Convert.ToInt32(0x6054B50));

    //ディスク分割情報　2+2bytes{ディスク分割はしていないのですべて0}
    readonly byte[] number_of_this_disk = BitConverter.GetBytes(Convert.ToInt32(0x0));

    //アーカイブしたファイルの個数 2+2bytes{ディスク分割はしていないので同じ内容の重複}
    byte[] total_number_of_entries;

    //全Central file headerに要したの合計バイト数 4bytes
    byte[] size_of_the_central_directory;

    //最初のCentral file headerの記述されたバイト位置 4bytes
    long offset_of_start_of_central_directory_int;
    byte[] offset_of_start_of_central_directory;

    //zipファイルへのコメントのサイズ 2bytes{今回は、指定しないので0x0000}
    readonly byte[] ZIP_file_comment_length = BitConverter.GetBytes(Convert.ToInt16(0x0));

    //◆zipファイル入出力など、取扱関係+++++++++++++++++++++++++++++
    //出力zipのバイナリ一時格納用。
    List<byte> ary_zip_file = null;

    //ファイルの各更新日時・時間を格納
    List<byte[]> ary_last_mod_file_dates = null;
    List<byte[]> ary_last_mod_file_times = null;

    //出力zipにアーカイブするファイルのパスのリスト
    List<string> Archive_List = null;


    public async System.Threading.Tasks.Task archive_to_zip(string[] Archive_FilePath_List) {
        //ファイルを、zipにアーカイブするルーチン
        //※簡素記述のためメモリをガッツリ使用します。ご注意を。

        //★前処理。****************************************************************************************
        //●出力するzipファイル・フルパスの作成++++++++++++++++++++++++++++++++++++++
        //→先頭のファイル名から、Shift_JISに統一して生成
        string Output_zip_FileName = FileNeme_Shift_JIS_Check(
            Path.GetFileNameWithoutExtension(Archive_FilePath_List[0])) + ".zip";

        //出力パスと結合して、フルパスを生成
        //今回は、先頭ファイルと同じ階層に作成することとする。
        string Output_zip_Directory = Path.GetDirectoryName(Archive_FilePath_List[0]);
        string Output_zip_Path = Path.Combine(Output_zip_Directory, Output_zip_FileName);

        //作ろうとしている同名zipファイルが存在しなくなるまでループ
        int zip_making_flg = 0;
        while (File.Exists(Output_zip_Path)) {
            Output_zip_Path = Path.Combine(Output_zip_Directory, Output_zip_FileName.Replace(".zip", "") + "[" + zip_making_flg.ToString() + "].zip");
            zip_making_flg += 1; //同名ファイルがなくなるまで連番にする
        }

        //初期化
        Archive_List = new List<string> { };
        Archive_List.Clear();


        //有効なファイルを選別＆格納するリストの生成。++++++++++++++++++++++++++++++++++++++
        //予約したリストの中から、処理するファイルをサブフォルダ内部を含めて全て列挙する。
        foreach (string filepath in Archive_FilePath_List) {
            FileAry_Set(filepath);
        }

        //有効なファイルがなかった場合++++++++++++++++++++++++++++++++++++++
        if (Archive_List.Count <= 0) { return; }


        //基準となるフォルダパスを取得++++++++++++++++++++++++++++++++++++++
        string BaseDirName = BaseDirName_Get(
            Path.GetDirectoryName(Archive_List[0]));
        //Shift-JISに統一する。
        BaseDirName = FileNeme_Shift_JIS_Check(BaseDirName);

        //◎使用する各配列の初期化++++++++++++++++++++++++++
        ary_zip_file = new List<byte> { };
        ary_last_mod_file_dates = new List<byte[]> { };
        ary_last_mod_file_times = new List<byte[]> { };
        ary_crc_32s = new List<byte[]> { };
        ary_uncompressed_sizes = new List<byte[]> { };
        ary_file_name_arys = new List<byte[]> { };
        relative_offset_of_local_header = new List<Int32> { };

        //zipは、無圧縮で自分でバイナリを打っていく。
        using (System.IO.FileStream fs = new System.IO.FileStream(Output_zip_Path, System.IO.FileMode.Create, System.IO.FileAccess.Write))
        {
            //★前半戦。Local file headerとバイナリ本体の記述********************************************
            //各ファイルごとにループする
            foreach (string FileName in Archive_List) {
                //アーカイブする元ファイルを開きバイナリを予め保持する。
                byte[] moto_bs = new byte[] { 0 };
                try { 
                    using (FileStream moto_fs = new FileStream(FileName,
                            System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read)) {
                        //ファイルを読み込むバイト型配列を作成する
                        Array.Resize(ref moto_bs, (int)moto_fs.Length);
                        //ファイルのバイナリを一度にすべて読み込んでおく（※メモリ使用量の増大注意）
                        moto_fs.Read(moto_bs, 0, moto_bs.Length);
                        //ファイルを閉じる
                        moto_fs.Close();
                    }
                }
                catch { continue; }

                //■Local file headerの作成------------ここから-----------------
                //●予めlocal_file_header_signatureの記述位置を格納しておく(4バイト)
                relative_offset_of_local_header.Add(Convert.ToInt32(fs.Length)); //つまり現在のファイルサイズ

                //○local_file_header_signatureの記述(4バイト)
                ary_zip_file.AddRange(local_file_header_signature);

                //○解凍に必要なバージョンの記述(2バイト)
                ary_zip_file.AddRange(version_needed_to_extract);

                //○オプションフラグと、圧縮方法の記述(2+2バイト)
                ary_zip_file.AddRange(general_purpose_and_compression_method);

                //○ファイルの更新時刻を取得＆格納(2バイト)//////////////////////////////////
                DateTime F_time = File.GetLastWriteTime(FileName);
                //▽ファイル更新日時をビット記述(全部で16ビット=2バイト)
                string F_time_str = "";
                string fff = ""; //2進数一時格納用

                //Hourの取得＆ビット化
                fff = Convert.ToString(F_time.Hour, 2);
                while (fff.Length != 5) { //5ビットまで用意
                    fff = "0" + fff;
                }
                F_time_str += fff;

                //Minuteの取得＆ビット化
                fff = Convert.ToString(F_time.Minute, 2);
                while (fff.Length != 6) { //6ビットまで用意
                    fff = "0" + fff;
                }
                F_time_str += fff;

                //Secondの取得(偶数化する)＆ビット化
                fff = Convert.ToString((int)(F_time.Second / 2), 2);
                while (fff.Length != 5) { //5ビットまで用意
                    fff = "0" + fff;
                }
                F_time_str += fff;



                //出来たビット文字列を2バイトに落とし込み記述する。
                byte[] last_mod_file_time = BitConverter.GetBytes(Convert.ToInt16(F_time_str, 2));
                ary_zip_file.AddRange(last_mod_file_time);

                //再計算が面倒なので、central file headerに再利用するためにここで記憶しておく。
                //※メモリを消費します。すみません。AddRangeでなく配列ごと格納しているのも、注意。
                ary_last_mod_file_times.Add(last_mod_file_time);



                //▽ファイルの更新年月日を取得＆格納(2バイト)//////////////////////////////////
                F_time_str = ""; //初期化

                //Year(1980年からの経過年数)の取得＆ビット化
                fff = Convert.ToString(F_time.Year - 1980, 2);
                while (fff.Length != 7) { //7ビットまで用意
                    fff = "0" + fff;
                }
                F_time_str += fff;

                //Monthの取得＆ビット化
                fff = Convert.ToString(F_time.Month, 2);
                while (fff.Length != 4) { //4ビットまで用意
                    fff = "0" + fff;
                }
                F_time_str += fff;

                //Dayの取得＆ビット化
                fff = Convert.ToString(F_time.Day, 2);
                while (fff.Length != 5) { //5ビットまで用意
                    fff = "0" + fff;
                }
                F_time_str += fff;


                //出来たビット文字列を2バイトに落とし込み記述する。
                byte[] last_mod_file_date = BitConverter.GetBytes(Convert.ToInt16(F_time_str, 2));
                ary_zip_file.AddRange(last_mod_file_date);

                //再計算が面倒なので、central file headerに再利用するために記憶しておく。
                ary_last_mod_file_dates.Add(last_mod_file_date);


                //○CRC32(4バイト)
                //元ファイルのバイナリからCRC-32を取得する。
                crc_32 = await CRC_32_Checksum(moto_bs);
                ary_zip_file.AddRange(crc_32);
                //再計算が面倒なので、central file headerに再利用するために記憶しておく。
                ary_crc_32s.Add(crc_32);

                //○圧縮後のファイルサイズの取得(4バイト)｛無圧縮なので圧縮後のサイズと同値｝
                compressed_size = BitConverter.GetBytes(Convert.ToInt32(moto_bs.Length));
                ary_zip_file.AddRange(compressed_size);

                //○圧縮前のファイルサイズの取得(4バイト)｛無圧縮なので圧縮後のサイズと同値｝
                ary_zip_file.AddRange(compressed_size);
                //再計算が面倒なので、central file headerに再利用するために記憶しておく。
                ary_uncompressed_sizes.Add(compressed_size);

                //○圧縮前のファイル名のサイズの取得(2バイト)
                file_name = FileNeme_Shift_JIS_Check(FileName); //Shift-JISで初期化

                //親元のフォルダパス部分を削除してzip内でのファイルパスを確定する
                file_name = file_name.Replace(BaseDirName, "");
                if (file_name.Length > 1 && file_name.StartsWith("\\"))
                {   //左に余分にできる\マークを消す
                    file_name = file_name.Substring(1, file_name.Length - 1);
                }
                //文字列をバイナリ化する
                file_name_ary = System.Text.Encoding.GetEncoding("SHIFT-JIS").GetBytes(file_name);
                //文字列長を取得する。
                file_name_length = BitConverter.GetBytes(Convert.ToInt16(file_name_ary.Length));
                ary_zip_file.AddRange(file_name_length); // 格納

                //○拡張フィールドのサイズ指定(2バイト){今回は指定なし}
                ary_zip_file.AddRange(extra_field_length); //格納

                //○アーカイブ前のファイル名の格納
                ary_zip_file.AddRange(file_name_ary);
                //再計算が面倒なので、file_name_aryに再利用するために記憶しておく。
                ary_file_name_arys.Add(file_name_ary);

                try
                { //一旦、書き貯めたバイト型配列の内容をファイルに出力する
                    fs.Write(ary_zip_file.ToArray(), 0, ary_zip_file.Count);
                    fs.Flush();
                }
                catch { }
                ary_zip_file.Clear(); //初期化

                //■Local file headerの作成------------ここまで-----------------

                //■ファイル本体のバイナリを書き込む
                try
                { //ファイルに出力する
                    fs.Write(moto_bs, 0, moto_bs.Length);
                    fs.Flush();
                }
                catch { }
                //初期化
                await System.Threading.Tasks.Task.Delay(10);//これがないと、書き込み途中で放棄される？？
                Array.Clear(moto_bs, 0, moto_bs.Length);
            }

            //★中盤戦。central file headerの記述********************************************
            //予め最初のCentral file headerの記述開始バイト位置を記憶しておく
            offset_of_start_of_central_directory_int = fs.Length;
            offset_of_start_of_central_directory = BitConverter.GetBytes(Convert.ToInt32(offset_of_start_of_central_directory_int));


            //各ファイル分ループ
            for (int i = 0; i < ary_file_name_arys.Count; i++)
            {
                byte[] bs;
                //■central file headerの作成------------ここから-----------------
                //○central_file_header_signatureの記述(4バイト)
                ary_zip_file.AddRange(central_file_header_signature);

                //○zipを作った時のバージョンとOSの記述(1+1バイト)
                ary_zip_file.AddRange(version_made_by);

                //○解凍に必要なバージョンの記述(2バイト)
                ary_zip_file.AddRange(version_needed_to_extract);

                //○オプションフラグと圧縮方法の記述(2+2バイト)
                ary_zip_file.AddRange(general_purpose_and_compression_method);

                //○ファイルの更新時刻(2バイト)[固定値]
                ary_zip_file.AddRange(ary_last_mod_file_times[i]);

                //○ファイルの更新年月日(2バイト)[固定値]
                ary_zip_file.AddRange(ary_last_mod_file_dates[i]);

                //○CRC-32(4バイト)
                ary_zip_file.AddRange(ary_crc_32s[i]);


                //○圧縮後のファイルサイズ(4バイト) {今回は、無圧縮なので、圧縮前のファイルサイズを流用}
                bs = ary_uncompressed_sizes[i];
                ary_zip_file.AddRange(bs);

                //○圧縮前のファイルサイズ(4バイト)
                ary_zip_file.AddRange(bs);
                Array.Clear(bs, 0, bs.Length);//初期化

                //○ファイル名のサイズ(2バイト)
                byte[] name_bs = ary_file_name_arys[i];
                ary_zip_file.AddRange(BitConverter.GetBytes(Convert.ToInt16(name_bs.Length)));


                //○拡張フィールドのサイズ(2バイト)
                ary_zip_file.AddRange(extra_field_length);

                //○ファイルの属性などの情報(2+2+2+4バイト)
                ary_zip_file.AddRange(file_attributes_etc);

                //○対応するファイルのlocal File header信号のある位置(4バイト)
                bs = BitConverter.GetBytes(relative_offset_of_local_header[i]);
                ary_zip_file.AddRange(bs);
                Array.Clear(bs, 0, bs.Length);//初期化


                //○アーカイブ前のファイル名の格納
                ary_zip_file.AddRange(name_bs);
                Array.Clear(name_bs, 0, name_bs.Length);//初期化

                try
                { //一旦、書き貯めたバイト型配列の内容をファイルに出力する
                    fs.Write(ary_zip_file.ToArray(), 0, ary_zip_file.Count);
                    fs.Flush();
                }
                catch { }
                ary_zip_file.Clear(); //初期化
                //■central file headerの作成------------ここまで-----------------
                await System.Threading.Tasks.Task.Delay(10);
            }

            //◎使用する各配列の一旦、クリア++++++++++++++++++++++++++
            ary_crc_32s.Clear();
            ary_uncompressed_sizes.Clear();
            relative_offset_of_local_header.Clear();


            //★終盤戦End of central directory recordの記述********************************************
            //全Central file headerに要したの合計バイト数を予め算出しておく
            size_of_the_central_directory = BitConverter.GetBytes(
                Convert.ToInt32(fs.Length - offset_of_start_of_central_directory_int));

            //■End of central directory recordの作成------------ここから-----------------
            //○end_of_central_dir_signatureの記述(4バイト)
            ary_zip_file.AddRange(end_of_central_dir_signature);

            //○ディスク分割情報(2+2バイト)
            ary_zip_file.AddRange(number_of_this_disk);

            //○アーカイブしたファイルの個数(2+2バイト){同じ内容を重複して格納}
            total_number_of_entries = BitConverter.GetBytes(Convert.ToInt16(ary_file_name_arys.Count));
            ary_zip_file.AddRange(total_number_of_entries);
            ary_zip_file.AddRange(total_number_of_entries); //※重複分

            //○全Central file headerに要したの合計バイト数を格納(4バイト)
            ary_zip_file.AddRange(size_of_the_central_directory);

            //○最初のCentral file headerの記述されたバイト位置(4バイト)
            ary_zip_file.AddRange(offset_of_start_of_central_directory);

            //○ファイルへのコメントのサイズ(2バイト){今回はなし}
            ary_zip_file.AddRange(ZIP_file_comment_length);

            try
            { //一旦、書き貯めたバイト型配列の内容をファイルに出力する
                fs.Write(ary_zip_file.ToArray(), 0, ary_zip_file.Count);
                fs.Flush();
                await System.Threading.Tasks.Task.Delay(10);//これがないと、書き込み途中で放棄される？？
            }
            catch { }
            ary_zip_file.Clear(); //初期化
            //■End of central directory recordの作成------------ここまで-----------------


            //バイナリ・メモリの占有を開放する。
            ary_zip_file.Clear();
            ary_zip_file = null;

            //初期化
            Archive_List.Clear();
            Archive_List = null;
        }
    }

    private string FileNeme_Shift_JIS_Check(string Motono_FileName) {
        //■文字コードをシフトJISに、統一して、禁止文字を削除する。

        //一旦、シフトJISとしてバイナリ化
        byte[] bytesData = Encoding.GetEncoding(932).GetBytes(Motono_FileName);
        //Shift JISとして文字列に還元する。
        Motono_FileName = Encoding.GetEncoding(932).GetString(bytesData);

        //ファイル名に使用できない文字があれば消す
        //http://dobon.net/vb/dotnet/file/invalidpathchars.html
        foreach (char invalidChars in Path.GetInvalidFileNameChars()) { 
            //パスの区切り文字は除外しておく
            if ((invalidChars == '\\') || (invalidChars == '/')) { continue; }

            if (Motono_FileName.IndexOf(invalidChars) > 0)
            {   //禁止文字はすべて_に置換しておく
                Motono_FileName = Motono_FileName.Replace(invalidChars, '_');
            }
        }

        //チェック後の文字列を返す
        return Motono_FileName;
    }


    private string BaseDirName_Get(string DirName) {
        //zip圧縮する際に、基準となる親フォルダのパスを検知・取得する。

        //初期値
        string BaseDirName = DirName;
        //空白の場合はコロンを返す
        if (DirName == "") { return ":";  }

        string[] Archive_FilePath_List = Archive_List.ToArray();

        foreach (string fff in Archive_FilePath_List) { 
            //親元となるフォルダパスを検索する
            for (int i = 1; i < BaseDirName.Length; i++) { 
                if (fff.Substring(0, i) != BaseDirName.Substring(0, i)) {
                    //最初の親元となるフォルダパスと相違点が見つかった場合
                    //さらに上のフォルダを親元のフォルダとする。
                    BaseDirName = Path.GetDirectoryName(BaseDirName);
                    break; //出る
                }
            }
        }

        //得られた基幹となるフォルダ名を返す
        //空白の場合はコロンを返す。安全装置
        if (BaseDirName == "") {
            return ":";
        }
        else {
            return BaseDirName;
        }
    }

    private void FileAry_Set(string File_DirName) {
        //有効なファイルをArchive_Listリストに加える。

        if (Directory.Exists(File_DirName)) { 
            //それがフォルダだった場合
            if (Directory.GetDirectories(File_DirName).Length == 0 
                && Directory.GetFiles(File_DirName).Length == 0) {
                //空のフォルダだった場合
                //\記号を末尾につけて、zip追加リストに加える
                Archive_List.Add(File_DirName + "\\");
                return; //出る
            }
            //ファイルを加える
            Archive_List.AddRange(Directory.GetFiles(File_DirName)); //zip追加リストに加える

            //サブフォルダも検索する
            foreach (string subFolder in Directory.GetDirectories(File_DirName)) {
                //自己参照する。
                FileAry_Set(subFolder);
            }
        }
        else if (File.Exists(File_DirName))
        {
            //それがファイルだった場合
            Archive_List.Add(File_DirName); //zip追加リストに加える
        }
    }
    private async System.Threading.Tasks.Task<byte[]> CRC_32_Checksum(byte[] bytes)
    {
        //★CRC32の計算をするところ  
        //UIntegerの方法では、上手くできないのですorz  
        //また、返数をByte()でなく、Long型にすると、値が狂います。  

        //https://code.msdn.microsoft.com/office/VBACRC-32-dad7d087  
        //https://blog.csdn.net/ciaos/article/details/12490911
        //http://sanity-free.org/12/crc32_implementation_in_csharp.html

        //計算に時間がかかるので、待機させる
        System.Threading.Tasks.Task<byte[]> uploadTask = System.Threading.Tasks.Task.Factory.StartNew<byte[]>(
        () => {
            //■CRC32のテーブル情報の事前生成  
            uint[] crc32Table = new UInt32[256];
            //CRC-32 の除数0x104c11db7を、予めビットの並び順を左右逆転させた値(0xEDB88320) を使用する。
            uint poly = 0xedb88320;

            uint temp = 0;
            for (uint i = 0; i < crc32Table.Length; ++i)
            {
                temp = i;
                for (int j = 8; j > 0; --j)
                {
                    if ((temp & 1) == 1) //&で、andのビット演算
                    {
                        temp = (uint)((temp >> 1) ^ poly); //^で、xorのビット演算
                    }
                    else
                    {
                        temp >>= 1; //÷(2の1乗)と同じ効果  
                    }
                }
                crc32Table[i] = temp;
            }


            //初期化  
            uint crc = 0xffffffff;

            //■渡されたファイルのバイナリから、CRC-32を算出して返す 
            for (int i = 0; i < bytes.Length; ++i)
            {   //■計算の引き合いに出すテーブルのインデックス番号の算出  
                byte index = (byte)(((crc) & 0xff) ^ bytes[i]);

                //■次のビットにスライドする。  
                //→(crc >> 8)は、crcを(2の8乗)で割っていることと同義  
                crc = (uint)((crc >> 8) ^ crc32Table[index]);
            }

            //ビット反転後バイト配列に変換して返す
            return BitConverter.GetBytes(~crc); //^で、notのビット演算
        });

        return await uploadTask;
    }

}

```

例えば、
以下のようなコードで、ファイルを選択して
変換することが出来ます。


```c#:Form1.cs
        private async void Form1_Load(object sender, EventArgs e)
        {   //ここは適当に記述しています。
            using (OpenFileDialog fileDialog = new OpenFileDialog())
            {   //PDFに直接変換したい画像ファイルを複数選択できるようにしている。
                fileDialog.Filter = "アーカイブするデータ|*.*";
                fileDialog.Multiselect = true;

                //「開く」ボタンが選択された時の処理
                if (fileDialog.ShowDialog() == DialogResult.OK)
                {
                    //変換したい画像のリストを取得
                    string[] fileNames = fileDialog.FileNames;  //こんな感じで選択されたファイルのパスが取得できる

                    //PDFは、今回は仮に、最初のファイルと同じフォルダに「test.pdf」として生成される。
                    clsZIP cls = new clsZIP();
                    await cls.archive_to_zip(fileNames);

                    //おしまいのメッセージ
                    MessageBox.Show("END");
                }
            }

            //さようなら
            Application.Exit();
        }
```

以上のコードを実行すると
指定したファイルと同じ階層のフォルダに、zipが出力されてあると思います。


## おまけ

尚、アーカイブする際の更新日時をうまくして
アーカイブすれば、
アーカイブしたファイルのタイムスタンプが、任意に変えられます。