利用manifest文件对程序目录下的dll进行分类

1 背景

  对于大部分的券商和机构投资者,只能通过有交易所交易系统接入资质的券商提供的柜台系统来进行现货交易。相对于期货市场,现货市场的柜台系统千差万别,接入协议有明文字符串、二进制数据和FIX协议等,接入方式有TCP连接、COM组件和dll动态库等。要想开发一个覆盖市面上所有的现货柜台的报盘系统,就必须能同时支持这些柜台的所有接入方式。在开发的过程中遇到的关于动态库版本兼容问题有以下几个:

  1. 同一柜台系统提供商发布了2套柜台系统,用于对接这2套系统的开发包是一样的,只是由于版本不同不能通用
  2. 不同柜台系统提供的动态库包含了不同版本的第三方库文件(如libeay32.dll等)
  3. 部分券商对柜台系统进行了自定义开发,在柜台提供的开发包中加入了自己开发的动态库,以增加对接入授权的控制

  当系统中存在同名的且不同版本的动态库时,为了能方便管理和更新程序,只能对这些动态库进行分类整理。具体做法是在程序执行目录下对每个类别的柜台分别建立一个文件夹,用于存放对接该柜台所需要的所有的动态库文件。由于程序采用了静态加载的方式去加载dll,为了能让可执行文件在启动时能找到这些动态库,manifest文件就派上用途了。

2 关于manifest文件

  manifest文件是用于组织和描述并行组件或独立应用程序的xml文件。它主要包含了用于绑定和激活COM类、接口和库的相关信息,这些信息以往是存储在系统注册表中的[1]。在xp及以后的windows系统中,系统在执行EXE可执行文件时会首先读取Manifest文件,获得exe文件需要调用的DLL列表(此时获得的,并不直接是DLL文件的本身的位置,而是DLL的manifest),操作系统再根据DLL的Manifest文件提供的信息去寻找对应的DLL ,这样就可以区别不同版本的相同文件名的DLL文件[2]

微软msdn上的一个配图比较直观地描述了这一过程[3]

3 组件查找顺序

  在windows系统中,如果应用程序指定了组件依赖关系,则程序启动时首先在WinSxS文件夹中查找共享组件,若未找到,则在程序安装目录下查找私有组件。

  在大部分的情况下,组件的查找顺序如下所示:

  1. WinSxS 文件夹
  2. \\<appdir>\<assemblyname>.DLL
  3. \\<appdir>\<assemblyname>.manifest
  4. \\<appdir>\<assemblyname>\<assemblyname>.DLL
  5. \\<appdir>\<assemblyname>\<assemblyname>.manifest

更详细的描述可以查看msdn中的Assembly Searching Sequence

4 实现

场景一 程序需加载两个不同版本的sample.dll

解决办法:

    1. 分别建立两个文件夹(dlla,dllb),将两个不同版本的dll分别放入这两个文件夹
    2. 文件夹下分别新建一个manifest文件,文件名同文件夹名,例dlla.manifest、dllb.manifest
    3. 编辑manifest文件,内容如下(以dlla为例):

      1 <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
      2   <assemblyIdentity name="dlla" processorArchitecture="x86" version="1.0.0.0" type="win32" />  <-- 这边的name必须为manifest文件所在的文件夹的名字
      3   <file name = "sample.dll"/>
      4 </assembly>
    4. 在需要用到动态库提供的函数的cpp中增加以下预处理指令(以dlla为例):

      1 // 指定加载dll的位置;
      2 #pragma comment(linker,"/manifestdependency:\"type=‘win32‘ "3     "name=‘dlla‘ "4     "version=‘1.0.0.0‘ "5     "processorArchitecture=‘x86‘ "6     "language=‘*‘ "7     "\"")

重新编译后,程序便能正确找到指定的动态库了。

场景二 程序需加载两个不同版本的sample.dll,且不同版本的sample.dll分别又依赖不同的第三方开发库

解决办法:在场景一的解决办法的基础之上,在manifest文件中增加依赖的第三方库的信息,例:

1 <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
2
3   <assemblyIdentity name="dlla" processorArchitecture="x86" version="1.0.0.0" type="win32" />
4
5   <file name = "sample.dll"/>
6   <file name = "libeay32.dll"/>
7   <file name = "ssleay32.dll"/>
8
9 </assembly>

场景三 程序需加载两个不同版本的sample.dll,但是调用这2个动态库的代码存在某种引用关系。

解决办法:在这种场景下,由于预编译宏会互相覆盖,导致动态库不能正确加载,只能通过LoadLibrary的方式进行动态加载。事实上,通过动态加载可以加载任意路径的动态库,只是在使用方式上没有静态加载方便。

4 总结

利用manifest文件同时加载多版本的同名的动态库的方式是windows系统特有的功能,在平时c++的开发中也很少接触到。这边仅作记录供以后碰到相同问题的时候作为参考。

1. https://msdn.microsoft.com/en-us/library/aa375365(v=vs.85).aspx
2. http://blog.csdn.net/suxinpingtao51/article/details/42870937
3. https://msdn.microsoft.com/en-us/library/ff951640(v=vs.85).aspx

本文为原创内容,若有错误的地方烦请指正

本文地址:http://www.cnblogs.com/morebread/p/4953497.html

时间: 2024-12-07 23:23:43

利用manifest文件对程序目录下的dll进行分类的相关文章

(转)Inno Setup入门(六)——在程序目录下创建文件夹

本文转载自:http://blog.csdn.net/yushanddddfenghailin/article/details/17250789 创建文件夹可以使用[dirs]段实现,代码如下: [setup] ;全局设置,本段必须 AppName=Test AppVerName=TEST DefaultDirName="E:\TEST" AppVersion=1.0 [files] Source: "F:\desktop\test\ipmsg.exe"; Dest

android将asseet当中的数据库文件拷到程序目录

/** * 将asseet当中的数据库文件拷到程序目录 * @param activity 当前Activity * @param filePath 你的程序目录 getApplicationContext().getFilesDir().getAbsolutePath(); * @param fileName 你的数据库名称 */ public static void copyEmbassy2Databases(Activity activity, String filePath, Strin

从assets中拷贝from文件到指定目录下

/** * * 从assets中拷贝from文件到to目录下 * **/ public void copyFile(Context context, String sourPath, String toPath) { OutputStream output = null; InputStream input = null; try { int read = 0; input = context.getResources().getAssets().open(sourPath); output =

java利用WatchService实时监控某个目录下的文件变化并按行解析(注:附源代码)

首先说下需求:通过ftp上传约定格式的文件到服务器指定目录下,应用程序能实时监控该目录下文件变化,如果上传的文件格式符合要求,将将按照每一行读取解析再写入到数据库,解析完之后再将文件改名. 一. 一开始的思路 设置一个定时任务,每隔一分钟读取下指定目录下的文件变化,如果有满足格式的文件,就进行解析. 这种方式很繁琐,而且效率低,效率都消耗在了遍历.保存状态.对比状态上了! 而且无法利用OS的很多功能. 二. WatchService介绍 1. 该类的对象就是操作系统原生的文件系统监控器!我们都知

利用批处理命令复制指定文件到指定目录下

复制文件到指定路径 关于复制指定文件到指定路径,一般而言指的是对备份文件,因为其具有增长性, 所以添加任务计划之后会按时进行备份,对于常规文件同样适用. 其步骤大致分为: 1:  设定要复制文件的名称(若为每日备份文件要获取系统时间) 2:  设定复制文件的原路径和目标路径进行复制 3:  退出复制程序 例如:复制Y盘目录下文件到D盘目录下 rem 关闭回显 @echo off rem   设定文件时间 set d=%date:~0,10% set d=%d: =0% rem 设定需要复制的文件

利用wget批量下载http目录下文件

原理:下载你需要down的目录页面的index.html,可能名字不是如此!!!之后用wget下载该文件里包含的所有链接! 例如:wget -vE -rLnp -nH --tries=20 --timeout=40 --wait=5 http://mirrors.163.com/gentoo/distfiles/或者简单点:wget -m http://mirrors.163.com/gentoo/distfiles/你 会得到distfiles页面的index.html文件,该文件内容当然不用

C/C++ 遍历目录文件,默认目录下

每次遇到这样的问题总会折腾很久,到网上搜,或者查资料,弄了很多次,但就是没记住,这次写程序又遇到了,干脆就把它都弄清楚了,然后顺便在这里记录一下,以后再遇到就不用到处去找了. 用 C/C++ 遍历目录文件主要有两种方式,分别对应在 Windows VS 环境下和 Linux\Unix 环境下的方法,它们各自所使用的函数如下: (Windows VS)_findfirst, _findnext, _findclose (Linux\Unix)opendir, readdir, closedir 下

小黑的日常折腾-复制外部命令的可执行文件和依赖库文件到指定目录下的对应目录

清明三天假期基本都是在写脚本中度过了,今天又折腾了一个新的脚本,该脚本的作用是快速复制一个或多个命令的可执行文件和依赖库文件到一个模拟的根文件系统下的相应目录下,这个脚本平时运维估计用不到,只有自己制作一个小的Linux发行版时才有可能使用该脚本. 脚本具体的功能如下: 1)提示用户选择要从文本中读取要复制的命令名还是从当前终端中交互式输入命令名. 2)用户选择前者,会自动使用vim打开一个文件,用户根据格式说明填入要复制的命令的名称,可以是多个命令,保存退出后自动执行复制操作. 3)用户选择后

Android首次开机通过设置向导拷贝文件到sdcard目录下

MTK平台机器,首次开机会启动OOBE设置向导,我们可以在此应用中增加一个界面,从/system/目录下拷贝文件到/mnt/sdcard/目录下. 1.首先编译时要将文件从代码路径拷贝到对应的out目录,可以用在mk文件中实现: 方法①:如果文件较少,可以采用逐条拷贝方式,范例如下: CUR_PATH := vendor/ThirdParty/App/tchip PRODUCT_COPY_FILES += $(CUR_PATH)/bootanimation.zip:system/media/bo