是啥导致在单元测试(NUnit 或 MSTest)中从 C# 调用的 C++ 函数与在控制台应用程序中运行的相同代码产生不同的结果?

Posted

技术标签:

【中文标题】是啥导致在单元测试(NUnit 或 MSTest)中从 C# 调用的 C++ 函数与在控制台应用程序中运行的相同代码产生不同的结果?【英文标题】:What is causing a C++ function called from C# in a unit test (NUnit or MSTest) to produce a different result from the same code run in a console app?是什么导致在单元测试(NUnit 或 MSTest)中从 C# 调用的 C++ 函数与在控制台应用程序中运行的相同代码产生不同的结果? 【发布时间】:2012-11-09 19:27:11 【问题描述】:

我正在使用 DllImport 通过 P/Invoke 从 C# 调用 C++ DLL。该 DLL 由第三方生成,并且仅支持 x86(因此我们的 C# 代码也构建为 x86)。

如果我从控制台应用程序调用 DLL 上的某个函数,我总是得到一个正确的结果。该函数采用文件路径并从文件中提取一些信息。

函数的签名是:

private static extern int Function(
    string str1,
    int bool1,
    ref uint outUint1,
    ref uint outUint2,
    ref uint outUint3,
    ref double outDouble1,
    ref double outDouble2,
    StringBuilder outStr1);

而意外的结果出现在ref uint 参数之一中。

如果我创建一个单元测试并调用完全相同的代码(包含所有参数和此类硬编码),我会得到完全不同的结果,这是不正确的。错误的结果总是一样的。我已经使用各种不同的跑步者尝试了 MSTest 和 NUnit 测试,结果相同。

产生正确结果的案例:

控制台应用 Winform 应用程序 运行代码但从单元测试 (NUnit) 启动的控制台应用

产生错误结果的案例:

Resharper Test Runner (NUnit) 的测试运行 从 Visual Studio 测试运行程序 (MSTest) 运行测试 从 NUnit GUI Test Runner 运行测试 从 NUnit 控制台测试运行器测试运行

我的测试环境是 Windows 8,C# 是为 x86 构建的,目标是 .NET 框架 4。

你们中是否有人对导致此问题的原因或我可以做些什么来进一步调试它有任何想法?

我肯定会尝试联系创建 DLL 的第三方,但如果对导致此问题的确切原因有一个很好的了解将大大增加问题得到解决的机会。

【问题讨论】:

好结果和坏结果有何不同?减少一个或很多?是否所有“不正确”的跑步者都会产生相同的错误结果? 所有不正确的跑步者都会产生同样的错误结果。经过进一步研究,我发现错误的结果与将外部库设置为使用不同方法从提供的文件中提取信息时产生的结果相同,因此我怀疑它无法使用主要方法提取信息(这将导致正确的结果)并回退到另一种方法(这会产生不正确的结果)。现在我只需要弄清楚为什么它在从 NUnit 运行时无法使用主要方法。 @phyatt 提到 NUnit 在其自己的沙箱中运行您的代码,该沙箱可能与您的输出目录不同。您可以使用ProcMon 来查看您的 DLL 正在尝试访问哪些文件,这可能会对正在发生的事情有所了解。只是一个想法。 【参考方案1】:

听起来它所期望的“文件路径”可能是相对于工作目录的,或者工作目录甚至应用程序目录对 dll 很重要。

该 dll 也可能尝试引用其目录中的其他 dll,但无法找到它们,因为它是从自己的文件夹加载的,而不是在其目录中运行的 exe。

大约一半的this page描述了dll解析的搜索顺序:

    加载应用程序的目录。

    当前目录。

    系统目录。使用 GetSystemDirectory 函数获取 该目录的路径。

    16 位系统目录。没有函数可以获取 该目录的路径,但会被搜索到。

    Windows 目录。使用 GetWindowsDirectory 函数获取 该目录的路径。

    PATH 环境变量中列出的目录。 请注意,这不包括指定的每个应用程序路径 通过 App Paths 注册表项。 App Paths 键在以下情况下不使用 计算 DLL 搜索路径。

希望对您有所帮助。

编辑:另一个想法...您可以为该 DLL 编写一个包装器,以便它返回正确的值,并在包装​​器中创建一个与您的单元测试器一起使用的接口。

【讨论】:

一个好主意,但文件路径是绝对的。我还尝试在 NUnit 运行器中禁用卷影复制,以确保结果相同。 能否从dll的路径执行NUnit runner?甚至将单元测试可执行文件复制到dll的路径中? 事实证明它正在根进程可执行文件的位置寻找它的依赖项之一(在测试的情况下,这是 NUnit bin 目录)。不知道我们将如何解决这个问题,但至少我们知道它现在是什么。 你能把范围缩小到这个范围真是太棒了。如果您不知道,您在某些圈子中输入了所谓的DLL He**。祝你好运。

以上是关于是啥导致在单元测试(NUnit 或 MSTest)中从 C# 调用的 C++ 函数与在控制台应用程序中运行的相同代码产生不同的结果?的主要内容,如果未能解决你的问题,请参考以下文章

Visual Studio 2013/2015 测试项目模板 - 用于 NUnit?

NUnit3 测试不在 TFS 构建上运行

MSTest Visual Studio 2015:未找到测试容器直接或间接引用的程序集或模块“xxxxxxx”

习题-第3章单元测试

使用Xunit来进行单元测试

如何使用 mstest 增加单元测试运行的全局超时