006-03  前へ←  ホームへ  →次へ  007-01

No.006 - 04  zlib圧縮の基本


zlib 圧縮の基本をやります。
zlibファイル 訳(当サイト内)  zlib.h  readme  faq  index


・zlib
zlib zip や PNG画像 の圧縮などに使われている圧縮アルゴリズムライブラリです。 フリーソフトです。
執筆時 zlib の最新のバージョンは 1.2.2 です。
zlib の入手先は zlib Home Page
PNG は PNG Home Page


・なぜzlibなのか
当初、筆者も自前の圧縮アルゴリズムを考えたこともありますが、正直危険です。
自前の圧縮アルゴリズムなんて研究してたら年単位のヒマがつぶれます。
最終目的も今んとこそこじゃないし。

というわけでフリーのアルゴリズムを使います。
zlib は国際的な圧縮方法なので高い信頼性があります。PNGにも使われてます。

このサイトでは圧縮アルゴリズムはzlibしか扱いませんが
UNIX系で探したりすればもっといろいろしてみると選択肢は広がるはずですので必要に応じて探してみるといいかもしれません。


・まずzlib入手
zlib Home Page へ行ってソースコードを手にいれましょう。
ファイル形式はzlib122.zipでもzlib-1.2.2.tar.gz.tarでも構いません。
ミラーサイトがいっぱいありますが、US (www.zlib.net) とか France (www.gzip.org) からじかにダウンロードすりゃ十分だと思います。

さて早速ダウンロードしたファイルを解凍します。


・拡張子 C → CPP に変える
解凍したファイルをいざコンパイル!!と若干前のめりになりがちですが
慌ててはいけません。
拡張子CでコンパイルしたものとCPPでコンパイルしたものに互換性がないからです。
同時コンパイル可能なのにCとCPPでは互いの関数は使えません。せつなさいっぱいです。

というわけで各Cファイル拡張子をCPPに変えます。
一個一個手動でかえても構いませんが面倒なので下記に簡単な「拡張子一括変更プログラム」を用意しました。
150行そこらのソースでコンパイルも面倒だろうってコトで今回のみバイナリ・ファイルもあります。
   ※ 「拡張子一括変更プログラム」 ext_changer.zip(28KB)
説明:指定したフォルダの拡張を一括変更します。サブディレクトリは無視します。

探せばvectorとか窓の杜にもっといいソフトウェアがあるかと思います。


・コンパイル
zlib ライブラリは
adler32.c
compress.c
crc32.c
crc32.h
deflate.c
deflate.h
infback.c
inffast.c
inffast.h
inffixed.h
inflate.c
inflate.h
inftrees.c
inftrees.h
trees.c
trees.h
uncompr.c
zutil.c
zutil.h
をリンクして使う訳ですがexample.c、minigzip.c はサンプルなので無理にリンクする必要はないです。
さらに今回gzファイルを扱わないのでgzio.cもいらないです。
inflate.cpp のinflate()関数等であろうことか this なんて変数を宣言しています。
当然 C++ではコンパイル・エラー。ぐふぅ。
しかたがないのでthisをなんか適当な名前(例えばz_thisとか)に変えてやる必要があります。
ほかにもc →c++の移行にともなうキャスト演算エラーが発生するので順次修正します。

自分のファイルに#include するのはzlib.hだけでいいようです。


・ライブラリ作成する?
こういう一般ライブラリを毎回コンパイルすんのは大変なので
必要に応じて .lib とか DLL にしたほうが楽かもしれません。
ライブラリの作り方自体はもうわんさといろんなサイトで紹介されてるので、そちらを参考にしてください。



圧縮サンプルソース解説

圧縮

1. zlib ストリームを使うにはまず z_stream 構造体を宣言し、初期化します
zlib ストリームはその内部で使う 内部のメモリ確保・解放関数を指定できます。
alloc_func zalloc; /* 内部のメモリ確保に使われます */
free_func zfree; /* 内部のメモリ解放に使われます */
voidpf opaque;   /* zalloc と zfree に渡される プライベートデータ・オブジェクト */
Z_NULLを渡せばデフォルト関数になります。

下記サンプルソースでは z_stream z を宣言し
  z.zalloc = Z_NULL;
  z.zfree = Z_NULL;
  z.opaque = Z_NULL;

として、すべてのメモリ管理をライブラリに任せています。

2. 次に deflateInit() 関数を呼んで
zlib ライブラリ内部の初期化(メモリ確保とか)をします。

第一引数は z_stream 構造体へのポインタです。
第二引数は0〜9の圧縮レベルです。1が最速圧縮、9が最高圧縮率になります。
0なら無圧縮です(0って意味あるの?)
下記サンプルソースでは
Z_DEFAULT_COMPRESSION (== 6) という標準値を渡しています

deflateInit(&z, Z_DEFAULT_COMPRESSION);

deflateInit の返り値が Z_OK なら関数成功です。それ以外はエラーです。
戻り値 内容
Z_OK 関数成功です。
Z_MEM_ERROR メモリ不足です。
Z_STREAM_ERROR 圧縮レベルが有効値ではありません
Z_VERSION_ERROR zlib ライブラリのバージョンエラーです。(非互換)
エラー時のエラーメッセージはmsg メンバにあります。msg がnull ならエラーメッセージはありません。
3.





deflate() 関数を 【Z_NO_FLUSH(第二引数)】 で繰り返し呼んで
圧縮を実行します。

deflate関数の 第一引数は z_stream 構造体へのポインタです。
第二引数はフラッシュ(強制出力)させるかどうかのフラグです。通常は Z_NO_FLUSH です。

deflate() を呼ぶ前にz_stream 構造体の入出力バッファ関連のメンバ変数を設定してやる必要があります
z.next_in ・・・入力ポインタ
z.avail_in ・・・入力バッファの有効バイト数
z.next_out ・・・出力ポインタ
z.avail_out ・・・出力バッファの有効サイズ

これらのメンバ変数の値は deflate() 関数内部で使う際に変更されてしまうので
元の値を呼び出し側で保持しておく必要があります。

deflate(&z, Z_NO_FLUSH);   // 圧縮する

関数を呼んだ後のメンバ変数
z.avail_in ・・・入力バッファの残量が入ってます。
z.next_in ・・・入力ポインタは処理が終わった分だけ進められたポインタです。
z.avail_out ・・・出力バッファの残量が入ってます。
z.next_out ・・・出力ポインタは処理が終わった分だけ進められたポインタです。

この場合のdeflate関数の戻り値は
Z_OKなら成功、それ以外はエラーです。
戻り値 内容
Z_OK 関数成功です。処理が進んだことを示します。
Z_STREAM_ERROR ストリーム状態が異常(例えば next_in や next_out が NULL のとき)です
Z_BUF_ERROR 処理が進められなかったことを示します(例えば avail_in や avail_out が zero だったとき)。
これはたいしたエラーではありません。
入力を追加したり出力領域を追加したりすることでdeflate() を何度でも呼び出すことができます

4.
deflate() 関数を 【Z_FINISH(第二引数)】 で呼んで
最終圧縮を実行します。(必要なら繰り返し呼ぶ)


deflate(&z, Z_FINISH);   // 圧縮する

戻り値はZ_STREAM_ENDなら圧縮が完全に終わったことを示します。
Z_OKなら再びdeflate関数を呼んで圧縮を続行してください。
それ以外はエラーです。
戻り値 内容
Z_STREAM_END 圧縮が完全に終わったことを示します。
(この値が返るのは第二引数にZ_FINISHをセットしたときだけです)。
この値が返ったら圧縮終了の手続きへと進んでください。
Z_OK 処理が進んだことを示します。再びdeflate関数を呼んでください。
Z_STREAM_ERROR ストリーム状態が異常(例えば next_in や next_out が NULL のとき)です
Z_BUF_ERROR 処理が進められなかったことを示します(例えば avail_in や avail_out が zero だったとき)。
これはたいしたエラーではありません。
入力を追加したり出力領域を追加したりすることでdeflate() を何度でも呼び出すことができます

5. deflateEnd() 関数を呼んで
zlib ライブラリ内部の後始末(メモリ解放とか)をします。


deflateEnd の返り値が Z_OK なら関数成功です。それ以外はエラーです。
戻り値 内容
Z_OK 関数成功です。
Z_STREAM_ERROR ストリームが異常です。
Z_DATA_ERROR メモリ解放が早すぎることを示します。
(どっかの入力か出力で破棄されてしまった)
エラー時のエラーメッセージはmsg メンバにあります。msg がnull ならエラーメッセージはありません。
msg がセットされてる場合、そのときは静的文字列へのポインタです (メモリ解放してはいけません)。

以上が圧縮の手順ですが展開の場合も同様です。
deflate ⇒ inflate になるだけです。




・参考
奥村晴彦 教授 のzlib入門
http://oku.edu.mie-u.ac.jp/~okumura/compression/zlib.html


拡張子一括変更プログラム
ext_changer.cpp

#include <windows.h>
#include <stdio.h>
#include <shlobj.h>

// 拡張子変更実行
int chang_ext(HWND hwnd, HWND hedit_path, HWND hedit1, HWND hedit2){
    int file_cnt=0, slen;
    char tmp[MAX_PATH], tmp2[MAX_PATH], dir_path[MAX_PATH], find_file[MAX_PATH], ext1[MAX_PATH], ext2[MAX_PATH];
    GetWindowText(hedit_path, dir_path, MAX_PATH);
    GetWindowText(hedit1, ext1, MAX_PATH);  sprintf(find_file, "%s\\*%s", dir_path, ext1);
    GetWindowText(hedit2, ext2, MAX_PATH);

    SHFILEOPSTRUCT sfo;   // SHFILEOPSTRUCT構造体
    memset(&sfo, 0, sizeof(SHFILEOPSTRUCT));
    sfo.hwnd = hwnd;            // ウィンドウハンドル
    sfo.wFunc = FO_RENAME;      // 実行する操作

    WIN32_FIND_DATA win32_find_data;
    HANDLE hfind = FindFirstFile(find_file, &win32_find_data);
    if(hfind != INVALID_HANDLE_VALUE){
        do{
            sprintf(tmp2, "%s\\%s", dir_path, win32_find_data.cFileName);
            GetFullPathName(tmp2, MAX_PATH, tmp, NULL);
            slen = strlen(tmp)-strlen(ext1);
            strncpy(tmp2, tmp, slen);
            strcpy(tmp2+slen, ext2);
            sfo.pFrom = tmp;            // 対象ファイル名
            sfo.pTo = tmp2;             // 目的ファイル名
            tmp[strlen(tmp)+1] = NULL;
            tmp2[strlen(tmp2)+1] = NULL;
            SHFileOperation(&sfo);
            file_cnt++;
        } while(FindNextFile(hfind, &win32_find_data));
        FindClose(hfind);
    }
    if (file_cnt)   sprintf(tmp, "%d 個の[%s]ファイルを[%s]に変更しました", file_cnt, ext1, ext2);
    else            sprintf(tmp, "変更ファイルはありませんでした", file_cnt, ext1, ext2);
    MessageBox(hwnd, tmp, "ファイル数", MB_OK);

    return 0;
}

// フォルダの選択ダイアログ(コールバック関数)
int CALLBACK BrowseCallbackProc(HWND hwnd, UINT msg, LPARAM lparam, LPARAM lpdata){
    if(msg==BFFM_INITIALIZED) SendMessage(hwnd, BFFM_SETSELECTION, (WPARAM)TRUE, lpdata);
    return DefWindowProc(hwnd, msg, lparam, lpdata);
}
// フォルダの選択ダイアログ
call_folderdialog(HWND hwnd, HWND hedit){
    BROWSEINFO binfo;
    ITEMIDLIST *p_id_list;
    TCHAR dir_path[MAX_PATH];
    GetWindowText(hedit, dir_path, MAX_PATH);

    // BROWSEINFO構造体に値を設定
    binfo.hwndOwner         = hwnd;         // ダイアログの親ウインドウのハンドル
    binfo.pidlRoot          = NULL;                     // ルートフォルダを示すITEMIDLISTのポインタ (NULLの場合デスクトップフォルダが使われます)
    binfo.pszDisplayName    = dir_path;                 // 選択されたフォルダ名を受け取るバッファのポインタ
    binfo.lpszTitle         = TEXT("フォルダの選択");   // ツリービューの上部に表示される文字列 
    binfo.ulFlags           = BIF_NEWDIALOGSTYLE;   // 表示されるフォルダの種類を示すフラグ
    binfo.lpfn              = BrowseCallbackProc;       // BrowseCallbackProc関数のポインタ
    binfo.lParam            = (LPARAM)dir_path;         // コールバック関数に渡す値

    // フォルダ選択ダイアログを表示
    p_id_list = ::SHBrowseForFolder(&binfo);
    if(p_id_list != NULL){  // フォルダ選択実行
        if(::SHGetPathFromIDList(p_id_list, dir_path)) SetWindowText(hedit, dir_path);
        ::CoTaskMemFree( p_id_list );   // p_id_listのメモリを開放
    }
}
// テキスト作成
HWND make_stext(HWND parent, int x, int y, int cx, int cy, char *caption){
    return CreateWindow(
        TEXT("static"), caption, WS_CHILD | WS_VISIBLE,
        x, y, cx, cy, parent, (HMENU)0, GetModuleHandle(NULL), NULL
    );
};
// ボタン作成
HWND make_button(HWND parent, int x, int y, int cx, int cy, char *caption, int id){
    return CreateWindow(
        TEXT("BUTTON"), caption, WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
        x, y, cx, cy, parent, (HMENU)id, GetModuleHandle(NULL), NULL
    );
};
// エディットボックス作成
HWND make_edit(HWND parent, int x, int y, int cx, int cy, char *caption, int id){
    return CreateWindow(
        TEXT("EDIT"), caption, WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT | ES_AUTOHSCROLL,
        x, y, cx, cy, parent, (HMENU)id, GetModuleHandle(NULL), NULL
    );
};
// ウィンドウ作成メッセージ
on_create(HWND hwnd){
    char s[MAX_PATH];
    make_stext(hwnd, 10, 10, 100, 25, "ディレクトリ");
    make_stext(hwnd, 120, 80, 100, 25, "→");
    make_button(hwnd, 380, 30, 50, 25, "参照", 101);
    make_edit(hwnd, 10, 30, 360, 25, "", 102);  // ファイルパス
    make_edit(hwnd, 40, 80, 60, 25, ".c", 103);
    make_edit(hwnd, 160, 80, 60, 25, ".cpp", 104);
    make_button(hwnd, 240, 80, 60, 25, "変更", 105);
    SetFocus(GetDlgItem(hwnd, 102));
    GetCurrentDirectory(MAX_PATH, s);
    SetWindowText(GetDlgItem(hwnd, 102), s);
};
// ウィンドウ・プロシージャ
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
    switch(msg) {
    case WM_CREATE:  on_create(hwnd); break;
    case WM_DESTROY: PostQuitMessage(0); break;
    case WM_COMMAND:
        switch(LOWORD(wp)){
        case 101: call_folderdialog(hwnd, GetDlgItem(hwnd, 102));   break;
        case 105: chang_ext(hwnd, GetDlgItem(hwnd, 102), GetDlgItem(hwnd, 103), GetDlgItem(hwnd, 104)); break;
        }
        break;
    }
    return DefWindowProc(hwnd, msg, wp, lp);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, int nCmdShow ) {
    HWND hwnd;
    WNDCLASS winc;
    MSG msg;

    winc.style      = CS_HREDRAW | CS_VREDRAW;
    winc.lpfnWndProc    = WndProc;
    winc.cbClsExtra = winc.cbWndExtra   = 0;
    winc.hInstance      = hInstance;
    winc.hIcon      = LoadIcon(NULL, IDI_APPLICATION);
    winc.hCursor        = LoadCursor(NULL, IDC_ARROW);
    winc.hbrBackground  = (HBRUSH)(COLOR_BTNFACE+1);
    winc.lpszMenuName   = NULL;
    winc.lpszClassName  = TEXT("EXT_CHANGER");

    if (!RegisterClass(&winc)) return 0;

    hwnd = CreateWindow(
        TEXT("EXT_CHANGER"), TEXT("拡張子を一括で変更するプログラム"), WS_OVERLAPPEDWINDOW  | WS_VISIBLE,
        CW_USEDEFAULT, CW_USEDEFAULT, 450, 150, NULL, NULL, hInstance, NULL
    );

    if (hwnd == NULL) return 0;

    while(GetMessage(&msg , NULL , 0 , 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}






zlib 圧縮のテストプログラムです。
圧縮ボタンを押すと「元のファイル」を圧縮して「圧縮ファイル名」に保存します。
展開ボタンを押すと「圧縮ファイル名」を展開して「元のファイル」に保存します。

ファイルが上書き保存されないように注意してください。


zlib_test.cpp

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include "zlib.h"

#define INBUFSIZ   4096                 // 入力バッファサイズ(任意)
#define OUTBUFSIZ  4096                 // 出力バッファサイズ(任意)

// 圧縮
void do_compress(char *in_file, char *out_file){
        char inbuf[INBUFSIZ];           // 入力バッファ
        char outbuf[OUTBUFSIZ];         // 出力バッファ
        char err_msg[250];
        FILE *fin, *fout;       // 入力・出力ファイル
        z_stream z;             // ライブラリとやりとりするための構造体
        int count, flush, status;
        if (!(fin = fopen(in_file, "rb"))) return;
        if (!(fout = fopen(out_file, "wb"))) return;

        // すべてのメモリ管理をライブラリに任せる
        z.zalloc = Z_NULL;
        z.zfree = Z_NULL;
        z.opaque = Z_NULL;

        // 初期化  第2引数は圧縮の度合。0〜9 の範囲の整数で,0 は無圧縮, Z_DEFAULT_COMPRESSION (= 6) が標準
        if (deflateInit(&z, Z_DEFAULT_COMPRESSION) != Z_OK) {
                sprintf(err_msg, "圧縮初期化エラー(deflateInit関数): %s\n", (z.msg) ? z.msg : "???");
                MessageBox(NULL, err_msg, "エラー", MB_OK);
                fclose(fin);
                fclose(fout);
                return;
        }

        z.avail_in = 0;                         // 入力バッファ中のデータのバイト数
        z.next_out = outbuf;            // 出力ポインタ
        z.avail_out = OUTBUFSIZ;        // 出力バッファのサイズ

        // 通常は deflate() の第2引数は Z_NO_FLUSH にして呼び出す
        flush = Z_NO_FLUSH;

        while (1) {
                if (z.avail_in == 0) {  // 入力が尽きれば
                        z.next_in = inbuf;      // 入力ポインタを入力バッファの先頭に
                        z.avail_in = fread(inbuf, 1, INBUFSIZ, fin); // データを読み込む

                        // 入力が最後になったら deflate() の第2引数は Z_FINISH にする
                        if (z.avail_in < INBUFSIZ) flush = Z_FINISH;
                }
                status = deflate(&z, flush); // 圧縮する
                if (status == Z_STREAM_END) break; // 完了
                if (status != Z_OK) {   // エラー
                        sprintf(err_msg, "圧縮エラー(deflate関数): %s\n", (z.msg) ? z.msg : "???");
                        MessageBox(NULL, err_msg, "エラー", MB_OK);
                        fclose(fin);
                        fclose(fout);
                        return;
                }
                if (z.avail_out == 0) { // 出力バッファが尽きれば
                        // まとめて書き出す
                        if (fwrite(outbuf, 1, OUTBUFSIZ, fout) != OUTBUFSIZ) {
                                MessageBox(NULL, "書込エラー(fwrite関数)\n", "エラー", MB_OK);
                                fclose(fin);
                                fclose(fout);
                                return;
                        }
                        z.next_out = outbuf; // 出力バッファ残量を元に戻す
                        z.avail_out = OUTBUFSIZ; // 出力ポインタを元に戻す
                }
        }

        // 残りを吐き出す
        if ((count = OUTBUFSIZ - z.avail_out) != 0) {
                if (fwrite(outbuf, 1, count, fout) != count) {
                        MessageBox(NULL, "書込エラー(fwrite関数)\n", "エラー", MB_OK);
                        fclose(fin);
                        fclose(fout);
                        return;
                }
        }

        // 後始末
        if (deflateEnd(&z) != Z_OK) {
                sprintf(err_msg, "圧縮完了エラー(deflateEnd関数): %s\n", (z.msg) ? z.msg : "???");
                MessageBox(NULL, err_msg, "エラー", MB_OK);
                fclose(fin);
                fclose(fout);
                return;
        }
        fclose(fin);
        fclose(fout);
        return;
}
// 展開(復元)
void do_decompress(char *in_file, char *out_file){
        char inbuf[INBUFSIZ];           // 入力バッファ
        char outbuf[OUTBUFSIZ];         // 出力バッファ
        char err_msg[250];
        FILE *fin, *fout;       // 入力・出力ファイル
        int count, status;
        z_stream z;             // ライブラリとやりとりするための構造体
        if (!(fin = fopen(in_file, "rb"))) return;
        if (!(fout = fopen(out_file, "wb"))) return;

        // すべてのメモリ管理をライブラリに任せる
        z.zalloc = Z_NULL;
        z.zfree = Z_NULL;
        z.opaque = Z_NULL;

        // 初期化
        z.next_in = Z_NULL;
        z.avail_in = 0;
        if (inflateInit(&z) != Z_OK) {
                sprintf(err_msg, "展開初期化エラー(inflateInit関数): %s\n", (z.msg) ? z.msg : "???");
                MessageBox(NULL, err_msg, "エラー", MB_OK);
                fclose(fin);
                fclose(fout);
                return;
        }

        z.next_out = outbuf;            // 出力ポインタ
        z.avail_out = OUTBUFSIZ;        // 出力バッファ残量
        status = Z_OK;

        while (status != Z_STREAM_END) {
                if (z.avail_in == 0) {  // 入力残量がゼロになれば
                        z.next_in = inbuf;      // 入力ポインタを元に戻す
                        z.avail_in = fread(inbuf, 1, INBUFSIZ, fin); // データを読む
                }
                status = inflate(&z, Z_NO_FLUSH); // 展開
                if (status == Z_STREAM_END) break; // 完了
                if (status != Z_OK) {   // エラー
                        sprintf(err_msg, "展開エラー(inflate関数): %s\n", (z.msg) ? z.msg : "???");
                        MessageBox(NULL, err_msg, "エラー", MB_OK);
                        fclose(fin);
                        fclose(fout);
                        return;
                }
                if (z.avail_out == 0) { // 出力バッファが尽きれば
                        // まとめて書き出す
                        if (fwrite(outbuf, 1, OUTBUFSIZ, fout) != OUTBUFSIZ) {
                                MessageBox(NULL, "書込エラー(fwrite関数)\n", "エラー", MB_OK);
                                fclose(fin);
                                fclose(fout);
                                return;
                        }
                        z.next_out = outbuf; // 出力ポインタを元に戻す
                        z.avail_out = OUTBUFSIZ; // 出力バッファ残量を元に戻す
                }
        }

        // 残りを吐き出す
        if ((count = OUTBUFSIZ - z.avail_out) != 0) {
                if (fwrite(outbuf, 1, count, fout) != count) {
                        MessageBox(NULL, "書込エラー(fwrite関数)\n", "エラー", MB_OK);
                        fclose(fin);
                        fclose(fout);
                        return;
                }
        }

        // 後始末
        if (inflateEnd(&z) != Z_OK) {
                sprintf(err_msg, "展開終了エラー(inflateEnd関数): %s\n", (z.msg) ? z.msg : "???");
                MessageBox(NULL, err_msg, "エラー", MB_OK);
                fclose(fin);
                fclose(fout);
                return;
        }
        fclose(fin);
        fclose(fout);
        return;
}

// ファイルオープン・ダイアログ
int call_open_dialog(HWND hwnd, HWND hedit){
        char s[MAX_PATH];
        GetWindowText(hedit, s, MAX_PATH);
        static OPENFILENAME ofn;
        int res;
        ofn.lStructSize = sizeof (OPENFILENAME);
        ofn.hwndOwner = hwnd;
        ofn.lpstrFilter = TEXT("All files {*.*}\0*.*\0\0");
        ofn.lpstrCustomFilter = NULL;
        ofn.nMaxCustFilter = 0;
        ofn.nFilterIndex = 0;
        ofn.lpstrFile = s;
        ofn.nMaxFile = MAX_PATH;
        ofn.Flags = 0;
        res = GetOpenFileName(&ofn);
        if (res) SetWindowText(hedit, s);
        return res;
}
// テキスト作成
HWND make_stext(HWND parent, int x, int y, int cx, int cy, char *caption){
        return CreateWindow(
                TEXT("static"), caption, WS_CHILD | WS_VISIBLE,
                x, y, cx, cy, parent, (HMENU)0, GetModuleHandle(NULL), NULL
        );
};
// ボタン作成
HWND make_button(HWND parent, int x, int y, int cx, int cy, char *caption, int id){
        return CreateWindow(
                TEXT("BUTTON"), caption, WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
                x, y, cx, cy, parent, (HMENU)id, GetModuleHandle(NULL), NULL
        );
};
// エディットボックス作成
HWND make_edit(HWND parent, int x, int y, int cx, int cy, char *caption, int id){
        return CreateWindow(
                TEXT("EDIT"), caption, WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT | ES_AUTOHSCROLL,
                x, y, cx, cy, parent, (HMENU)id, GetModuleHandle(NULL), NULL
        );
};
// ウィンドウ作成メッセージ
on_create(HWND hwnd){
        make_stext(hwnd, 10, 10, 200, 25, "元のファイル名");
        make_edit(hwnd, 10, 30, 360, 25, "", 101);      // ファイルパス
        make_button(hwnd, 380, 30, 50, 25, "参照", 102);

        make_stext(hwnd, 10, 120, 200, 25, "圧縮ファイル名");
        make_edit(hwnd, 10, 140, 360, 25, "", 103);     // ファイルパス
        make_button(hwnd, 380, 140, 50, 25, "参照", 104);

        make_button(hwnd, 50, 70, 80, 35, "圧縮 ↓", 105);
        make_button(hwnd, 200, 70, 80, 35, "↑ 展開", 106);

        SetFocus(GetDlgItem(hwnd, 101));
};
// ウィンドウ・プロシージャ
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
        char s1[MAX_PATH], s2[MAX_PATH];
        switch(msg) {
        case WM_CREATE:
                on_create(hwnd);
                break;
        case WM_DESTROY:
                PostQuitMessage(0);
                break;
        case WM_COMMAND:
                switch(LOWORD(wp)){
                case 102: call_open_dialog(hwnd, GetDlgItem(hwnd, 101));        break;
                case 104: call_open_dialog(hwnd, GetDlgItem(hwnd, 103));        break;
                case 105:
                                GetWindowText(GetDlgItem(hwnd, 101), s1, MAX_PATH);
                                GetWindowText(GetDlgItem(hwnd, 103), s2, MAX_PATH);
                                do_compress(s1, s2);
                                break;
                case 106:
                                GetWindowText(GetDlgItem(hwnd, 101), s1, MAX_PATH);
                                GetWindowText(GetDlgItem(hwnd, 103), s2, MAX_PATH);
                                do_decompress(s2, s1);
                                break;
                }
                break;
        }
        return DefWindowProc(hwnd, msg, wp, lp);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, int nCmdShow ) {
        HWND hwnd;
        WNDCLASS winc;
        MSG msg;

        winc.style              = CS_HREDRAW | CS_VREDRAW;
        winc.lpfnWndProc        = WndProc;
        winc.cbClsExtra = winc.cbWndExtra       = 0;
        winc.hInstance  = hInstance;
        winc.hIcon              = LoadIcon(NULL, IDI_APPLICATION);
        winc.hCursor    = LoadCursor(NULL, IDC_ARROW);
        winc.hbrBackground      = (HBRUSH)(COLOR_BTNFACE+1);
        winc.lpszMenuName       = NULL;
        winc.lpszClassName      = TEXT("ZLIB_WIN");

        if (!RegisterClass(&winc)) return 0;

        hwnd = CreateWindow(
                TEXT("ZLIB_WIN"), TEXT("zlib圧縮するプログラム"), WS_OVERLAPPEDWINDOW  | WS_VISIBLE,
                CW_USEDEFAULT, CW_USEDEFAULT, 450, 220, NULL, NULL, hInstance, NULL
        );

        if (hwnd == NULL) return 0;

        while(GetMessage(&msg , NULL , 0 , 0)) {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
        }
        return msg.wParam;
}


 006-03  前へ←  ホームへ  →次へ  007-01