visual-studio 使用 Visual Studios C++ 清单文件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/588712/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 10:12:47  来源:igfitidea点击:

Working with Visual Studios C++ manifest files

c++visual-studiodllmanifest

提问by Voltaire

I have written some code that makes use of an open source library to do some of the heavy lifting. This work was done in linux, with unit tests and cmake to help with porting it to windows. There is a requirement to have it run on both platforms.

我编写了一些代码,利用开源库来完成一些繁重的工作。这项工作是在 linux 中完成的,通过单元测试和 cmake 来帮助将其移植到 Windows。需要让它在两个平台上运行。

I like Linux and I like cmake and I like that I can get visual studios files automatically generated. As it is now, on windows everything will compile and it will link and it will generate the test executables.

我喜欢 Linux,我喜欢 cmake,我喜欢我可以自动生成 Visual Studios 文件。就像现在一样,在 Windows 上,一切都将编译并链接并生成测试可执行文件。

However, to get to this point I had to fight with windows for several days, learning all about manifest files and redistributable packages.

然而,为了达到这一点,我不得不与 Windows 斗争了几天,学习了所有关于清单文件和可再发行包的知识。

As far as my understanding goes:

就我的理解而言:

With VS 2005, Microsoft created Side By Side dlls. The motivation for this is that before, multiple applications would install different versions of the same dll, causing previously installed and working applications to crash (ie "Dll Hell"). Side by Side dlls fix this, as there is now a "manifest file" appended to each executable/dll that specifies which version should be executed.

在 VS 2005 中,Microsoft 创建了 Side By Side dll。这样做的动机是,以前,多个应用程序会安装同一个 dll 的不同版本,导致以前安装和工作的应用程序崩溃(即“Dll Hell”)。Side by Side dll 解决了这个问题,因为现在有一个“清单文件”附加到每个可执行文件/dll,指定应该执行哪个版本。

This is all well and good. Applications should no longer crash mysteriously. However...

这一切都很好。应用程序不应再神秘地崩溃。然而...

Microsoft seems to release a new set of system dlls with every release of Visual Studios. Also, as I mentioned earlier, I am a developer trying to link to a third party library. Often, these things come distributed as a "precompiled dll". Now, what happens when a precompiled dll compiled with one version of visual studios is linked to an application using another version of visual studios?

Microsoft 似乎在每次发布 Visual Studios 时都会发布一组新的系统 dll。此外,正如我之前提到的,我是一名试图链接到第三方库的开发人员。通常,这些东西是作为“预编译的 dll”分发的。现在,当使用一个版本的 Visual Studios 编译的预编译 dll 链接到使用另一个版本的 Visual Studios 的应用程序时会发生什么?

From what I have read on the internet, bad stuff happens. Luckily, I never got that far - I kept running into the "MSVCR80.dll not found" problem when running the executable and thus began my foray into this whole manifest issue.

从我在互联网上阅读的内容来看,坏事会发生。幸运的是,我从来没有走那么远——我在运行可执行文件时一直遇到“未找到 MSVCR80.dll”的问题,因此我开始涉足整个清单问题。

I finally came to the conclusion that the only way to get this to work (besides statically linking everything) is that all third party libraries must be compiled using the same version of Visual Studios - ie don't use precompiled dlls - download the source, build a new dll and use that instead.

我终于得出结论,让这个工作的唯一方法(除了静态链接所有东西)是所有第三方库都必须使用相同版本的 Visual Studios 编译 - 即不使用预编译的 dll - 下载源代码,构建一个新的 dll 并使用它。

Is this in fact true? Did I miss something?

这是真的吗?我错过了什么?

Furthermore, if this seems to be the case, then I can't help but think that Microsoft did this on purpose for nefarious reasons.

此外,如果情况确实如此,那么我不禁认为微软出于邪恶的原因故意这样做。

Not only does it break all precompiled binaries making it unnecessarily difficult to use precompiled binaries, if you happen to work for a software company that makes use of third party proprietary libraries, then whenever they upgrade to the latest version of visual studios - your company must now do the same thing or the code will no longer run.

它不仅会破坏所有预编译的二进制文件,从而使使用预编译的二进制文件变得不必要地困难,如果您碰巧为使用第三方专有库的软件公司工作,那么每当他们升级到最新版本的 Visual Studios 时,您的公司都必须现在做同样的事情,否则代码将不再运行。

As an aside, how does linux avoid this? Although I said I preferred developing on it and I understand the mechanics of linking, I haven't maintained any application long enough to run into this sort of low level shared libraries versioning problem.

顺便说一句,linux如何避免这种情况?虽然我说过我更喜欢在它上面开发并且我了解链接的机制,但我没有维护任何应用程序足够长的时间来遇到这种低级共享库版本控制问题。

Finally, to sum up: Is it possible to use precompiled binaries with this new manifest scheme? If it is, what was my mistake? If it isn't, does Microsoft honestly think this makes application development easier?

最后,总结一下:是否可以在这个新的清单方案中使用预编译的二进制文件?如果是,我的错误是什么?如果不是,Microsoft 是否真的认为这使应用程序开发更容易?

Update - A more concise question: How does Linux avoid the use of Manifest files?

更新 - 一个更简洁的问题:Linux 如何避免使用 Manifest 文件?

采纳答案by Christopher

All components in your application must share the same runtime. When this is not the case, you run into strange problems like asserting on delete statements.

应用程序中的所有组件必须共享相同的运行时。如果不是这种情况,您会遇到奇怪的问题,例如在 delete 语句上断言。

This is the same on all platforms. It is not something Microsoft invented.

这在所有平台上都是一样的。这不是微软发明的。

You may get around this 'only one runtime' problem by being aware where the runtimes may bite back. This is mostly in cases where you allocate memory in one module, and free it in another.

您可以通过了解运行时可能会反击的位置来解决这个“只有一个运行时”的问题。这主要是在您在一个模块中分配内存并在另一个模块中释放它的情况下。

a.dll
    dllexport void* createBla() { return malloc( 100 ); }

b.dll
    void consumeBla() { void* p = createBla(); free( p ); }

When a.dll and b.dll are linked to different rumtimes, this crashes, because the runtime functions implement their own heap.

当 a.dll 和 b.dll 链接到不同的 rumtimes 时,这会崩溃,因为运行时函数实现了它们自己的堆。

You can easily avoid this problem by providing a destroyBla function which must be called to free the memory.

您可以通过提供一个必须被调用以释放内存的 destroyBla 函数来轻松避免此问题。

There are several points where you may run into problems with the runtime, but most can be avoided by wrapping these constructs.

有几个地方可能会遇到运行时问题,但大多数都可以通过包装这些结构来避免。

For reference :

以供参考 :

  • don't allocate/free memory/objects across module boundaries
  • don't use complex objects in your dll interface. (e.g. std::string, ...)
  • don't use elaborate C++ mechanisms across dll boundaries. (typeinfo, C++ exceptions, ...)
  • ...
  • 不要跨模块边界分配/释放内存/对象
  • 不要在 dll 接口中使用复杂的对象。(例如 std::string, ...)
  • 不要跨 dll 边界使用复杂的 C++ 机制。(类型信息,C++ 异常,...)
  • ...

But this is not a problem with manifests.

但这不是清单的问题。

A manifest contains the version info of the runtime used by the module and gets embedded into the binary (exe/dll) by the linker. When an application is loaded and its dependencies are to be resolved, the loader looks at the manifest information embedded in the exe file and uses the according version of the runtime dlls from the WinSxS folder. You cannot just copy the runtime or other modules to the WinSxS folder. You have to install the runtime offered by Microsoft. There are MSI packages supplied by Microsoft which can be executed when you install your software on a test/end-user machine.

清单包含模块使用的运行时的版本信息,并由链接器嵌入到二进制文件 (exe/dll) 中。当应用程序加载并解析其依赖项时,加载程序会查看嵌入在 exe 文件中的清单信息,并使用 WinSxS 文件夹中相应版本的运行时 dll。您不能只将运行时或其他模块复制到 WinSxS 文件夹。您必须安装 Microsoft 提供的运行时。当您在测试/最终用户计算机上安装软件时,可以执行 Microsoft 提供的 MSI 软件包。

So install your runtime before using your application, and you won't get a 'missing dependency' error.

因此,在使用您的应用程序之前安装您的运行时,您将不会收到“缺少依赖项”错误。



(Updated to the "How does Linux avoid the use of Manifest files" question)

(更新到“Linux 如何避免使用清单文件”问题)

What is a manifest file?

什么是清单文件?

Manifest files were introduced to place disambiguation information next to an existing executable/dynamic link library or directly embedded into this file.

引入清单文件是为了将消歧信息放在现有的可执行文件/动态链接库旁边或直接嵌入到此文件中。

This is done by specifying the specific version of dlls which are to be loaded when starting the app/loading dependencies.

这是通过指定启动应用程序/加载依赖项时要加载的 dll 的特定版本来完成的。

(There are several other things you can do with manifest files, e.g. some meta-data may be put here)

(您还可以使用清单文件做其他几件事,例如可以将一些元数据放在这里)

Why is this done?

为什么要这样做?

The version is not part of the dll name due to historic reasons. So "comctl32.dll" is named this way in all versions of it. (So the comctl32 under Win2k is different from the one in XP or Vista). To specify which version you really want (and have tested against), you place the version information in the "appname.exe.manifest" file (or embed this file/information).

由于历史原因,该版本不是 dll 名称的一部分。所以“comctl32.dll”在它的所有版本中都是这样命名的。(所以Win2k下的comctl32与XP或Vista下的不同)。要指定您真正想要的版本(并针对其进行测试),请将版本信息放在“appname.exe.manifest”文件中(或嵌入此文件/信息)。

Why was it done this way?

为什么这样做?

Many programs installed their dlls into the system32 directory on the systemrootdir. This was done to allow bugfixes to shared libraries to be deployed easily for all dependent applications. And in the days of limited memory, shared libraries reduced the memory footprint when several applications used the same libraries.

许多程序将它们的 dll 安装到 systemrootdir 上的 system32 目录中。这样做是为了允许为所有相关应用程序轻松部署共享库的错误修复。在内存有限的时代,当多个应用程序使用相同的库时,共享库减少了内存占用。

This concept was abused by many programmers, when they installed all their dlls into this directory; sometimes overwriting newer versions of shared libraries with older ones. Sometimes libraries changed silently in their behaviour, so that dependent applications crashed.

这个概念被许多程序员滥用,当他们将所有的 dll 安装到这个目录中时;有时会用旧版本覆盖较新版本的共享库。有时,库的行为会悄无声息地改变,因此依赖的应用程序会崩溃。

This lead to the approach of "Distribute all dlls in the application directory".

这导致了“分发应用程序目录中的所有dll”的方法。

Why was this bad?

为什么这很糟糕?

When bugs appeared, all dlls scattered in several directories had to be updated. (gdiplus.dll) In other cases this was not even possible (windows components)

当出现错误时,分散在几个目录中的所有 dll 都必须更新。(gdiplus.dll) 在其他情况下,这甚至是不可能的(Windows 组件)

The manifest approach

清单方法

This approach solves all problems above. You can install the dlls in a central place, where the programmer may not interfere. Here the dlls can be updated (by updating the dll in the WinSxS folder) and the loader loads the 'right' dll. (version matching is done by the dll-loader).

这种方法解决了上述所有问题。您可以将 dll 安装在程序员不会干预的中央位置。在这里可以更新 dll(通过更新 WinSxS 文件夹中的 dll)并且加载程序加载“正确”的 dll。(版本匹配由 dll-loader 完成)。

Why doesn't Linux have this mechanic?

为什么 Linux 没有这个机制?

I have several guesses. (This is really just guessing ...)

我有几个猜测。(这真的只是猜测……)

  • Most things are open-source, so recompiling for a bugfix is a non-issue for the target audience
  • Because there is only one 'runtime' (the gcc runtime), the problem with runtime sharing/library boundaries does not occur so often
  • Many components use C at the interface level, where these problems just don't occur if done right
  • The version of libraries are in most cases embedded in the name of its file.
  • Most applications are statically bound to their libraries, so no dll-hell may occur.
  • The GCC runtime was kept very ABI stable so that these problems could not occur.
  • 大多数东西都是开源的,所以重新编译一个错误修复对目标受众来说不是问题
  • 因为只有一个“运行时”(gcc 运行时),运行时共享/库边界的问题不会经常发生
  • 许多组件在接口级别使用 C,如果处理得当,这些问题就不会发生
  • 在大多数情况下,库的版本嵌入在其文件名中。
  • 大多数应用程序静态绑定到它们的库,因此可能不会发生 dll-hell。
  • GCC 运行时保持 ABI 非常稳定,因此不会发生这些问题。

回答by crashmstr

If a third party DLL will allocate memory and youneed to free it, you need the same run-time libraries. If the DLL has allocate and deallocate functions, it can be ok.

如果第三方 DLL 将分配内存并且需要释放它,则您需要相同的运行时库。如果 DLL 具有分配和解除分配功能,则可以。

It the third party DLL uses stdcontainers, such as vector, etc. you could have issues as the layout of the objects may be completely different.

如果第三方 DLL 使用std容器,例如vector等。您可能会遇到问题,因为对象的布局可能完全不同。

It is possible to get things to work, but there are some limitations. I've run into both of the problems I've listed above.

可以让事情正常工作,但有一些限制。我遇到了上面列出的两个问题。

回答by Peter Jeffery

If a third party DLL allocates memory that you need to free, then the DLL has broken one of the major rules of shipping precompiled DLL's. Exactly for this reason.

如果第三方 DLL 分配了您需要释放的内存,则该 DLL 违反了传送预编译 DLL 的主要规则之一。正是因为这个原因。

If a DLL ships in binary form only, then it should also ship all of the redistributable components that it is linked against and its entry points should isolate the caller from any potential runtime library version issues, such as different allocators. If they follow those rules then you shouldn't suffer. If they don't then you are either going to have pain and suffering or you need to complain to the third party authors.

如果 DLL 仅以二进制形式提供,则它还应提供与其链接的所有可再发行组件,并且其入口点应将调用者与任何潜在的运行时库版本问题(例如不同的分配器)隔离开来。如果他们遵守这些规则,那么你就不应该受苦。如果他们不这样做,那么您要么会感到痛苦,要么需要向第三方作者投诉。

回答by dash-tom-bang

I finally came to the conclusion that the only way to get this to work (besides statically linking everything) is that all third party libraries must be compiled using the same version of Visual Studios - ie don't use precompiled dlls - download the source, build a new dll and use that instead.

我终于得出结论,让这个工作的唯一方法(除了静态链接所有东西)是所有第三方库都必须使用相同版本的 Visual Studios 编译 - 即不使用预编译的 dll - 下载源代码,构建一个新的 dll 并使用它。

Alternatively (and the solution we have to use where I work) is that if the third-party libraries that you need to use all are built (or available as built) with the same compiler version, you can "just" use that version. It can be a drag to "have to" use VC6, for example, but if there's a library you must use and its source is not available and that's how it comes, your options are sadly limited otherwise.

或者(以及我们必须在我工作的地方使用的解决方案)是,如果您需要使用的所有第三方库都是使用相同的编译器版本构建(或构建时可用),则您可以“仅”使用该版本。例如,“必须”使用 VC6 可能会很麻烦,但是如果您必须使用某个库并且其源不可用,那么它就是这样,否则您的选择将非常有限。

...as I understand it. :)

……按照我的理解。:)

(My line of work is not in Windows although we do battle with DLLs on Windows from a user perspective from time to time, however we do have to use specific versions of compilers and get versions of 3rd-party software that are all built with the same compiler. Thankfully all of the vendors tend to stay fairly up-to-date, since they've been doing this sort of support for many years.)

(我的工作不在 Windows 中,尽管我们不时从用户的角度与 Windows 上的 DLL 作斗争,但是我们确实必须使用特定版本的编译器并获取所有使用相同的编译器。值得庆幸的是,所有供应商都倾向于保持最新状态,因为他们多年来一直在提供这种支持。)