概要

 シェルが提供するIShellDispatchを用いてフォルダーをZIPで圧縮します。
 IShellDispatchには、ファイル名を指定して実行(FileRun)や検索などがあります。
 Windows XPより標準でZIP圧縮機能が搭載されました。
 本プログラムでは、C:¥temp2¥addfolderフォルダーをC:¥temp2¥sample.zipに追加します。
 プログラムの説明のためスレッドの一覧(_tprintfの行)を表示するようにしていますが、削除しても問題ありません。

テスト環境

コンパイラ

Visual C++ 2013 Express 32/64bit Windows 7/10

プログラムソースの概要

_tmain関数

 Windowsから最初に_tmain関数が呼び出されます。
 COMインターフェースをCoInitializeExで初期化します。
 CoCreateInstanceによりIShellDispatchオブジェクトを取得します。
 AddDirZip関数を呼び出してフォルダーをZIPで圧縮します。
 CoUninitializeでCOMインターフェースを解放します。

AddDirZip

 圧縮元のフォルダー型からZIPファイルのFolder型へコピーすることにより圧縮することができますが、圧縮は非同期に実行されるので、コピー動作(CopyHere)を呼び出してコピーが終了する前に関数が終了してしまします。
 各オブジェクトを終了前に解放してしまうと圧縮に失敗します。例えば1秒ほど待機してからという処理も考えられますが、ファイル数が膨大の場合は1秒で終わる保証はありません。
 Windows 7/10ではコピー処理を呼び出すと3つの新しいスレッドが起動しすぐに元のプログラムへ処理が戻ります。
 ここでは、コピー処理前とコピー中のスレッドを比較し新たに作成されたスレッドについて終了するまで待機した後に各オブジェクトの解放をすることにしました。以下のような手順で処理します。
  1.  圧縮元のフォルダー名(TCHAR*型)からVARIANT型を作成しNameSpaceによりFolder型オブジェクトを作成します。
    VARIANT型は不要ですのでVariantClearにより解放します。
  2.  ZIPファイルが存在するかどうかをPathFileExists APIにより確認しない場合は、ZIPファイルの元になるファイルを作成します。
  3.  ZIPファイルも圧縮元のフォルダーと同様にFolder型オブジェクトを作成します。
  4.  CreateToolhelp32Snapshot APIによりコピー前のスレッドのスナップショットを作成します。
    Thread32First,Thread32Next APIによりスレッドを検索し今のプロセスと同じIDのスレッドで自身のスレッドIDと異なるスレッド数を数えます。
  5.  ZIPファイルのフォルダー型のCopyHere関数により圧縮します。前述のとおりWindowsは内部で3つのスレッドを起動してすぐに元のプログラムに処理が戻ります。
  6.  CreateToolhelp32Snapshot APIによりコピー中のスレッドのスナップショットを作成します。
    Thread32First,Thread32Next APIによりスレッドを検索し今のプロセスと同じIDのスレッドで自身のスレッドIDと異なるスレッド数を数えコピー前のスレッド数を超えた時点でコピーのために新たに起動されたスレッドであることが判明しますのでハンドルを取得し配列に保存します。
  7.  WaitForMultipleObjects APIにより新たに起動されたスレッドが終了するまでプログラムを待機します。
  8.  新たに起動されたスレッドが全部終了するとWaitForMultipleObjects APIの次の行へプログラムが進みます。
  9.  新たに起動されたスレッドのハンドルをクローズし、各オブジェクトを解放して関数を終了します。

プログラムソース

// ZIPファイルにフォルダーを追加するサンプル(Unicode専用)
// Visual C++ 2013

#include <windows.h>
#include <shlobj.h>
#include <shlwapi.h>
#include <tlhelp32.h>
#include <locale.h>
#include <stdio.h>
#include <tchar.h>
#include <vector>

#pragma comment(lib, "shlwapi")

// ZIPファイルにフォルダーを追加する
BOOL AddDirZip(IShellDispatch *pShellDisp, TCHAR* ZipPath, TCHAR* AddDirPath);

int _tmain(void){
	HRESULT        hr;
	IShellDispatch *pShellDisp;
	// UNICODE文字を標準出力に正しく表示させるためにロケールを設定
	_tsetlocale(LC_ALL, _TEXT(""));

	CoInitializeEx(0, COINIT_MULTITHREADED);

	hr = CoCreateInstance(CLSID_Shell, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pShellDisp));
	if (FAILED(hr)) {
		CoUninitialize();
		return 0;
	}
	//                                  ZIPファイル              追加するフォルダー
	AddDirZip(pShellDisp, _TEXT("c:¥¥temp2¥¥sample.zip"), _TEXT("c:¥¥temp2¥¥addfolder"));

	CoUninitialize();

	return 0;
}

// ZIPファイルにフォルダーを追加する
BOOL AddDirZip(IShellDispatch *pShellDisp, TCHAR* ZipPath, TCHAR* AddDirPath){
	HRESULT hr;
	VARIANT vSrcDir;
	Folder  *pSrcDir;
	// 追加Folderオブジェクトを作成
	VariantInit(&vSrcDir);
	vSrcDir.vt = VT_BSTR;
	vSrcDir.bstrVal = SysAllocString(AddDirPath);
	hr = pShellDisp->NameSpace(vSrcDir, &pSrcDir);
	VariantClear(&vSrcDir);
	if (hr != S_OK){
		MessageBox(NULL, _TEXT("追加フォルダーが見つかりませんでした。"), _TEXT("エラー"), MB_ICONWARNING);
		return FALSE;
	}
	// ZIPファイルが存在しない場合にZIPファイルの種を作成する
	if (PathFileExists(ZipPath) == FALSE){
		BYTE bin[] = { 80, 75, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
		FILE *fp;
		if (_tfopen_s(&fp, ZipPath, _TEXT("wb"))){
			pSrcDir->Release();
			MessageBox(NULL, _TEXT("ZIPファイルが作成できませんでした。"), _TEXT("エラー"), MB_ICONWARNING);
			return FALSE;
		}
		fwrite(bin, sizeof(bin), 1, fp);
		fclose(fp);
	}
	// ZIPファイルのFolderオブジェクトを作成
	VARIANT vZip;
	Folder  *pZipFile;
	VariantInit(&vZip);
	vZip.vt = VT_BSTR;
	vZip.bstrVal = SysAllocString(ZipPath);
	hr = pShellDisp->NameSpace(vZip, &pZipFile);
	VariantClear(&vZip);
	if (hr != S_OK) {
		pSrcDir->Release();
		MessageBox(NULL, _TEXT("ZIPファイルが見つかりませんでした。"), _TEXT("エラー"), MB_ICONWARNING);
		return FALSE;
	}

	VARIANT varItem, varOpt;

	//	兄弟スレッド、子スレッドの数を取得
	DWORD ThreadOldCount = 0;
	HANDLE h;
	h=CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0);
	if (h != INVALID_HANDLE_VALUE) {
		THREADENTRY32 te;
		te.dwSize = sizeof(te);
		if (Thread32First(h, &te)) {
			do {
				if ((te.th32OwnerProcessID == GetCurrentProcessId()) 
					&& (te.th32ThreadID != GetCurrentThreadId())){
					
					_tprintf(_TEXT("追加前 %i プロセルID 0x%04x スレッドID 0x%04x¥n"), 
						ThreadOldCount,te.th32OwnerProcessID, te.th32ThreadID);
					ThreadOldCount++;
				}
				te.dwSize = sizeof(te);
			} while (Thread32Next(h, &te));
		}
		CloseHandle(h);
	}

	// ZIPへフォルダーをコピー
	VariantInit(&varItem);
	varItem.vt = VT_DISPATCH;
	varItem.pdispVal = pSrcDir;
	VariantInit(&varOpt);
	varOpt.vt = VT_I4;
	varOpt.lVal = 0; //FOF_SILENTを指定すると処理中の経過が表示されなくなります

	hr = pZipFile->CopyHere(varItem, varOpt);
	if (hr != S_OK) {
		pZipFile->Release();
		pSrcDir->Release();
		MessageBox(NULL, _TEXT("フォルダーを追加できませんでした。"), _TEXT("エラー"), MB_ICONWARNING);
		return FALSE;
	}
	Sleep(100);
	if (hr == S_OK) {
		/*	兄弟スレッド、子スレッドの数を取得し最後に見つかったスレッドを
			コピースレッドとみなし終了するまで待機する*/
		HANDLE hThread=0;
		DWORD ThreadNewCount = 0;
		DWORD ThreadMax = 0;
		DWORD n;
		h = CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0);
		if (h != INVALID_HANDLE_VALUE) {
			THREADENTRY32 te;
			DWORD ThreadID;
			HANDLE hThreads[8];
			te.dwSize = sizeof(te);
			if (Thread32First(h, &te)) {
				do {
					if ((te.th32OwnerProcessID == GetCurrentProcessId())
						&& (te.th32ThreadID != GetCurrentThreadId())){

						if (ThreadOldCount <= ThreadNewCount){//追加されたスレッド
							hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, te.th32ThreadID);
							hThreads[ThreadMax++] = hThread;
							if(ThreadMax == sizeof(hThreads) / sizeof(hThreads[0])){
								for (n = 0; n < ThreadMax; n++)
									CloseHandle(hThreads[n]);
								pZipFile->Release();
								pSrcDir->Release();
								MessageBox(NULL, _TEXT("スレッド数がオーバーしました。"), _TEXT("エラー"), MB_ICONWARNING);
								return FALSE;
							}
							_tprintf(_TEXT("Copy-"));
						}
						_tprintf(_TEXT("追加後 %i プロセルID 0x%04x スレッドID 0x%04x¥n"),
							ThreadNewCount, te.th32OwnerProcessID, ThreadID = te.th32ThreadID);
						ThreadNewCount++;
					}
					te.dwSize = sizeof(te);
				} while (Thread32Next(h, &te));
			}
			CloseHandle(h);
			if (ThreadOldCount + 1 <= ThreadNewCount)	//	最後に追加されたスレッドが終了するまで待機
				WaitForMultipleObjects(ThreadMax, hThreads, TRUE, INFINITE);
			for (n = 0; n < ThreadMax; n++)
				CloseHandle(hThreads[n]);
		}
	}

	pZipFile->Release();
	pSrcDir->Release();

	return TRUE;
}

ソースファイルと実行ファイルのダウンロード

ダウンロード zip_add.zip(62.1kByte)
ZIPファイルに含まれるファイル
zip_add.cpp
zip_add.exe