COM相互運用機能は、アセンブリの読み込みのために.NET AppDomainの境界を尊重しますか?
-
04-07-2019 - |
質問
主な問題は次のとおりです。 COM相互運用を使用している.NETアプリケーションがあります別のAppDomain。 COMスタッフは、COMスタッフが呼び出されているAppDomainではなく、デフォルトドメインにアセンブリをロードしているようです。
私が知りたいのは、これは予想される動作ですか、またはこれらのCOM関連アセンブリが間違ったAppDomainにロードされるようにするために何か間違ったことをしていますか?以下の状況の詳細な説明をご覧ください...
アプリケーションは3つのアセンブリで構成されています。 -メインEXE、アプリケーションのエントリポイント。 -インターフェースIControllerのみを含むcommon.dll(IPluginスタイル) -IControllerおよびMarshalByRefObjectを実装するControllerクラスを含むcontroller.dll。このクラスはすべての作業を行い、COM相互運用機能を使用して別のアプリケーションと対話します。
メインEXEの関連部分は次のようになります。
AppDomain controller_domain = AppDomain.CreateDomain("Controller Domain");
IController c = (IController)controller_domain.CreateInstanceFromAndUnwrap("controller.dll", "MyNamespace.Controller");
result = c.Run();
AppDomain.Unload(controller_domain);
common.dllには、次の2つのもののみが含まれます。
public enum ControllerRunResult{FatalError, Finished, NonFatalError, NotRun}
public interface IController
{
ControllerRunResult Run();
}
また、controller.dllにはこのクラスが含まれています(COM相互運用機能も呼び出します):
public class Controller: IController, MarshalByRefObject
最初にアプリケーションを実行すると、Assembly.GetAssemblies()は両方のAppDomainにcommon.dllがロードされ、controller.dllがコントローラードメインにのみロードされるため、期待どおりに見えます。ただし、c.Run()を呼び出した後、COM相互運用機能に関連するアセンブリは、COM相互運用機能が実行されるAppDomainではなく、デフォルトのAppDomainにロードされていることがわかります。
なぜこれが発生しているのでしょうか?
そしてもし興味があるなら、ここにちょっとした背景があります:
もともとこれは1つのAppDomainアプリケーションでした。インターフェースとなるCOMスタッフは、長期間使用しても安定しないサーバーAPIです。 COMからCOMException(その原因に関する有用な診断情報がない)が発生すると、COM接続が再び機能する前に、アプリケーション全体を再起動する必要があります。 COMアプリサーバーに再接続するだけで、すぐにCOM例外が再び発生します。これに対処するために、私はCOM相互運用機能を別のAppDomainに移動して、謎のCOMExceptionが発生したときにAppDomainをアンロードし、新しいAppDomainを作成して、アプリケーションを手動で再起動せずに再起動できるようにしました。とにかくそれは理論でした...
解決
残念ながら、COMドメインはAppDomainのコンテキスト内ではなく、プロセススペース内にロードされます。したがって、ネイティブDLLを手動で破棄(リリースおよびアンロード)する必要があります(COMとP / Invokeの両方に適用されます)。単純にappdomainを破棄するだけでは何の役にも立ちませんが、COM状態をリセットするためにプロセス全体を再生成する必要はないはずです(単にCOMオブジェクトの再作成も正常に機能するはずです。これは、コンポーネントプロバイダーコード内のバグのようです。彼らはそれに対処できますか?)
参照
他のヒント
これは、ショーン・ウィルソンの答えが正しいという証拠です
コントローラーMBRを作成しないでください。 2番目のドメインにコントローラーをロードして起動する小さなプロキシを作成します。この方法では、コントローラーdllは最初のドメインにロードされません。