( 更新) D言語

【D言語】exeにアイコンやバージョン情報を付与する

作ったソフトにアイコンが欲しい

ゲーム制作に使っている自作のマップエディタがあるのだが、アイコンが長らくデフォルトのままだった。

もちろん動作に影響はないのだが、なんかこう、見た目がちょっと寂しい。

そこで、ゲーム制作の合間にオリジナルのアイコンを作ってexeに埋め込んでみた。

ついでにバージョン情報も添付してみたので、このあたりの手順もあわせてご紹介したい。

なお、開発にはD言語を使っており、dmdのバージョンはv2.100.0である。

リソーススクリプトを書く

まず、アイコンやバージョン情報をexeに埋め込むためにリソーススクリプト(.rc)を作る必要がある。
ここはC++とさほど変わりはない。

内容は以下のようになっている。

自分のソフト用の設定をそのまま持ってきているので、各項目の内容は適宜読み替えていただきたい。

#pragma code_page(65001)

#include <windows.h>

/* アイコン */
101 ICON "map_editor.ico"

/* バージョン情報 */
VS_VERSION_INFO VERSIONINFO
    FILEVERSION    1,0,0,0
    PRODUCTVERSION 1,0,0,0
    FILEFLAGSMASK  VS_FFI_FILEFLAGSMASK
    FILEFLAGS      0x0L
    FILEOS         VOS_NT_WINDOWS32
    FILETYPE       VFT_APP
    FILESUBTYPE    VFT2_UNKNOWN
BEGIN
    BLOCK "StringFileInfo"
    BEGIN
        BLOCK "041104b0"
        BEGIN
            VALUE "CompanyName",      "A3サイズの升目帖\0"
            VALUE "FileDescription",  "マップデータを編集するソフト\0"
            VALUE "FileVersion",      "1.0.0.0\0"
            VALUE "InternalName",     "mapeditor\0"
            VALUE "LegalCopyright",   "Copyright (C) 2022 えーさん ますめ\0"
            VALUE "OriginalFilename", "mapeditor.exe\0"
            VALUE "ProductName",      "マップエディタ\0"
            VALUE "ProductVersion",   "1.0.0.0\0"
        END
    END

    BLOCK "VarFileInfo"
    BEGIN
        VALUE "Translation", 0x411, 0x4b0
    END
END

リソーススクリプト自体は普通にテキストエディタで編集すればいいのだが、拡張子は.rcにする必要がある。

あと、アイコンファイルは同じフォルダに入れておこう。

ICONのリソースIDについて

アイコンのリソースIDを101というふうにハードコーディングしているが、本当はresource.hなどの別ファイルに分けて定義するのが望ましい。

しかし、現時点ではImportCが#defineプリプロセッサに未対応なので、ヘッダファイルに分けてしまうとそれをD言語側からも参照するためにhtodなどのツールで変換しなければいけなくなる。

高々1個の定数のために変換を噛ませるのは費用対効果が低いので、これで済ませているというわけだ。

要約:横着しました♥


ちなみに、ImportCというのはD言語のコンパイラでC言語のソースコードを直接コンパイルできるようにしてしまおうとかいうとんでもない代物で、dmd v2.098.0でリリースされた目玉機能だ。

先述のとおり、プリプロセッサには未対応などまだまだ発展途上だが、#defineによる定数定義には対応の兆しも見えており、今後が楽しみである。


2023/5/14追記

更新が遅くなってしまったが、dmd v2.101.0にて、ImportCが#defineによる定数定義に対応したため、リソースIDを別ファイルで簡単に管理できるようになった。

リソースIDを定義するC言語ファイルとして、以下のようにresource.iを作り、リソーススクリプトと同じディレクトリに置いておく。

#define IDI_APP 101

リソーススクリプト側にもresource.iを追加し、リソースIDを定数に置き換える。

#include <windows.h>
#include "resource.i"

/* アイコン */
IDI_APP ICON "map_editor.ico"

続いて、dub.jsonresource.iへのパスを追加しておく。

"sourceFiles": [
    "../../my/path/resource.i"
],

あとは、Dのソース側でも定数を参照する。Cのソースはパスさえ通っていれば通常のDのソースと同じようにimportできる。

import resource; // ←CのソースをDと同じようにimportできる!
HICON hIcon = LoadIconA(hInstance, MAKEINTRESOURCEA(IDI_APP));

ちなみに、Cのソースの拡張子をいつもの.cではなく、プリプロセス済みを表す.iにしているのは、手元のcl.exeがプリプロセスオプション(/Zc:preprocessor)に未対応だったためであり、Visual Studio 2019以降を使っている人なら拡張子は.cで問題ない。

リソーススクリプトをコンパイルする

リソーススクリプトはそのままではリンクできないので、rc.exeでコンパイルする必要がある。

なお、rc.exeの在り処だが、筆者の環境ではC:\Program Files (x86)\Windows Kits\10\bin\10.0.xxxxx.0\x64あたりに入っていた。
たぶんVisual Studioをインストールした際に同梱されていたんだと思う。

コンパイルのコマンドは以下のとおり。

rc -fo"出力パス" "入力パス"

入力パスには、先ほど作成したリソーススクリプト(.rc)のパスを指定する。

出力パスには、拡張子を.resに変更したパスを指定する。

ファイル名をhogeとするならこんな感じになる。

rc -fo"hoge.res" "hoge.rc"

ビルドと同時にコンパイルしたい場合は、これをdub.json"preBuildCommands"に追加するのがいいかもしれない。

// 対象はWindowsだけなので、一応"-windows"サフィックスを付けている。
// また、筆者はファイルの更新を確認してコンパイルの要否を判断する、もうちょっと複雑なスクリプトを組んでいる。

"preBuildCommands-windows": [
  "rc -fo\"hoge.res\" \"hoge.rc\""
]

なお、INCLUDE環境変数にwindows.hへのパスが登録されていないとコンパイルエラーになるぞ!(1敗)

リソースをリンクする

.resファイルをリンクする設定を追加する。

dub.json"lflags"(リンカへのパラメータ)に、.resファイルのパスを指定すればOK。

"lflags": [
  "hoge.res"
]

アイコンをウインドウの左上に表示する

ここまでの手順を踏めば、エクスプローラ上のexeには指定したアイコンが適用されているはずだ。

しかし、ウインドウの左上に表示されるアイコンはまだデフォルトのままだと思う。

ここにもアイコンを表示するには、ウインドウの生成時に明示的にアイコンを指定してやる必要がある。

ウインドウを表示する処理から、ポイントとなる個所を抜粋した。

HINSTANCE hInstance = GetModuleHandleA(null);

// アイコンハンドルを取得する。リソーススクリプトで定義したIDをここに指定する。
HICON hIcon = LoadIconA(hInstance, MAKEINTRESOURCEA(101));

// あとは、ウインドウ生成のパラメータ(hIcon)に設定する。
WNDCLASSEXA wc;
wc.hIcon = hIcon;
/* ... ほかのパラメータ色々 ... */

auto atom = RegisterClassExA(&wc);

/* ... ウインドウを表示する処理などが続く ... */

Win32APIでウインドウを表示する詳しい方法については今回は割愛…

出来上がりの例

上手くいけば、こんな風にアイコンが表示される。やっぱりアイコンがあると箔が付くなあ。

左上にアイコンが付いたマップエディタ画面

タスクバーはこうなってる。
アイコンは16x16pxと32x32pxの2種類を用意したが、タスクバーでは後者のほうが使われているようだ。

アイコンが付いたタスクバー

また、マップデータファイルをマップエディタに関連付けているので、エクスプローラ上のファイルにもアイコンが適用されていい感じだ。

ファイルにアイコンが付いたエクスプローラ

今回得たノウハウは、今後ゲーム本体にアイコンやバージョン情報を付与する際にも役立つ。

収穫がたくさんあってよかった。

参考文献