相對于開發(fā)工作,部署顯得不是那么重要,但卻是最困難的一步,也是最后一步。
控件的部門總的說來要完成以下幾件事,或者說實現(xiàn)以下幾個功能:
1、客戶端第一次訪問頁面時自動安裝(自動提示用戶下載).
2、有新版本發(fā)布時自動下載最新版并安裝。
經(jīng)測試還發(fā)現(xiàn),我的2.0框架下開發(fā)的控件,不能直接在客戶機(jī)上進(jìn)行注冊(regAsm.exe:C#開發(fā)的ActiveX控件只能用此程序注冊,而不能使用RegSvr32),不知道是不是框架版本的問題,只能做成安裝文件。
打包成安裝文件具體的就不說了,和打包其他項目一樣的步驟,但需要注意以下幾點(diǎn):
1、針對控件生成的DLL中有一個Register屬性,該屬性一定要設(shè)置為vsdraCOM,否則安裝包只會將DLL文件簡單拷到你的目標(biāo)目錄下,設(shè)置該值后,安裝包才會進(jìn)行注冊表的寫入,相當(dāng)于進(jìn)行regasm注冊。
2、還有一個屬性Permanent,看該屬性的描述說明是說指定該DLL文件在卸載程序時是否移除,一般性的慣性思維都是設(shè)置為true時表示在卸載程序時要移除該dll文件,但經(jīng)過測試發(fā)現(xiàn)設(shè)置為flase時才會進(jìn)行移除操作。如果不移除老版本的文件,新版本安裝時不會覆蓋原有的文件。所以此屬性顯得很重要了。
一般說來發(fā)布OCX最常用的就是打成CAB包,該方式最簡單但也最麻煩,需要搞證書。所以我就自己實現(xiàn)了一種不需要證書的發(fā)布方式(前提是要生成安裝包:XXXX.msi)。
為了實現(xiàn)自動下載并安裝,我把MSI包又制作為自解壓包,之后文件名就變成了XXXXXXX.EXE了(這個包的制作就不多說了,用RAR直接可以進(jìn)行壓縮,網(wǎng)上也有很多介紹),這樣當(dāng)用戶通過IE下載到本地后,IE會針對EXE類型的文件提示用戶是否要現(xiàn)在運(yùn)行,如果用戶選擇運(yùn)行,就會進(jìn)行自動安裝了。整個實現(xiàn)原理就是如此的簡單,但在簡單的過程中卻有比較麻煩的步驟,讓頁面通知客房端IE下載控件的步驟就較麻煩了。
在控件所在的頁面(我用的是JSP頁面)只能使用JS腳本進(jìn)行判斷是否需要安裝控件或者開載新版本進(jìn)行安裝。
下面我說說通過JS來判斷控件的問題。需要判斷以下2個內(nèi)容:
1、判斷是否需要新安裝。
2、是否發(fā)布了新版本的判斷。
需要通過JS讀取注冊表(有這個東東注定該項目不能移植到非WINDOWS操作系統(tǒng)上了)下的控件的相關(guān)項或者鍵值。如果沒找到相應(yīng)的鍵則表示沒有進(jìn)行安裝,需要新安裝。具體判斷邏輯直接上代碼:
主函數(shù):
var lastModiDatetime = "<%=lastModiDatetime%>";//讀取的服務(wù)器上的控件文件的最后修改日期
//判斷當(dāng)前本地計算機(jī)上是否已注冊了控件。
//如果已注冊了控件,再次判斷是否是最新版本(JAVA現(xiàn)在沒有直接提供對文件版本的獲取,通過獲取文件最后修改日期來變相對比文件新舊)
//要實現(xiàn)此功能,要求每一次發(fā)布新版本控件時必須在應(yīng)用服務(wù)器上安裝并注冊最新的控件。
//并把安裝路徑下的最新RuntimeData.dll及RunData.dll和最新的安裝包:
//wellalarmruntimedata.exe(根據(jù)RuntimeDataSetup.msi制作的自解壓RAR安裝包)拷到WEB應(yīng)用程序下的petrol/cab下,拷RuntimeData.dll及RunData.dll是為了讓JAVA能夠獲取控件的最新修改日期,拷wellalarmruntimedata.exe是為了讓客戶端IE在服務(wù)器上能找到下載路徑及文件。
window.onload=function()
{
//在系統(tǒng)提示用戶是否允許交互前判斷當(dāng)前控件是否加載,通過此屬性判斷當(dāng)前是否注冊了COM組件
var isReg = true;
//通過獲取注冊表屬性得到安裝路徑
var hkey_root,hkey_path,hkey_key,path;
hkey_root="HKEY_CLASSES_ROOT"
hkey_file://\\CLSID\\{80C07FCA-841C-43C9-BD34-E9F3A6E87A85}\\InprocServer32\\">\\CLSID\\{80C07FCA-841C-43C9-BD34-E9F3A6E87A85}\\InprocServer32\\"; //80C07FCA-841C-43C9-BD34-E9F3A6E87A85就是開發(fā)篇強(qiáng)調(diào)的控件的GUID
try
{
var RegWsh = new ActiveXObject("WScript.Shell");
hkey_key="CodeBase" ;
path = RegWsh.RegRead(hkey_root+hkey_path+hkey_key);
//獲取本地機(jī)器上的組件版本號
var assembly = RegWsh.RegRead(hkey_root+hkey_path+"Assembly");
if(assembly!=null)
{
//獲取版本號
var versionNos = assembly.split("Version=")[1];
var versionNo = versionNos.substring(0,versionNos.indexOf(","));
//查找是否注冊此版本號.找到最新版本的就采用此版本的安裝路徑
try
{
var path = RegWsh.RegRead(hkey_root+hkey_path+ versionNo + "\\" + hkey_key);
}
catch(ex)
{
isReg = false;
}
}
}
catch(e){}
//注意,當(dāng)用戶不允許腳本運(yùn)行時path也是為NULL值,但只要安裝注冊了控件,不放管用戶是否同意腳本運(yùn)行,
//MWellAlm控件都會被創(chuàng)建成object
if((path==null && document.getElementById("MWellAlm")==null) || !isReg)
{
//沒有注冊此控件或者此控件已被卸載
msg.style.display = '';
ctlload.style.display = 'none';
msg.innerHTML = '本地計算機(jī)上未安裝“井場預(yù)警及實時數(shù)據(jù)顯示控件”或此控件已被卸載,不能正確顯示此頁面,需要下載最新的控件!<br>';
msg.innerHTML += "請[運(yùn)行]自動下載的文件 或者 <a id='loadctl' href='<%=context%>/petrol/cab/wellalarmruntimedata.exe'>點(diǎn)擊下載最新顯示控件</a>";
document.getElementById("loadctl").click();
return;
}
if(path!=null && document.getElementById("MWellAlm")==null)
{
alert("當(dāng)前控件已安裝注冊但不能正確顯示,請關(guān)閉所有的IE瀏覽器后重試!");
return;
}
document.getElementById("MWellAlm").stop(); //調(diào)用控件的方法之一
//以下就是判斷是否發(fā)布了新版本
if(path!=null && path!="")
{
var fso = new ActiveXObject("Scripting.FileSystemObject");
var f1 = fso.GetFile(path);
//FSO獲取的文件最后修改日期是UTC格式的,JAVASCRIPT還沒有對此日期格式操作對象.所以只有轉(zhuǎn)換成字符來處理
//而且此UTC日期串與從北京時間轉(zhuǎn)換來的UTC串格式不相同
var lastDate = ""+f1.DateLastModified; //轉(zhuǎn)換成字符串
var v = new Date();
v.setTime(lastModiDatetime);
//直接通過toUTCString()的格式:Thu, 18 Dec 2008 06:46:04 UTC
//獲取的文件最后修改時間的UTC格式:Thu Dec 18 14:46:04 UTC+0800 2008
//經(jīng)測試,這2個UTC時間之前除格式不同外,在小時上還不相同。前者比后者少了8小時
//在小時上+上8小時,因為這個UTC時間是從北京時間轉(zhuǎn)換來的
v.setHours(v.getHours()+8);
//把從北京時間轉(zhuǎn)換來的UTC時間轉(zhuǎn)換成數(shù)組,在后者中逐個匹配,都匹配上就說明時間相等
var ds = v.toUTCString().replace(",","").split(" ");
//為了保證不出現(xiàn)左右滑動匹配的情況(18不能與118、181匹配),在匹配項與被匹配項左右都加上一個空格
var baseUtc = " "+lastDate.replace("+0800","")+" ";
var isEqual = true;
for(var i=0; i<ds.length; i++)
{
var temp = " "+ds+" ";
if(baseUtc.indexOf(temp)==-1)
{
isEqual = false;
break;
}
}
if(!isEqual)
{
document.getElementById("MWellAlm").click();
document.getElementById("MWellAlm").clearAll();
msg.style.display = '';
ctlload.style.display = 'none';
msg.innerHTML = '本地計算機(jī)上安裝的“井場預(yù)警及實時數(shù)據(jù)顯示控件”不是最新版本,不能正確顯示此頁面,需要下載最新的控件!<br>';
msg.innerHTML += "請[運(yùn)行]自動下載的文件 或者 <a id='loadctl' href='<%=context%>/petrol/cab/wellalarmruntimedata.exe'>點(diǎn)擊下載最新顯示控件</a>";
msg.innerHTML += '<br>注意:在安裝最新控件之前請保證已通過[控制面板->添加或者刪除程序]卸載了舊版本的控件!';
document.getElementById("loadctl").click();
return;
}
msg.style.display = 'none';
ctlload.style.display = '';
document.getElementById("MWellAlm").click();
document.getElementById("MWellAlm").start();
}
}
以上就是我實現(xiàn)的控件發(fā)布的全部內(nèi)容。為了保證能順利發(fā)布,要求客戶端IE最好把控件所在的站點(diǎn)加為信任站點(diǎn),并調(diào)低安全限制,否則容易發(fā)生控件能顯示,但不能進(jìn)行用戶交互的情況。
以上功能全部測試通過,而且現(xiàn)在項目中也在正常使用。
OCX技術(shù)現(xiàn)在不被人們所看好,也不被推廣,但在實際項目中很多時候卻不得不運(yùn)用這些雞肋技術(shù)來解決實際問題,或者實現(xiàn)特定功能,所以很多問題還要參考實際問題找到合適的解決辦法。