So I've been trying set up tests on my solution using the visual studio test environment. The setup looks like this:
Third party native library --> Managed C++ wrapper DLL --> C# project being tested
^ ^
Foundation C# Projects -----------------|------------------------|
There are two theoretically开发者_JS百科 interchangeable third party libraries with corresponding wrappers. I need to be able to support both.
So, when I run my test project, here's what happens:
- The project under test gets loaded successfully
- The foundation C# projects get loaded successfully
- C++ wrapper A fails to load; if I use wrapper B everything is fine.
The exception callstack for wrapper A is:
mscorlib.dll!System.Runtime.InteropServices.Marshal.ThrowExceptionForHR(int errorCode) + 0x23 bytes
msvcm90d.dll!<CrtImplementationDetails>::DoCallBackInDefaultDomain(int* function = 0x1018E1D0, bool cookie = false) Line 451 C++
[Native to Managed Transition]
[Managed to Native Transition]
Wrapper.dll!<CrtImplementationDetails>::DefaultDomain::Initialize() Line 284 C++
Wrapper.dll!<CrtImplementationDetails>::LanguageSupport::InitializeDefaultAppDomain() Line 519 C++
Wrapper.dll!<CrtImplementationDetails>::LanguageSupport::_Initialize() Line 730 C++
Wrapper.dll!<CrtImplementationDetails>::LanguageSupport::Initialize() Line 876 C++
Wrapper.dll!?.cctor@@$$FYMXXZ() Line 922 + 0x9 bytes C++
[Native to Managed Transition]
[Managed to Native Transition]
TestProject.dll!TestProject.TestProjectBase.TestProjectBase() Line 44 + 0x8 bytes C#
[Native to Managed Transition]
[Managed to Native Transition]
TestProject.dll!TestProject.Test.Test() Line 17 + 0x8 bytes C#
[Native to Managed Transition]
[Managed to Native Transition]
Microsoft.VisualStudio.QualityTools.Tips.UnitTest.Adapter.dll!Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestExecuter.CreateTestClassInstance() + 0x174 bytes
I did search for this online and found lots of people asking about the same callstack with very little in the way of responses.
I looked at what's happening with ProcMon. I compared what happens when loading wrapper A and wrapper B. Things appear to be more or less the same until the point where wrapper B finishes loading. At the same point, wrapper A inexplicably starts loading all over again, this time trying to load the C# foundation DLLs that were already loaded by the test project. But it doesn't find these DLLs because the DLL search path doesn't contain the test folder (it looks for the DLLs in the GAC and the VSTestHost directory).
Another thing I noticed is that the C# code is not running in the default appdomain. So my theory is that the following is happening:
- Wrapper A tries to load in the sandboxed appdomain.
- Something happens to make the system think that Wrapper A didn't load successfully
- The system tries to load Wrapper A in the default appdomain. In this appdomain, the C# DLLs aren't loaded, so it tries and fails to load them.
So, what gives? What could possibly be happening at step 2? What are some possible reasons why wrapper B would load successfully and wrapper A wouldn't?
In case you're wondering how wrapper A and wrapper B differ: their projects have the same settings, and they support the same interfaces; otherwise they are completely different. But I would like to know what differences could lead to this situation.
I should add that if I add the DLLs to the GAC or copy them to the VSTestHost directory, wrapper A finds the DLLs and loads successfully. However, I'd still like to understand what's going wrong.
Perhaps your Wrapper A is creating a native thread that later starts running managed code. If so, then try this API:
msclr::call_in_appdomain(appDomainId, func, object);
This allows you to specify which AppDomain that the thread func
should run in. Otherwise when the native thread tries to run managed code, the threads uses whatever the process deems the default AppDomain to be, which would explain the #1 and #3 points in your question. So if your code gets stuck in the wrong AppDomain, it won't find any dependent DLLs/assemblies, since the current working directory will probably be different from what you want.
EDIT: Run ProcessMonitor and setup a filter to watch for Wrapper A and all of its dependencies, this will help make sure that all of these are actually getting loaded when you run your test project.
精彩评论