CAD日記

主にAutoCADのことについて書いているけど、近頃は投資系ネタに注力している。自動売買、仮想通貨、PC関係、プログラミングなど。@caddiary

ソフト開発

C#でzip圧縮と解凍したりasync/awaitで非同期処理をするソースコード公開

投稿日:2020年3月7日 更新日:

zipcopyという名の開発プロジェクトはここらで終わりにしようと思い至ったので、そのソースコードを公開する。
zipcopyのソースコード
開発環境はVisualStudio 2017 C#で、NuGetでDotNetZipを組み込んだプロジェクト一式。
DotNetZipは一応組み込んでみただけで、メインは別口でインストールした7zipのプログラムを呼び出して使うほうだ。

ただ公開してもつまらないので、ポイントを以下で明らかにする。
まずはzipcopyがどんなアプリなのか。

コピー元のフォルダを選択、コピー先のフォルダを選択して、実行ってやれば、コピー元でzip圧縮した結果をコピー先にコピーしてくれるってもの。ネットワークのフォルダ共有の場合、圧縮してコピーしたほうが帯域制御になって、かつ速い場合もあるんじゃないかと思って作ったものだが、その当初の発想はかなりまちがっていたことがわかったので、その点は参考にならないかな。。

【ポイント1:7zipの圧縮/解凍はGUIからやるだけでなく、CUIのインターフェースが公開されている】
C:\Program Files\7-Zipの中にある7zG.exeがそうで、以下のコマンドラインを実行してやれば圧縮解凍できる。
7zG.exe a [圧縮後ファイル] [圧縮元フォルダ] ※詳細はCompress関数を参照
7zG.exe x -y -o [解凍先フォルダ] [解凍する圧縮ファイル] 詳細はExtract関数を参照
CompressおよびExtract関数内では、DotNetZipによる圧縮解凍もしているので参考にされたし。

// コピー元でzip圧縮
private bool Compress(ref string strZip)
{
	strZip = uty.GetZipName(opt.m_strCopySrc);
	string detail = "";
	bool stat = true;
	Stopwatch sw = uty.timeStart();
	DateTime dt = DateTime.Now;
	try
	{
		if (opt.m_i7zipUse == 1)
		{
			string param1 = "\"" + strZip + "\"";
			string param2 = "\"" + opt.m_strCopySrc + "\"";
			Process p = Process.Start(opt.m_str7zipPath2, "a " + param1 + " " + param2);
			p.WaitForExit();
			if(p.ExitCode != 0)  // 失敗
			{
				p.Dispose();
				throw new FormatException("zip圧縮に失敗");
			}
			p.Dispose();
		}
		else
		{
			ZipFile zip = new ZipFile(Encoding.GetEncoding("shift_jis"));
			zip.CompressionLevel = CompressionLevel.BestCompression;
			if (Environment.Is64BitProcess)
				zip.UseZip64WhenSaving = Zip64Option.AsNecessary;
			if (uty.IsSrcFolder(opt.m_strCopySrc))
			{
				string strFolder = Path.GetFileName(opt.m_strCopySrc);
				zip.AddDirectory(opt.m_strCopySrc, strFolder);
			}
			else
				zip.AddFile(opt.m_strCopySrc, "");
			zip.Save(strZip);
			zip.Dispose();
		}
	}
	catch (Exception ex)
	{
		stat = false;
		detail = string.Format("例外エラー({0})", ex.Message);
	}
	finally
	{
		Invoke(new logInfo(addLogDelegate), dt, "コピー元で圧縮", opt.m_strCopySrc, opt.m_strCopyDst, stat, uty.timeEnd(sw), detail);
	}
	return stat;
}

// コピー先でzip解凍
private bool Extract(string dstFull)
{
	string detail = "";
	bool stat = true;
	Stopwatch sw = uty.timeStart();
	DateTime dt = DateTime.Now;
	try
	{
		if (opt.m_i7zipUse == 1)
		{
			string param1 = "\"" + opt.m_strCopyDst + "\"";
			string param2 = "\"" + dstFull + "\"";
			Process p = Process.Start(opt.m_str7zipPath2, "x -y -o" + param1 + " " + param2);
			p.WaitForExit();
			if (p.ExitCode != 0)  // 失敗
			{
				p.Dispose();
				throw new FormatException("zip解答に失敗");
			}
			p.Dispose();
		}
		else
		{
			var enc = new ReadOptions() { Encoding = Encoding.GetEncoding("shift_jis") };
			ZipFile zip = ZipFile.Read(dstFull, enc);
			zip.ExtractExistingFile = ExtractExistingFileAction.OverwriteSilently;
			zip.ExtractAll(opt.m_strCopyDst);
			zip.Dispose();
		}
	}
	catch (Exception ex)
	{
		stat = false;
		detail = string.Format("例外エラー({0})", ex.Message);
	}
	finally
	{
		Invoke(new logInfo(addLogDelegate), dt, "コピー先で解凍", opt.m_strCopySrc, opt.m_strCopyDst, stat, uty.timeEnd(sw), detail);
	}
	return stat;
}
</pre>

【ポイント2:時間のかかる処理はasync/awaitで非同期とすべし】
同期処理で時間がかかる処理(10秒以上)をやらせると、自分が応答なしの状態になるので気持ち悪い。非同期で時間がかかる処理をやらせておいて、自分はクローズできない状態に制御しておけば、GUI操作で自分と呼び出し先の処理を行ったり来たりできていい感じになる。以下、実行ボタンを押したときの処理。asyncとawaitの使い方の参考になるかな。
<pre class="brush: c-sharp;">private async void button3_Click(object sender, EventArgs e)
{
	DateTime dt = DateTime.Now;
	UpdateOption();
	string errMsg = "";
	if (!PreCheck(ref errMsg))
	{
		Invoke(new logInfo(addLogDelegate), dt, "事前チェック", opt.m_strCopySrc, opt.m_strCopyDst, false, 0, errMsg);
		return;
	}

	bool stat = false;
	button3.Enabled = false;
	this.Text += " 【実行中】";
	exec = true;
	try
	{
		await Task.Run(() =>
		{
			string strZip = "";
			if (Compress(ref strZip))
			{
				string dstFull = "";
				if (CopyFile(strZip, ref dstFull))
				{
					stat = true;
					if (opt.m_iSrcZipDel == 1)
						stat = uty.FileDelete(listView1, strZip, opt.m_iDelTimeout);
					if (opt.m_iDstZipOpen == 1 && Extract(dstFull))
					{
						if (opt.m_iDstZipDel == 1)
							stat = uty.FileDelete(listView1, dstFull, opt.m_iDelTimeout);
					}
				}
			}
		});
	}
	catch (Exception)
	{
		...

呼び出し先から呼び出し元のコントロールをいじる際は注意が必要で、delegate宣言した関数としておく。以下は、圧縮などの実行経過を自分のリストビューにログとして追加する処理。

delegate void logInfo(DateTime dt, string operation, string src, string dst, bool stat, double time, string detail);
public void addLogDelegate(DateTime dt, string operation, string src, string dst, bool stat, double time, string detail)
{
	ListViewItem lvi;
	lvi = listView1.Items.Add(dt.ToString());
	lvi.SubItems.Add(time.ToString());
	lvi.SubItems.Add(operation);
	lvi.SubItems.Add(src);
	lvi.SubItems.Add(dst);
	lvi.SubItems.Add(stat ? "成功" : "失敗");
	lvi.SubItems.Add(detail);
	listView1.EnsureVisible(lvi.Index);
	listView1.Update();
}

その他ポイントは、以下に箇条書きで書いておくので、詳細はソースコードの中から見つけてくれ。カッコ内は関数名。
・zipのように巨大なファイルは削除できるまでに時間がかかる場合があるので、少し時間をおいてからリトライすべし(FileDelete)
・処理時間を計測するならStopwatchでStart&Stopすべし(timeStart、timeEnd)
・エクスプローラの右クリックメニュに追加・削除するのはレジストリ操作で可能(button5_Click、button6_Click)
・ファイル選択でフォルダ選択することだってできるのよ(button1_Click)
・ファイルコピーは、コピー中のGUI画面を出すことができるのさ(CopyFile)

-ソフト開発

執筆者:


comment

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください

関連記事

秀丸マクロがおもしろくて練習のため2つのマクロを作ってみた

秀丸マクロは、以下のようにキーボード操作の結果を記録して再生することはやっていた。 Shift+F1:キー操作の記録開始/終了 Shift+F2:キー操作の再生 テキストファイル1行分のテキスト編集操 …

PDFアレコレにページ挿入機能を追加

PDFアレコレのページ挿入機能とちょっとした不具合修正。 Ver2.08 2020/5/6 ・ページ挿入に対応した。指定したページの前に、指定したPDFファイルまたは空白ページを挿入できる。指定ページ …

zipcopy~圧縮してコピー~ができたのでアップしておく

【zipcopyの概要】 大きなファイルやフォルダをネットワーク越しにコピーする際、圧縮してからコピーすることで、ネットワークの帯域を少しで使わないようにすることを目的とする。以下3つの手順を自動的に …

no image

プログラムっておもしろいなぁとつくづく思うなり

ここんところぜんぜんプログラムを書いてなくて仕事がつまんなくなってきたところに、トラブル対応があってプログラムをじっくりと書いてみた。いやはやおもしろい。余計な仕事が飛び込んでこない環境で、スピッツ聞 …

no image

尻有

知る人ぞ知るアングラ系サイト。 尻有とか、どーもとか。 定期的にチェックして、マイソフトがクラックされてないかどうか チェックしてるんよ。 復活してましたね、どーもが。 入り口が、なかなか見つからなか …