【问题标题】:VS2010 static linking issueVS2010 静态链接问题
【发布时间】:2011-09-20 05:17:32
【问题描述】:

我公司最近从 VS2005 升级到 VS2010。我们有一个庞大的项目,它使用了很多静态链接到 exe 中的模块。但在 VS2010 中的链接似乎存在一些问题。

为了解释我们的问题,我们构建了一个最小的示例项目,其组成如下图所示:

有一个应用程序使用库 A 中的一个函数。库 A 调用每个库 B 和库 C 的一个函数。这两个库调用库 D 提供的一个函数。

对于 Framework and References 下的 Exe 1,我们将所有内容都设置为 false,除了 Link Library Dependencies 是设置为真。添加的唯一参考是链接到库 A。对于每个库,所有设置都设置为 false。库 A 仅获得对 BC 的引用,而这两个库仅获得对 D 的引用。 库 D 没有引用。

在构建应用程序时,它可以正常工作。应用程序注意到库 A 正在使用库 B,而 C 正在使用库 D,因此它知道它也必须链接这些库。库链接到 exe 没有问题。

现在我们在库 D 中进行一些更改。只是一点点不同,只有一个字母。现在我们尝试再次构建应用程序,它注意到更改并重新编译 library D,但是:它不再链接到它。结果是 library BC 中的链接错误,因为它们使用 library D。我们必须先运行Rebuild,以强制完成构建,然后然后再次链接所有内容。

对于最小示例和我们的主要项目都会发生这种情况。当然,我们可以将每个库添加为 exe 的附加依赖项,但如果它能够像第一次构建项目时那样工作并在代码更改后继续工作,那就太好了。我们注意到,当将 Use Library Dependency Inputs 设置为 true 时,它会再次起作用,但是它不会链接 *.lib 文件而是链接 *.obj 文件当然不是我们想要的。

有没有人有过类似的经历或者有没有人解决这个问题?这是 VS2010 的错误行为吗?

TIA。

p.s.:所有库和可执行文件都是原生 C++。


编辑:(解决方法取自this site

%ProgramsFile%\MSBuild\Microsoft.cpp\v4.0\Microsoft.CPPBuild.Targets文件中有一行

<Target Name="GetResolvedLinkLibs" Returns="@(LibFullPath)" DependsOnTargets="$(CommonBuildOnlyTargets)">

如果您将该行更改为

<Target Name="GetResolvedLinkLibs" Returns="@(LibFullPath)" DependsOnTargets="$(CommonBuildOnlyTargets);ResolvedLinkLib">

链接工作正常,所有需要的库都被隐式链接到。链接器输出不仅显示 lib_a.lib,还显示所有其他链接的库、lib_b、lib_c、lib_d,而无需手动将它们作为依赖项添加到 exe。

这似乎更像是一种解决方法而不是解决方案,也许有一种适当的方法来实现隐式链接。

【问题讨论】:

    标签: c++ visual-studio visual-studio-2010 linker static-linking


    【解决方案1】:

    【讨论】:

    • 感谢您的建议。您的一个链接导致了一个很好的解决方法,但这不是一个正确的解决方案。查看我编辑的帖子。
    【解决方案2】:

    我只知道我遇到过类似的情况是因为错误地使用了同名但具有不同架构的库。可以说,我有一个适用于 x86 的 Dll(我们称之为 mydll.dll),并将其导入到我的项目中它会起作用。如果我对 x64 dll(同名 mydll.dll)做同样的事情,它会起作用。 但是,如果我想同时包含这两个库,则不允许仅将其重命名为 mydllx86.dll / mydllx64.dll。 我现在可以将这两个库都包含到 Visual Studio 中但在编译或重新启动 Visual Studio 时,两个库之一将无法访问

    在这种情况下,查看库架构和使用的命名空间/API 名称可能会有所帮助。

    问候

    【讨论】:

      【解决方案3】:

      如果您正在修改框架和引用,您会在该对话框中迷失方向,这些设置仅适用于托管代码。术语“引用”仅适用于 .NET 程序集。链接器不支持将编译后的托管代码存储在 .lib 中。我将假设您实际上正在更改链接器设置。如果您在库 D 中进行更改,那么您还必须更改其头文件。这本身就足以重建 B 和 C,因为它们的一个或多个源代码文件应该#include 该标头。

      Link Library Dependencies 所做的唯一一件事就是自动使 .lib 成为依赖项,与 Linker + Input、Additional Dependency 设置相同。但是,这需要您显式设置项目依赖项。右键单击 B 项目,单击 Project Dependencies 并勾选 D。对 C 重复。对 A 重复并勾选 B 和 C。对 EXE 重复并勾选 A。

      这很少是你想要的,它使 D 库嵌入到 B 和 C 库中。 B 和 C 嵌入在 A 中。EXE 现在只依赖于 A。它可以工作,但它会使 .lib 文件变得不必要地强大。 而且如果你没有正确设置项目依赖项,你会遇到你描述的问题,重建 D 不会导致 B 和 C 重新链接。

      您应该做的不是在 .lib 之间设置依赖关系,它们没有任何依赖关系。每个都可以在没有其他 .lib 存在的情况下构建。 .lib 只不过是一袋 .obj 文件。所需要的只是使 EXE 项目的所有 4 个 .lib 项目依赖项。这样可以确保在链接器尝试链接 EXE 之前构建它们。

      【讨论】:

      • 感谢您的回答。我们已经尝试过,它会起作用,但这意味着您已经知道哪些库被用于由 exe 使用的库使用的库中使用的库......你明白吗?此外,在我们之前使用的 VS2005 中,可以只为主可执行文件设置一些依赖项,其余必要的库都被隐式链接到 exe 中。
      • .lib 的文档描述需要链接的其他 .lib 是非常常见的。例如,对 Windows .lib 的任何依赖都以这种方式处理。您可以在代码中使用 #pragma comment(lib, "foo.lib") 将其自动化。我描述了将依赖库嵌入到库中需要做的事情。使用 DLL 代替静态库是另一种流行的方法。几乎任何事情都比在代码不变的情况下重建 .lib 更好。
      • 这让我很困惑。起初我以为你只在使用“.net”时在框架和参考中设置任何东西......我认为你想说的是如果你的项目编译成可执行文件,那么你调整框架和参考页面否则,你不要触摸框架和参考页面。对吗?
      • "如果您正在修改框架和参考,您会在该对话框中迷失方向,这些设置仅适用于托管代码。" VS2010 不再适用.请参阅上面的@Bojan 参考资料。
      【解决方案4】:

      在迁移到 Visual Studio 2015(以及 VS2017)后,我们再次开始看到此问题。该问题似乎只存在于以下配置中:

      Project (EXE)
        --> Static Library A (Reference)
          --> Static Library B (Specified in Linker->Additional Dependencies)
            --> Static Library C (Not in solution, specified in Linker->Additional Dependencies)
      

      解决方案中有几个其他项目使用 A 和 B。当项目 B 或 C 中的某些内容发生更改时,其中许多项目都会发出 LNK4099 警告。 在我们的例子中,解决方案是使用以下内容:

      代码生成 > 输出文件 > 程序数据库文件名:$(TargetDir)$(TargetName).pdb

      图书管理员 > 常规 > 输出文件:$(TargetDir)$(TargetName)$(TargetExt)

      $(TargetDir) 使用绝对路径,而默认的 $(OutDir) 在我们的例子中是相对的。似乎正确的路径会因多级间接而丢失。

      一个有趣的事情是,如果您将编译线程数切换为 1,它似乎不会发生(可能是 Visual Studio 中的某种竞争条件?)。

      【讨论】:

        猜你喜欢
        • 2012-07-26
        • 2019-12-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-06-13
        • 2012-01-24
        • 2012-05-19
        • 2013-04-17
        相关资源
        最近更新 更多