トップ回答者
複数プロセスから使われるdllで、初期化と後始末処理を1回だけ行うコードを書くには?

質問
-
タイトルの質問をさせて頂きたく、投稿を致しました。↓が詳細です。どうぞ宜しくお願い致します。
【状況】
- a.dll を作り その各機能は、複数のプロセスから使われる
- a.dll には初期化/後始末処理があり、 「 a.dll が 初めてメモリへロードされた時のみ1回だけ 」 、a.dll側のコードの中で行いたい
↑ の実現にまず思いついた処理方法は ↓ です。
- a.dll に共有メモリ( 例 メモリマップドファイル )を用意し、そこに参照カウンタ変数を設置。初期値は0
- DllMain()がDLL_PROCESS_ATTACHを検出時、↑のカウンタは++する。それが1の時のみ 初期化処理をする。
- DllMain()がDLL_PROCESS_DETACHを検出時、↑のカウンタを--する。それが0の時のみ、後始末処理をする。
が、「 より単純な方法が既存では? 」と思い、相談させて頂いた次第です。特に、共有メモリでなく他の機構( より単純なコードで済む方法 )があるかもしれない、と思っています。
- 編集済み ぶた 2016年3月29日 3:19
回答
-
一度だけの検出は、名前付きMutexあたりの名前付きオブジェクトを作ってGetLastErrorあたりでチェックすればよろしいでしょう。
が、そもそもDllMainはLoader Lockを取ってから呼ばれるわけで、あまり複雑な初期化処理をやるとトラブルの元なので、ご留意ください。
(CRTは使えない、Threadは起こせない、Kernel32以外のDLLの関数も呼べない、ロックも待てない。
TerminateProcessで殺されたときDLL_PROCESS_DETACHが飛んでこない、などなど)https://msdn.microsoft.com/ja-jp/library/windows/desktop/ms682583%28v=vs.85%29.aspx
jzkey
- 回答としてマーク ぶた 2016年3月30日 1:04
すべての返信
-
一度だけの検出は、名前付きMutexあたりの名前付きオブジェクトを作ってGetLastErrorあたりでチェックすればよろしいでしょう。
が、そもそもDllMainはLoader Lockを取ってから呼ばれるわけで、あまり複雑な初期化処理をやるとトラブルの元なので、ご留意ください。
(CRTは使えない、Threadは起こせない、Kernel32以外のDLLの関数も呼べない、ロックも待てない。
TerminateProcessで殺されたときDLL_PROCESS_DETACHが飛んでこない、などなど)https://msdn.microsoft.com/ja-jp/library/windows/desktop/ms682583%28v=vs.85%29.aspx
jzkey
- 回答としてマーク ぶた 2016年3月30日 1:04
-
うっかりしてました。jzkeyさんのおっしゃる通りです。
DllMain()関数の説明にもありますが、複雑な初期化処理が必要な場合は、
当該処理用のエクスポート関数を用意して、それを実行することを義務付けるのが、
安全で実用的な方法です。
https://msdn.microsoft.com/ja-jp/library/cc429094.aspx
の「警告」部分ですね。 -
ご返信、誠に有難うございました。DllMain()の中に独自コードを書く事は 特別な理由が無い限り止めます。
DllMain()内で行う予定だった処理は、そのDLLを使うプロセスに呼ばせる仕様にします。
「 DllMain()が呼ばれた段階では、kernel32.dll以外のDLL( OSシステムコール や C/C++標準ライブラリ等 )が使える状態になっている保証はない 」、という事を、今回初めて知った事と、下記の理由のためです。
MSDN内の説明「 TLS、オブジェクト作成、ファイル関数以外の Win32 関数を呼び出すと、診断しにくい問題が発生する可能性があります 」で、"オブジェクト" が具体的に何か不明なため、やって良い事と悪い事も不明。不明な事に手を出せない。
手を出せたとしても、kernel32.dllしか使えないなら、「 DllMain() では何もすべきでない 」という独自規定を設けた方が、無価値な問題を招かずに済みそう。
- 編集済み ぶた 2016年3月29日 3:18
-
独自の初期化・解放関数を呼ばせる方式を採用されるとのことですので、一つアイデアを。
共有メモリですが、実は簡単に作れます。#pragma section及び__declspec(allocate)を使って
#pragma section("shared-data", read, write, shared) __declspec(allocate("shared-data")) static volatile int counter = 0;
と記述しますと、変数counterは共有メモリ上に配置されます。(外部プロセスからの書き換えを前提としているためvolatile修飾するとよいでしょう。)