今天实际应用时,又进行了一些测试,发现与以前看到资料中一些不同的表现,见最后的【补充】部分
通常,实例化 COM 组件前(包括采用这一技术的 DLL/ActiveX 控件),都需要先用 regsvr32 注册该组件或控件,从 XP sp2 开始,微软提供了一种采用 manifest(文件清单)的方式来替代,详见最后的 MSDN 参考资料。由于 COM 的注册信息要写入注册表,一方面给 Win7 以后没有管理员身份的应用带来了麻烦,另一个人一直存在的问题的就是所谓的DLL Hell,造成同一控件不同版本之间的干扰。MS 推出 .Net 时采用了程序集的方式来避免这个问题,同时也用文件清单的方式给 COM 带来了新的调用方案,由于不需要将类信息写入注册表,COM 文件的放置位置也可以放到 exe 所在文件夹或它的子目录中,自然也就避免了 DLL Hell 的问题。
文件清单是一个 xml 格式的文本文件,其文件名后缀为 manifest(例如:程序名为 a.exe,则这个清单文件名为 a.exe.manifest),创建类实例时,操作系统(实际上是 ole32.dll 的 CoCreateInstance API 函数)会先检查是否存在清单文件,如果存在有效的清单文件,从把从注册表获取 COM 信息的过程就转变成了从清单文件中读取。这个文件有很多作用,例如:指定运行时的操作身份权限,是否使用系统主题,以及这里提到的描述 COM 注册信息,... 等等
如果你用的是 vfp9,当你 build 成 exe 后,用 ResHacker 打开这个 exe 时,你会发现存在一个序号为 24 的资源,其内容为:
(图一)
是不是很像前面提到的 MSDN 文章中的清单文件?如果你用 eXeScope 打开这个 exe。会发现这个 24 号资源,它就是一个名为 manifest 清单的内容:
(图二)
也就是说,vfp9 的 exe 编译时,已经嵌入了一个默认的 manifest
由于 Windows 默认内置的 manifest 优先于外部的 manifest,因此,我们只要写一个添加了 COM 类声明的新 manifest,重新编译后的 exe 就实现了免注册功能。有一点也许很多人还不知道:vfp9 在编译 exe 时,如果发现存在与要编译的 exe 同名的 manifest 文件,就会用它替换掉默认的清单文件。
下面我们就来验证一下:
1. 创建一个测试用 COM
a. 新建一项目 -> mycomm
b. 代码 -> 新建,粘贴下面内容并保存为 mycom.PRg
Define Class myComFunc as session olepublicFunction getName() Return 'dkfdtf'EndFuncEndDefine c. 编译成 dll2. 创建一个测试用 EXE
a. 新建项目 -> test
b. 代码 -> 新建,粘贴下面内容并保存为 test.prg
Local oo, ccTry oo = NewObject('mycom.myComFunc') cc = oo.getName() MessageBox("RegFree COM: value = " + cc)Catch MessageBox('无法创建 COM 对象.')EndTry c. 编译成 exe3. 运行这个 exe,应该看到这样一个对话框:
(图三)
4. 现在用 Regsvr32 /u mycomm.dll 注销掉这个 COM,应该看到这个:
(图四)
OK,一切都与未使用免注册技术的情况相符。
现在开始实现免注册功能,下面的内容只是介绍如何实现你自己应用程序实现免注册的步骤和方法;只想了解一下的话,只要下载后面的示例包运行就可以了,其中包含了下面的代码。
1. 在项目所在文件夹,新建一个文本文件,用前面提到的 ResHacker 打开 exe,找到 24号资源,将其中的内容复制粘贴到新建的文本文件中,最后重命名为 test.exe.manifest
,然后用 RegFree 工具包 中的 mt 或 regsvr42 提取 mycom.dll 中注册信息,加入其中,全部内容如下:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"><assemblyIdentity version="1.0.0.0" type="win32" name="Microsoft.VisualFoxPro" processorArchitecture="x86"/><description>Visual FoxPro</description><trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> <security> <requestedPrivileges> <requestedExecutionLevel level="asInvoker" /> </requestedPrivileges> </security></trustInfo><file name="mycom.dll"><comClass progid = "mycom.myComFunc" clsid="{B20DF2B2-7810-4D08-8F3A-2B96786AF03E}" threadingModel="Apartment" /><typelib tlbid="{31358D79-374B-49BD-AC99-BFE798831194}" version="1.0" helpdir="" /></file><dependency> <dependentAssembly> <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" language="*" processorArchitecture="x86" publicKeyToken="6595b64144ccf1df" /> </dependentAssembly></dependency></assembly>注释:上面内容直接从 test.exe 的 24 号资源复制过来,并加入了 <file> ... </file> COM 类描述信息部分;为简明起见,这里只加入了必须的类描述元素,另外,如果 mycom.dll 与 test.exe 不在同一文件夹中,需要加上路径部分,可以是相对或绝对路径。增加的那些 GUID 值,你也可以直接在 mydll.vbr 中找到。如果是第三方控件,大部分情况下你没有这个 vbr 文件,所以我提供给你了上面的 RegFree 工具包来从 dll/ocx 中提取这些信息,用法很简单:
用 mt 来提取:mt.exe -tlb:TBL文件名 -dll: DLL/OCX文件名 -out: 输出结果文件名.txt 没有专门提供TBL文件的话,TBL就用DLL/OCX 文件
用 regsvr42 提取:regsvr42.exe mycom.dll
两个工具都有缺陷,mt 缺少 progid 项目信息,regsvr42 在 Win7 以上即使以管理员权限运行也可能失败,在 xp 下运行没问题
2. 重新编译 test.exe,vfp 会用上面这个清单文件替换默认的 manifest (24 号资源)
3. 运行 test,应该可以再次看到图三的画面。反复用 regsvr32 注册和注销 mycom.dll,结果应该都一样,这说明无论 COM 是否已注册,都可正常使用;更极端一点,你只复制 mycom.dll 和 test.exe 这两个文件到其他有 vfp 运行库的机器上,不用注册,就可以正常运行。
验证示例:RegFree.rar
参考:
关于COM的Reg-Free(免注册)技术简介及实例讲解
Registration-Free Activation of COM Components: A Walkthrough
regsvr42: Generate SxS Manifest Files from Native DLLs for Registration-Free COM
补充:
可以将类描述部分单独放在清单文件中,不一定要写入 exe 24 号资源中,也就是说,不更改 vfp 编译 exe 时默认生成的 24 号资源,另外单独写一个清单文件一起发布,不重复的部分不会相互干扰。与上面的示例包中的 mycom.dll 对应的清单文件 test.exe.manifest 如下:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"><file name="mycom.dll"><comClass progid="mycom.myComFunc" clsid="{B20DF2B2-7810-4D08-8F3A-2B96786AF03E}" tlbid="{31358D79-374B-49BD-AC99-BFE798831194}" /></file></assembly>不过要记住,前面说过,vfp 编译时如果发现项目文件夹中存在同名的 manifest 文件,就会用它替换默认的清单。所以,如果要单独发布清单文件(为了便于 COM 版本更新时只更新 dll 而不用更新 exe),就不要把这个文件放在开发机器上项目所在文件夹中。否则,还是像前面描述的那样比较可靠,将类描述信息插入到复制的默认清单文件中,并在更新 COM 时,同时发布更新的 COM 和重新编译的 EXE 文件。
补充2:
找到一个比较完美的提取并自动生成清单文件的工具:
http://www.rdctools.com/Downloads/SetupRDCToolsCOMManifestBuilderPro.exe
完全免费(首次运行时会显示要激活,只要点击获取激活码按钮,会自动连接到官网,显示你的激活码,不需要输入任何信息)
该工具还有其他一些功能:检查/添加数字签名,压缩,检查/注册/注销 COM/OCX/DLL,等等
新闻热点
疑难解答