【发布时间】:2013-08-02 07:32:39
【问题描述】:
我们有一个大型 .NET 解决方案,其中包含相互引用的 C# 和 C++/CLI 项目。 我们还有几个单元测试项目。我们最近从 Visual Studio 2010 和 .NET 4.0 升级到 Visual Studio 4.5 和 .NET 4.5,现在当我们尝试运行单元测试时,在测试期间加载某些 DLL 似乎出现问题。
问题似乎是因为单元测试是在单独的 AppDomain 上执行的。单元测试过程(例如 nunit-agent.exe)创建了一个新的 AppDomain,其中 AppBase 设置为测试项目的位置,但是根据 Fusion Log,一些 DLL 以 nunit 的可执行文件目录作为 AppBase 而不是 AppDomain 的 AppBase 加载.
我已经设法用一个更简单的场景重现了这个问题,它创建了一个新的 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:C++ 模块在 vtable 初始化期间加载失败。 ---> System.IO.FileNotFoundException:无法加载文件或程序集“CSharpDll,版本=8.80.0.0,文化=中性,PublicKeyToken=null”或其依赖项之一。该系统找不到指定的文件。 在 ?A0xb992d574.??__E??_7CAppletAction@CPlusPlusDll@SomeNamespace@@6B@@@YMXXZ() 在 _initterm_m((fnptr)* pfbegin, (fnptr)* pfend) 在 f:\dd\vctools\crt_bld\self_x86\crt\src\puremsilcode.cpp:line 219 在 .LanguageSupport.InitializeVtables(LanguageSupport* ) 在 f:\dd\vctools\crt_bld\self_x86\crt\src\mstartup.cpp:line 331 在 .LanguageSupport._Initialize(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 --- 内部异常堆栈跟踪结束 --- 在 f:\dd\vctools\crt_bld\self_x86\crt\src\minternal.h:line 194 中的 .ThrowModuleLoadException(String errorMessage, Exception 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 错误代码,IntPtr 错误信息) 在 System.Runtime.InteropServices.Marshal.ThrowExceptionForHR(Int32 错误代码) 在 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() 在 .LanguageSupport.InitializeDefaultAppDomain(LanguageSupport* ) 在 f:\dd\vctools\crt_bld\self_x86\crt\src\mstartup.cpp:line 342 在 .LanguageSupport._Initialize(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 --- 内部异常堆栈跟踪结束 --- 在 f:\dd\vctools\crt_bld\self_x86\crt\src\minternal.h:line 184 中的 .ThrowNestedModuleLoadException(Exception innerException, Exception nestedException) 在 .LanguageSupport.Cleanup(LanguageSupport* , Exception 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 Log 中看到的(我已将 DLL 的名称更改为 SomeDLL.dll 而不是原来的):
*** 装配活页夹日志条目 (8/1/2013 @ 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,Version=8.80.0.0,Culture=neutral,PublicKeyToken=null (完全指定) 日志:Appbase = file:///c:/users/yshany/documents/visual studio 2012/Projects/MyTester/MyTester/bin/Debug/ 日志:初始 PrivatePath = NULL 日志:动态基础 = NULL 日志:缓存基础 = NULL 日志:AppName = MyTester.exe 调用程序集:(未知)。 === LOG:此绑定在默认加载上下文中开始。 日志:使用应用程序配置文件:c:\users\yshany\documents\visual studio 2012\Projects\MyTester\MyTester\bin\Debug\MyTester.exe.Config LOG:使用主机配置文件: LOG:使用 C:\Windows\Microsoft.NET\Framework\v4.0.30319\config\machine.config 中的机器配置文件。 LOG:此时未将策略应用于引用(私有、自定义、部分或基于位置的程序集绑定)。 日志:尝试下载新的 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 是 MyTester.exe 所在的位置,而不是 SomeDLL.dll 所在的位置(与单元测试 dll 的位置相同)。这发生在几个 DLL 中,包括上面例外中提到的两个 DLL。
我还尝试使用更简单的单元测试项目(一个包含 3 个项目的小型 VS2012 解决方案 - 一个引用另一个 C# 项目的 C++/CLI 项目的 C# 项目)来重现问题,但问题没有重现,并且运行良好.正如我之前提到的,在我们升级到 VS2012 和 .NET 4.5 之前,单元测试是可以的。
我能做什么? 谢谢!
【问题讨论】:
-
它只发生在 NUnit-TestRunner 上吗?你也可以用 MSTest 重现它吗?
-
它发生在 NUnit、MSTest 以及我在这里编写的 Tester 程序中。
-
这种混淆对我们没有帮助。 “CSharpDll”和“SomeDLL”是什么关系?
-
正如我在这里所写的,“SomeDLL”代表融合日志中出现的各种 DLL,包括 CSharpDll 和 CPlusPlusDll。他们都有相同的错误。我为混淆表示歉意,但我不能写出我正在从事的项目的名称。
-
这似乎在 .NET 4.5.1 中已修复 - 我重新运行了我的测试程序(见下文),它不再抛出异常。
标签: c# unit-testing c++-cli .net-4.5 appdomain