CRT (C Runtime) に非依存とするために・・・

VisualStudio (Visual C++) を使って開発をしている際にちょっと厄介になってくるのが、CRTの選択についてどれにするかを決める点です。これは実行体(アプリケーション)を作っているときにはまだ軽微な話なのですが、ライブラリを作る際にはとても大変な話になってます。

というのも、デバッグ/リリース、そして、CRTのDLLリンク/スタティックリンクの組み合わせ、最近だとそれに 32bit/64bit という組み合わせを考えなくてはなりません。つまり 2x2x2 の8種類存在することになります。ライブラリの提供者になる場合、特にそのライブラリをパッケージとして納品するなどする場合には、これらの組み合わせのライブラリを1セットとする必要があると思います。

しかし!そんな8種類も納品したくない&動作テストしたくもないのが本音です。
ただ利用者にとっては自分のアプリケーションで使うために、一致するライブラリを選択できているのが望ましいです。数が多いと選択に困るかもしれませんが、ライブラリのせいで構成を縛られてしまう、という点だけは避けたいところです。またオープンソース等であればプロジェクト設定を変更して、利用状況に応じてオプションを変えることで対処することも可能です。・・・でも個人的にはこれもやりたくはない作業だと感じています。

ライブラリの個数を減らすことを考えてみる

8種類もあるのが大変なので減らすことを考えてみます。どうしても減らせないのは 32bit/64bit のケースでしょう。そもそもABIが違うのでこれはどうしようもありません。
ランタイムDLLを要求するケースはclr使う際には必要です。つまりC++/CLIを使って C#から使うようなケースでは必須となります。楽にGUIを作れるのでこれは考慮しておく必要があります。ただCランタイムのDLLについて配布先でインストールが必要になります。おなじみの再頒布ランタイムってやつですね。
スタティックリンクを使用する場合、exe単体配布で動くようにできるというのが最大のメリットでしょう。

つまりDLL/スタティックリンクどちらもメリット・デメリットがあります。つまり捨てられない・・・。

これらのランタイムは何を示しているかを考えてみます。
そう、CRT なので、Cランタイムのタイプがどれであるか、を決めているわけです。ということは、Cランタイムを使わない、CRT非依存を実現できればこれらの問題から解放される、と予想できます。
つまり、CRT非依存を実現できれば DLL/スタティックリンク の組み合わせはどちらでもよい(=最終exeをつくるプロジェクトに任せられる)ということになります。

次に Debug/Release の違いも検討してみます。DLL版においては Debug,Release で別々のランタイムDLLに依存することがわかります(Dependency Walker とか使えば簡単です)。でもやっぱり CRTに関係する実装が Debug/Release と切り替わるだけなので、CRTに非依存が実現できればこのDebug/Releaseの差異も気にしなくてよくなりそうです。

ここまでの予想をまとめると、「CRTに非依存にできれば x86用ライブラリと x64用ライブラリの2つを提供するだけでよくなる!」 となります。非常に魅力的です。
・・・現実問題としてデバッグ用ビルドは用意して提供する気がしますが。組み合わせとして4つ程度で済むというのはとても助かることじゃないでしょうか。

具体的にどう開発をするのか

CRT非依存の開発をするにあたってどうすればいいのか、ホントに実現できるのかという点で自分が調べた範囲でのメモをここに残しておこうと思います。

そもそもCRT非依存にして開発できるのか?という問題ですが、結構手間がかかりますが実現不可能ではありません。このときに使ってよいのは Windows API のみとなります。Cの標準関数は使用不可です。当然ですね。標準関数に含まれる機能が欲しいときには、自作 or Windows API で代替を探す、という感じになります。

たとえば、ファイル操作では fopen, fread, fwrite は使えません。代わりに CreateFile, ReadFile, WriteFile を使用します。printf も使えないので、自力で WriteFile にてコンソール用出力先にデータを書き込む方法で対処とかになります。これは結構大変です・・・。

もうちょっと具体的な話

基本的に C言語の範疇でコードを記述します。
またVisualC++のコンパイラが色々とデバッグ時に役立つ機能を埋め込んでくれるのですが、これらについてオフにします。だいたい以下のような設定をプロジェクトに行います。

  • デバッグ情報: プログラムデータベース(/Zi)
  • SDLチェック: いいえ (/sdl-)
  • 最小リビルドを有効にする: いいえ(/Gm-)
  • C++の例外を有効にする: いいえ
  • 基本ランタイムチェック: 規定
  • セキュリティチェック: いいえ(/GS-)
  • ランタイム型情報を有効にする: いいえ(/GR-)
  • インクリメンタルリンクを有効にする: いいえ
  • エントリポイント: (自分の決めたエントリポイント関数)

また ZeroMemory マクロも実際のところはmemset に展開されるので使用禁止ですし、配列や構造体を={0}のように記述して初期化する際にも memset が使用されてしまうようなのでこれも禁止です。

このようにしてアプリケーションを作っていく際には、慣れないうちには特にですが、ビルドしてできたexeをDependencyWalker で調査しつつ作業を進めるのがよいかと思います。そうしないとどこでCRT依存を混入してしまったのか見つけるのが大変です。

シェアする

  • このエントリーをはてなブックマークに追加

フォローする