【Delphi】使い捨て (或いは検証用) アプリケーションの内容を確認できるようにする

# はじめに
Delphi で使い捨て用のアプリを書くことがあります。多くはコンソールアプリケーションで、一時的に何かに組み込んで使う事もあります。Qiita 用の検証コードもコンソールアプリケーションで作ることが多いです。

割と雑に書かれたコードなので、実行可能ファイルをどこか別の場所に置いたまま時間が経過すると**「あれ？これ何のために作ったんだっけ？」**ってなります。ファイル名もデフォルトの **Project1.exe** とかなので判別できない事もあります。

気を利かせて **HogeHoge.exe** ってリネームしてあったとしても、元々のプロジェクトファイルが **Project1.dproj** のようにデフォルトのままだと、**「あれ？これどのプロジェクトから作ったんだっけ？」**となります。

...なりますよね？

# アプリケーションの内容確認
スクリプト言語なら、そのスクリプトをテキストエディタで開けばいい事なのですが、コンパイラ型言語はソースファイルと実行可能ファイルが別々に存在するため **ソースコードを紛失する** という事態が発生します。

先述のように実行可能ファイルの名前を変更したために、プロジェクトファイル名と一致しなくなる事もあります [^1]。

検証用アプリケーションだと、同じプロジェクトファイルを使いまわす事があるため、異なる挙動をする **Project1.exe** が生成され、

 - **Project1.exe** 
 - **_Project1.exe**
 - **__Project1.exe**
 - **Project1_new.exe** 
 - **Project1_old.exe**
 - **Project1_bak.exe**
 - **Project1_test.exe**

というファイルが散らばることになります。Delphi IDE ではソースコードの履歴を管理できるのですが、![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/f8561900-79b6-3143-ae73-473d7e897743.png)
実行可能ファイルが**どのリビジョンで作られたものなのかが判断できない**ため、実行するのに危険が伴う事もあります。

## 解決法？
「バージョン管理に EXE も突っ込む」
「必ずプロジェクトファイルをコピーして使う」
「バージョンリソースに情報を入力する」

...使い捨て (或いは検証用) で**"ない"**アプリケーションならこれでいいのでしょうけれど。

## シンプルな解決法

例えば、次のような FizzBuzz アプリケーションを作ったとします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/6e870d91-2698-c313-0552-7a813ae3bfa4.png)

```pascal:Project1.dpr
program Project1;

{$APPTYPE CONSOLE}

begin
  for var i := 1 to 100 do
    begin
      if ((i mod 3) +  (i mod 5)) = 0 then
        Writeln('Fizz Buzz')
      else if (i mod 3) = 0 then
        Writeln('Fizz')
      else if (i mod 5) = 0 then
        Writeln('Buzz')
      else
        Writeln(i);
    end;
end.
```

この状態で [プロジェクト | リソースと画像] でリソース管理ダイアログを表示します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/36eae2bc-10ec-ffd5-84a9-435a9bc18c60.png)
ここに**自分自身 (コンソールアプリケーションの場合 *.dpr)** を指定します。リソース識別子は判りやすいように `SourceCode` にしていますが、デフォルトのままでも構いません。リソースタイプは `RCDATA` です。

こうしておくと 2 つのメリットが生まれます。

一つは本来の目的である、**実行可能ファイルにコンパイル時点のソースコードが埋め込まれる**事です。この実行可能ファイルがどういった動作をするのかは、リソースエディタで開けば解ります。ちゃんと保存している限り [^2]、(リソースファイルの) ソースコードのロジックと実行可能ファイルの挙動は必ず一致します。ファイル名が変更されようが、どこに放置されようが、です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/4a613e47-211d-2e26-6f0f-b1809e8bf582.png)

もう一つは小さなメリットですが、プロジェクトファイルを間違って閉じても**プロジェクトマネージャからダブルクリックで開ける**ようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/5b0bed3b-1198-130f-4047-55df403d08fe.png)

# おわりに
リソースエディタでイチイチ確認するのが面倒なのであれば、次のようなロジックを入れておきましょう。

```pascal:project1.dpr
program Project1;

{$APPTYPE CONSOLE}

{$R *.dres}

uses                                             // <- 追加
  System.SysUtils, System.Classes, System.Types; // <- 追加

begin
  { 追加: ここから }
  if FindCmdLineSwitch('ShowSource', True) then
    begin
      var RS := TResourceStream.Create(HInstance, 'SOURCECODE', RT_RCDATA); // 第 2 パラメータはリソース識別子
      var SL := TStringList.Create;
      try
        SL.LoadFromStream(RS);
        Writeln(SL.Text);
      finally
        SL.Free;
        RS.Free;
      end;
      Exit;
    end;
  { 追加: ここまで}

  for var i := 1 to 100 do
    begin
      if ((i mod 3) +  (i mod 5)) = 0 then
        Writeln('Fizz Buzz')
      else if (i mod 3) = 0 then
        Writeln('Fizz')
      else if (i mod 5) = 0 then
        Writeln('Buzz')
      else
        Writeln(i);
    end;
end.
```

普通に実行すると FizzBuzz が表示され、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/424192ac-49d9-f349-f0c6-d0c43d858c74.png)
コマンドラインパラメータに `-ShowSource` (または `/ShowSource`) を指定するとソースコードが表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/ca6a82f9-6257-1ab2-a108-aaaecbe08daa.png)
リソースを表示する部分は手続き化して別ユニットにすると `-ShowSource` した時に読みやすいコードになると思います。

```pascal:uShowSource.pas
unit uShowSource;

interface

uses
  System.SysUtils, System.Classes, System.Types;

implementation

procedure ShowSource;
begin
  if IsConsole and FindCmdLineSwitch('ShowSource', True) then
    begin
      var RS := TResourceStream.Create(HInstance, 'SOURCECODE', RT_RCDATA);
      var SL := TStringList.Create;
      try
        SL.LoadFromStream(RS);
        Writeln(SL.Text);
      finally
        SL.Free;
        RS.Free;
      end;
      Halt(0);
    end;
end;

Initialization
  ShowSource;
end.
```

`uShowSource` を **uses** に追加するだけなのでお手軽です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/ad769e4a-8637-61ad-6504-5514c15fa42c.png)
macOS 用にビルドしてももちろん動作します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/21785/91f25515-f0a3-1a19-4e8e-eb4f8279ee0f.png)

[^1]: まぁ、リネームしなくても実行ファイルを別の名前で出力する事はできますが。
[^2]: EXE 生成前 (コンパイル前) には`〔Ctrl〕+〔S〕`で確実にプロジェクトファイルを保存しておく必要があります。