Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2014年10月23日] [更新 2015年03月21日] Delphi サンプルプログラム集
742_Tar32.dll を使用したファイルの圧縮・解凍
動作確認等 Windows 7 U64(SP2) + Delphi X(UP1) Pro + Tar32.dll 2.42.0.1
742_tar32dll_Test.zip [2,472 KB] 2015年03月21日版 (EXE 同梱)



  • 2015年03月21日
  • 進捗状況を表示するサンプルを追加
  • アーカイブコマンドに -z を追加 (圧縮しない単なるアーカイブだけだった)




Tar32.dll を使用したファイルの圧縮・解凍


Tar32.dll を使用したファイルの圧縮・解凍のサンプルです.
Tar32.dll を使用すると,現在,UNIX 系の OS でよく使用されている 拡張子が .tar.gz 等のファイルの圧縮や解凍ができます.gz は GZIP 形式の圧縮を意味します.

UNIX 系のコンピュータでは,テープでバックアップの意味で TAR (テープアーカイバー) が使用されています.これは圧縮ではありません.tar でアーカイブしたファイルをパイプで gzip すると,tarでアーカイブしながら同時に gzip で圧縮します.つまり,アーカイブして圧縮すると .tar.gz いう拡張子となります.拡張子が .gz の gzip 自体にはアーカイバ (複数のファイルの格納) の機能はありません..gz で格納できるファイルは 1 つだけです.

Tar32.dll で扱える書庫形式については,以下で確認してください.
Tar32.dll は [総合アーカイバプロジェクト] のページからダウンロードできますが,以下の作者のサイトからもダウンロードできます.
コード内の DLL のコマンドラインスイッチの説明は,DLL に説明書が添付されています.

Tar32.dll の API (DLL 内の関数) と コマンド (関数の引数) の説明は,最新のバージョンには詳しい説明がありません.以下のリンクの記事にある [TAR32.DLL Ver0.xx(古いバージョン)] の [パッケージ] のページからダウンロードすると,その中にあります.



当サイトには,この他,以下のようなアーカイブファイルの圧縮・解凍関係のサンプルがあります.これらの記事中のリンクも参考にしてください.






01_Tar32.dll 内の Tar 関数を使用したファイルの解凍


Tar32.dll を使用するということは,DLL を使用するということです.DLL を使用するということは,DLL 内の関数類を使用するということになります.

本サンプルでは,DLL をシステムに登録しないで利用することにしています.そこで,DLL を EXE と同じフォルダに置いて使用します.DLL は LoadLibrary でロードし,終了時に FreeLibrary で解放しています.圧縮した書庫ファイルの解凍だけのサンプルとなっています.解凍には,Tar32.dll 内の Tar という関数を使用します.この関数 Tar の引数の値で圧縮か解凍を指定し,ファイル名やオプション等も関数の引数として指定します.

下の図は添付の test.tar.gz というファイルを,[Temp] というフォルダに解凍した結果です.



[備考]
Tar32.dll がシステムに登録されている場合,この Tar32.dll を EXE と同じフォルダに置く必要はありません.システムに登録した時の場所から自動的に読み込まれます.



図1
設計時画面
図2
解凍する書庫の内容
  • Tar32.dll 内の関数を利用すれば,この図のような書庫内のファイルの表示もできる
図3
上の図のファイルを解凍した結果

リスト1
Tar32.dll 内の Tar 関数を使用したファイルの解凍
DLL を動的 (実行時) にロードして使用する場合
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    OpenDialog1: TOpenDialog;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
    function ExtractTar(SrcPath, DestPath: String; var AMsg: String): Boolean;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}
type
  TTarFunc = function (const hWnd: HWND; szCmdLine: LPCSTR; szOutPut: LPSTR;
    constdwSize: DWORD): Integer; stdcall;


//=============================================================================
//  tar32.dllを使用した書庫ファイルの解凍のテスト
//  解凍先のディレクトリがない場合は自動作成して上書き保存
//
//  tar32.dllで扱える書庫の例
// -----------------------------------------------------
//  形式         定数名               定数値
// -----------------------------------------------------
//  TAR          ARCHIVETYPE_TAR      1
//  TAR.GZ(TGZ)  ARCHIVETYPE_TARGZ    2
//  TAR.Z(TAZ)   ARCHIVETYPE_TARZ     3       展開のみ
//  GZ(GZIP)     ARCHIVETYPE_GZ       4
//  Z            ARCHIVETYPE_Z        5       展開のみ
//  TAR.BZ2      ARCHIVETYPE_TARBZ2   6
//  BZ2          ARCHIVETYPE_BZ2      7
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  SrcFile   : String;
  DestDir   : String;
  ResultMsg : string;
  flgResult : Boolean;
begin
  //解凍対象の書庫ファイルを選択
  OpenDialog1.InitialDir := ExtractFileDir(ParamStr(0));
  OpenDialog1.Filter     := 'tar (TGZ,TAZ,BZ2) 書庫|*.tar.gz;*.tar.z;*.tar.bz2|' +
                            'tar 書庫|*.tar|' +
                            'gz (GZIP) 書庫|*.gz|' +
                            'z 書庫|*.z|' +
                            'bz2 (BZIP2) 書庫|*.bz2|';
  OpenDialog1.FileName   := '';
  if not OpenDialog1.Execute then exit;

  //SrcFileは解凍する対象のファイルのフルパス
  //DestDirは解凍したファイルを格納するディレクトリ名のフルパス
  SrcFile  := OpenDialog1.FileName;
  DestDir := ExtractFilePath(Application.ExeName)+ 'Temp';

  //解凍
  flgResult := ExtractTar(SrcFile, DestDir, ResultMsg);

  if flgResult then begin
    MessageBox(Handle, '解凍終了', '実行結果', MB_ICONINFORMATION);
  end else begin
    MessageBox(Handle, '解凍失敗', '実行結果', MB_ICONSTOP);
  end;
end;

//-----------------------------------------------------------------------------
//  tar32.dll内のTarという関数を使用して書庫を解凍する
//-----------------------------------------------------------------------------
function TForm1.ExtractTar(SrcPath, DestPath: String;
  var AMsg: String): Boolean;
const
  BUFF_SIZE = 8192;
var
  DllHandle : THandle;
  TarFunc   : TTarFunc;
  Command   : AnsiString;
  Output    : array[0..BUFF_SIZE-1] of AnsiChar;
begin
  Result := False;

  //DLLをロード
  DllHandle := LoadLibrary('tar32.dll');
  if DllHandle < HINSTANCE_ERROR then exit;

  try
    //DLL内のTarという関数のアドレスを取得
    @TarFunc := GetProcAddress(DllHandle, 'Tar');
    if @TarFunc = nil then exit;

    //ヒントや警告が表示されるので明示的にキャストしておく
    Command := AnsiString('-x --display-dialog=0 ' +
                 AnsiQuotedStr(SrcPath, '"') + ' ' +
                 AnsiQuotedStr(IncludeTrailingPathDelimiter(DestPath), '"'));

    FillChar(Output, SizeOf(Output), 0);
    try
      //実際にDLL内の関数を実行する部分
      TarFunc(Handle, PAnsiChar(Command), OutPut, BUFF_SIZE);
      AMsg   := String(OutPut);
      Result := True;
    except
    end;
  finally
    FreeLibrary(DllHandle);
  end;
end;

end.




02_Tar32.dll 内の関数類を利用するクラスを作成


Tar32.dll の関数を使用するコードを,以下のリンクのサンプルのように,クラスにする例です.
単に DLL 内の関数を使用する定義だけのコードをユニットにするだけでもいいのですが,クラスとして構成しておくと便利です.
圧縮と解凍だけの実装になっています.圧縮は .tar.gz 専用となっています.
下の図とコードは,作成したクラスをテストしたコードと実行結果です.


図4
設計時画面
図5
圧縮するフォルダ
  • [AAA] フォルダ内のファイル全てを格納した書庫を作成する
図6
作成した書庫
  • [Temp] フォルダ内に Test.tar.gz ができる
図7
作成した書庫を解凍した結果
  • [Temp] フォルダ内に展開する


リスト2
Tar32.dll 内の関数類を利用するクラスを使用したサンプル
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    OpenDialog1: TOpenDialog;
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses plArchieverTAR;

{$R *.dfm}

//=============================================================================
//  ファイルの圧縮
//  指定したディレクトリ内のファイル全てを.tar.gz形式で圧縮
//  以下のコードでは,プロジェクトのファルダ下の[Temp]フォルダ内に保存
//
//  tar32.dllで扱える書庫の例
// -----------------------------------------------------
//  形式         定数名               定数値
// -----------------------------------------------------
//  TAR          ARCHIVETYPE_TAR      1
//  TAR.GZ(TGZ)  ARCHIVETYPE_TARGZ    2
//  TAR.Z(TAZ)   ARCHIVETYPE_TARZ     3      展開のみ
//  GZ(GZIP)     ARCHIVETYPE_GZ       4
//  Z            ARCHIVETYPE_Z        5      展開のみ
//  TAR.BZ2      ARCHIVETYPE_TARBZ2   6
//  BZ2          ARCHIVETYPE_BZ2      7
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  Arch      : TplArchieverTAR;
  FileDlg   : TFileOpenDialog;
  RetFlag   : Boolean;
  SrcDir    : String;
  DestFile  : String;
  ResultMsg : string;
begin
  //初期選択フォルダパス
  SrcDir  := ExtractFilePath(Application.ExeName);

  RetFlag := False;
  //Windows Vista以降専用コード
  FileDlg := TFileOpenDialog.Create(nil);
  try
    FileDlg.Options := [fdoPickFolders,
                        fdoPathMustExist,
                        fdoForceFileSystem];
    if FileDlg.Execute then begin
      Update;
      SrcDir  := FileDlg.FileName;
      RetFlag := True;
    end;
  finally
    FreeAndNil(FileDlg);
  end;
  if not RetFlag then exit;

  Screen.Cursor := crHourGlass;
  Arch := TplArchieverTAR.Create(Self);
  try
    //圧縮後のファイル名(フルパス)
    //このプロジェクトのフォルダとする
    //PackFilesメソッドはフォルダが存在しないと自動作成する
    DestFile := ExtractFilePath(Application.ExeName) + 'Temp' + PathDelim + 'Test.tar.gz';

    //圧縮
    RetFlag := Arch.PackFiles(SrcDir, DestFile, ResultMsg);
  finally
    FreeAndNil(Arch);
    Screen.Cursor := crDefault;
  end;

  if RetFlag then begin
    MessageBox(Handle, '圧縮終了', '実行結果', MB_ICONINFORMATION);
  end else begin
    MessageBox(Handle, '圧縮失敗', '実行結果', MB_ICONWARNING);
  end;
end;

//=============================================================================
//  書庫の解凍
//  解凍先のディレクトリがない場合は自動作成して上書き保存
//  以下のコードでは,プロジェクトのフォルダ下の[Temp]フォルダ内に解凍
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  Arch      : TplArchieverTAR;
  SrcFile   : String;
  DestDir   : String;
  ResultMsg : string;
  flgResult : Boolean;
begin
  //解凍対象の書庫ファイルを選択
  OpenDialog1.InitialDir := ExtractFileDir(ParamStr(0));
  OpenDialog1.Filter     := 'tar (TGZ,TAZ,BZ2) 書庫|*.tgz;*.tar.gz;*.tar.z;*.tar.bz2|'
                           + 'tar 書庫|*.tar|'
                           + 'gz (GZIP) 書庫|*.gz|'
                           + 'z 書庫|*.z|'
                           + 'bz2 (BZIP2) 書庫|*.bz2|';
  OpenDialog1.FileName   := '';
  if not OpenDialog1.Execute then exit;

  Screen.Cursor := crHourGlass;
  Arch := TplArchieverTAR.Create(Self);
  try
    SrcFile := OpenDialog1.FileName;
    DestDir := ExtractFilePath(Application.ExeName) + 'Temp';

    //解凍
    flgResult := Arch.Extract(SrcFile, DestDir, ResultMsg);
  finally
    FreeAndNil(Arch);
    Screen.Cursor := crDefault;
  end;

  if flgResult then begin
    MessageBox(Handle, '解凍終了', '実行結果', MB_ICONINFORMATION);
  end else begin
    MessageBox(Handle, '解凍失敗', '実行結果', MB_ICONWARNING);
  end;
end;

end.


作成したクラスのコードです.
コンストラクタ (Create メソッド) で DLL をロードし,デコンストラクタで DLL のハンドルを無効にしています.

リスト3
Tar32.dll 内の関数類を利用するクラスのユニット
unit plArchieverTAR;

interface

uses
    Windows, Messages, SysUtils, Classes, Controls, Forms;

const
  lpszWMARCEXTRACT   = 'wm_arcextract';


type
  TplArchieverTAR = class(TComponent)
  private
    FHandle : HWND;
  protected
    FDllHandle : THandle;
    function Execute(Cmd: String; var AMsg: String): Boolean;
  public
    constructor Create( AOwner:TComponent ); override;
    destructor Destroy(); override;
    function Extract(SrcPath, DestPath: String; var AMsg: String): Boolean;
    function PackFiles(SrcPath, DestPath: String; var AMsg: String): Boolean;
  end;

implementation

{ TplArchieverTAR }

type
  TTar = function (const hWnd: HWND; szCmdLine: LPCSTR; szOutPut: LPSTR;
    constdwSize: DWORD): Integer; stdcall;

var
  FTar : TTar;

//=============================================================================
//  Create処理
//=============================================================================
constructor TplArchieverTAR.Create(AOwner: TComponent);
begin
  inherited;

  FDllHandle := LoadLibrary('tar32.dll');
  if FDllHandle < HINSTANCE_ERROR then begin
    FDllHandle := 0;
  end else begin
    FHandle := TWinControl(AOwner).Handle;

    //DLL内のTarという関数のアドレスを取得
    @FTar := GetProcAddress(FDllHandle, 'Tar');
  end;
end;

//=============================================================================
//  Destroy処理
//=============================================================================
destructor TplArchieverTAR.Destroy;
begin
  if FDllHandle <> 0 then begin
    FreeLibrary(FDllHandle);
  end;
  FDllHandle := 0;

  inherited;
end;

//=============================================================================
//  圧縮メソッド
//  SrcPath  : 圧縮対象のディレクトリ名(このディレクトリ内のファイルを全て格納)
//  DestPath : 圧縮後のファイル名のフルパス
//
//-----------------------------------------------------------------------------
//  圧縮の種類のコマンドオプションの例
//
// -z[N]         gzip相当の圧縮.Nは圧縮レベルで1~9(デフォルトは6)
//  --gzip=[N]    同上
//  -B[N]         bzip2相当の圧縮.Nは圧縮レベルで1~9(デフォルトルは9)
//  --bzip(=[N])  同上
//  --bzip2(=[N]) 同上
//  -G            gzipまたはbzip2相当の圧縮のみ実行
//  -J[N]         xz相当の圧縮.Nは圧縮レベルで1~9(デフォルトルは6)
//  --xz(=[N])    同上
//  --lzma(=[N])  lzma相当の圧縮.Nは圧縮レベルで1~9(デフォルトルは6)
//=============================================================================
function TplArchieverTAR.PackFiles(SrcPath, DestPath: String;
  var AMsg: String): Boolean;
var
  FCommand : String;
begin
  Result := False;
  if FDllHandle = 0 then exit;

  ForceDirectories(ExtractFileDir(DestPath));
  FCommand := '-c -z --display-dialog=0 ' +
              AnsiQuotedStr(DestPath, '"') + ' ' +
              AnsiQuotedStr(IncludeTrailingPathDelimiter(SrcPath), '"') + ' ' +
              '*.*';

  //引数を与えて関数を実行
  Result := Execute(FCommand, AMsg);
end;

//=============================================================================
//  解凍メソッド
//  SrcPath  : 解凍対象のファイルのフルパス
//  DestPath : 解凍したファイルを格納するディレクトリ
//=============================================================================
function TplArchieverTAR.Extract(SrcPath, DestPath: String;
  var AMsg: String): Boolean;
var
  FCommand : String;
begin
  Result := False;
  if FDllHandle = 0 then exit;

  FCommand := '-x --display-dialog=0 ' +
              AnsiQuotedStr(SrcPath, '"') + ' ' +
              AnsiQuotedStr(IncludeTrailingPathDelimiter(DestPath), '"');

  //引数を与えて関数を実行
  Result := Execute(FCommand, AMsg);
end;

//=============================================================================
//  圧縮または解凍の実行ルーチン
//  圧縮か解凍かは引数のコマンドで決まる
//=============================================================================
function TplArchieverTAR.Execute(Cmd: String; var AMsg: String): Boolean;
const
  BUFF_SIZE = 8192;
var
  ACommand : AnsiString;
  Output   : array[0..BUFF_SIZE-1] of AnsiChar;
begin
  Result := False;

  if @FTar <> nil then begin
    FillChar(Output, SizeOf(Output), 0);
    //ヒントや警告が表示されるので明示的にキャストしておく
    ACommand := AnsiString(Cmd);
    try
      //実際にDLL内の関数を実行する部分
      //tar32.dll内のTarという関数を実行
      FTar(FHandle, PAnsiChar(ACommand), OutPut, BUFF_SIZE);
      Result := True;
    except
    end;
  end;
  AMsg := String(OutPut);
end;

end.




03_進捗状況の表示等の機能を追加


前項のサンプルに,進捗状況を表示する機能を追加したテストです.
そのために,DLL を利用するクラス内のコードに,メッセージを送信するためのコードを追加しています.受信したメッセージから情報を取得するための構造体の定義も追加しています.圧縮は .tar.gz 専用のコードとなっています.

メッセージの値は WM_ARCEXTRACT となっています.総合アーカイバプロジェクトでは全てこの値を使用するようになっています.



図8
設計時画面
図9
圧縮中の画面


リスト4
Tar32.dll 内の関数類を利用するクラスを使用したサンプル
前項のサンプルにプログレスバー関係の処理を追加
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls, ComCtrls;

type
  TForm1 = class(TForm)
    OpenDialog1: TOpenDialog;
    Button1: TButton;
    Button2: TButton;
    ProgressBar1: TProgressBar;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  protected
    procedure WndProc(var Message: TMessage); override;
  private
    { Private 宣言 }
    FTotalSize : UInt64;
    FWriteSize : UInt64;
    procedure GetDirFilesSize(ADir: String; var ASumSize: UInt64);
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses plArchieverTAR, IOUtils;

{$R *.dfm}

//=============================================================================
//  ファイルの圧縮
//  指定したディレクトリ内のファイル全てを.tar.gz形式で圧縮
//  以下のコードでは,プロジェクトのファルダ下の[Temp]フォルダ内に保存
//
//  tar32.dllで扱える書庫の例
// -----------------------------------------------------
//  形式         定数名               定数値
// -----------------------------------------------------
//  TAR          ARCHIVETYPE_TAR      1
//  TAR.GZ(TGZ)  ARCHIVETYPE_TARGZ    2
//  TAR.Z(TAZ)   ARCHIVETYPE_TARZ     3      展開のみ
//  GZ(GZIP)     ARCHIVETYPE_GZ       4
//  Z            ARCHIVETYPE_Z        5      展開のみ
//  TAR.BZ2      ARCHIVETYPE_TARBZ2   6
//  BZ2          ARCHIVETYPE_BZ2      7
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  Arch      : TplArchieverTAR;
  FileDlg   : TFileOpenDialog;
  RetFlag   : Boolean;
  SrcDir    : String;
  DestFile  : String;
  ResultMsg : string;
begin
  //初期選択フォルダパス
  SrcDir  := ExtractFilePath(Application.ExeName);

  RetFlag := False;
  //Windows Vista以降専用コード
  FileDlg := TFileOpenDialog.Create(nil);
  try
    FileDlg.Options := [fdoPickFolders,
                        fdoPathMustExist,
                        fdoForceFileSystem];
    if FileDlg.Execute then begin
      Update;
      SrcDir  := FileDlg.FileName;
      RetFlag := True;
    end;
  finally
    FreeAndNil(FileDlg);
  end;
  if not RetFlag then exit;

  Screen.Cursor := crHourGlass;
  Arch := TplArchieverTAR.Create(Self);
  try
    //圧縮後のファイル名(フルパス)
    //このプロジェクトのフォルダとする
    //PackFilesメソッドはフォルダが存在しないと自動作成する
    DestFile := ExtractFilePath(Application.ExeName) + 'Temp' + PathDelim + 'Test.tar.gz';

    //圧縮するファイルのサイズを取得
    FTotalSize := 0;
    FWriteSize := 0;
    GetDirFilesSize(SrcDir, FTotalSize);
    ProgressBar1.Max := 1000;
    //圧縮
    RetFlag := Arch.PackFiles(SrcDir, DestFile, ResultMsg);
  finally
    FreeAndNil(Arch);
    Screen.Cursor := crDefault;
  end;

  if RetFlag then begin
    MessageBox(Handle, '圧縮終了', '実行結果', MB_ICONINFORMATION);
  end else begin
    MessageBox(Handle, '圧縮失敗', '実行結果', MB_ICONWARNING);
  end;
end;

//=============================================================================
//  書庫の解凍
//  解凍先のディレクトリがない場合は自動作成して上書き保存
//  以下のコードでは,プロジェクトのフォルダ下の[Temp]フォルダ内に解凍
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  Arch      : TplArchieverTAR;
  SrcFile   : String;
  DestDir   : String;
  ResultMsg : string;
  flgResult : Boolean;
begin
  //解凍対象の書庫ファイルを選択
  OpenDialog1.InitialDir := ExtractFileDir(ParamStr(0));
  OpenDialog1.Filter     := 'tar (TGZ,TAZ,BZ2) 書庫|*.tgz;*.tar.gz;*.tar.z;*.tar.bz2|'
                           + 'tar 書庫|*.tar|'
                           + 'gz (GZIP) 書庫|*.gz|'
                           + 'z 書庫|*.z|'
                           + 'bz2 (BZIP2) 書庫|*.bz2|';
  OpenDialog1.FileName   := '';
  if not OpenDialog1.Execute then exit;

  Screen.Cursor := crHourGlass;
  Arch := TplArchieverTAR.Create(Self);
  try
    SrcFile := OpenDialog1.FileName;
    DestDir := ExtractFilePath(Application.ExeName) + 'Temp';

    //アーカイブ内のファイルサイズを取得
    FWriteSize := 0;
    FTotalSize := Arch.GetOriginalSize(SrcFile);
    ProgressBar1.Max := 1000;
    //解凍
    flgResult := Arch.Extract(SrcFile, DestDir, ResultMsg);
  finally
    FreeAndNil(Arch);
    Screen.Cursor := crDefault;
  end;

  if flgResult then begin
    MessageBox(Handle, '解凍終了', '実行結果', MB_ICONINFORMATION);
  end else begin
    MessageBox(Handle, '解凍失敗', '実行結果', MB_ICONWARNING);
  end;
end;

//=============================================================================
//  DLLから圧縮・解凍時のメッセージ受信
//  ここではWndProcを使用
//  プログレスバーに進捗状況を表示
//
//  LPExtractingInfo,WM_ARCEXTRACT,ARCEXTRACT_XXXはplArchieverTAR.pas内の定義
//=============================================================================
procedure TForm1.WndProc(var Message: TMessage);
var
  State : UINT;
  lpEis : LPExtractingInfo;
begin
  inherited;

  if Message.Msg = WM_ARCEXTRACT then begin
    State := Message.wParam;
    lpEis := LPExtractingInfo(Message.lParam);
    if State = ARCEXTRACT_INPROCESS then  begin
      if FTotalSize > 0 then begin
        FWriteSize := FWriteSize + lpEis.dwWriteSize;
        ProgressBar1.Position := Trunc(FWriteSize / FTotalSize * 1000);
      end;
    end;

    //処理継続(Message.Result := 1とすれば処理中止)
    //キャンセル機能を追加する場合に利用する
    Message.Result := 0;
  end;
end;

//-----------------------------------------------------------------------------
//  指定ディレクトリ内のファイルの合計サイズを取得
//  引数のASumSizeに合計サイズを返す
//-----------------------------------------------------------------------------
procedure TForm1.GetDirFilesSize(ADir: String; var ASumSize: UInt64);
var
  LSearchPath : string;
  LSearchRec  : TSearchRec;
  LFullPath   : string;
begin
  LSearchPath := IncludeTrailingPathDelimiter(ADir) + '*.*';
  if FindFirst(LSearchPath, faAnyFile, LSearchRec) = 0 then
  begin
    repeat
      if not ((LSearchRec.Name = '..') or (LSearchRec.Name = '.')) then begin
        if (LSearchRec.Attr and faDirectory) = faDirectory then begin
          LFullPath := IncludeTrailingPathDelimiter(ADir) + LSearchRec.Name;
          //パス名を与えて自分自身を呼出(再帰処理)する
          GetDirFilesSize(LFullPath, ASumSize);
        end;
        ASumSize := ASumSize + LSearchRec.Size;
      end;
    until FindNext(LSearchRec) <> 0;
    FindClose(LSearchRec);
  end;
end;

end.


本テストで使用した Tar32.dll の関数を利用するためのユニットのコードです.
前項のサンプルでは,DLL 内の Tar という関数を利用するだけでしたが,本サンプルはその他のいくつかの関数を定義して使用しています.それらは,メッセージの送信の設定と,GetOriginalSize というクラスメソッド内で使用しています.



リスト5
Tar32.dll 内の関数類を利用するクラスのユニット
unit plArchieverTAR;

interface

uses
    Windows, Messages, SysUtils, Classes, Controls, Forms;

const
  lpszWMARCEXTRACT     = 'wm_arcextract';
  ARCEXTRACT_BEGIN     = 0; //該当ファイルの処理開始
  ARCEXTRACT_INPROCESS = 1; //該当ファイルの展開中
  ARCEXTRACT_END       = 2; //処理終了.関連メモリを開放
  ARCEXTRACT_OPEN      = 3; //該当書庫の処理の開始
  ARCEXTRACT_COPY      = 4; //ワークファイルの書き戻し

  M_CHECK_ALL_PATH      = $00000100;
  M_CHECK_FILENAME_ONLY = $00000200;
  FNAME_MAX32 = 512;

var
  WM_ARCEXTRACT : UINT;

type
  HARC = HGLOBAL;

  TExtractingInfo = packed record
    dwFileSize       : DWORD;
    dwWriteSize      : DWORD;
    szSourceFileName : array [0..FNAME_MAX32] of AnsiChar;
    dummy1           : array [0..2] of AnsiChar;
    szDestFileName   : array [0..FNAME_MAX32] of AnsiChar;
    dummy            : array [0..2] of AnsiChar;
  end;
  LPEXTRACTINGINFO = ^TExtractingInfo;

  TExtractingInfoEx = packed record
    exinfo           : TExtractingInfo;
    dwCompressedSize : DWORD;
    dwCRC            : DWORD;
    uOSType          : UINT;
    wRatio           : WORD;
    wDate            : WORD;
    wTime            : WORD;
    szAttribute      : array[0..7] of AnsiChar;
    szMode           : array[0..7] of AnsiChar;
  end;
  LPEXTRACTINGINFOEX = ^TExtractingInfoEx;

  TINDIVIDUALINFO = packed record
    dwOrinalSize   : DWORD;
    dwCompressSize : DWORD;
    dwCRC          : DWORD;
    uFlag          : UINT;
    uOSType        : UINT;
    wRatio         : WORD;
    wDate          : WORD;
    wTime          : WORD;
    szFileName     : array [0..FNAME_MAX32] of AnsiChar;
    dummy          : array [0..2] of AnsiChar;
    szAttribute    : array [0..7] of AnsiChar;
    szMode         : array [0..7] of AnsiChar;
  end;
  LPINDIVIDUALINFO = ^TINDIVIDUALINFO;

  TplArchieverTAR = class(TComponent)
  private
    FHandle : HWND;
  protected
    FDllHandle : THandle;
    FArcHandle : HARC;
    function Execute(Cmd: String; var AMsg: String): Boolean;
  public
    constructor Create( AOwner:TComponent ); override;
    destructor Destroy(); override;
    function Extract(SrcPath, DestPath: String; var AMsg: String): Boolean;
    function PackFiles(SrcPath, DestPath: String; var AMsg: String): Boolean;
    function GetOriginalSize(SrcPath: String): UInt64;
  end;

implementation

{ TplArchieverTAR }

type
  //Tar32.DLLのAPIの関数定義
  TTar = function (const hWnd: HWND; szCmdLine: LPCSTR; szOutPut: LPSTR;
    const dwSize: DWORD): Integer; stdcall;
  TTarOpenArchive = function(const hWindow: HWND; szFileName: LPCSTR;
    const dwMode: DWORD): HARC; stdcall;
  TTarCloseArchive = function(ArcHandle: HARC): Integer; stdcall;
  TTarFindFirst = function (ArcHandle: HARC; szWildName: LPCSTR;
    lpSubInfo: LPINDIVIDUALINFO): Integer; stdcall;
  TTarFindNext = function(ArcHandle: HARC; lpSubInfo: LPINDIVIDUALINFO):
    Integer; stdcall;
  TTarSetOwnerWindow = function (hWindows: HWND): BOOL; stdcall;
  TTarClearOwnerWindow = function(): BOOL; stdcall;

var
  FTar                     : TTar;
  FTarOpenArchive          : TTarOpenArchive;
  FTarCloseArchive         : TTarCloseArchive;
  FTarFindFirst            : TTarFindFirst;
  FTarFindNext             : TTarFindNext;
  FTarSetOwnerWindow       : TTarSetOwnerWindow;
  FTarClearOwnerWindow     : TTarClearOwnerWindow;


//=============================================================================
//  Create処理
//=============================================================================
constructor TplArchieverTAR.Create(AOwner: TComponent);
begin
  inherited;

  FDllHandle := LoadLibrary('tar32.dll');
  if FDllHandle < HINSTANCE_ERROR then begin
    FDllHandle := 0;
  end else begin
    //メッセージを登録
    //このユニットを使用するプログラムでWM_ARCEXTRACTメッセージを受信する
    WM_ARCEXTRACT := RegisterWindowMessage(lpszWMARCEXTRACT);

    //このユニット(DLL)を使用するウィンドウのハンドル
    FHandle := TWinControl(AOwner).Handle;

    //DLL内の関数のアドレスを取得
    @FTar             := GetProcAddress(FDllHandle, 'Tar');
    @FTarOpenArchive  := GetProcAddress(FDllHandle, 'TarOpenArchive');
    @FTarCloseArchive := GetProcAddress(FDllHandle, 'TarCloseArchive');
    @FTarFindFirst    := GetProcAddress(FDllHandle, 'TarFindFirst');
    @FTarFindNext     := GetProcAddress(FDllHandle, 'TarFindNext');

    @FTarSetOwnerWindow   := GetProcAddress(FDllHandle, 'TarSetOwnerWindow');
    @FTarClearOwnerWindow := GetProcAddress(FDllHandle, 'TarClearOwnerWindow');

    //FHandleのウィンドウへのメッセージ送信処理開始
    FTarSetOwnerWindow(FHandle);
  end;
end;

//=============================================================================
//  Destroy処理
//=============================================================================
destructor TplArchieverTAR.Destroy;
begin
  if FDllHandle <> 0 then begin
    //メッセージ送信処理終了
    FTarClearOwnerWindow;
    FreeLibrary(FDllHandle);
  end;
  FDllHandle := 0;

  inherited;
end;

//=============================================================================
//  GetOriginalSize
//  圧縮ファイル内のファイルの圧縮前の合計サイズを取得する関数
//=============================================================================
function TplArchieverTAR.GetOriginalSize(SrcPath: String): UInt64;
var
  LSrcPath : AnsiString;
  LMask    : AnsiString;
  LInfo    : TINDIVIDUALINFO;
  LSize    : UInt64;
begin
  LSize  := 0;

  LSrcPath   := AnsiString(SrcPath);
  LMask      := AnsiString('*.*');
  FArcHandle := FTarOpenArchive(FHandle, PAnsiChar(LSrcPath), M_CHECK_ALL_PATH);

  if FArcHandle <> 0 then begin
    if FTarFindFirst(FArcHandle, PAnsiChar(LMask), @LInfo) >= 0 then begin
      repeat
        LSize := LSize + LInfo.dwOrinalSize;
        Application.ProcessMessages;
      until FTarFindNext(FArcHandle, @LInfo) < 0;
    end;
    FTarCloseArchive(FArcHandle);
  end;
  Result := LSize;
end;

//=============================================================================
//  圧縮メソッド
//  SrcPath  : 圧縮対象のディレクトリ名(このディレクトリ内のファイルを全て格納)
//  DestPath : 圧縮後のファイル名のフルパス
//
//-----------------------------------------------------------------------------
//  圧縮の種類のコマンドオプションの例
//
// -z[N]         gzip相当の圧縮.Nは圧縮レベルで1~9(デフォルトは6)
//  --gzip=[N]    同上
//  -B[N]         bzip2相当の圧縮.Nは圧縮レベルで1~9(デフォルトルは9)
//  --bzip(=[N])  同上
//  --bzip2(=[N]) 同上
//  -G            gzipまたはbzip2相当の圧縮のみ実行
//  -J[N]         xz相当の圧縮.Nは圧縮レベルで1~9(デフォルトルは6)
//  --xz(=[N])    同上
//  --lzma(=[N])  lzma相当の圧縮.Nは圧縮レベルで1~9(デフォルトルは6)
//=============================================================================
function TplArchieverTAR.PackFiles(SrcPath, DestPath: String;
  var AMsg: String): Boolean;
var
  FCommand : String;
begin
  Result := False;
  if FDllHandle = 0 then exit;

  ForceDirectories(ExtractFileDir(DestPath));
  FCommand := '-c -z --display-dialog=0 ' +
              AnsiQuotedStr(DestPath, '"') + ' ' +
              AnsiQuotedStr(IncludeTrailingPathDelimiter(SrcPath), '"') + ' ' +
              '*.*';

  //引数を与えて関数を実行
  Result := Execute(FCommand, AMsg);
end;

//=============================================================================
//  解凍メソッド
//  SrcPath  : 解凍対象のファイルのフルパス
//  DestPath : 解凍したファイルを格納するディレクトリ
//=============================================================================
function TplArchieverTAR.Extract(SrcPath, DestPath: String;
  var AMsg: String): Boolean;
var
  FCommand : String;
begin
  Result := False;
  if FDllHandle = 0 then exit;

  FCommand := '-x --display-dialog=0 ' +
              AnsiQuotedStr(SrcPath, '"') + ' ' +
              AnsiQuotedStr(IncludeTrailingPathDelimiter(DestPath), '"');

  //引数を与えて関数を実行
  Result := Execute(FCommand, AMsg);
end;

//=============================================================================
//  圧縮または解凍の実行ルーチン
//  圧縮か解凍かは引数のコマンドで決まる
//=============================================================================
function TplArchieverTAR.Execute(Cmd: String; var AMsg: String): Boolean;
const
  BUFF_SIZE = 8192;
var
  ACommand : AnsiString;
  Output   : array[0..BUFF_SIZE-1] of AnsiChar;
begin
  Result := False;

  if @FTar <> nil then begin
    FillChar(Output, SizeOf(Output), 0);
    //ヒントや警告が表示されるので明示的にキャストしておく
    ACommand := AnsiString(Cmd);
    try
      //実際にDLL内の関数を実行する部分
      //tar32.dll内のTarという関数を実行
      FTar(FHandle, PAnsiChar(ACommand), OutPut, BUFF_SIZE);
      Result := True;
    except
    end;
  end;

  AMsg := String(OutPut);
end;

end.




04_Tar32.dll がシステムに登録されている場合


DLL がシステムに登録されているか,EXE と同じフォルダ内に存在する場合は,LoadLibrary で DLL を読み込まなくても利用できます.また,利用する関数がその DLL 内に確実に存在することが分かっている場合,関数の存在をチェックする必要もありません.
筆者の環境には Tar32.dll が登録されています.したがって,最初のサンプルプログラムを次のように変更しても動作します.これを DLL の静的リンクとも言います.

この方法は,Delphi で直接使用できない Windows API を使用する方法と同じです.つまり,Windows が提供している DLL 内の関数を使用する方法と同じです.



リスト6
Tar32.dll がシステムに登録されている場合
DLL が確実にシステムに存在して,利用する関数も DLL 内に確実に存在する場合
implementation

{$R *.dfm}

  function Tar(const hWnd: HWND; szCmdLine: LPCSTR; szOutPut: LPSTR;
    constdwSize: DWORD): Integer; stdcall; external 'tar32.dll';


// TForm1.Button1Clickのコードは同じなので省略    

//-----------------------------------------------------------------------------
//  tar32.dll内のTarという関数を使用して書庫を解凍する
//-----------------------------------------------------------------------------
function TForm1.ExtractTar(SrcPath, DestPath: String;
  var AMsg: String): Boolean;
const
  BUFF_SIZE = 8192;
var
  Command   : AnsiString;
  Output    : array[0..BUFF_SIZE-1] of AnsiChar;
begin
  Result := False;

  //ヒントや警告が表示されるので明示的にキャストしておく
  Command := AnsiString('-x --display-dialog=0 ' +
               AnsiQuotedStr(SrcPath, '"') + ' ' +
               AnsiQuotedStr(IncludeTrailingPathDelimiter(DestPath), '"'));

  FillChar(Output, SizeOf(Output), 0);
  try
    //実際にDLL内の関数を実行する部分
    Tar(Handle, PAnsiChar(Command), OutPut, BUFF_SIZE);
    AMsg   := String(OutPut);
    Result := True;
  except
  end;
end;

end.




05_Tar32.dll と総合アーカイバプロジェクト


Tar32.dll は,総合アーカイバプロジェクトの仕様に準拠した DLL です.

現在のようにインターネットが普及する前は,パソコン通信がコミュニケーションの手段として利用されていました.ニフティ株式会社が運営していたパソコン通信サービスのコミュニティの場で生まれたのが総合アーカイバプロジェクトです.
異なるアーカイバでも API を可能な限り統一して,アプリケーションから容易にアーカイブファイルの操作を可能にすることを目的としたプロジェクトです.

多くの開発者がこの主旨に賛同し,多くの DLL が提供されてきました.そして,多くの圧縮・解凍ツールがこれらの DLL を使用しています.DLL は各種のプログラム言語から共通に使用することを目的にしたライブラリです.DLL で提供されていれば Delphi からも利用可能です.もちろん,Delphi に限らず,DLL 内の関数を使用するための定義 (ヘッダファイル等の作成) は必要です.

総合アーカイバプロジェクトで提供さてれいる DLL は,32 ビット版です.Unicode には対応していない DLL もあります.Unicode に対応していない DLL ではファイル名にユニコードな文字は使用できません.本ページのサンプルプログラムで使用した Tar32.dll のバージョンは,Unicode には対応していませんが,UTF-8, UTF-8-MAC, 日本語 EUC の自動変換に対応しています.
一部ですが,64 ビット版の DLL も公開されているようです.