山本ワールド
Windowsプログラミング
アルゴリズム Vitual C++ 2008/2013によるWin32/Win64 APIレベルのプログラム 基礎 Vitual C++ 2008/2013によるAPIレベルのプログラム(32/64bit) Wix3でインストーラーを作る Visual C++ 2008 Standard Editonによるフォームアプリケーションのプログラム(32/64bit) Vitual C++ 2008 Standard EditonによるAPIレベルのプログラム(32/64bit) Windows 7対応 Visual C++ 2008 ExpressによるAPIレベルのプログラム Visual C++ 2005 ExpressによるAPIレベルのプログラム Visual C++ Versiosn 5 BORLAND C++ Windowsプログラム全般 Excel VBA その他ファイルをZIPで圧縮する
概要
シェルが提供するIShellDispatchを用いてファイルをZIPで圧縮します。
IShellDispatchには、ファイル名を指定して実行(FileRun)や検索などがあります。
Windows XPより標準でZIP圧縮機能が搭載されました。
本プログラムでは、C:¥temp2¥sample.txtファイルをC:¥temp2¥sample.zipに追加します。
プログラムの説明のためスレッドの一覧(_tprintfの行)を表示するようにしていますが、削除しても問題ありません。
IShellDispatchには、ファイル名を指定して実行(FileRun)や検索などがあります。
Windows XPより標準でZIP圧縮機能が搭載されました。
本プログラムでは、C:¥temp2¥sample.txtファイルを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
圧縮元のファイルのVARIANTからZIPファイルのFolder型へコピーすることにより圧縮することができますが、圧縮は非同期に実行されるので、コピー動作(CopyHere)を呼び出してコピーが終了する前に関数が終了してしまします。各オブジェクトを終了前に解放してしまうと圧縮に失敗します。例えば1秒ほど待機してからという処理も考えられますが、ファイル数が膨大の場合は1秒で終わる保証はありません。
Windows 7/10ではコピー処理を呼び出すと3つの新しいスレッドが起動しすぐに元のプログラムへ処理が戻ります。
ここでは、コピー処理前とコピー中のスレッドを比較し新たに作成されたスレッドについて終了するまで待機した後に各オブジェクトの解放をすることにしました。以下のような手順で処理します。
- 圧縮元のファイル(TCHAR*型)からVARIANT型を作成します。
- ZIPファイルが存在するかどうかをPathFileExists APIにより確認しない場合は、ZIPファイルの元になるファイルを作成します。
- ZIPファイルも圧縮元のフォルダーと同様にFolder型オブジェクトを作成します。VARIANT型は不要ですのでVariantClearにより解放します。
- CreateToolhelp32Snapshot APIによりコピー前のスレッドのスナップショットを作成します。
Thread32First,Thread32Next APIによりスレッドを検索し今のプロセスと同じIDのスレッドで自身のスレッドIDと異なるスレッド数を数えます。 - ZIPファイルのフォルダー型のCopyHere関数により圧縮します。前述のとおりWindowsは内部で3つのスレッドを起動してすぐに元のプログラムに処理が戻ります。
- CreateToolhelp32Snapshot APIによりコピー中のスレッドのスナップショットを作成します。
Thread32First,Thread32Next APIによりスレッドを検索し今のプロセスと同じIDのスレッドで自身のスレッドIDと異なるスレッド数を数えコピー前のスレッド数を超えた時点でコピーのために新たに起動されたスレッドであることが判明しますのでハンドルを取得し配列に保存します。 - WaitForMultipleObjects APIにより新たに起動されたスレッドが終了するまでプログラムを待機します。
- 新たに起動されたスレッドが全部終了するとWaitForMultipleObjects APIの次の行へプログラムが進みます。
- 新たに起動されたスレッドのハンドルをクローズし、各オブジェクトを解放して関数を終了します。
プログラムソース
// 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>
#pragma comment(lib, "shlwapi")
BOOL AddFileZip(IShellDispatch *pShellDisp, TCHAR* ZipPath, TCHAR* AddFilePath);
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ファイル 追加するファイル
AddFileZip(pShellDisp, _TEXT("c:¥¥temp2¥¥sample.zip"), _TEXT("c:¥¥temp2¥¥sample.txt"));
CoUninitialize();
return 0;
}
BOOL AddFileZip(IShellDispatch *pShellDisp, TCHAR* ZipPath, TCHAR* AddFilePath){
HRESULT hr;
VARIANT vSrcFile;
// 追加するファイル名のVARIANT型を作成
VariantInit(&vSrcFile);
vSrcFile.vt = VT_BSTR;
vSrcFile.bstrVal = SysAllocString(AddFilePath);
// 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"))){
MessageBox(NULL, _TEXT("ZIPファイルが作成できませんでした。"), _TEXT("エラー"), MB_ICONWARNING);
VariantClear(&vSrcFile);
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) {
VariantClear(&vSrcFile);
pZipFile->Release();
MessageBox(NULL, _TEXT("ZIPファイルが見つかりませんでした。"), _TEXT("エラー"), MB_ICONWARNING);
return FALSE;
}
VARIANT 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(&varOpt);
varOpt.vt = VT_I4;
varOpt.lVal = 0;// FOF_SILENTを指定すると処理中の経過が表示されなくなります
hr = pZipFile->CopyHere(vSrcFile, varOpt);
if (hr != S_OK) {
pZipFile->Release();
VariantClear(&vSrcFile);
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();
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();
VariantClear(&vSrcFile);
return TRUE;
}
ソースファイルと実行ファイルのダウンロード
Copyright (C) 2012 山本ワールド All Rights Reserved.