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

  • 質問

  • タイトルの質問をさせて頂きたく、投稿を致しました。↓が詳細です。どうぞ宜しくお願い致します。

    【状況】

    • 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
    2016年3月24日 13:26

回答

  • 一度だけの検出は、名前付き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
    2016年3月25日 2:40

すべての返信

  • そのやり方で正しいです。
    メモリマップドファイル以外で、
    「そのPCに唯一のフラグ」を用意するには

     1.ミューテックス等システム内での唯一性が保障された資源
     2.レジストリ上にエントリされた資源
     3.ディスク上のファイル

    等が考えられます。
    制御対象が単純なフラグの場合、1.ミューテックスを使用するケースが多いようです。
    2016年3月25日 1:20
  • 一度だけの検出は、名前付き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
    2016年3月25日 2:40
  • うっかりしてました。jzkeyさんのおっしゃる通りです。
    DllMain()関数の説明にもありますが、複雑な初期化処理が必要な場合は、
    当該処理用のエクスポート関数を用意して、それを実行することを義務付けるのが、
    安全で実用的な方法です。
    https://msdn.microsoft.com/ja-jp/library/cc429094.aspx
    の「警告」部分ですね。

    2016年3月25日 4:00
  • ご返信、誠に有難うございました。DllMain()の中に独自コードを書く事は 特別な理由が無い限り止めます。
    DllMain()内で行う予定だった処理は、そのDLLを使うプロセスに呼ばせる仕様にします。

    「 DllMain()が呼ばれた段階では、kernel32.dll以外のDLL( OSシステムコール や C/C++標準ライブラリ等 )が使える状態になっている保証はない 」、という事を、今回初めて知った事と、下記の理由のためです。

    MSDN内の説明「 TLS、オブジェクト作成、ファイル関数以外の Win32 関数を呼び出すと、診断しにくい問題が発生する可能性があります 」で、"オブジェクト" が具体的に何か不明なため、やって良い事と悪い事も不明。不明な事に手を出せない。

    手を出せたとしても、kernel32.dllしか使えないなら、「 DllMain() では何もすべきでない 」という独自規定を設けた方が、無価値な問題を招かずに済みそう。





    • 編集済み ぶた 2016年3月29日 3:18
    2016年3月27日 4:30
  • 独自の初期化・解放関数を呼ばせる方式を採用されるとのことですので、一つアイデアを。

    共有メモリですが、実は簡単に作れます。#pragma section及び__declspec(allocate)を使って

    #pragma section("shared-data", read, write, shared)
    __declspec(allocate("shared-data"))
    static volatile int counter = 0;
    

    と記述しますと、変数counterは共有メモリ上に配置されます。(外部プロセスからの書き換えを前提としているためvolatile修飾するとよいでしょう。)

    2016年3月27日 12:41
  • https://msdn.microsoft.com/ja-jp/library/5bkb2w6t.aspx ですね。

    https://msdn.microsoft.com/ja-jp/library/h90dkhs0(v=vs.90).aspx の方法しか知らなかった為、ためになりました。有難うございました。

    2016年3月30日 1:08