今天實際應用時,又進行了一些測試,發現與以前看到資料中一些不同的表現,見最后的【補充】部分
通常,實例化 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,等等
新聞熱點
疑難解答