使用Visual Studio C ++清单文件 [英] Working with Visual Studios C++ manifest files

查看:221
本文介绍了使用Visual Studio C ++清单文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我写了一些使用开源库的代码来做一些繁重的工作。这项工作是在linux,单元测试和cmake帮助将其移植到windows。需要在两个平台上运行。



我喜欢Linux,我喜欢cmake,我喜欢我可以得到可视工作室文件自动生成。因为它是现在,一切都将编译,它将链接,并将生成测试可执行文件。



但是,为了到这一点,我不得不战斗与windows



根据我的理解:



与VS 2005,微软创建了Side By Side DLL。这样做的动机是,以前,多个应用程序将安装相同dll的不同版本,导致先前安装和工作的应用程序崩溃(即Dll地狱)。并排dll修复这个,因为现在有一个清单文件附加到每个可执行文件/ dll,指定应该执行哪个版本。



这很好,好。应用程序不应再崩溃神秘。但是...



微软似乎每次发布Visual Studio都会发布一套新的系统DLL。此外,正如我前面提到的,我是一个开发者试图链接到第三方库。通常,这些东西以预编译dll的形式分发。现在,当使用一个版本的视觉工作室编译的预编译的dll与使用另一个版本的视觉工作室的应用程序链接时,会发生什么?



根据我在互联网上阅读的内容,坏东西发生。幸运的是,我从来没有这么远 - 我运行可执行文件时遇到了MSVCR80.dll未找到问题,从而开始了我的整个清单问题。



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



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



此外,如果这似乎是这样,那么我不得不认为微软这样做是出于恶意的原因。 p>

它不仅打破了所有预编译的二进制文件,使得使用预编译的二进制文件变得不必要,如果你碰巧使用第三方专有库的软件公司工作,他们升级到最新版本的视觉工作室 - 你的公司现在必须做同样的事情或代码将不再运行。



另外,linux如何避免这?虽然我说我喜欢开发它,我明白链接的机制,我没有维护任何应用程序足够长的时间,以进入这种低级共享库版本化问题。



最后,总结一下:使用这个新的清单方案可以使用预编译的二进制文件吗?如果是,我的错误是什么?



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

解决方案

应用程序中的所有组件必须共享同一个运行时。如果不是这种情况,你会碰到一些奇怪的问题,例如在delete语句上声明。



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



你可以通过意识到运行时可能咬回来解决这个只有一个运行时的问题。
这主要是在一个模块中分配内存,并在另一个模块中释放内存的情况。

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

b.dll
void consumeBla(){void * p = createBla();自由(p);当a.dll和b.dll链接到不同的rumtimes时,这会崩溃,因为运行时(Runtime)函数实现自己的堆。



您可以通过提供一个destroyBla函数来避免这个问题,必须调用这个函数来释放内存。



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



参考: p>


  • 不要跨模块边界分配/释放内存/对象

  • 不要使用复杂对象你的dll接口。 (例如std :: string,...)

  • 不要在dll边界上使用复杂的C ++机制。 (typeinfo,C ++ exceptions,...)

  • ...





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



因此,在使用应用程序之前安装运行时,你将不会得到一个缺少依赖的错误。






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



什么是清单文件?



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



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



(您可以使用清单文件进行其他几项操作,例如可以在此处放置元数据)



为什么这样做?



由于历史原因,版本不是dll名称的一部分。所以comctl32.dll在其所有版本中以这种方式命名。 (因此Win2k下的comctl32不同于XP或Vista中的comctl32)。要指定您真正想要的版本(并已经过测试),请将版本信息放在appname.exe.manifest文件中(或嵌入此文件/信息)。



为什么这样做?



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



这个概念被许多程序员滥用,当他们安装所有dlls进入这个目录;有时用旧的共享库覆盖新版本的共享库。有时图书馆会在其行为中默默变更,导致依赖应用程序崩溃。



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

为什么会这样?



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



清单方法



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



为什么Linux没有这个机制?



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




  • 大多数事情都是开源的,所以重新编译bug修复是一个非问题

  • 因为只有一个运行时(gcc运行时),运行时共享/库边界的问题不会经常发生

  • 许多组件在接口级别使用C,如果执行正确,这些问题不会发生

  • 大多数情况下,库的版本嵌入在其文件的名称中。 / li>
  • 大多数应用程序都静态绑定到它们的库,所以不会出现dll-hell。

  • GCC运行时保持非常稳定,


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.

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.

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

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.

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?

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.

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.

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.

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.

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?

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

解决方案

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.

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.

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

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, ...)
  • ...

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.

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)

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.

(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).

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.

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.

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

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)

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).

Why doesn't Linux have this mechanic?

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.

这篇关于使用Visual Studio C ++清单文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆