【问题标题】:C# to C++/CLI to C DLL System.IO.FileNotFoundExceptionC# 到 C++/CLI 到 C DLL System.IO.FileNotFoundException
【发布时间】:2009-03-15 04:39:12
【问题描述】:

我在运行调用 C++/CLI 程序集的 C# 代码时收到 System.IO.FileNotFoundException: The specified module could not be found,而后者又调用纯 C DLL。只要实例化一个调用纯 C DLL 函数的对象,就会发生这种情况。

BackingStore 是纯 C 语言。 CPPDemoViewModel 是调用 BackingStore 的 C++/CLI,它具有对 BackingStore 的引用。

我尝试了最简单的情况 - 添加一个新的 C# 单元测试项目,它只是尝试创建一个在 CPPDemoViewModel 中定义的对象。我从 C# 项目中添加了对 CPPDemoViewModel 的引用。

一个 C++/CLI 测试项目只需添加对 CPPDemoViewModel 的引用就可以正常工作,因此它需要在语言之间进行切换。

我正在使用带有 .Net 3.5 SP1 的 Visual Studio 2008 SP1。我在 Vista x64 上构建,但一直小心确保我的平台目标设置为 x86。

这感觉像是我错过了一些愚蠢和明显的事情,但浪费时间试图私下解决它会更愚蠢,所以我在这里让自己尴尬!

这是对移植大量遗留 C 代码的项目的测试,我将这些代码保存在 DLL 中,并使用 C++/CLI 实现的 ViewModel。

编辑 检查目录后,我可以确认BackingStore.dll没有被复制。

我有使用典型多项目解决方案创建的标准唯一项目文件夹。

WPFViewModelInCPP 后台存储 CPPViewModel CPPViewModelTestInCS 斌 调试 调试

令我惊讶的是,更高级别的 Debug 似乎是 C 和 C++/CLI 项目使用的公共文件夹。

WPFViewModelInCPP\Debug 包含 BackingStore.dll、CPPDemoViewModel.dll、CPPViewModelTest.dll 及其关联的 .ilk 和 .pdb 文件

WPFViewModelInCPP\CPPViewModelTestInCS\bin\Debug 包含 CPPDemoViewModel 和 CPPViewModelTestInCS .dll 和 .pdb 文件,但不 BackingStore。但是,手动将 BackingStore 复制到该目录并不能修复错误。

CPPDemoViewModel 具有属性 Copy Local 集,我假设它负责在引用 if 时复制其 DLL。我无法将 C# 项目的引用添加到纯 C DLL - 它只是说 无法添加对 Backing Store 的引用。

我不确定我是否只有一两个问题。

我可以使用老式的复制构建步骤将 BackingStore.dll 复制到任何给定 C# 项目的目录中,尽管我希望新的 .net 模型不需要这样做。

DependencyWalker 告诉我丢失的文件是 GPSVC.dll,has been suggested 表示安全设置问题。我怀疑这是一个红鲱鱼。

edit2 通过将 BackingStore.dll 的手动副本与可执行文件相邻,GUI 现在可以正常工作。 C# 测试项目仍然存在问题,我怀疑这是由于测试项目的运行时环境造成的,但我现在可以不用它。

【问题讨论】:

    标签: c# build-process c++-cli unmanaged


    【解决方案1】:

    C 和 C++ DLL 是否与正在执行的 C# 程序集位于同一目录中?

    您可能必须更改您的项目输出设置,以便 C# 程序集和其他 DLL 最终都位于同一个文件夹中。

    在这种情况下,我经常使用Dependency Walker;这是一个健全性检查,表明实际上可以找到所有依赖项。

    在您的应用运行后,您可能还想在您正在运行的代码上试用Process Monitor,以查看引用了哪些 DLL 以及它们的位置。

    【讨论】:

    • 我没有想过在 .Net 上使用好旧的depends.exe(我是老派的 Win32/MFC 和其他 C++ 人)。好点子。它确认了丢失的 DLL。
    【解决方案2】:

    除了更改输出设置之外,GUI 的答案是添加了预构建步骤

    copy $(ProjectDir)..\Debug\BackingStore.* $(TargetDir)
    

    测试项目的答案是将缺少的 DLL 添加到 testrunco​​nfig 的“部署”选项卡中。您可以通过直接编辑默认的 LocalTestRun.testrunco​​nfig(出现在解决方案项下的解决方案中)或右键单击解决方案并添加新的测试运行配置,然后将出现在主测试下菜单。

    感谢this SO question 上关于测试配置的回答,引导我找到答案。

    【讨论】:

    • 是的,我知道将自己的答案标记为已接受很厚颜无耻,但没有其他人接近测试的真实答案,这需要花费一个小时的挖掘。我还是惊呆了,居然要用这么老套的技术来复制DLL!
    • 安迪,你还是应该检查一下我提出的观点,我感觉你在初始化顺序/时间上很幸运,未来可能会因为握紧而变得不幸运在这个上。
    【解决方案3】:

    发生这种情况的原因是因为您要么在 CRT 有机会初始化之前从托管代码加载 DLLMAIN。您可能没有任何托管代码,会在 DllMain 通知的影响下直接或间接执行。 (请参阅:Expert C++/CLI:面向 Visual C++ 程序员的 .Net,第 11 章++)。

    或者您没有定义任何本地入口点,但您已链接到 MSVCRT。 CLR 会使用 /clr 自动为您初始化,这个细节会引起很多混乱,必须考虑在内。混合模式 DLL 实际上延迟加载 CLR,方法是使用热修补类中的所有托管入口点 vtable。

    围绕这个主题有许多类初始化问题,加载器锁定和延迟加载 CLR 有时有点棘手。尝试声明 global 的 static 并且不要使用 #pragma managed/unmanaged,使用 /clr per-file 隔离您的代码。

    如果您无法将您的代码与托管代码隔离开来,并且遇到了问题(在采取了其中一些步骤之后),您还可以考虑自己托管 CLR,或许还可以通过创建域管理器的努力,这将确保您完全“在循环”运行时事件和引导。

    这正是为什么,它与您的搜索路径或初始化无关。不幸的是,Fusion 日志查看器并没有太大帮助(这是查找 .NET CLR 程序集绑定问题而不是依赖遍历器的常用位置)。

    静态链接也与此无关。您可以不静态链接混合模式的 C++/CLI 应用程序。

    1. 将您的 DLLMAIN 函数单独放入一个文件中。
    2. 确保此文件不在构建选项(文件构建选项)中设置了/CLR
    3. 确保您与 /MD 或 /MDd 的链接,以及您链接的所有依赖项使用完全相同的 CRT。
    4. 评估 /DEFAULTLIB 和 /INCLUDE 的链接器设置以确定任何可能的引用问题,您可以在代码中声明原型并使用 /INCLUDE 覆盖默认库链接解析。

    祝你好运,也看看那本书,它非常好。

    【讨论】:

    • 感谢您提出这些问题 - 我们已认真对待这些问题,并将其添加到我们的代码审核任务中
    • 关于不设置 /CLR 的第 2 点帮助了我。谢谢。
    【解决方案4】:

    确保目标系统具有正确的 MS Visual C 运行时,并且您不会意外使用调试运行时构建 C dll。

    【讨论】:

    • 这是否表示不应使用 Debug 目标构建本机 C++ DLL? “我确定”到底是什么?我有一个非常相似的情况(C# 调用 C++/CLI DLL 调用本机 C++ DLL 来合并遗留代码)& 我想避免这里发现的任何陷阱。
    • @richp:IIRC,依赖于 msvcrtd.dll。 Depends.exe 应该确认。
    【解决方案5】:

    这是一个有趣的困境。在从 C# 调用本地 .DLL 之后,我从未听说过从 C++/CLI 加载本机 .DLL 的问题。我只能假设问题是 @Daniel L 建议的那样,并且您的 .DLL 根本不在程序集加载器可以找到的路径中。

    如果 Daniel 的建议不起作用,我建议您尝试将本地 C 代码静态链接到 C++/CLI 程序,如果可以的话。这肯定会解决问题,因为 .DLL 将被完全吸收到 C++/CLI .DLL 中。

    【讨论】:

    • 静态链接是个好主意,但不可能——出于安全原因,代码必须保留在纯 C DLL 中,因为它是古老的 K&R C,无法转换为 ANSI(讨厌的数学函数)
    【解决方案6】:

    切换到 64 位 Vista 时遇到同样的问题。我们的应用程序正在调用 Win32 DLL,这混淆了应用程序的目标构建。为了解决它,我们执行了以下操作:

    1. 转到项目属性;
    2. 选择构建选项卡;
    3. 将“平台目标:”选项更改为 x86;
    4. 重建应用程序。

    当我重新运行应用程序时,它可以工作了。

    【讨论】:

      猜你喜欢
      • 2013-07-25
      • 1970-01-01
      • 2011-08-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-16
      相关资源
      最近更新 更多