DOBON.NET DOBON.NETプログラミング掲示板過去ログ

Unmanaged コードで書かれた DLL から Managed のメソッドをコールバックしたい

環境/言語:[Windows7 C++/CLI .NET Framework3.5]
分類:[.NET]

こんにちわ。
Unmanaged(C++) で書かれた DLL から、Managed(C++/CLI)のメソッドをコールバックで呼び出したいのですが、一部はうまくいき、一部は例外が発生してしまいます。


         callback +---------------------+ callback
         +--------|Native DLL(Unmanaged)|-------+
         |        +---------------------+       |
         |                  ↑callback ptr 取得 |
 +--------------+書 +----------------+ 書+--------------+
 |App A(Managed)|-->|MemoryMappedFile|<--|App B(Managed)|
 +--------------+   +----------------+   +--------------+
                                         
                                         
MemoryMappedFile に、Managed なアプリ(A/B)がそれぞれコールバックポインタを書き込みます。
Native DLL は、それをMemoryMappedFileから取得し、指定の機会にそれぞれをコールバックする。
というような構成です。

(1)App A がNative DLLを起動している。そして、App Aのコールバックメソッドは問題なく呼び出される。
(2)App B は、同じ手順でポインタ登録をしているが、DLLがコールバックしようとした時に
  例外(AccessViolationException)が発生してしまいます。

App B のコールバックメソッドをDLLから問題なく呼び出すには、
どのように解決すればいいのでしょうか?
よろしくお願いいたします。
これだけでは「できると思います」としか書けません。
AppA もたまたま動いているだけかもしれません。
やらないといけないことの何ができていないかを判断するためにも、もう少し具体的な情報を出してみませんか?

一例:
・その関数ポインターはどうやって取得していますか?
・その関数はマネージクラス・構造体の関数でしょうか?
・再現可能なコードの提示、あるいはそれを類推できる断片的なコードを示せないでしょうか?
2012/07/04(Wed) 23:04:45 編集(投稿者)

Azuleanさん
返信ありがとうございます。

    typedef void (__cdecl *CallbackFunc)(void);

    public ref class appA
    {
    public:
        appA()
        {
            callbackDelegate = gcnew cbFunc(this, &appA::Func);

            SetMemMapCallback(    /* MemoryMappedFile にポインタを登録 */
                (CallbackFunc)Marshal::GetFunctionPointerForDelegate(callbackDelegate).ToPointer()
            );
        }

        [UnmanagedFunctionPointerAttribute(CallingConvention::Cdecl)]
        delegate void cbFunc(Void);

        cbFunc^ callbackDelegate;

        void Func(void)
        {
            /* コールバックして来たときのコード */
        }
    };

だいたい、このようなコードです。appA も appB も同じものを使っています。
状況的に違うといえば、appA が DLLを起動している、という事だけです。
まだ情報不足かもしれませんが、よろしくお願いいたします。
App BとNativeDLLは、別プロセスでは?
メモリ空間の異なるプロセス同士で、ポインタに互換はないんじゃないでしょうか。
もし、別プロセスの関数ポインタを呼び出せるなら、私も知りたい。
琥珀さん

そうですね、別プロセスです。
常識的に、不可能なのでしょうか。
浅学で申し訳ないです。
AppA, AppB と書かれているが、コールバックは同一プロセス内に限るという前提で書きます。(つまり、AppA は AppA のコールバックだけ、AppB は AppB のコールバックだけを呼び出す)

GetFunctionPointerForDelegate メソッドで取得した関数ポインターの有効期間は、その関数に渡したデリゲートの有効期間と同程度のはずです。
このため、このデリゲートを保有する、appA クラスのインスタンスが有効に存在していること(GC で回収されないこと)が大前提になります。
AppA, AppB ともにそれは保障していますか?AppA はたまたまセーフになる使い方をしていませんか?
入れ違いで書かれていた。。。

■No30697に返信(Udonさんの記事)
> そうですね、別プロセスです。
> 常識的に、不可能なのでしょうか。

プロセスをまたぐ場合、関数ポインターに限らず、あらゆるポインターはそのまま使えません。(メモリ空間が異なるため)
プロセス間通信としてきちんと作る必要があります。
ありがとうございます。

プロセスが違う時点で、呼び出しは不可能といいますか、同じ方法ではダメと
いうことですね。

プロセス間通信というと、メッセージを投げ、処理する、という感じで考えて
よろしいでしょうか?
■No30700に返信(Udonさんの記事)
> プロセス間通信というと、メッセージを投げ、処理する、という感じで考えて
> よろしいでしょうか?

それでもよいですし、以下のサポート情報で上げられているようなキーワードでもよいと思います。
http://support.microsoft.com/kb/95900/ja

あとは、.NET が用意している WCF 方面なんかも手ですね。
とりあえず、メッセージを投げ、該当クラスのイベントを起動するような
方法でコールバックを実装してみようかと思います。

回答してくださいました皆様、ありがとうございました。
解決済み!

DOBON.NET | プログラミング道 | プログラミング掲示板