atexit, _onexit, _onexit_m について

atexit, _onexit, _onexit_m の違いについてのメモ。

atexit 関数
  • 登録した関数がコールバックされるときには、依存する DLL がアンロードされている可能性がある。
  • C++/CLI 上では使用できない*1
  • 関数のプロトタイプ: void (__cdecl *)(void)
_onexit 関数
  • 依存する DLL がアンロードされる前に実行される。
  • DLL の場合 DLL_PROCESS_DETACH を処理した後にコールバックされる。
  • C++/CLI 上では使用できない*2
  • 関数のプロトタイプ: int (__cdecl *)(void)
_onexit_m 関数
  • C++/CLIの _onexit 関数。
  • 関数のプロトタイプ: int (__clrcall *)(void)
まとめ
  • VC++ では、できるだけ _onexit 関数を使うようにする。
  • C++/CLI を使う場合は _onexit_m 関数以外を呼び出さない。
  • VC++ 以外では atexit 関数を使う。
使い分けの例
#include <cstdlib>
#ifdef _MSC_VER
  #include <process.h> // VC++ を使っている場合のみインクルード
#endif

// 終了時にコールバックする関数
void foo() { ... }

#ifdef _MSC_VER
// 戻り値を int にする (キャストして誤魔化しても動くけど、なんか不安なので) 
int foo_wrapper() {
  foo();
  return 0;
}
#endif

// atexit, _onexit, _onexit_m を呼び出す関数
void bar() {
#if defined(_MSC_VER) && defined(__cplusplus_cli)
  _onexit_m(foo_wrapper); // C++/CLI with VC++
#elif defined(_MSC_VER)
  _onexit(foo_wrapper); // C++ with VC++
#else
  atexit(foo); // ANSI 準拠
#endif
}

*1:登録した関数がコールバックされない

*2:登録した関数がコールバックされない