Hatena::ブログ(Diary)

Luoyang Press RSSフィード

2010-11-28

[][]Link to msvcrt.dll.

VS2003あたり以降では、CRTとしてmsvcrXXX.dllという名前のDLLが用いられていますが、VSのバージョンごとにファイルが変わることもあり、ソフトウェアの利用者としては迷惑な話です。もちろん、以前は同じmsvcrt.dllでも版毎に実装されている関数が異なるということもあったりしたわけで、それはそれで困るのですけど、やはり別途DLLを要するのはできるだけ避けたい感じです。以前、あるDLL不要を謳うアーカイバで、開発環境の変更に伴ってCRTのインストールが必要になり、少し前にそれでかなり批判されたようですが…(まあ、作る側としては動けば何でもいいはずなので、私は別にそれを批判する気はないのですけど)。

まあ、要するに追加する必要のあるDLLは少ない方がいいですよね、ということで、VS2008でmsvcrt.dllにリンクした実行ファイルを作る方法を紹介します。といっても、「丁稚な日々」というサイトの内容を参考にして、wxをコンパイルするにはどうすればよいか、ということだけなのですけど。

で、とりあえず手順です。

  1. VS2008をインストール(当たり前ですね(^^;)。
  2. Windows Driver Kitダウンロードします。
  3. WDKを普通にインストールしてもよいのですが、ISOの中にある次のファイルを展開します。cabをそのまま展開しても無秩序な状態になってしまうので、id:ratbeta:20100928あたりをご覧になっていただき、MSIを管理者モードで実行すればOKです。
    • WDK\libs_x86fre_cab001.cab
    • WDK\libs_x86fre.msi
    • WDK\headers_cab001.cab
    • WDK\headers.msi
  4. さて、WinDDKから得られたディレクトリをWinDDK\incとWinDDK\libとします。incの中にapi,crt,ddkなどのディレクトリが、libの中にはCrtディレクトリとwin7ディレクトリがある状態です。VS2008の設定のインクルードパスの先頭にWinDDK\inc\crt,WinDDK\inc\api,WinDDK\inc\api\crt\stl60*1を、ライブラリの検索パスの先頭にWinDDK\lib\Crt\i386,WinDDK\lib\win7\i386を追加します*2

ここまでで普通にビルドするとmsvcrt.dllにリンクされるはずなのですが、残念ながら事はそう単純ではなく、wxなどはそのままではビルドできません。その原因としては、まずmsvcrt.dllにVS2008で使えるすべてのCRT関数が実装されているわけではないということ、そしてDDKのヘッダに不完全な部分があるということが挙げられます。

幸いなことに、(先のように内容を削った)wxでは修正すべき部分はほとんどありません。

修正: _wstati64関数が未定義

これはDDKの方に不備があるようで、wchar.hで_wstati64関数が宣言されていないのが原因のようです。sys/stat.hでは宣言されているので、そちらから宣言をwchar.hにコピーしてあげればOKです。

修正: ftime関数、timeb構造体が未定義

wx/src/common/stopwatch.cppで使用されているftime関数とtimeb構造体が未定義なので、それぞれ_ftime関数と_timeb構造体にリネームします*3

なお、DDKで_ftime関数が宣言されていないと言われる場合があるようです。この場合には、DDKのtime.hでsys/timeb.hを読み込むようにしておけばOKです。

修正: _get_timezone関数が未定義

wx/src/common/time.cppのwxGetTimeZone関数で用いられる_get_timezone関数は、DDKのヘッダではちゃんと定義されているのですが、ライブラリには含まれていないようです。

いくつかのバージョンのコードが用意されているので、先のftimeの修正を施した後、_ftime関数を使うバージョンのコードに書き換えてやるとOKです。

修正: _vswprintf_p関数が未定義

DDKのヘッダでは_vswprintf_p関数が定義されているのですが、DDKのmsvcrt.libと手元のmsvcrt.dllには_vswprintf_p関数が存在しないようなので、どうやらヘッダがおかしいようです*4。wxでは幸いなことに_vswprintf_p関数を自前で実装するソースも含まれているので、wxcrtvararg.hの次の行をコメントアウトすると、自前のものを使うようになります。

          #define wxCRT_VsnprintfW    _vswprintf_p

修正: ロケール関係の関数が未定義

_create_locale関数、wcstod_l関数などは新しい関数のせいか、DDKのヘッダでもmsvcrt.dllでも存在しないようです。これはどうしようもないので、先のsetup.hの編集時にxlocaleを無効にしておけばよいのですけど、もしかすると弊害があるかもしれません。

修正: _wenviron関数が未定義

wx/src/common/utilscmn.cppで用いられているグローバル変数_tenvironの実体は_wenvironですが、この変数はDDKのヘッダではちゃんと定義されているのですが、ライブラリには含まれていません。

代わりに_get_wenviron関数というものが存在するので、この関数を用いるようにコードを変更すれば解決します。

修正: マクロ再定義の警告

警告なので放置しておいて問題ないのですが、_CRT_SECURE_NO_DEPRECATEを再定義するという警告が気になる方は、DDK/inc/crt/crtdefs.hで次のdefineをコメントアウトすれば警告が抑制されます。

#ifndef _CRT_SECURE_FORCE_DEPRECATE
#define _CRT_SECURE_NO_DEPRECATE
#endif

修正: 過去のmsvcrt.dllとの互換性

このままでコンパイルやリンクを行っても無事に動くバイナリが出来上がるのですが、このバイナリはWindows Vista以降でないと動作しません。XPなどでは「msvcrt.dllに__except_handler4_common関数がない」とエラーが出てしまい、起動できません。

このエラーが出る要因は私が知っているうちでは二つあるようですが、今回の場合はWinDDKで要求されているバージョンより古いmsvcrt.dllしかシステムに存在しなかった、という至って素朴なものです(「Dynamically linking with MSVCRT.DLL using Visual C++ 2005 - KK’s Blog」より)。WinDDKにはそういう場合のために互換パッチともいうべきmsvcrt_win2000.obj,msvcrt_winxp.obj,msvcrt_win2003.objが用意されており、例えばmsvcrt_winxp.objをリンクさせると__except_handler4_common関数などを呼ばなくなり、XP以降のOSで動作するようになります。但し、これらのファイルは本当にただのパッチなので、リンクさせるとバイナリのサイズが若干増加します。古い環境を考慮する必要がないならリンクしなくても良いでしょう。


以上のように修正すると、無事にTPIとmsvcrXXX.dllの縁が切れます(^^;

とはいえ、特にロケールの関係で動作がおかしくなるなどの弊害がある可能性があるので、実際にはまだ暫く様子見、といったところでしょうか。

なお、ここの内容については特にライセンス的に問題ないはずだと思うのですが、もし問題があるようであればお知らせいただければ幸いです。

2011/01/13追記

Windows Vista以前のOSで動作するバイナリを作成する方法と、RTTIを無効化することでwxを軽量化する方法を追記しました。

*1:70だとなぜかエラーがてんこ盛りになります(^^;

*2:いずれも先頭に入れるのが重要です。そうでないとVSの標準のものが使われてしまいます。

*3:stopwatchは無効にしているはずなのに、なぜかコンパイルされます(^^;

*4:というより、実際は_vsprintf_pや_vswprintf_p_lはあるのに_vswprintf_pだけがないmsvcrt.dllの方がおかしい気がしますが…。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

リンク元