У нас есть большое .NET-решение с проектами С# и С++/CLI, которые ссылаются друг на друга. У нас также есть несколько проектов тестирования модулей. Недавно мы обновили Visual Studio 2010 и .NET 4.0 до Visual Studio 4.5 и .NET 4.5, и теперь, когда мы пытаемся запустить модульные тесты, по-видимому, возникает проблема с загрузкой некоторых DLL во время теста.
Проблема возникает, потому что модульное тестирование выполняется на отдельном AppDomain. Процесс модульного тестирования (например, nunit-agent.exe) создает новый AppDomain с AppBase, установленным для местоположения тестового проекта, но, согласно журналу Fusion, некоторые библиотеки DLL загружаются с исполняемым каталогом nunit в качестве AppBase вместо AppDomain AppBase.
Мне удалось воспроизвести проблему с более простым сценарием, который создает новый AppDomain и пытается запустить там тест. Вот как это выглядит (я изменил имена классов unit test, методы и расположение 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);
}
}
}
Это исключение, которое я получаю при попытке выполнить unit test. Как вы можете видеть, проблема возникает, когда инициализируется dll С++ и пытается загрузить С# dll (я изменил имена DLL, связанных с CPlusPlusDll и CSharpDll):
System.TypeInitializationException: The type initializer for '' threw an exception. ---> .ModuleLoadExceptionHandlerException: A nested exception occurred after the primary exception that caused the C++ module to fail to load. ---> System.TypeInitializationException: The type initializer for '' threw an exception. ---> .ModuleLoadException: The C++ module failed to load during vtable initialization. ---> System.IO.FileNotFoundException: Could not load file or assembly 'CSharpDll, Version=8.80.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified. at [email protected]@[email protected]@[email protected]@@YMXXZ() at _initterm_m((fnptr)* pfbegin, (fnptr)* pfend) in f:\dd\vctools\crt_bld\self_x86\crt\src\puremsilcode.cpp:line 219 at .LanguageSupport.InitializeVtables(LanguageSupport* ) in f:\dd\vctools\crt_bld\self_x86\crt\src\mstartup.cpp:line 331 at .LanguageSupport._Initialize(LanguageSupport* ) in f:\dd\vctools\crt_bld\self_x86\crt\src\mstartup.cpp:line 491 at .LanguageSupport.Initialize(LanguageSupport* ) in f:\dd\vctools\crt_bld\self_x86\crt\src\mstartup.cpp:line 702 --- End of inner exception stack trace --- at .ThrowModuleLoadException(String errorMessage, Exception innerException) in f:\dd\vctools\crt_bld\self_x86\crt\src\minternal.h:line 194 at .LanguageSupport.Initialize(LanguageSupport* ) in f:\dd\vctools\crt_bld\self_x86\crt\src\mstartup.cpp:line 712 at .cctor() in f:\dd\vctools\crt_bld\self_x86\crt\src\mstartup.cpp:line 754 --- End of inner exception stack trace --- at System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32 errorCode, IntPtr errorInfo) at System.Runtime.InteropServices.Marshal.ThrowExceptionForHR(Int32 errorCode) at .DoCallBackInDefaultDomain(IntPtr function, Void* cookie) in f:\dd\vctools\crt_bld\self_x86\crt\src\minternal.h:line 406 at .DefaultDomain.Initialize() in f:\dd\vctools\crt_bld\self_x86\crt\src\mstartup.cpp:line 277 at .LanguageSupport.InitializeDefaultAppDomain(LanguageSupport* ) in f:\dd\vctools\crt_bld\self_x86\crt\src\mstartup.cpp:line 342 at .LanguageSupport._Initialize(LanguageSupport* ) in f:\dd\vctools\crt_bld\self_x86\crt\src\mstartup.cpp:line 539 at .LanguageSupport.Initialize(LanguageSupport* ) in f:\dd\vctools\crt_bld\self_x86\crt\src\mstartup.cpp:line 702 --- End of inner exception stack trace --- at .ThrowNestedModuleLoadException(Exception innerException, Exception nestedException) in f:\dd\vctools\crt_bld\self_x86\crt\src\minternal.h:line 184 at .LanguageSupport.Cleanup(LanguageSupport* , Exception innerException) in f:\dd\vctools\crt_bld\self_x86\crt\src\mstartup.cpp:line 662 at .LanguageSupport.Initialize(LanguageSupport* ) in f:\dd\vctools\crt_bld\self_x86\crt\src\mstartup.cpp:line 710 at .cctor() in f:\dd\vctools\crt_bld\self_x86\crt\src\mstartup.cpp:line 754 --- End of inner exception stack trace ---
Это то, что я вижу в журнале Fusion (я изменил имя DLL на SomeDLL.dll вместо оригинала):
*** Assembly Binder Log Entry (8/1/2013 @ 01:47:48 PM) *** The operation failed. Bind result: hr = 0x80070002. The system cannot find the file specified. Assembly manager loaded from: C:\Windows\Microsoft.NET\Framework\v4.0.30319\clr.dll Running under executable c:\users\yshany\documents\visual studio 2012\Projects\MyTester\MyTester\bin\Debug\MyTester.exe --- A detailed error log follows. === Pre-bind state information === LOG: User = WF-IL\yshany LOG: DisplayName = SomeDLL, Version=8.80.0.0, Culture=neutral, PublicKeyToken=null (Fully-specified) LOG: Appbase = file:///c:/users/yshany/documents/visual studio 2012/Projects/MyTester/MyTester/bin/Debug/ LOG: Initial PrivatePath = NULL LOG: Dynamic Base = NULL LOG: Cache Base = NULL LOG: AppName = MyTester.exe Calling assembly : (Unknown). === LOG: This bind starts in default load context. LOG: Using application configuration file: c:\users\yshany\documents\visual studio 2012\Projects\MyTester\MyTester\bin\Debug\MyTester.exe.Config LOG: Using host configuration file: LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework\v4.0.30319\config\machine.config. LOG: Policy not being applied to reference at this time (private, custom, partial, or location-based assembly bind). LOG: Attempting download of new URL file:///c:/users/yshany/documents/visual studio 2012/Projects/MyTester/MyTester/bin/Debug/SomeDLL.DLL. LOG: Attempting download of new URL file:///c:/users/yshany/documents/visual studio 2012/Projects/MyTester/MyTester/bin/Debug/SomeDLL/SomeDLL.DLL. LOG: Attempting download of new URL file:///c:/users/yshany/documents/visual studio 2012/Projects/MyTester/MyTester/bin/Debug/SomeDLL.EXE. LOG: Attempting download of new URL file:///c:/users/yshany/documents/visual studio 2012/Projects/MyTester/MyTester/bin/Debug/SomeDLL/SomeDLL.EXE. LOG: All probing URLs attempted and failed.
Как вы можете видеть, проблема в том, что в AppBase находится где находится MyTester.exe, а не где находится SomeDLL.dll(это то же место, что и dll unit test). Это происходит для нескольких DLL, включая обе библиотеки DLL, упомянутые в вышеприведенном исключении.
Я также попытался воспроизвести более простой проект unit test (небольшое решение VS2012 с 3 проектами - проект С#, который ссылается на проект С++/CLI, который ссылается на другой проект С#), но проблема не воспроизводилась, и она работала perfecty. Как я уже упоминал ранее, модульные тесты были одобрены до того, как мы обновились до VS2012 и .NET 4.5.
Что я могу сделать? Спасибо!