Add-In / Plug-In の強さと脆さ (2)

ちなみに,Add-In / Plug-In が難しいのはなにも .NET だけの問題ではありません.例えば DLL に関する Windows の状況は,かなり深い病巣を抱えていますが,多くの人々は単に「動けば正義」と目を瞑って,あるいは何も知らずに使い続けているだけです.
一例として,以前 CodeZine の記事で紹介した _beginthreadex の問題を再度取り上げてみましょう.
多くの C++ プログラマは,「Win32 環境でスレッドを作るときは,CreateThread API の代わりに _beginthreadex を使わないとメモリリークすることがある」という注意を聞いたことがあるかと思います.Jeffrey Richter の名著『Advanced Windows』でも取り上げられている,比較的有名なトピックです.では「_beginthreadex を使っても CRT のスレッド管理領域がリークすることがあるのか?」についてはどうでしょうか.実はそれが起こりうるということを,CodeZine の『コラム4 マルチスレッドCRT』にて指摘しました.これは Advanced Windows でも触れられていないケースです.もう一度見てみましょう.
_beginthreadex は,次の図のように,ユーザの指定したスレッド・プロシージャのあとに,後始末関数を割り込ませることで,CRT のスレッド管理領域を解放しています.

しかし,この _beginthreadex が解放できるのは,この _beginthreadex に対応する CRT が確保した管理ヒープのみであることに注意してください.スレッド・プロシージャが.異なる CRT にリンクした関数を呼び出したときには,この _beginthreadex の仕組みではどうしようもありません.

一方で,_beginthreadex 以外にも CRT のスレッド管理領域の解放に使用できるタイミングがあります.CRT のソースコードを参照すると,以下のようなタイミングで CRT はスレッド管理領域を解放することが分かります.

  • _beginthreadex を利用した後始末関数の割り込み (前述の件)
    • _beginthreadex を提供する CRT のみを対象とした手動解放
    • 他の CRT の面倒はみない(みられない)
  • DllMain の DLL_THREAD_DETACH コールバック
    • スレッドが破棄されるたびにコールバックされる
    • DLL 型の CRT が活用
    • CRT に静的リンクした DLL プロジェクトでも利用される
    • CRT に静的リンクした EXE プロジェクトでは使用できない
  • FlsAlloc のコールバック
    • Windows Server 2003 以降でのみ使用可能
    • CRT に静的リンクした EXE プロジェクトでも利用できる

結局のところ,足りないピースは「スレッドが破棄されるときのコールバックの仕組みが,DLL にのみ提供され,EXE 向けには存在しなかった」というものでした.その根本的な問題が修正されたのがつい最近の Windows Server 2003 だったというのが何とも難しいところです*1.「スレッドを作成するのには _beginthreadex を使えばよい」という思考停止は,残念ながら問題の根本的な解決には役立ちませんでしたし,Windows Server 2003 以降ではその知識は現実とズレ始めてすらいるわけです.
さて,ここまで来れば少し意識も変わったでしょうから,次の問題を考えてみてください.
C++/CLI で作成した exe 型の .NET アセンブリを,別のアプリケーションからロードして安全か?」
答えはもちろん C++ の CRT ソースコードの中にあります.

*1:32-bit 版の Windows XP SP2 でも FlsAlloc は使えません.ただし Windows Server 2003 のコードベースから派生した,Windows XP Professional 64-bit Edition では使用可能です.「今の Windows VistaWindows XP SP2 から派生した」と聞いたけど大丈夫かって? Vista でも普通に FlsAlloc は使えますよ.