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 を使っています。コメントデータの処理方法の詳細はこちらをご覧ください

関連記事

no image

random

JavaScriptと格闘してみたよ。 テーマはランダム。 10個の画像の中から、任意の3個の画像をWebサイトに表示する、 ってのが要求仕様。 Math.floor(Math.random() * …

Teigha改めODAでDWGをDXFに変換するプログラムをつくってみよう!【Part.2】

Part.1ではあれこれと前提の話を書いたわけだが、今回は実際にVCのプロジェクトを作る準備をしてみよう。 1.ODAモジュールのダウンロード 2019 Update 2のKernelとDrawing …

扱えるテキストファイルの最大行数は1億行

扱えるテキストファイルの行数の限界が1億行なのが秀丸エディター。その他⇒動作環境⇒環境にて、編集可能な最大行数が10万行~1億行で設定できる(32bitの場合は1000万行まで)。1億行のテキストファ …

iTextSharpのバージョンとライセンスが難しい

iTextSharpがPDFを作るオープンソースであることはよく知られているが、そのバージョンとライセンスについてくわしく調べようとすると深みにはまる。 バージョンについて。 4系、5系、7系があって …

PDFium ViewerによるPDFレンダリングがイケてる

PDFが電子ペーパーの絶対的な標準になったなか、PDFをプログラムであつかうのがとても簡単になってきた。