【问题标题】:Problem loading C++ DLL from within visual studio test environment从 Visual Studio 测试环境中加载 C++ DLL 时出现问题
【发布时间】:2011-04-04 18:46:12
【问题描述】:

所以我一直在尝试使用 Visual Studio 测试环境在我的解决方案上设置测试。设置如下所示:

Third party native library --> Managed C++ wrapper DLL --> C# project being tested
                                        ^                        ^
Foundation C# Projects -----------------|------------------------|

有两个理论上可互换的第三方库和相应的包装器。我需要能够同时支持两者。

所以,当我运行我的测试项目时,会发生以下情况:

  1. 被测项目加载成功
  2. 基础 C# 项目加载成功
  3. C++ 包装 A 加载失败;如果我使用包装器 B,一切都很好。

包装器 A 的异常调用栈是:

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  

我确实在网上搜索了这个,发现很多人都在询问同一个调用堆栈,但响应方式却很少。

我查看了 ProcMon 的情况。我比较了加载包装器 A 和包装器 B 时发生的情况。在包装器 B 完成加载之前,情况似乎或多或少相同。同时,包装器 A 莫名其妙地重新开始加载,这次尝试加载测试项目已经加载的 C# 基础 DLL。但它没有找到这些 DLL,因为 DLL 搜索路径不包含测试文件夹(它在 GAC 和 VSTestHost 目录中查找 DLL)。

我注意到的另一件事是 C# 代码没有在默认的 appdomain 中运行。所以我的理论是以下情况正在发生:

  1. 包装器 A 尝试在沙盒应用程序域中加载。
  2. 发生了什么事让系统认为 Wrapper A 没有成功加载
  3. 系统尝试在默认应用程序域中加载 Wrapper A。在此 appdomain 中,未加载 C# DLL,因此它尝试加载但失败。

那么,什么给了?第 2 步可能会发生什么?包装器 B 会成功加载而包装器 A 不会加载的可能原因有哪些?

如果您想知道包装器 A 和包装器 B 有何不同:它们的项目具有相同的设置,并且支持相同的接口;否则它们是完全不同的。但我想知道可能导致这种情况的差异。

我应该补充一点,如果我将 DLL 添加到 GAC 或将它们复制到 VSTestHost 目录,包装器 A 会找到 DLL 并成功加载。但是,我仍然想了解发生了什么问题。

【问题讨论】:

  • 你的 Wrapper A 在做什么?它是否以原生 C++ 代码的形式开始生命,然后开始运行托管代码?

标签: c# .net visual-studio-2008 dll vstesthost


【解决方案1】:

也许您的 Wrapper A 正在创建一个本机线程,该线程稍后会开始运行托管代码。如果是这样,那就试试this API:

msclr::call_in_appdomain(appDomainId, func, object);

这允许您指定线程 func 应该在哪个 AppDomain 中运行。否则,当本机线程尝试运行托管代码时,线程使用进程认为默认 AppDomain 的任何内容,这将解释 #1和你的问题中的#3 点。因此,如果您的代码卡在错误的 AppDomain 中,它将找不到任何依赖的 DLL/程序集,因为当前工作目录可能与您想要的不同。

编辑:运行 ProcessMonitor 并设置一个过滤器以监视 Wrapper A 及其所有依赖项,这将有助于确保在您运行测试项目时所有这些都实际加载。

【讨论】:

  • DLL 如何在加载时执行代码?我知道有这样的东西是 DllMain,但有问题的 DLL 没有。
  • 我在related thread 中找到了一条评论:“我们发现这是由于我们的 C++ 代码中使用其他 DLL 中定义的函数的静态表的初始化引起的。其中一些函数当时正在在为该 DLL 初始化 C 运行时之前调用...一旦我们将这些表的初始化更改为我们控制的顺序,错误就会消失。”这会敲响警钟吗?我怎么能这样做?
  • 我知道如何做到这一点的唯一方法是如果您拥有本机库的源代码。在这种情况下,您可以控制创建静态对象的方式/时间。例如,人们认为要避免的是在构造函数中做任何事情。你必须确保静态初始化做的工作少得多,或者使用惰性初始化,或者干脆别的。
猜你喜欢
  • 1970-01-01
  • 2012-03-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-21
  • 1970-01-01
  • 2017-01-07
  • 1970-01-01
相关资源
最近更新 更多