日曜日にBCB!



Do It Yourselfでひとつプログラムでも作ってみませんか。


第10話「オンラインソフトへの道」

さて寿分割は望みどおりに完成しました。既に職場で、プライベートでと活躍しています。折角ですからオンラインソフトとして一般に公開したいものです。もしかした有名人になれるかも。でも、この「折角」が曲者です。気持ちは良くわかります。でも、自分だけが使うプライベートソフトと、全く知らない人も使うオンライン(パブリック)ソフトではいろいろな違いがあります。

  プライベートソフト パブリックソフト
不具合 見ないフリできる。 我慢できない。
操作法 当然ながら隅から隅まで知っている。 はじめて起動したときに直感的に操作が浮かんでくるものだと思われている。説明書なんて読みたくない。
修正・機能追加 自分で行える。 どんどん(ある意味では無責任に)作者に訴えたい。
損害 何か出ても納得できる。 万が一出たら絶対に許せない。

こんなことを書いたら、オンラインソフトを作ろうと意気込んでいる人のやる気を削ぐみたいですね。決してそうではないのです。これぐらいの問題をクリアするぐらいでないとオンラインソフトは出せない、と言うことです。具体的に問題をクリアする方法は、
  1. 出来る限り不具合をなくしましょう
    自分一人でなくすのは寡黙で困難なものです。かく言う私もたくさんの不具合つきオンラインソフトを公開してしまいました。私は友人や既存ユーザーにデバッグをお願いしています。公開β版でのバグフィックスも行っています。それでもなかなか消えません。最近は自動操作ソフトウェアが活用できないかとも考えています。それでも完全に消えないと思いますが。肝心なことは「まあ大丈夫だろう」とタカをくくらないことです。プログラムの不具合は「正しいはず」と言う目でデバッグしているからです。また修正個所が他に及ぼす影響を十分に考えていないためです。「自分は間違っている」からデバッグしましょう。完全に消えないのなら重要性の高いところから不具合を調べることも肝心です。しかし、ぐだぐだと言ってる割りには言ってる内容が半分精神論になっていることは否めないのですが。
  2. 初めてでも直ぐに使い始められるソフトを作りましょう
    プログラムのユーザーインターフェースは作者の個性が顕著に表れるものです。個性の違いを感じてもらうだけならいいのですが、「こいつちょっと変だな」と思われては何のためにオンラインソフトを公開したかわかりません。起動はしたけど、使い方がわからずに途端に終了されるソフトでは悲しすぎます。折角ダウンロードしたのに課金の無駄だったなんて思われると、ソフトを作ったほうも辛いですし。やはりWindowsの標準Look&Feelに合わせるのが無難でしょう。特にF1キーでヘルプが出たり、ダイアログのワンポイントヘルプがあると安心できるのは私だけではないと思いますが。
    当然ですがオンラインヘルプはつけましょう。初めて使うときや、ソフトを活用するにはどうしてもヘルプに頼ります。これがないと「所詮プライベートソフトだな」と思われても仕方ないと思います。別にWindowsヘルプでなくても良いと思いますが、使い切ってもらうための手段は考えましょう。
  3. ユーザーに対するサポート体制はしっかりしておきましょう
    これはフリーソフトでもシェアウェアでも全く同じです。ホームページを持っているならその中で、ないのなら絶対に作りましょう。オンラインソフトは作者とユーザーを密接に結びつけることが出来るのが大きな長所です。作りっぱなし、公開しっぱなしでは直ぐにソッポを向かれます。特に不具合の報告をもらっておいてナシノツブテではいただけません。直ぐに修正できないにしろ、必ずバグ情報は公開しましょう。 「フリーソフトでそこまで手間がかけられるか」と思っているのならシェアウェアにすれば良いでしょう。ただし、登録料を頂くならそれなりのソフトでないと売れないのは当たり前ですが。
    寿空間ではユーザーからバグ報告を送信する画面を設けています。バグ報告を受けると内容を確認して、必要であればバグ情報を載せます。本当は掲示板を設けて自由に書きこみしてもらうと意見交換や技術情報交換もできて良いのですが、とても掲示板までは管理ができませんのでご勘弁を。私は最低限でもバグ情報や機能追加の要求を頂いたユーザーには必ず返信しています。
  4. 免責事項はかならず書きましょう
    ソフトを使って損害を被ることもあるかもしれません。はたまた詐欺まがいの言いがかりをつけられるかも知れません。ユーザーが受けた損害を補償するのはまず不可能でしょうから、プログラムの導入説明書(ReadMe)やヘルプには必ず免責事項を書きましょう。「そんなことあるわけない」と思わずに、保険のつもりで書くべきです。まあ、書くのはタダですしね。

寿分割の様なファイル分割ソフトは既に山のようにあります。あえて寿分割をオンラインソフトとして公開する理由は以下のとおりです、 私が既にいくつかオンラインソフトを公開しているので慣れていると言うのもあるかもしれません。別に人気が出なくったっていいですしね。


シェアウェアかフリーソフトか

あなたが登録料を払ってシェアウェアを買う動機はなんでしょう。私は以下のように思います。
  1. そのソフトしかできない
    最も大きな動機でしょう。ドクターUnitsは単位換算式を数式で定義できる世界唯一のソフトウェアです。おかげ様で多くの人にシェアウェア登録頂いています。やはり不可能を可能にするソフトはシェアウェアでも購入される確立が高いです。ただし同じことが出来る他のフリーソフトが出れば、当然そのフリーソフトを使うでしょう。
  2. 幸せになれるソフトだ
    これも購入動機の大きな要因です。こんなソフトに出会って幸せだ、と思わせるソフトは絶対に売れます。異体字転はいろいろな方から感謝のメールを頂いたり、知らないHPサイトで「お薦めソフト」としていくつか紹介されています。「窓の杜」でも数回紹介されて、寿空間のカウンターが一気に上がったこともありました。雑誌にも数多く掲載頂いています。未だに嫁さんは「シェアウェアにしておけば儲かったのに」と言いますが、まあフリーソフトで十分だと私は思っています。
  3. 機能制限がない
    これはちょっと変な言い方ですが、要は「払わなくても良い」と見えるシェアウェアです。決してそうではありません。気に入れば人の道にならって登録料を払うべきです。私はちゃんとヘルプカード99の登録料を支払っています。機能制限はありませんが、継続して使っているので支払いを済ませています。以前はよく未登録のWinZipを使っている人を見かけました。「払え」のメッセージでは英語では効き目ないのでしょうか。余談ですが、機能制限を考えるのもなかなか大変です。当然ながら最機密事項ですが。
私にも欲があります。寿分割を10年前に開発していたなら間違いなくシェアウェアにしたでしょう。しかし、高機能なファイル分割ソフトは既にいくつかあって、とても上記の条件を満たせません。寿分割でちょっと幸せになってもらえれば幸いですので、フリーソフトとします。


オンラインソフトにするならば

寿分割をオンラインソフトとするため、いくつかの項目を追加します。
  1. 導入説明書を作る
  2. オンラインヘルプを作る

導入説明書

導入説明書はテキストファイルで作成します。どのような環境でも読めることが大切です。サイズも小さくなければなりません。配布イメージには解凍ソフトが一般的ですから、自己解凍を実行すると必ず表示するようにしましょう。これは「読む」責任をユーザーに持たせるためです。内容にはソフトウェアの概要、動作環境、インストール方法、起動方法、免責事項を書きこみます。自己解凍して直ぐにつかえるならインストール方法は不要かもしれません。改定履歴やアンインストール方法を記述するのも親切です。私はいつも定型書式で記入しています。

プログラム名 寿分割 Version 1.00
初めにお読み下さい
作成日付 2001/2/17
作成者 Digital Design Technology
斉藤 寿成
ソフトウェア概要 このたびは寿分割をダウンロードしていただき誠にありがとうございます。寿分割はシンプルかつ単機能のファイル分割アクセアリです。分割したファイルは同時に作成する結合バッチファイルで行います。

最短で分割元ファイルをドラッグアンドドロップして、分割実行ボタンを押すだけの2ステップで分割が完了します。ユーザーインターフェースも簡単かつ自然になっています。

寿分割は「寿空間」(http://www.eonet.ne.jp/~kotobukispace)でプログラムの起案から作成までを全て載せているプログラムです。設計コンセプト、画面設計、詳細設計からプログラムの細部に渡るまで全て公開しています。Borland C++ Builder 3.0形式のプロジェクトファイル込みでソースリストもダウンロードできます。ダウンロードしたソースリスト、リソース、プロジェクトは全てライセンスフリーです。「言語は習ったけれども、実用のプログラムがつくれないな」と言う方は是非参考にしてください。ただし、独断と偏見に満ち溢れていますので、ご了解ください。
免責事項 1. ソフトウェアを使用する前に

寿分割の使用者が寿分割を使用していかなる不利益を被った場合も、寿分割の作者は一切責任を負いませんのでご了解ください。
ソフトウェア構成 2. ソフトウェア構成

kotocut.exe .... 寿分割本体
kotocut.hlp .... 寿分割ヘルプ
kotocut.cnt .... 寿分割ヘルプ目次
kotocut.txt .... このテキスト
動作環境 3. 動作環境

Windows95/98/NT4.0/2000以上
起動方法 4. ソフトウェアの起動

kotocut.exeを起動します。または分割元ファイルをkotocut.exeにドラッグアンドドロップしてください。
使用方法 5. 使用方法

寿分割の使用方法は同梱のヘルプをご覧ください。
アンインストール 6. アンインストール

「2.ソフトウェア構成」のファイル全てを削除して下さい。レジストリに変更は加えません。
改定履歴 7. 改訂履歴

Version 1.00 2001/2/17
寿分割リリース

オンラインヘルプ

オンラインヘルプを作るのは正直面倒なものです。構成や文章を考えるのもさることながら、操作をわかりやすく説明するためにはいろいろな画像を作る必要もあります。また、画像をクリックすると関連するヘルプにジャンプするようなホットスポットも設けます。でも、折角オンラインソフトとしたならば活用してもらう、使い切ってもらいたものです。しっかりと作りましょう。
最近は最近はHTML形式のヘルプが流行ですが、未だに私はWindowsヘルプ形式を作っています。Windowsヘルプ形式はリッチテキスト形式で文章やコンテキストを書いて、画像埋め込みのマクロを定義します。それらをMicrosoft Help Compilerにかけてヘルプファイルを作成します。その昔はMicrosoft Wordで作っていたのですが、流石に大変で、今はヘルプ作成環境としてmomoさんこと梅木 泰宏さんの有名なシェアウェアの「ヘルプカード1999」を使っています。これを使うと各ページ(トピック)の管理や画像やマクロの編集が簡単に行え、ヘルプ作成が比較的容易になります。「比較的」と言うのはヘルプの構成や文章を考えるのが大変かつ面倒と言う意味です。
ヘルプファイルには当然詳しい操作方法を記述しますが、私はそれに加えてソフトウェアの概要、動作環境、免責事項を記述しています。特に免責事項は導入説明書と重複しますが必ず書きます。操作方法の書き方はプログラムによってまちまちです。しかし、必ず「基本操作」のページは必ず設けて、プログラムのメイン画面でF1キーを押したらこのページを表示するようにしています。そこからトピックジャンプで細かな機能を説明します。また、オンラインヘルプを通して読めばそのプログラムの機能を大まかに把握できるような構成にしています。時々メニューやボタンの操作が箇条書きに書かれているオンラインヘルプを読むことがありますが、使う側にしてみれば「こんなことをしたのだが、どのメニューやボタンからたどって行けば良いのかかわからない」となってしまいがちです。まあ、私自身も文章を書くのはあまり得意ではありません。よく誤字・脱字もします。読む側のことを常に意識している、とだけ思って勘弁してやってください。
まずヘルプ構成から考えます。

ヘルプページ 初めに プログラムの特徴、補足
ヘルプページ 動作環境 プログラムの動作環境、ファイル構成
ヘルプブック 操作方法  
  ヘルプページ 基本操作 分割元ファイルの読み込み、分割実行、メイン画面でF1キーを押すと表示
  ヘルプページ 分割サイズ 分割サイズの変更、均等分割
  ヘルプページ 分割先 分割先の変更
ヘルプブック その他  
  ヘルプページ 著作権について 著作権に関する明記
  ヘルプページ 免責事項 免責事項の明記
  ヘルプページ 謝辞 謝辞
  ヘルプページ バグレポート バグレポート、サポート方法の明記

次に各ページを作ります。「百聞は一見にしかず」と言います。わかりやすい説明には画像も必要ですので、実際に寿分割を起動して必要な画面ハードコピーをとります。この時、色数に注意してください。最近のパソコンはフルカラー(16万色)やハイカラー(65,536色)が多いので画像データも大きくなります。一方プログラム画面は16色や256色で表現できるものが多くあります。必ず減色処理をしましょう。そうでないとヘルプファイルが無意味に大きくなります。当然画像サイズにも気をつけて、無駄に大きくすることは避けましょう。キーワードも必要です。ホットスポットも便利です。要はプログラムと同様に「直感的に調べられる」ことが大切です。
これ以上説明してもわかりにくいでしょうから、こちらからヘルプ作成に必要なヘルププロジェクト、画像、ホットスポットデータをダウンロードできます ダウンロード(12,078バイト)。ご自由にお使いください。ヘルプコンパイルには別途ヘルプカード1999とヘルプコンパイラ(BCB同梱)が必要です。ホットスポットの編集には別途ホットスポットエディタ(私はかなり昔のものを使っています、今はMicrosoftのVisual Studioに同梱もしくは専用のシェアウェアがあると思います)が必要です。

プログラムとヘルプの結合

ヘルプファイルが出来たら寿分割と結合します。
  1. ヘルプファイル設定
    寿分割のプロジェクト設定(BCBの「プロジェクト/オプション...」メニュー)で、アプリケーションのヘルプファイルに作成したヘルプファイル'kotocut.hlp'を設定。

  2. 寿分割画面のヘルプコンテキスト設定
    ヘルプカード1999でHMヘッダを作成します(ヘルプカードの「表示/HM ヘッダ...」メニュー)。ヘルプコンテキストID一覧が表示されますので、これをコピーして、寿分割のMainWNDのヘッダファイル'main.h'に定義します。寿分割画面にヘルプコンテキストIDを設定するだけでも良いのですが、今後画面からヘルプを参照できるようにプログラム中にヘルプコンテキストをとりこみます。以下に私が作ったヘルプコンテキストIDを示します。ヘルプカードでの各ページの作成順によりID値は変わります。
    // Context ID list
    #define IDH_ATFIRST 1   //初めに
    #define IDH_ENVIRONMENT 2   //動作環境
    #define IDH_OPERATIONS 3   //操作方法
    #define IDH_BASICOPERATION  4   //基本操作
    #define IDH_CHANGESIZE  5   //分割サイズ
    #define IDH_CHANGEPATH  6   //分割先
    #define IDH_ETC 7   //その他
    #define IDH_AUTHORIZATION  8    //著作権
    #define IDH_ESCAPE  21  //免責事項
    #define IDH_THANKS  9   //謝辞
    #define IDH_REPORT  10  //バグレポート
    #define IDH_NEWFILE 11  //新規作成
    #define IDH_OPENFILE    12  //開く
    #define IDH_GODIVIDE    17  //分割実行
    #define IDH_SHOWHELP    13  //
    #define IDH_ABOUT   14  //
    #define IDH_TOOLBAR 15  //
    #define IDH_STATUSBAR   16  //
            
    メイン画面でF1キーを押すと基本操作のヘルプが表示されるように、MainWNDのHelpContextに上記「基本操作」のヘルプコンテキストID値を設定します。
    ・ HelpContext (ヘルプコンテキスト番号) 4
  3. ヘルプメニューの作成
    ヘルプメニューを追加します。

    Caption Name 内容
    「ヘルプ(H)」 HelpMNU ヘルプに関するメニュー。
      「ヘルプ(H)」 ShowHelpMNU ヘルプを表示する。
      「バージョン情報(A)...」 AboutMNU 寿分割のバージョン情報を表示する。


    このメニューのコールバックでヘルプを表示します。
        
    //---------------------------------------------------------------------------
    void __fastcall TMainWND::ShowHelpMNUClick(TObject *Sender)
    {
        Application->HelpCommand(HELP_FINDER, 0);
    }
    //---------------------------------------------------------------------------
        
  4. ツールボタンーの作成
    ツールボタンを追加します。

    メニュー 有効イメージ 無効イメージ コメント
    ヘルプ/ヘルプ ヘルプ有効イメージ ヘルプ無効イメージ 無効イメージは実際には表示されません


    このボタンにをイメージリストに追加し、コールバックを登録します。
    コントロール Name
    (名前)
    ImageIndex
    (イメージ番号)
    コールバック
    ヘルプ有効イメージ HelpBTN 5 ShowHelpMNUClick関数
  5. ヘルプ終了処理
    ヘルプを表示したまま寿分割を終了すると、ヘルプは開いたままとなります。寿分割を終了すると自動的にヘルプを閉じる処理を追加します。これは寿分割の終了コールバックにヘルプ終了処理を記述します。
        
    //---------------------------------------------------------------------------
    void __fastcall TMainWND::FormCloseQuery(TObject *Sender, bool &CanClose)
    {
        Application->HelpCommand(HELP_QUIT, 0);      // ヘルプを閉じる
    }
    //---------------------------------------------------------------------------
        
以上でヘルプ処理は完了です。


ショートカットドロップ

ショートカットさて、全て出来あがったと思ったらもう一つ大切な機能を忘れていました。寿分割のようなアクセサリはデスクトップ上にショートカットを作って分割元ファイルをそこにドロップする動作も便利です。これに対応するにはプログラムの起動時にコマンド引数を調べます。BCBにはコマンド引数の数を得るParamCountと引数文字列配列を得るParamStr関数があります。例えば'source.dat'と言う分割元ファイルを寿分割もしくはそのショートカットにドロップすると、寿分割のコマンド引数にはParamCount()=1、ParamStr(1)="source.dat"となります。単に寿分割が起動されただけならParamCount=0となります。コマンド引数があれば、FormCrete関数内でこの第1引数から分割一覧を作成すればよいわけです。
ここで「互換性の呪縛」と言う問題が生じます。例えば分割元ファイル名が'IWillDivideByKotocut.dat'だった場合、コマンド引数で取得できる文字列は何故か"IWILLD~1.DAT"となります。これはWindowsの前身の16bitシステムのMS-DOSの「8+3ファイル名」の名残です(Windowsではショートファイル名と呼びます)。BCBが悪いのではなく、Windowsの仕様です。これを元の"IWillDivideByKotocut.dat"(ロングファイル名)に変換するにはBCB関数のFindFirst関数を用います。FindFirst関数はGetFileSize関数でも使用したファイルに関する様々な情報を取得できる関数です。情報の中にはロングファイル名もあります。つまりショートファイル名で検索するだけで、対応するロングファイル名が得られるのです。また、ファイルパスもショートファイル名になってしまいますので、これを同様の手法でロングファイル名に変換します。このような処理は汎用性がありますので、メンバ関数とはせずに独立させます。

GetLongPathName関数

//---------------------------------------------------------------------------
AnsiString GetLongPathName(AnsiString ShortName)
// ショートファイル(パス)名をロングファイル(パス)名に変換する
{
    if (ShortName=="") return("");  // 無効ショートファイル名
    if (ExtractFileDrive(ShortName)=="") return("");    // 無効ショートファイル名
    AnsiString LongName = "";       // ロングファイル名
    TSearchRec sr;                  // FindFirst関数(BCBライブラリ関数)で使用する検索レコード型
    bool IsTopPath = false;         // パス先頭(UNCを含む)の場合True
    do{         // ファイル名ショート→ロング変換ループ
       int Status = FindFirst(ShortName, faAnyFile, sr);        // ロングファイル名検索
        if (Status==0) LongName = "\\" + sr.Name + LongName;    // ロングファイル名取得の場合、ロングパス名作成
        else break;                                             // ファイルが存在しない
        ShortName = ExtractFilePath(ShortName);                 // 親パス取得
        ShortName = ShortName.SubString(1, ShortName.Length()-1); // 末尾区切りターミネータ削除
        if (ExtractFileDrive(ShortName)==ShortName) IsTopPath = true;   // ディレクトリトップ(ローカル&ネットワーク)
    }while(!IsTopPath);
    LongName = ShortName + LongName;    // ロングファイル名作成
    FindClose(sr);                      // FindFirst関数ワーク領域解放

    return(LongName);              // ロングパス名を返す
}
//---------------------------------------------------------------------------
寿分割の起動時にコマンド引数がある場合は、ファイルがショートカットドロップされたことを示します。コマンド引数の第1引数を分割元ファイル名としてショート→ロングファイル名変換し、分割元ファイル設定と分割先ファイルパスの作成を行います。

改造FormCreate関数

//---------------------------------------------------------------------------
void __fastcall TMainWND::FormCreate(TObject *Sender)
{
    SizeMNUClick(Size1440MNU);        // 初期分割サイズ→1.44M
    DragAcceptFiles(Handle, true);    // ドロップ受け取り登録

// 分割ファイル起動時引数
    if (ParamCount()>=1){             // コマンド引数あり(ショートカットドロップ)
        AnsiString LongName = GetLongPathName(ParamStr(1));  // ショート→ロングファイル名変換
        if (LongName!=""){                          // ロングファイル名あり
            OpenDLG->FileName = LongName;           // 分割元ファイル名を設定
            ListDivideFiles(OpenDLG->FileName);     // 分割先ファイル一覧および結合バッチファイル名を設定
            SetupDestPath(OpenDLG->FileName);       // 分割先パスをステータスバーに設定。
        }
    }

    UpdateMenu();    // メニュー更新
}
//---------------------------------------------------------------------------

これでショートカットドロップに対する処理ができました。ところがここで問題がおきました。これまでの寿分割ではドライブ空き容量取得GetDiskFree関数とセクタサイズを取得GetSectorSize関数に、ドライブ番号を使用していました。ところがネットワークコンピュータはドライブ番号では指定できません(ネットワークドライブを除く)。ネットワークコンピュータ上のドライブはUNC(Universal Naming Convention、統一命名規約)で、
\\コンピュータ名\共有名\ディレクトリ\...
となります。寿分割では「単にパス名の先頭3文字をとればドライブ名が取得できる」のではUNCに対応できません。なぜ第7話でこのようなことを書いたか言訳をすると、開発環境がネットワークではなく、ネットワーク上のファイルを分割したり、ネットワークコンピュータにファイルを分割する機会がなかったからです。GetLongPathName関数を作っているときUNCの問題が発覚しました。早速GetDiskFree関数GetSectorSize関数をUNC対応にします。具体的にはファイルパスからローカルドライブおよびUNCドライブの名前をVCLのExtractFileDrive関数を使用して取得します。ただし、この関数はローカルドライブではドライブ番号+':'を、ネットワークドライブでは'\\'+コンピュータ名+'\'+共有名を返します。一方ドライブの空き容量やセクタサイズを取得するAPI関数はパス区切りが必要ですので、ドライブ名の最後にターミネータ'\'を付加します。このような関数仕様の些細な違いは常にありますので、注意が必要です。


ネットワーク対応

ネットワークコンピュータドライブまず、ドライブ番号を引数として使用するGetDiskFree関数GetSectorSize関数2つの関数を改造しUNC対応に変更ます。ただし初代Windows95では2G以上のHD空き領域を正しく認識できないので、ネットワークコンピュータ上の分割先ドライブに2Gの空きがあっても「ドライブの空き容量が足りません」のメッセージが出て分割不可能になります。これは初代Windows95の仕様ですので、私になんとかしろと言っても無理ですのであしからず

改造GetDiskFree関数

//---------------------------------------------------------------------------
double GetDiskFree(AnsiString DriveName)
// DriveName "#:\" ローカルドライブ名
//           "\\ComputerName\ShareName\Directory\.." ネットワーク統一命名規約
{
    static BOOL (WINAPI *APIGetDiskFreeSpaceEx)(LPCSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER);
                                // 関数のエントリポインタを保持する変数
    double FreeSize = -1.0;     // 空き容量(実数表現)
// ドライブ番号→ローカルドライブ名変換処理を削除
    ULARGE_INTEGER FreeAvBytes; // 使用可能空き容量(64ビット整数表現)
    ULARGE_INTEGER TotalBytes;  // 総容量(64ビット整数表現)
    ULARGE_INTEGER FreeBytes;   // 空き容量(64ビット整数表現)

    HANDLE DLLInstance = LoadLibrary("KERNEL32.DLL");   // DLL動的ロード
    APIGetDiskFreeSpaceEx = (BOOL(WINAPI *)(LPCSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER))(GetProcAddress(DLLInstance, "GetDiskFreeSpaceExA"));
                                // GetDiskFreeSpaceExA関数エントリ取得
    if (APIGetDiskFreeSpaceEx==NULL){           // GetDiskFreeSpaceExA関数がない(初代Win95)
        DWORD SecPerClu, BytePerSec, NumFreeClu, TotalClu; // クラスタ・セクタ情報
        int Result = GetDiskFreeSpace(DriveName.c_str(), &SecPerClu, &BytePerSec, &NumFreeClu, &TotalClu);
                               // 旧API関数で全容量取得
        if (Result) FreeSize = (int)SecPerClu*(int)BytePerSec*(int)NumFreeClu;
    }
    else{                                       // GetDiskFreeSpaceExA関数がある(それ以降のWindows)
        int Result = APIGetDiskFreeSpaceEx(DriveName.c_str(), &FreeAvBytes, &TotalBytes, &FreeBytes);
                               // 新API関数で全容量取得
        if (Result) FreeSize = (double)FreeAvBytes.u.HighPart*4294967296.0 + (double)FreeAvBytes.u.LowPart;
    }

    FreeLibrary(DLLInstance);  // DLL解放

    return(FreeSize);     // 空き容量(実数表現)を返す
}
//---------------------------------------------------------------------------

改造GetSectorSize関数

//---------------------------------------------------------------------------
int GetSectorSize(AnsiString DriveName)
{
    DWORD SecPerClu, BytePerSec, NumFreeClu, TotalClu;
// ドライブ番号→ローカルドライブ名変換処理を削除
    int SecSize;

    int Result = GetDiskFreeSpace(DriveName.c_str(), &SecPerClu, &BytePerSec, &NumFreeClu, &TotalClu);
    if (Result) SecSize = (int)BytePerSec;
    else        SecSize = -1;

    return(SecSize);
}
//---------------------------------------------------------------------------
パス名の先頭3文字からドライブ名を取得しているメイン画面分割スレッド処理と、上記2関数を呼び出すGoDivideMNUClick関数、CheckDivide2Fix関数、OnNoWriteSpace関数の処理を変更します。

改造GoDivideMNUClick関数

//---------------------------------------------------------------------------
void __fastcall TMainWND::GoDivideMNUClick(TObject *Sender)
{
// 分割実行チェック
    AnsiString DriveName = ExtractFileDrive(StatusBAR->SimpleText) + "\\";   // ドライブ名取得
    bool Removable = (GetDriveType(DriveName.c_str())==DRIVE_REMOVABLE);
    if (!Removable){                                // 固定ディスク
        if (!CheckDivide2Fix(DriveName)) return;    // 分割中断
    }

// 分割リスト作成
         :
}
//---------------------------------------------------------------------------

改造CheckDivide2Fix関数

//---------------------------------------------------------------------------
bool __fastcall TMainWND::CheckDivide2Fix(AnsiString DrivePath)
{
// セクタサイズ取得
    int SectorSize = GetSectorSize(DrivePath);       //  セクタサイズ取得

// セクタサイズを考慮した空き容量チェック
    AnsiString ConnectBatch = MakeConnectBatch(OpenDLG->FileName.c_str(), ListVEW->Items->Count-1);
    int RequireSize = (int)((ConnectBatch.Length()-1)/SectorSize+1)*SectorSize;
                                                                // 結合バッチテキストサイズ
    for (int i=0;iItems->Count;i++){                  // 分割ファイルサイズ加算ループ
        int ACutSize = (int)ListVEW->Items->Item[i]->Data;      // 各分割ファイルサイズ
        ACutSize = (int)((ACutSize-1)/SectorSize+1)*SectorSize;
        RequireSize += ACutSize;                                    // 分割ファイルサイズ加算
    }
    if (GetDiskFree(DrivePath)<(double)RequireSize){                // 空き容量チェック
        Application->MessageBox("ドライブの空き容量が足りません",
                                "分割実行", MB_ICONEXCLAMATION | MB_OK);   // 容量不足
        return(false);
    }

// オーバーライトチェック
        :
}
---------------------------------------------------------------------------//

改造OnNoWriteSpace関数

//---------------------------------------------------------------------------
void __fastcall TMainWND::OnNoWriteSpace(TMessage &Message)
{
    AnsiString DriveName = ExtractFileDrive(StatusBAR->SimpleText) + "\\";    // ドライブ名取得
    bool Removable = (GetDriveType(DriveName.c_str())==DRIVE_REMOVABLE);    // ドライブタイプ取得
    if (!Removable){                    // 固定メディア?
        Message.Result = dsNoSpace;     // 空き容量不足返答
        return;
    }
    int okcancel = Application->MessageBox("ディスクを交換してください", "分割実行", MB_OKCANCEL);
    if (okcancel==IDCANCEL){           // 交換中断?
        Message.Result = dsUserBreak;  // ユーザー中断返答
        return;
    }
    Message.Result = dsSuccess;        // メディア交換返答
}
//---------------------------------------------------------------------------

改造Execute関数

//---------------------------------------------------------------------------
void __fastcall TDivideThread::Execute()
{
        :
    // 分割先ファイル情報取得
        cDivideFile *File = (cDivideFile*)DstFiles->Objects[i];      // 分割ファイルオブジェクト取得
        int ACutSize = File->GetSize();                              // 分割サイズ取得
        AnsiString DriveName = ExtractFileDrive(DstPath) + "\\";     // ドライブ名取得
        while(GetDiskFree(DriveName)<(double)ACutSize){      // 空き容量確保ループ
            Synchronize(NoDivideSpace);                     // 分割先容量不足通知
            if (DivideStatus<dsSuccess){     // 分割中断?
                DivideStatus = dsUserBreak;     // ユーザー中断
                delete buffer;                  // 分割バッファ開放
                fclose(SrcFP);                  // 分割先ファイルクローズ
                return;                         // 分割中断
            }
        }

    // 分割先ファイル作成
        :
}
//---------------------------------------------------------------------------

こちらから今回作成したBCBのプロジェクト一式をダウンロードできます(ヘルプ一式は含んでいません)。ダウンロード(19,818バイト) BCBのバージョン3、4、5でビルドできます。参考にしてください。BCBバージョン5ではの修正を行ってください。


寿分割10話にわたってプログラムの起案から完成までを連載してきました。いかがでしたが? 出来あがった寿分割はネット上で公開されているファイル分割ソフトと比べても引けを取らない完成度になったと思います。今回で一段落して寿分割をバージョン1.00としてリリースします。寿分割の実行形式はこちらからダウンロードしてください。
次回からは、さらに機能を追加していきたいと思います。当然Digital Design Technologyのポリシーである「ちょっと小粋なWindowsアクセサリ」を基本として使いやすさに磨きをかけていきたいと思います。是非ご意見、ご感想、要望項目を クリックするとメーラーを起動しま までお寄せください。なお次回からは不定期の更新となります。次回はDigital Design Technologyらしい機能を追加したいと思います。お楽しみに。

今回のプログラミング行数: 67行
今回までのプログラミング行数: 607行


目次に戻る目次に戻る トップに戻るトップに戻る