到這裏又遇到問題了,命令行參數是拆分到數組裏的,如果不指定命令參數時,則數組保持爲“空數組”。而對“空數組”的任何操作(包括用UBound函數測長度)都會引發錯誤,這個問題困擾了我很久。怎麼才能將這種情況區分出來呢?沒辦法,最終還是隻能借助API函數:SafeArrayGetDim,其實這個函數的真正作用是:取得 OLE 或 Variant 數組元素的數組維度,不過,勉強解決問題吧。
從命令行參數得到XML配置文件地址後,接着是解析XML文件獲取更新信息與文件列表。當然,在解析XML配置信息之前,得先定好XML配置文件的格式,如下面的代碼:
<?xml version="1.0" encoding="gb2312" ?> <force>0</force> <publishDate>20090902 19:51:32</publishDate> <version>1.0.0.0</version> <remark /> <run /> </publish> <configUrl url="Update.xml" /> <configPath path="D:/Current/VBUpdate/Publisher/" /> <baseUrl url="http://localhost/update/" /> <localPath url="D:/Current/VBUpdate/Publisher/" /> <remotePath url="D:/Current/VBUpdate/Publisher/" /> </paths> <file name="Publisher.exe" main="1" version="1.0.0.0" /> <file name="../Update/Update.exe" version="1.0.0.0" /> <file name="../Update/olelib.tlb" version="20040201 22:54:16" /> <file name="../Update/SetSys.ico" version="20090813 10:38:54" /> </files> </update> |
其中,publish結點描述的上所發佈程序的基本信息,包括以下子結點:
序號 |
結點名稱 |
用途 |
可能值 |
備註 |
1 |
force |
指明是否強制更新。 |
0(可取消更新)或1(強制更新)。 |
非0值均解析爲1(強制更新)。 |
2 |
publishDate |
表示發佈時間。 |
發佈當時時間。 |
|
3 |
version |
主程序版本號。 |
主程序文件版本號。 |
通過比較這個版本號決定是否需要更新。 |
4 |
remark |
發佈說明。 |
歷次發佈的修訂內容。 |
每次的修訂內容佔一行,新的在上面。 |
5 |
run |
更新後運行命令。 |
DOS命令。 |
一般爲調用批處理。 |
paths結點描述與更新及發佈相關的路徑,包括以下子結點:
序號 |
結點名稱 |
用途 |
可能值 |
備註 |
1 |
configUrl |
指定XML配置文件URL。 |
包括XML文件名的完整URL。 |
要與主程序中讀取的URL一致。 |
2 |
configPath |
保存XML配置文件的遠程地址。 |
文件路徑,以“/”結束。 |
不支持FTP路徑。 |
3 |
baseUrl |
指定下載基地址。 |
http://路徑,以“/”結束。 |
|
4 |
localPath |
指定本地路徑。 |
本地待發布程序路徑,以“/”結束。 |
一般以主程序路徑爲基準。 |
5 |
remotePath |
指定遠程路徑。 |
程序發佈到的路徑,以“/”結束。 |
不運行FTP路徑。 |
files描述要更新文件列表,每個文件以一個子結點表示,結點爲固定爲“file”,“name”屬性指定源文件名,以上述的下載基地址(baseUrl)爲基準,支持相對路徑;“target”屬性指定目的文件名,以主程序所在路徑爲基準,支持相對路徑,缺省時直接使用源文件名;“version”屬性表示該文件發佈的版本號,如果文件不包括版本信息,則用“date”屬性指定其最後修改時間,“version”或“date”決定了該文件的新舊、是否需要更新;“main”屬性指定該文件是否主程序,1(或非0值)表示是,0表示否。
XML配置文件格式清楚了,下面要開始VB讀取XML之旅了,好XML解析用的是Microsoft的XML分析器組件,而且在Delphi中已經用過,寫起來還不是很太吃力。MSXML分析器有很多版本,但爲了安全起見還是引用了較低的版本:2.0(Microsoft XML, version 2.0)。
要解析XML配置之前,首先需要創建XML文檔處理對象,由於習慣了OOP,當然在VB裏也要這樣用好一點,所以將XML配置解析寫成了類:XmlConfiguration,在構造函數(我自認爲Class_Initialize過程應該就等同於構造函數吧?)中創建XML文檔實例:
Set XmlDoc = New DOMDocument,
XmlDoc.preserveWhiteSpace = False ‘這句感覺是多餘的,本來是想讓它自動縮進、換行的,但就是沒找到適合的屬性,保存得到的XML文件亂糟糟的。
創建XML文檔實例後,開始裝載XML配置文件,用到Load方法:
XmlDoc.Load(ConfigFile)
與Delphi中不同的是,這裏的Load是異步執行的,調用後尚不能立即讀取結點,需要監測其狀態,等待裝載完成後才行。我是通過Timer定時(100毫秒)檢測的,當XmlDoc.readyState = 4時即開始解析XML配置。首先寫一個Analysis方法,依次讀取update(根結點)下的每一個子結點,然後再處理每一個子結點:
'{ 解析每個子結點的值到各個屬性。Cable Fan 2009-08-14 } Public Sub Analysis() Dim i As Integer Dim Root As IXMLDOMNode Dim Node As IXMLDOMNode
Set Root = XmlDoc.documentElement If Root Is Nothing Then MsgBox "無法讀取XML配置!" Else For i = 0 To Root.childNodes.Length - 1 Set Node = Root.childNodes(i) Select Case Node.nodeName Case "publish" LoadPublish Node '發佈信息。 Case "paths" LoadPaths Node ' 裝載下載路徑。 Case "files" LoadFiles Node ' 裝載文件列表。 End Select Next End If End Sub
'{ 裝載下載路徑。Cable Fan 2009-08-14 } Private Sub LoadPublish(PNode As IXMLDOMNode) On Error GoTo CATCH
' 遍歷所有文件子結點。 Dim Node As IXMLDOMNode Dim Attr As IXMLDOMNode Dim i As Integer
For i = 0 To PNode.childNodes.Length - 1 Set Node = PNode.childNodes(i) Select Case Node.nodeName Case "force" m_Force = (Node.Text = "1") Case "publishDate" m_PublishDate = CDate(Node.Text) Case "version" m_Version = Node.Text Case "remark" m_Remark = Node.Text Case "run" m_RunCmd = Node.Text End Select Next
Exit Sub CATCH: Err.Raise MODULEID + 12, "XMLConfiguration.LoadPublish" Err.Clear End Sub
'{ 裝載下載路徑。Cable Fan 2009-08-14 } Private Sub LoadPaths(PNode As IXMLDOMNode) On Error GoTo CATCH
' 遍歷所有文件子結點。 Dim Node As IXMLDOMNode Dim Attr As IXMLDOMNode Dim i As Integer
For i = 0 To PNode.childNodes.Length - 1 Set Node = PNode.childNodes(i) Select Case Node.nodeName Case "configUrl" Set Attr = Node.Attributes.getNamedItem("url") If Not Attr Is Nothing Then m_ConfigUrl = Attr.nodeValue Case "configPath" Set Attr = Node.Attributes.getNamedItem("path") If Not Attr Is Nothing Then m_ConfigPath = Attr.nodeValue Case "baseUrl" Set Attr = Node.Attributes.getNamedItem("url") If Not Attr Is Nothing Then m_BaseUrl = Attr.nodeValue Case "localPath" Set Attr = Node.Attributes.getNamedItem("path") If Not Attr Is Nothing Then m_LocalPath = Attr.nodeValue Case "remotePath" Set Attr = Node.Attributes.getNamedItem("path") If Not Attr Is Nothing Then m_RemotePath = Attr.nodeValue End Select Next
Exit Sub CATCH: ' Err.Raise MODULEID + 12, "XMLConfiguration.LoadPaths" ' Err.Clear End Sub
'{ 裝載下載路徑。Cable Fan 2009-08-14 } Private Sub LoadPaths(PNode As IXMLDOMNode) On Error GoTo CATCH
' 遍歷所有文件子結點。 Dim Node As IXMLDOMNode Dim Attr As IXMLDOMNode Dim i As Integer
For i = 0 To PNode.childNodes.Length - 1 Set Node = PNode.childNodes(i) Select Case Node.nodeName Case "configUrl" Set Attr = Node.Attributes.getNamedItem("url") If Not Attr Is Nothing Then m_ConfigUrl = Attr.nodeValue Case "configPath" Set Attr = Node.Attributes.getNamedItem("path") If Not Attr Is Nothing Then m_ConfigPath = Attr.nodeValue Case "baseUrl" Set Attr = Node.Attributes.getNamedItem("url") If Not Attr Is Nothing Then m_BaseUrl = Attr.nodeValue Case "localPath" Set Attr = Node.Attributes.getNamedItem("path") If Not Attr Is Nothing Then m_LocalPath = Attr.nodeValue Case "remotePath" Set Attr = Node.Attributes.getNamedItem("path") If Not Attr Is Nothing Then m_RemotePath = Attr.nodeValue End Select Next
Exit Sub CATCH: ' Err.Raise MODULEID + 12, "XMLConfiguration.LoadFiles" ' Err.Clear End Sub |
與Delphi中不同的是,讀取一個結點的屬性值時,要判斷屬性的存在性,試圖讀取返回的空值將引發錯誤。