はじめに
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 ではソースコードの履歴を管理できるのですが、
実行可能ファイルがどのリビジョンで作られたものなのかが判断できないため、実行するのに危険が伴う事もあります。
解決法?
「バージョン管理に EXE も突っ込む」
「必ずプロジェクトファイルをコピーして使う」
「バージョンリソースに情報を入力する」
...使い捨て (或いは検証用) で**"ない"**アプリケーションならこれでいいのでしょうけれど。
シンプルな解決法
例えば、次のような FizzBuzz アプリケーションを作ったとします。
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.
この状態で [プロジェクト | リソースと画像] でリソース管理ダイアログを表示します。
ここに*自分自身 (コンソールアプリケーションの場合 .dpr) を指定します。リソース識別子は判りやすいように SourceCode
にしていますが、デフォルトのままでも構いません。リソースタイプは RCDATA
です。
こうしておくと 2 つのメリットが生まれます。
一つは本来の目的である、実行可能ファイルにコンパイル時点のソースコードが埋め込まれる事です。この実行可能ファイルがどういった動作をするのかは、リソースエディタで開けば解ります。ちゃんと保存している限り 2、(リソースファイルの) ソースコードのロジックと実行可能ファイルの挙動は必ず一致します。ファイル名が変更されようが、どこに放置されようが、です。
もう一つは小さなメリットですが、プロジェクトファイルを間違って閉じてもプロジェクトマネージャからダブルクリックで開けるようになります。
おわりに
リソースエディタでイチイチ確認するのが面倒なのであれば、次のようなロジックを入れておきましょう。
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 が表示され、
コマンドラインパラメータに -ShowSource
(または /ShowSource
) を指定するとソースコードが表示されます。
リソースを表示する部分は手続き化して別ユニットにすると -ShowSource
した時に読みやすいコードになると思います。
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 に追加するだけなのでお手軽です。
macOS 用にビルドしてももちろん動作します。