LHA ファイルの検索と TextFile の表示



LHA で圧縮された TEXT ファイル名を検索して、解凍した文字列を表示します。
吉崎栄泰氏が開発された unlha32 をダウンロードして使用します。

前田稔(Maeda Minoru)の超初心者のプログラム入門

ライブラリの準備

  1. 吉崎栄泰氏が開発された unlha32 をダウンロードして下さい。
    検索サイトで調べればすぐ見つかるはずです。
  2. ダウンロードしたファイルを解凍して下さい。
    プログラム作成に必要なのは次のファイルです。
    ファイル名 説明
    unlhavc.lib LIB ファイルです
    unlha32.h Lib Header ファイルです
    unlha32.dll DLL ファイルです

アプリケーションの作成

  1. [ファイル] [新規作成] [プロジェクト] から新規プロジェクト(LzhSrh)を作成します。
  2. ダウンロードしたファイルフォルダーから次のファイルをコピーしてきます。
    unlhavc.lib コンパイルのとき使用する LIB ファイルです
    unlha32.h コンパイルのとき使用する Lib Header ファイルです
    unlha32.dll 実行時に呼び出される DLL ファイルです
  3. test.lzh を用意します。
    最大10個の TEXT ファイルを圧縮して格納します。
    ただしメニュー設定の関係で画面に表示されるのは最初の3個だけです。
    file_a.txt
    file_b.txt
    file_c.txt
  4. [プロジェクト] [プロパティ] [リンカ] [入力] から unlhavc.lib をリンクします。
    #pragma を使ってリンクすることもできます。
  5. ファイルメニューの後に次のメニュー追加します。
    IDM_FILE_A: FILE_Aの表示(&A)
    IDM_FILE_B: FILE_Bの表示(&B)
    IDM_FILE_C: FILE_Cの表示(&C)
    IDM_FILE_I: FILE 情報の表示(&I)
  6. StdAfx.h に次のヘッダーファイルを取り込んで下さい。
        // TODO: プログラムで必要なヘッダー参照を追加してください。
        #include <time.h>
        #include "unlha32.h"
        
  7. テンプレートで作成された LzhSrh.cpp のソースコードを修正します。
    LhaOpen(); と ViewText(); は新たに追加する関数の宣言です。
        // グローバル変数:
        INDIVIDUALINFO  g_ii[10];           // LZH FileInfor
        int             g_cnt;              // LZH Member Count
        BYTE            *g_buf= NULL;       // Memory Buffer Address
        DWORD           g_len;              // Buffer Size
        int             g_no;               // Text File Number
    
        int             LhaOpen(HWND hWnd);
        void            ViewText(HWND hwnd, int n);
        
  8. WndProc() の修正です。
    WM_CREATE: を追加して LhaOpen(); と ViewText(); を呼び出します。
        LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
        {               :
                        :
            char szBuf[256];
    
            switch( message ) 
            {   case WM_CREATE:
                    g_cnt= LhaOpen(hWnd);
                    ViewText(hWnd,0);
                    break;
        
  9. WndProc() の修正の続きです。
    メニュー選択で TEXT 文字列を表示するコードです。
            switch( wmId ) 
            {
                case IDM_FILE_A:
                    ViewText(hWnd,0);
                    break;
                case IDM_FILE_B:
                    ViewText(hWnd,1);
                    break;
                case IDM_FILE_C:
                    ViewText(hWnd,2);
                    break;
                case IDM_FILE_I:
                    wsprintf(szBuf,"FileName: %s  CompressedSize: %d  OriginalSize: %d",
                                    g_ii[g_no].szFileName,g_ii[g_no].dwCompressedSize,
                                    g_ii[g_no].dwOriginalSize);
                    MessageBox(hWnd,szBuf,"File Info",MB_OK);
                    break;
        
  10. WndProc() の修正の続きです。
            case WM_PAINT:
                hdc = BeginPaint (hWnd, &ps);
                // TODO: この位置に描画用のコードを追加してください...
                RECT rt;
                GetClientRect( hWnd, &rt );
                DrawText(hdc,(const char *)g_buf,g_len,&rt,DT_CENTER);
                EndPaint( hWnd, &ps );
                break;
        
  11. WndProc() の修正の続きです。
            case WM_DESTROY:
                if (g_buf)   delete [] g_buf;
                PostQuitMessage( 0 );
                break;
        
  12. LzhSrh.cpp の最後に LhaOpen() 関数を追加して下さい。
    // LZH で圧縮されているファイル情報を取得
    int LhaOpen(HWND hWnd)
    {   HARC    hArc;
        int     n;
    
        hArc = UnlhaOpenArchive(hWnd, "test.lzh", M_ERROR_MESSAGE_ON);
        if (hArc == NULL)
        {   MessageBox(hWnd, "書庫ファイルオープン失敗", "Error", MB_OK);
            return -1;
        }
        if (UnlhaFindFirst(hArc,"*.*",&g_ii[0]) == -1)
        {   UnlhaCloseArchive(hArc);
            return -1;
        }
        for(n=1; n<10; n++)
        {
            if (UnlhaFindNext(hArc, &g_ii[n]) == -1)    break;
        }
        UnlhaCloseArchive(hArc);
        return n;
    }
        
  13. その後に ViewText() 関数を追加して下さい。
    // n番目のファイルを解凍して画面に表示
    void ViewText(HWND hwnd, int n)
    {   TCHAR   szBuf[256];
    
        if (g_cnt>n)
        {   g_no= n;
            if (g_buf)  delete [] g_buf;
            g_len= g_ii[n].dwOriginalSize;
            g_buf= new BYTE[g_len];
            wsprintf(szBuf,"test.lzh c: %s",g_ii[n].szFileName);
            UnlhaExtractMem(hwnd,szBuf,g_buf,g_len,NULL,NULL,&g_len);
            InvalidateRect(hwnd,NULL,TRUE);
        }
    }
        
  14. [デバッグ] [デバッグなしで開始] を選択して、ビルド(コンパイル)に続いて実行を行います。
    コンパイルの進行状況とエラーがあれば、エラーメッセージが表示されます。
    メニューから「FILE 情報の表示」を選ぶと現在表示中の TextFile の情報が表示されます。

プログラムの説明

  1. グローバル変数の宣言です。
    g_ii[10]; が test.lzh に圧縮されているファイル情報を格納する領域で、詳細はページの後に掲載します。
    g_cnt は test.lzh に格納されているファイル数で、g_no が現在表示中の番号です。
    *g_buf は動的に割り当てた TEXT を解凍する領域のポインタで、g_len がそのサイズです。
        INDIVIDUALINFO  g_ii[10];           // LZH FileInfor
        int             g_cnt;              // LZH Member Count
        BYTE            *g_buf= NULL;       // Memory Buffer Address
        DWORD           g_len;              // Buffer Size
        int             g_no;               // Text File Number
        
  2. WM_CREATE: から LhaOpen(hWnd) を呼び出して、test.lzh に圧縮されているファイル情報を取得します。
    ViewText(hWnd,0) は、0番目の TEXT ファイルを解凍して、画面に表示する関数です。
            switch( message ) 
            {   case WM_CREATE:
                    g_cnt= LhaOpen(hWnd);
                    ViewText(hWnd,0);
                    break;
        
  3. メニュー選択で TEXT 文字列を表示するコードです。
    ファイルの番号(0~2)を指定して ViewText() を呼び出すだけです。
    IDM_FILE_I: では、現在表示中のファイル情報(ファイル名,圧縮サイズ,元のサイズ)を MessageBox で表示します。
            char szBuf[256];
            switch( wmId ) 
            {
                case IDM_FILE_A:
                    ViewText(hWnd,0);
                    break;
                case IDM_FILE_B:
                    ViewText(hWnd,1);
                    break;
                case IDM_FILE_C:
                    ViewText(hWnd,2);
                    break;
                case IDM_FILE_I:
                    wsprintf(szBuf,"FileName: %s  CompressedSize: %d  OriginalSize: %d",
                                    g_ii[g_no].szFileName,g_ii[g_no].dwCompressedSize,
                                    g_ii[g_no].dwOriginalSize);
                    MessageBox(hWnd,szBuf,"File Info",MB_OK);
                    break;
        
  4. LZH で圧縮されているファイル情報を取得します。
    UnlhaOpenArchive() で LZH をオープンします。
    UnlhaFindFirst() で最初のファイル名を検索します。
    UnlhaFindNext() で次のファイル名を検索します。
    UnlhaCloseArchive() で LZH をクローズします。
    // LZH で圧縮されているファイル情報を取得
    int LhaOpen(HWND hWnd)
    {   HARC    hArc;
        int     n;
    
        hArc = UnlhaOpenArchive(hWnd, "test.lzh", M_ERROR_MESSAGE_ON);
        if (hArc == NULL)
        {   MessageBox(hWnd, "書庫ファイルオープン失敗", "Error", MB_OK);
            return -1;
        }
        if (UnlhaFindFirst(hArc,"*.*",&g_ii[0]) == -1)
        {   UnlhaCloseArchive(hArc);
            return -1;
        }
        for(n=1; n<10; n++)
        {
            if (UnlhaFindNext(hArc, &g_ii[n]) == -1)    break;
        }
        UnlhaCloseArchive(hArc);
        return n;
    }
    
  5. n番目のファイルを解凍して画面に表示します。
    g_buf には動的に割り当てられた領域のポインタを格納します。
    もし前回に割り当てられた領域があるときは、その領域を開放します。
    g_len に必要な領域のサイズを設定して、new BYTE[g_len] で領域を割り当てます。
    最後に割り当てた領域を WM_DESTROY: で開放して下さい。
    ファイルの解凍は極めて簡単で UnlhaExtractMem() を実行するだけです。
    InvalidateRect() でウインドウを再描画します。
    // n番目のファイルを解凍して画面に表示
    void ViewText(HWND hwnd, int n)
    {   TCHAR   szBuf[256];
    
        if (g_cnt>n)
        {   g_no= n;
            if (g_buf)  delete [] g_buf;
            g_len= g_ii[n].dwOriginalSize;
            g_buf= new BYTE[g_len];
            wsprintf(szBuf,"test.lzh c: %s",g_ii[n].szFileName);
            UnlhaExtractMem(hwnd,szBuf,g_buf,g_len,NULL,NULL,&g_len);
            InvalidateRect(hwnd,NULL,TRUE);
        }
    }
    
  6. INDIVIDUALINFO の構造体の説明です。
    -----------------------------------------------------------------------
    %2. INDIVIDUALINFO の構造
    -----------------------------------------------------------------------
    構造体定義
    
            typedef struct {
                    DWORD           dwOriginalSize;
                    DWORD           dwCompressedSize;
                    DWORD           dwCRC;
                    UINT            uFlag;
                    UINT            uOSType;
                    WORD            wRatio;
                    WORD            wDate;
                    WORD            wTime;
                    char            szFileName[FNAME_MAX32 + 1];
                    char            dummy1[3];
                    char            szAttribute[8];
                    char            szMode[8];
            } INDIVIDUALINFO;
    
    メンバの説明
            dwOriginalSize          ファイルのサイズ。
            dwCompressedSize        圧縮後のサイズ。
            dwCRC                   格納ファイルのチェックサム用 CRC。ただし,
                                    上位 16 bit は常に 0。
            uFlag                   展開やテストを行った場合,格納ファイル毎の
                                    処理結果が返されます。 コードは Unlha() が
                                    返すものと同じ。その他の場合は 0。
            uOSType                 このファイルの作成に使われた OS。
                                             0      MS-DOS
                                             2      UNIX
                                             4      旧 MAC-OS
                                             5      OS/2
                                            10      その他
                                            11      OS9 (MAC の新型ではない)
                                            12      OS/68K
                                            13      OS/386
                                            14      HUMAN
                                            15      CP/M
                                            16      FLEX
                                            17      Runser
                                            18      WindowsNT
                                            19      Windows95
                                            -1      エラー
            wRatio                  パーミル (千分率) で表された圧縮率。
            wDate                   ftime の上位バイトと同じ次の構造で現された
                                    日付。
                                        struct {
                                            unsigned ft_day   : 5;
                                            unsigned ft_month : 4;
                                            unsigned ft_year  : 7;
                                        } _DosDate;
                                    ft_year に格納される値は 1980 年からの経過
                                    年数です。したがって,理論的には 2107 年いっ
                                    ぱいまで表現できることになります。
            wTime                   ftime の下位バイトと同じ次の構造で現された
                                    時刻。
                                        struct {
                                            unsigned ft_tsec : 5;
                                            unsigned ft_min  : 6;
                                            unsigned ft_hour : 5;
                                        } _DosTime;
                                    ft_tsec  に格納される値が秒を 2 で割ったも
                                    のである点に注意してください。
    
                                    <<重要>>
                                    NTFS 上のファイルや  UNIX 等が起源のファイ
                                    ルの場合, 00:00:01 等秒が奇数である場合が
                                    当然ありますが,そのようなデータの扱いがプ
                                    ラットフォームにより異なっていることに注意
                                    してください。Win32s,Win95 では丸めの際に
                                    切り捨てが行われますが,その他では切り上げ
                                    が行われます。 特に注意が必要なのは,Win98
                                    であり,API のバグから,SetFileTime() 等で
                                    は切り上げが行われるにもかかわらず   File-
                                    TimeToDosDateTime() では切り捨てられてしま
                                    います。
                                    UNLHA32.DLL においては,Win32 API を使用し
                                    ないことにより,Win98 においても切り上げた
                                    数値が返されるようになっています。
                                    (Ver 1.32 以降)
            szFileName              アーカイブファイル名。
            szAttribute             格納ファイルの属性。'l' コマンド等でのリス
                                    ト出力と同じ。
            szMode                  ダミー。UNLHA32.DLL では圧縮形式の文字列が
                                    入っています。
    
       定義をみれば分かるとおり, INDIVIDUALINFO 構造体を使用する限りにおいて
      は,タイムスタンプは ftime の制度でしか得られません。 正確なタイムスタン
      プを得たい場合は API を使用する必要があります。
    
       この構造体は UNLHA32.DLL 固有のものであり,他のアーカイバ関連 DLL で使
      用できるとは限りませんし,サイズ等についても 32 ビット整数でしか情報を得
      られません。 従って,情報の取得にはなるべく API を使用するようにしてくだ
      さい。
    

【演習】

現在は3個のファイルまでしか表示できませんが、20個のファイルまで対応できるように修正して下さい。
ファイル数が20個にもなると、メニューを増やすだけの対応では芸がなさすぎます。
DialogBox から検索するファイル名をタイプ入力するなどの工夫をして下さい。
    case IDM_FILE_A:
        ViewText(hWnd,0);
        break;
    case IDM_FILE_B:
        ViewText(hWnd,1);
        break;
    case IDM_FILE_C:
        ViewText(hWnd,2);
        break;

超初心者のプログラム入門(Win32API C++)