C++BuilderによるUnlha32.dllを使用した書庫の展開(1)

C#で書かれた「Unlha32.dll」を使用した書庫の展開を参考にしながら、
C++Builderで書庫を展開するコードを作成しました。

main() 関数

C#ではデスクトップの絶対パスを取得するのに System.Environment.GetFolderPath() を使用しています。
C++Builderでは、同様の機能がライブラリにないため、Win32API を使って実装しました。(GetFolderPath()関数)

C#では、パスから拡張子部分をのぞいたファイル名を取得するのに Path.GetFileNameWithoutExtension() を使用しています。
C++Builderでは、ChangeFileExt() を使い、同様の機能を実現しました。

GetFolderPath() 関数

GetFolderPath()関数は、デスクトップの絶対パスを取得する関数です。
SHGetMalloc()などを使用するために、#include vcl.h の前に、shlobj.h を include します。

#define NO_WIN32_LEAN_AND_MEAN
#include <shlobj.h>
#include <vcl.h>

LhaExtractArchive()関数

UNLHA32.DLLで書庫を展開する関数です。

C#の System.IO.File.Exists() と同様の機能が、C++Builder では FileExists() になります。

C#のプログラムでは SearchPath() で、DLLの有無を確認しています。
C++Builderの方は、実際にDLLをロードしてその成否によって確認するようにしました。
VCLのクラスである TDll を使うと、デストラクタで FreeLibrary() が呼ばれるため、自分で記述する必要が無くなり、ソースコードが簡潔になります。
TDllを使うには、utilcls.h を include します。

//DLL(Unlha32.dll)の読みこみ
TDll dll("unlha32.dll");
  //DLLの存在を確認
  if (!dll)
  {
    throw Exception("UNLHA32.DLLが見つかりません。");
  }

C#では、DLLの関数を使用するのに DllImport を使用します。
C++Builderでは、GetProcAddress() で関数のアドレスを取得して使用します。

typedef WORD (WINAPI *PUnlhaGetVersion)(void);
PUnlhaGetVersion UnlhaGetVersion = (PUnlhaGetVersion)dll.GetProcAddress("UnlhaGetVersion");
WORD ver = UnlhaGetVersion();

ToCommandStringFromFile()

ファイル名をコマンドとして適切な文字列に変換する関数です。
ホームページ上にはありませんが、ダウンロードしたソースコードには、この関数が作成されていました。

C#では、StartsWith() や EndsWith() が便利ですね。
C++Builderでは、AnsiPos() や SubString() を使いました。
もっと上手な方法がありそうです。

一方、文字列をダブルクオート(“)でくくる処理が、C#では、

cmd = "\"" + cmd + "\"";

となっているところを、C++Builderでは、AnsiQuotedStr() を使い、

cmd = AnsiQuotedStr(cmd, '"');

と書けます。

ソースコード

//---------------------------------------------------------------------------

#define NO_WIN32_LEAN_AND_MEAN
#include <shlobj.h>
#include <vcl.h>
#pragma hdrstop
#include <stdio.h>
#include <utilcls.h>
#include <string.h>
//---------------------------------------------------------------------------

#pragma argsused
//---------------------------------------------------------------------------
/**
 * デスクトップのファイルを物理的に格納するフォルダのパスを取得する
 * @return フォルダのパス。エラーが発生した時は、""
 */
AnsiString GetFolderPath()
{
  PItemIDList PIDL;
  LPMALLOC malloc;

  // IMallocインターフェイスへのポインタを取得
  SHGetMalloc(&malloc);

  //フォルダの識別子を取得
  if (FAILED(SHGetSpecialFolderLocation(Application->Handle, CSIDL_DESKTOPDIRECTORY, &PIDL)) == TRUE)
  {
    return "";
  }

  // 識別子をディレクトリに変換
  char szPath[MAX_PATH + 1];
  SHGetPathFromIDList(PIDL, szPath);
  malloc->Free(PIDL);

  return AnsiString(szPath);
}
//---------------------------------------------------------------------------
/**
 * ファイル名をコマンドとして適切な文字列に変換する
 * @param fileName ファイル名
 * @return コマンドとなる文字列
 */
AnsiString ToCommandStringFromFile(AnsiString fileName)
{
  AnsiString cmd = fileName;

  //ファイル名が「-」や「@」ではじまる時は、「.\」を付加
  if (cmd.AnsiPos("-") == 1 || cmd.AnsiPos("@") == 1)
  {
    cmd = ".\\" + cmd;
  }

  //ファイル名にスペースが含まれる時は、"で囲む
  if (cmd.AnsiPos(" ") != 0 &&
      !(cmd.AnsiPos("\"") == 0 && cmd.SubString(cmd.Length(), 1) == "\""))
  {
    cmd = AnsiQuotedStr(cmd, '"');
  }
  return cmd;
}
//---------------------------------------------------------------------------
/**
 * UNLHA32.DLLで書庫を展開する
 * @param archiveFile 書庫ファイル名
 * @param extractTo 展開先のフォルダ名
 */
void LhaExtractArchive(AnsiString archiveFile, AnsiString extractTo)
{
  //指定されたファイルがあるか調べる
  if (!FileExists(archiveFile))
  {
    throw Exception("指定されたファイル'" + archiveFile + "'が存在しません。");
  }

  //DLL(Unlha32.dll)の読みこみ
  TDll dll("unlha32.dll");

  //DLLの存在を確認
  if (!dll)
  {
    throw Exception("UNLHA32.DLLが見つかりません。");
  }

  //DLLのチェック
  typedef WORD (WINAPI *PUnlhaGetVersion)(void);
  PUnlhaGetVersion UnlhaGetVersion = (PUnlhaGetVersion)dll.GetProcAddress("UnlhaGetVersion");
  WORD ver = UnlhaGetVersion();
  printf("UNLHA32.DLLのバージョン:%d\n", ver);

  //動作中かチェック
  typedef BOOL (WINAPI *PUnlhaGetRunning)(void);
  PUnlhaGetRunning UnlhaGetRunning = (PUnlhaGetRunning)dll.GetProcAddress("UnlhaGetRunning");
  if (UnlhaGetRunning())
  {
    throw Exception("UNLHA32.DLLが現在動作中です。");
  }

  //展開できるかチェック
  typedef BOOL (WINAPI* PUnlhaCheckArchive)(LPCSTR, const int);
  PUnlhaCheckArchive UnlhaCheckArchive = (PUnlhaCheckArchive)dll.GetProcAddress("UnlhaCheckArchive");
  if (!UnlhaCheckArchive(archiveFile.c_str(), 0))
  {
    throw Exception("UNLHA32.DLLでは展開できません。");
  }

  //展開先フォルダの作成
  bool createdDir = false;
  if (!DirectoryExists(extractTo))
  {
    ForceDirectories(extractTo);
    createdDir = true;
  }

  //ファイル名とフォルダ名を修正する
  archiveFile = ToCommandStringFromFile(archiveFile);

  //フォルダ名は必ず\で終わる必要がある
  extractTo = IncludeTrailingPathDelimiter(extractTo);
  extractTo = ToCommandStringFromFile(extractTo);

  //展開する
  typedef int WINAPI (*PUnlha)(const HWND, LPCSTR, LPSTR , const DWORD);
  PUnlha Unlha = (PUnlha)dll.GetProcAddress("Unlha");
  char output[1024];
  AnsiString cmdLine = AnsiString().sprintf("x %s %s *", archiveFile, extractTo);
  int ret = Unlha(Application->Handle,
                  cmdLine.c_str(),
                  output, sizeof(output));
  //結果
  if (ret != 0)
  {
    //展開先フォルダが空の時は削除(削除できないかも)
    if (createdDir)
    {
      RemoveDir(extractTo);
    }

    throw new Exception("書庫の展開に失敗しました。");
  }
  printf(output);
}
//---------------------------------------------------------------------------
int main(int argc, char* argv[])
{
  if (argc < 1)
  {
    printf("展開するファイルが指定されていません。");
    return 0;
  }

  //展開
  for (int i = 1; i < argc; ++i)
  {
    AnsiString f = argv[i];
    //展開先を決定
    AnsiString extractDir = GetFolderPath();
    extractDir = IncludeTrailingPathDelimiter(extractDir) +
                 ChangeFileExt(ExtractFileName(f), "");
    //展開
    try
    {
      LhaExtractArchive(f, extractDir);
      printf("%sを%sに展開しました。\n", f.c_str(), extractDir.c_str());
    }
    catch (Exception &E)
    {
      printf("%sの展開に失敗しました。\n", f.c_str());
      printf("ERROR:%s\n", E.Message.c_str());
    }
  }

  getc(stdin);
  return 0;
}
//---------------------------------------------------------------------------

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください