相互に参照する C# プロジェクトと C++/CLI プロジェクトの両方を含む大規模な .NET ソリューションがあります。また、いくつかの単体テスト プロジェクトもあります。最近、Visual Studio 2010 & .NET 4.0 から Visual Studio 4.5 & .NET 4.5 にアップグレードしましたが、単体テストを実行しようとすると、テスト中に一部の DLL の読み込みに問題があるようです。
この問題は、単体テストが別の AppDomain で実行されるために発生するようです。ユニット テスト プロセス (たとえば、nunit-agent.exe) は、テスト プロジェクトの場所に設定された AppBase を使用して新しい AppDomain を作成しますが、Fusion Log によると、一部の DLL は、AppDomain の AppBase ではなく、AppBase として nunit の実行可能ファイルのディレクトリと共にロードされます。 .
新しい AppDomain を作成し、そこでテストを実行しようとする、より単純なシナリオで問題を再現することができました。これがどのように見えるかです(無実を保護するために、ユニットテストクラス、メソッド、およびdllの場所の名前を変更しました):
class Program
{
static void Main(string[] args)
{
var setup = new AppDomainSetup {
ApplicationBase = "C:\\DirectoryOfMyUnitTestDll\\"
};
AppDomain domain = AppDomain.CreateDomain("MyDomain", null, setup);
ObjectHandle handle = Activator.CreateInstanceFrom(domain, typeof(TestRunner).Assembly.CodeBase, typeof(TestRunner).FullName);
TestRunner runner = (TestRunner)handle.Unwrap();
runner.Run();
AppDomain.Unload(domain);
}
}
public class TestRunner : MarshalByRefObject
{
public void Run()
{
try
{
HtmlTransformerUnitTest test = new HtmlTransformerUnitTest();
test.SetUp();
test.Transform_HttpEquiv_Refresh_Timeout();
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}
これは、単体テストを実行しようとしたときに発生する例外です。ご覧のとおり、C++ dll が初期化され、C# dll をロードしようとすると問題が発生します (関連する DLL の名前を CPlusPlusDll および CSharpDll に変更しました)。
System.TypeInitializationException: '' の型初期化子が例外をスローしました。 ---> .ModuleLoadExceptionHandlerException: ネストされた例外が、C++ モジュールの読み込みに失敗する原因となったプライマリ例外の後に発生しました。 ---> System.TypeInitializationException: '' の型初期化子が例外をスローしました。 ---> .ModuleLoadException: vtable の初期化中に C++ モジュールをロードできませんでした。 ---> System.IO.FileNotFoundException: ファイルまたはアセンブリ 'CSharpDll, Version=8.80.0.0, Culture=neutral, PublicKeyToken=null' またはその依存関係の 1 つを読み込めませんでした。システムは、指定されたファイルを見つけることができません。 ?A0xb992d574.??__E??_7CAppletAction@CPlusPlusDll@SomeNamespace@@6B@@@YMXXZ() で at _initterm_m((fnptr)* pfbegin, (fnptr)* pfend) in f:\dd\vctools\crt_bld\self_x86\crt\src\puremsilcode.cpp:line 219 f:\dd\vctools\crt_bld\self_x86\crt\src\mstartup.cpp:line 331 の .LanguageSupport.InitializeVtables(LanguageSupport*) で f:\dd\vctools\crt_bld\self_x86\crt\src\mstartup.cpp:line 491 の .LanguageSupport._Initialize(LanguageSupport*) で f:\dd\vctools\crt_bld\self_x86\crt\src\mstartup.cpp:line 702 の .LanguageSupport.Initialize(LanguageSupport*) で --- 内部例外スタック トレースの終了 --- f:\dd\vctools\crt_bld\self_x86\crt\src\minternal.h:line 194 の .ThrowModuleLoadException (文字列 errorMessage、例外 innerException) で f:\dd\vctools\crt_bld\self_x86\crt\src\mstartup.cpp:line 712 の .LanguageSupport.Initialize(LanguageSupport*) で f:\dd\vctools\crt_bld\self_x86\crt\src\mstartup.cpp:line 754 の .cctor() で --- 内部例外スタック トレースの終了 --- System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal (Int32 errorCode、IntPtr errorInfo) で System.Runtime.InteropServices.Marshal.ThrowExceptionForHR (Int32 errorCode) で f:\dd\vctools\crt_bld\self_x86\crt\src\minternal.h:line 406 の .DoCallBackInDefaultDomain (IntPtr 関数、Void* cookie) で f:\dd\vctools\crt_bld\self_x86\crt\src\mstartup.cpp:line 277 の .DefaultDomain.Initialize() で f:\dd\vctools\crt_bld\self_x86\crt\src\mstartup.cpp:line 342 の .LanguageSupport.InitializeDefaultAppDomain(LanguageSupport*) で f:\dd\vctools\crt_bld\self_x86\crt\src\mstartup.cpp:line 539 の .LanguageSupport._Initialize(LanguageSupport*) で f:\dd\vctools\crt_bld\self_x86\crt\src\mstartup.cpp:line 702 の .LanguageSupport.Initialize(LanguageSupport*) で --- 内部例外スタック トレースの終了 --- f:\dd\vctools\crt_bld\self_x86\crt\src\minternal.h:line 184 の .ThrowNestedModuleLoadException (例外 innerException、例外 nestedException) で .LanguageSupport.Cleanup (LanguageSupport*、例外 innerException) で f:\dd\vctools\crt_bld\self_x86\crt\src\mstartup.cpp:line 662 f:\dd\vctools\crt_bld\self_x86\crt\src\mstartup.cpp:line 710 の .LanguageSupport.Initialize(LanguageSupport*) で f:\dd\vctools\crt_bld\self_x86\crt\src\mstartup.cpp:line 754 の .cctor() で --- 内部例外スタック トレースの終了 ---
これは、Fusion ログに表示されているものです (DLL の名前を元の名前ではなく SomeDLL.dll に変更しました)。
*** アセンブリ バインダー ログ エントリ (2013 年 8 月 1 日 @ 01:47:48 PM) *** 操作に失敗しました。 バインド結果: hr = 0x80070002。システムは、指定されたファイルを見つけることができません。 アセンブリ マネージャーの読み込み元: C:\Windows\Microsoft.NET\Framework\v4.0.30319\clr.dll 実行可能ファイル c:\users\yshany\documents\visual studio 2012\Projects\MyTester\MyTester\bin\Debug\MyTester.exe の下で実行 --- 詳細なエラー ログが続きます。 === プレバインド状態情報 === ログ: ユーザー = WF-IL\yshany ログ: DisplayName = SomeDLL、バージョン = 8.80.0.0、カルチャ = ニュートラル、PublicKeyToken = null (完全指定) ログ: Appbase = file:///c:/users/yshany/documents/visual studio 2012/Projects/MyTester/MyTester/bin/Debug/ ログ: 初期 PrivatePath = NULL ログ: 動的ベース = NULL ログ: キャッシュ ベース = NULL ログ: AppName = MyTester.exe 呼び出しアセンブリ : (不明)。 === ログ: このバインドはデフォルトのロード コンテキストで開始されます。 ログ: アプリケーション構成ファイルの使用: c:\users\yshany\documents\visual studio 2012\Projects\MyTester\MyTester\bin\Debug\MyTester.exe.Config ログ: ホスト構成ファイルの使用: ログ: C:\Windows\Microsoft.NET\Framework\v4.0.30319\config\machine.config のマシン構成ファイルを使用しています。 ログ: 現時点ではポリシーが参照に適用されていません (プライベート、カスタム、部分、または場所に基づくアセンブリ バインド)。 ログ: 新しい URL ファイルのダウンロードを試みています:///c:/users/yshany/documents/visual studio 2012/Projects/MyTester/MyTester/bin/Debug/SomeDLL.DLL. ログ: 新しい URL ファイルのダウンロードを試みています:///c:/users/yshany/documents/visual studio 2012/Projects/MyTester/MyTester/bin/Debug/SomeDLL/SomeDLL.DLL. ログ: 新しい URL ファイルのダウンロードを試みています:///c:/users/yshany/documents/visual studio 2012/Projects/MyTester/MyTester/bin/Debug/SomeDLL.EXE. ログ: 新しい URL ファイルのダウンロードを試みています:///c:/users/yshany/documents/visual studio 2012/Projects/MyTester/MyTester/bin/Debug/SomeDLL/SomeDLL.EXE. LOG: すべてのプローブ URL が試行され、失敗しました。
ご覧のとおり、問題は、AppBase が SomeDLL.dll が存在する場所 (単体テスト dll と同じ場所) ではなく、MyTester.exe が存在する場所であることです。これは、上記の例外に記載されている両方の DLL を含むいくつかの DLL で発生します。
また、より単純な単体テスト プロジェクト (3 つのプロジェクトを含む小さな VS2012 ソリューション - 別の C# プロジェクトを参照する C++/CLI プロジェクトを参照する C# プロジェクト) で再現しようとしましたが、問題は再現されず、完全に機能しました。前に述べたように、VS2012 & .NET 4.5 にアップグレードする前の単体テストは問題ありませんでした。
私に何ができる?ありがとう!