第272章 LHAによるファイルの圧縮


LHAはご存知のように 吉崎栄泰氏により開発された圧縮方式で、国内では圧倒的シェアを誇っています。 また、Micco氏によりdll化され誰でも簡単に圧縮・解凍ソフトが作れるようになりました。

今回はLHAによるファイルの圧縮をやります。unlha32.dllはダウンロードサイトから 無料でダウンロードすることができます。この時ヘッダーファイルとlibファイルが 手に入りますのでまだ、持っていない人はダウンロードしておいてください。



前準備は

1.unlha32.dllがシステムにインストールされていること 2.unlha32.hをインクルードします 3.(VC++の時は)unlhavc.libをプロジェクトに参加させます

あと、ビルドしてみるとわかりますが、unlha32.hのなかでtime_tを 使っているのでwindows.hの他にtime.hをインクルードする必要があります。 VC++でリンクするのはunlhavc.libです。間違ってunlha32.lib(BC++用)をリンクすると リンクエラーとなります。また、添付のテキストにも書いてあるように unlha32.dllのAPI等は仕様か変更になる可能性もあるので、常に最新のものを 使うようにしてください。

書庫(lzhファイル)を新規に作るにはメニューの「ファイル」「書庫の新規作成」を 選択します。すでに存在する書庫を開くには「書庫を開く」、書庫にファイルを 追加するには「書庫にファイルを追加する」を選択します。



では、プログラムを見てみましょう。

// unlha01.rcの一部 ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "書庫の新規作成(&N)...", IDM_NEW MENUITEM "書庫を開く(&O)...", IDM_OPEN MENUITEM "書庫にファイルを追加(&A)...", IDM_ADD MENUITEM SEPARATOR MENUITEM "終了(&X)...", IDM_END END END

普通のメニューのリソース・スクリプトです。

// unlha01.cpp #ifndef STRICT #define STRICT #endif #define ID_LISTVIEW 100 // unlha32.hでtime_tを使うためtime.hが必要です #include <time.h> // sprintf()を使う関係上stdio.hが必要です #include <stdio.h> #include <windows.h> #include <commctrl.h> #include "unlha32.h" #include "resource.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); ATOM InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); BOOL MyAdd(HWND, int); BOOL MyOpen(HWND, int); BOOL MySetColumn(HWND); char szClassName[] = "unlha01"; //ウィンドウクラス char szWindowTitle[] = "猫でもわかるアーカイバ [%s]"; HINSTANCE hInst; char szCmdLine[1024]; char szLZHFile[MAX_PATH], szOrgFile[MAX_PATH], szTitle[MAX_PATH]; HWND hList;

コモンコントロール(リストビュー)を使うので前準備を忘れないでください。 (commctrl.hのインクルード、comctl32.libのリンクなど)

なお、グローバル変数のszCmdLineはlhaのコマンドライン引数を格納しておくものです。 lhaのコマンドライン引数は複雑で大変難しいのですが、最も簡単な使い方を次に 示します。

a 圧縮ファイル名 書庫に加えるファイル

最初の「a」はコマンドで書庫にファイルを加えよ、という意味です。 2番目の引数は書庫名を指定します(拡張子のlzhはなくてもよい)。3番目の 引数が書庫に加えるファイルです。書庫がない場合は新規に作ってくれます。

実はこれだけわかると、圧縮ファイルが作れてしまいます。

int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { MSG msg; hInst = hCurInst; if (!InitApp(hCurInst)) return FALSE; if (!InitInstance(hCurInst, nCmdShow)) return FALSE; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } //ウィンドウ・クラスの登録 ATOM InitApp(HINSTANCE hInst) { WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; //プロシージャ名 wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst;//インスタンス wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = "MYMENU"; //メニュー名 wc.lpszClassName = (LPCSTR)szClassName; wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); return (RegisterClassEx(&wc)); } //ウィンドウの生成 BOOL InitInstance(HINSTANCE hInst, int nCmdShow) { HWND hWnd; hWnd = CreateWindow(szClassName, "猫でもわかるアーカイバ", //タイトルバーにこの名前が表示されます WS_OVERLAPPEDWINDOW, //ウィンドウの種類 CW_USEDEFAULT, //X座標 CW_USEDEFAULT, //Y座標 CW_USEDEFAULT, //幅 CW_USEDEFAULT, //高さ NULL, //親ウィンドウのハンドル、親を作るときはNULL NULL, //メニューハンドル、クラスメニューを使うときはNULL hInst, //インスタンスハンドル NULL); if (!hWnd) return FALSE; ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; }

これは、例によっていつもとほとんど同じです。

//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id; INITCOMMONCONTROLSEX ic; switch (msg) { case WM_CREATE: ic.dwSize = sizeof(INITCOMMONCONTROLSEX); ic.dwICC = ICC_LISTVIEW_CLASSES; InitCommonControlsEx(&ic); hList = CreateWindowEx(0, WC_LISTVIEW, "", WS_CHILD | WS_VISIBLE | LVS_REPORT, 0, 0, 0, 0, hWnd, (HMENU)ID_LISTVIEW, hInst, NULL); ListView_SetExtendedListViewStyle(hList, LVS_EX_GRIDLINES); MySetColumn(hList); break; case WM_SIZE: MoveWindow(hList, 0, 0, LOWORD(lp), HIWORD(lp), TRUE); break; case WM_COMMAND: switch (LOWORD(wp)) { case IDM_END: SendMessage(hWnd, WM_CLOSE, 0, 0); break; case IDM_ADD: //iOpt 1..追加 //iOpt 0..新規 MyAdd(hWnd, 1); break; case IDM_NEW: MyAdd(hWnd, 0); break; case IDM_OPEN: //iOpt 0...新規オープン MyOpen(hWnd, 0); break; } break; case WM_CLOSE: id = MessageBox(hWnd, "終了してもよいですか", "終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { DestroyWindow(hList); DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0; }

メイン・ウィンドウのプロシージャです。

WM_CREATEメッセージが来たら、コモンコントロールを初期化して、 リストビューを作ります。新しいスタイルのリストビューについては 第198章を参照してください。

WM_SIZEメッセージが来たらリストビューのサイズを調整します。

メニューからIDM_ADD, IDM_NEWが選択されたらMyAdd関数を呼びます。 2番目の引数ですでにオープンされている書庫にファイルを追加するのか 新規に書庫を作ってファイルを追加するのかを指定します。

IDM_OPENが選択されたら、MyOpen関数を呼びます。これも2番目の 引数がオプションとなっています。

BOOL MyAdd(HWND hWnd, int iOpt) { OPENFILENAME ofn; char szBuf[1024]; if (strcmp(szLZHFile, "") == 0 || iOpt == 0) { MessageBox(hWnd, "書庫ファイル名を付けてください", "新規書庫ファイル", MB_OK); memset(&ofn, 0, sizeof(OPENFILENAME)); ofn.lStructSize = sizeof(OPENFILENAME); ofn.lpstrTitle = "書庫ファイルの新規作成"; ofn.lpstrFile = szLZHFile; ofn.nMaxFile = sizeof(szLZHFile); ofn.lpstrFileTitle = szTitle; ofn.nMaxFileTitle = sizeof(szTitle); ofn.hwndOwner = hWnd; ofn.lpstrFilter = "LZH FILES(*.lzh)\0*.lzh\0\0"; ofn.Flags = OFN_OVERWRITEPROMPT | OFN_CREATEPROMPT; ofn.lpstrDefExt = "lzh"; if(!GetSaveFileName(&ofn)) { return FALSE; } } MessageBox(hWnd, "書庫に追加するファイルを選択してください", "ファイル選択", MB_OK); memset(&ofn, 0, sizeof(OPENFILENAME)); ofn.lStructSize = sizeof(OPENFILENAME); ofn.Flags = OFN_HIDEREADONLY; ofn.lpstrTitle = "書庫に格納するファイル"; ofn.lpstrFile = szOrgFile; ofn.nMaxFile = sizeof(szOrgFile); ofn.lpstrFilter = "Text File (*.txt)\0*.txt\0All Files(*.*)\0*.*\0\0"; ofn.lpstrDefExt = "txt"; if (!GetOpenFileName(&ofn)) { return FALSE; } strcpy(szCmdLine, "a "); strcat(szCmdLine, "\""); strcat(szCmdLine, szLZHFile); strcat(szCmdLine, "\""); strcat(szCmdLine, " "); strcat(szCmdLine, "\""); strcat(szCmdLine, szOrgFile); strcat(szCmdLine, "\""); Unlha(hWnd, szCmdLine, szBuf, sizeof(szBuf)); MyOpen(hWnd, 1); wsprintf(szBuf, szWindowTitle, szTitle); SetWindowText(hWnd, szBuf); return TRUE; }

書庫にファイルを追加します。2番目の引数が0かまたは、szLZHFileに ファイル名が格納されていないときは新規作成をします。

さて、unlha32.dllのUnlha関数は添付のAPI.TXTによると次のような使い方をします。

int WINAPI Unlha(const HWND _hwnd, LPCSTR _szCmdLine, LPSTR _szOutput, const DWORD _dwSize)

最初の引数は、unlha32.dllを呼び出すアプリケーションのウインドウハンドルです。
2番目の引数は、コマンドラインを指定します。コマンドラインの詳細に ついては添付のcommand.txtを参照してください。
3番目の引数は結果を受け取るバッファを指定し、4番目の引数はそのサイズです。

最初にも書いたようにコマンドラインを「a abc.lzh file.txt」とすると、 abc.lzhという書庫ファイルにfile.txtが追加されます。abc.lzhが存在しない場合は 新規に作られます。

さて、ファイルを追加したらMyOpen関数を呼んで書庫の内容を表示させます。

BOOL MyOpen(HWND hWnd, int iOpt) { OPENFILENAME ofn; HARC hArc; INDIVIDUALINFO ii; LVITEM item; int iNo = 0; char szBuf[256]; if (strcmp(szLZHFile, "") == 0 || iOpt == 0) { memset(&ofn, 0, sizeof(OPENFILENAME)); ofn.lStructSize = sizeof(OPENFILENAME); ofn.lpstrTitle = "書庫ファイルのオープン"; ofn.lpstrFile = szLZHFile; ofn.nMaxFile = sizeof(szLZHFile); ofn.lpstrFileTitle = szTitle; ofn.nMaxFileTitle = sizeof(szTitle); ofn.hwndOwner = hWnd; ofn.lpstrFilter = "LZH FILES(*.lzh)\0*.lzh\0\0"; ofn.Flags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST; ofn.lpstrDefExt = "lzh"; if(!GetOpenFileName(&ofn)) { return FALSE; } } strcpy(szBuf, "\""); strcat(szBuf, szLZHFile); strcat(szBuf, "\""); hArc = UnlhaOpenArchive(hWnd, szBuf, M_ERROR_MESSAGE_ON); if (hArc == NULL) { MessageBox(hWnd, "書庫ファイルオープン失敗", "Error", MB_OK); return FALSE; } ListView_DeleteAllItems(hList); if (UnlhaFindFirst(hArc, "*.*", &ii) != -1) { do { item.mask = LVIF_TEXT; item.pszText = ii.szFileName; item.iItem = iNo; item.iSubItem = 0; ListView_InsertItem(hList, &item); wsprintf(szBuf, "%d", ii.dwCompressedSize); item.pszText = szBuf; item.iSubItem = 1; ListView_SetItem(hList, &item); wsprintf(szBuf, "%d", ii.dwOriginalSize); item.pszText = szBuf; item.iSubItem = 2; ListView_SetItem(hList, &item); sprintf(szBuf, "%5.1f", ii.wRatio / 10.0); item.pszText = szBuf; item.iSubItem = 3; ListView_SetItem(hList, &item); iNo++; } while (UnlhaFindNext(hArc, &ii) != -1); } UnlhaCloseArchive(hArc); wsprintf(szBuf, szWindowTitle, szTitle); SetWindowText(hWnd, szBuf); return TRUE; }

書庫の中身をリストビューに表示する関数です。すでに表示してある 書庫にファイルを追加した場合と、新しく書庫をオープンする場合に呼ばれます。

新規に呼ばれる場合は(2番目の引数が0)、オープンするlzhファイル名を 取得する操作が増えます。

書庫ファイルの内容を表示するには、UnlhaOpenArchive関数で書庫を開き、 UnlhaFindFirst関数で、最初のファイルを調べます。あとは、UnlhaFindNext関数が -1を返すまで繰り返し呼び出します。そして、UnlhaCloseArchive関数で ファイルを閉じます。

INDIVIDUALINFO構造体については 添付のヘッダファイルを読むと中身がわかります。

BOOL MySetColumn(HWND hList) { LVCOLUMN lc; lc.mask = LVCF_TEXT | LVCF_SUBITEM | LVCF_WIDTH | LVCF_FMT; lc.fmt = LVCFMT_LEFT; lc.pszText = "ファイル名"; lc.iSubItem = 0; lc.cx = 200; ListView_InsertColumn(hList, 0, &lc); lc.fmt = LVCFMT_RIGHT; lc.pszText = "サイズ"; lc.cx = 100; lc.iSubItem = 1; ListView_InsertColumn(hList, 1, &lc); lc.pszText = "元サイズ"; lc.cx = 100; lc.iSubItem = 2; ListView_InsertColumn(hList, 2, &lc); lc.pszText = "圧縮率"; lc.cx = 100; lc.iSubItem = 3; ListView_InsertColumn(hList, 3, &lc); return TRUE; }

リストビューに項目を追加する関数です。

さて、今回は比較的簡単でした。解凍するにはコマンドラインのコマンドを 「e」にすればよいです。添付のテキスト等を参考に解凍にも挑戦してみてください。


[SDK第3部 Index] [総合Index] [Previous Chapter] [Next Chapter]

Update 29/May/2000 By Y.Kumei
当ホーム・ページの一部または全部を無断で複写、複製、 転載あるいはコンピュータ等のファイルに保存することを禁じます。