3.3.升级实现方案及编码实现

杀毒软件需要及时升级病毒库,更新特征码,以保证能够识别最新爆发的病毒。为了完成这个操作,需要用到升级服务器(或者虚拟主机也可以满足基本需要)。将要升级的文件,连同新的版本号文件放置在服务器固定位置上。软件中以IP地址或域名的方式访问这个版本号文件,获取最新的版本号,然后跟保存在用户端本地的版本号进行对比。如果检测到服务器上的版本号更高,表明有新的文件需要升级。
病毒检测与杀毒技术大揭秘7-LMLPHP
升级其实是一个很简单的功能,主要实现的是文件下载功能,为了能够在程序中进行版本号判断以及替换正在执行的文件等操作,我们将此功能分为两个部分,一部分写在应用程序中,一部分写做一个单独的模块控件。模块中的编码如下:

主动事件,用于向用户显示下载进度:
Public Event DownloadProcess(lProgress As Long)

主动事件,下载完文件后触发此函数:
Public Event DownLoadFinish()

主动事件,错误信息输出函数:
Public Event ErrorMassage(sDescription As String)

控件初始化函数:
Public Function Init() As Boolean
bConnect = True

该函数是第一个由应用程序调用的 WinINet 函数。它告诉 Internet DLL 初始化内部数据结构并准备接收应用程序之后的其他调用。当应用程序结束使用Internet函数时,应调用 InternetCloseHandle 函数来释放与之相关的资源:
lOpen=InternetOpen(scUserAgent,INTERNET_OPEN_TYPE_PRECONFIG,vbNullString,vbNullString,0)

通过一个完整的FTP、Gopher或HTTP网址打开一个资源:
lFile=InternetOpenUrl(lOpen,sUrl,vbNullString,0, INTERNET_FLAG_RELOAD, 0)
sBuffer = Space(1024)
lBuffer = 1024

从服务器获取HTTP请求头信息:
bRetQueryInfo = HttpQueryInfo(lFile, 21, sBuffer, lBuffer, 0)
sBuffer = Mid(sBuffer, 1, lBuffer)
End Function

开始下载文件函数:

Public Function StartUpdate() As Boolean

Dim bBuffer(1 To 1024) As Byte
Dim lRet As Long
Dim lDownloadSize As Long
Dim i As Long

Dim lFileNumber As Long
lFileNumber = FreeFile()

下载保存文件方式:下载保存为file.exe.bak下载完成后删除file.exe,再重命名 file.exe.bak 为file.exe,这是为了防止file.exe在下载时出现网络错误文件只被写入一部分数据而引起错误:

Dim sTempDownloadFile As String
sTempDownloadFile = Trim(sSaveFile) & “.bak”
If Dir(sTempDownloadFile) <> “” Then
Call DeleteFile(sTempDownloadFile)
End If

打开文件,保存下载数据:

Open sTempDownloadFile For Binary Access Write As #lFileNumber
Do

读取被下载文件内容:

InternetReadFile lFile, bBuffer(1), 1024, lRet
If lRet = 1024 Then

将读取到的数据写入文件:

Put #1, , bBuffer
Else
For i = 1 To lRet
Put #1, , bBuffer(i)
doEvents
Next i
end If
lDownloadSize = lDownloadSize + lRet

引发下载进度事件,这是为了在下载文件时让用户了解下载进展情况,防止用户以为程序假死:

RaiseEvent DownloadProcess(lDownloadSize)

Loop Until lRet < 1024
Close #lFileNumber

检查已下载字节数与要下载文件大小是否一至,一至说明下载完成了:
If lDownloadSize = CLng(GetHeader(“Content-Length”)) Then

下载完成后,删除原文件,重命名下载文件为原文件名:

If Dir(Trim(sSaveFile)) <> “” Then
Call DeleteFile(sSaveFile)
End If
Sleep 50
If Dir(sSaveFile) = “” Then

重命名下载的文件:

Name sTempDownloadFile As sSaveFile
End If
DoEvents
End If

End Function

获取要下载文件的信息:
Public Function GetHeader(Optional hdrName As String) As String

Dim sTemp As Long
Dim sTemp2 As String
Select Case UCase(hdrName)

获取要下载的文件大小:

Case “CONTENT-LENGTH”
sTemp = InStr(sBuffer, “Content-Length”)
sTemp2 = Mid(sBuffer, sTemp + 16, Len(sBuffer))
sTemp = InStr(sTemp2, Chr(0))
GetHeader = CStr(Mid(sTemp2, 1, sTemp - 1))
End Select

End Function

设置属性,即要下载的文件的网络地址:

Public Property Let URL(ByVal sInUrl As String)
sUrl = sInUrl
End Property

以上是控件中的编码,之所以使用控件,是为了能够使用主动事件,方便的与界面互交,比如:即时的显示下载进度,使用户能看到下载进行了多少、下载完成后能做出相应的消息提示。

在程序更新病毒库或其它程序文件时,首先会进行如下的方式进行调用,获取网络服务器文件中的版本号,如果版本号高于本地版本号,则进行更新,如果与本地版本号一至,则说明本地已经是最新版本,无需更新:

Dim sUpdateFiles As String sUpdateFiles=DetectUpdateFile("http://xxx.cn/update/update.txt")

DetectUpdateFile函数的代码如下:

Public Function DetectUpdateFile(sUrl As String) As String

Dim lInternetOpenUrl As Long
Dim lInternetOpen As Long
Dim sTemp As String * 1024
Dim lInternetReadFile As Long
Dim lSize As Long
Dim sContent As String
sContent = vbNullString
lInternetOpen = InternetOpen("update", 1, vbNullString, vbNullString, 0)
lInternetOpenUrl=InternetOpenUrl(lInternetOpen, sUrl, vbNullString, 0, INTERNET_FLAG_NO_CACHE_WRITE, 0)
Do
lInternetReadFile = InternetReadFile(lInternetOpenUrl, sTemp, 1024, lSize)
sContent = sContent & Mid(sTemp, 1, lSize)
Loop While (lSize <> 0)
lInternetReadFile = InternetCloseHandle(lInternetOpenUrl)

升级查询返回以“Update”开始为标识
If UCase(Left(sContent, 6)) = UCase("Update") Then
DetectUpdateFile = Right(sContent, Len(sContent) - 6)
Else
DetectUpdateFile = ""
End If

End Function

这部分代码与上面文件下载控件中的代码非常类似,都是用来下载文件的,不同在于此处下载文件后会读取文件内容。

文件内容是我们预设好的,本地设置文件中有版本号,升级时,从服务器下载版本号文件并从中读取内容获取最新整体版本号,与当前软件中的整体版本号进行比对,如果网络中的版本号更高,则进行更新,调用升级控件下载升级文件。每次升级把设置文件同时也下载,以同步版本号。

我们设定网络中版本号文件格式为:“Update”(6位,用于标识是升级校验文件)+版本号(6位)+待升级文件+"$"(结束符)

获取到文件信息后,然后对比版本号,如果服务器中的版本号更高,则表示有文件需要下载更新,那么我们就需要下载文件。编码如下:

取得服务器文件中的版本号:

Dim lNewWholeVersion As Long
lNewWholeVersion = Left(sUpdateFiles, 6)
sUpdateFiles = Right(sUpdateFiles, Len(sUpdateFiles) - 6)

取得本地版本号,在这里Version.ini文件中存放着本地版本号:

Dim sVersionFile As String
sVersionFile = App.Path & “\Version.ini”
Dim lOldWholeVersion As Long
lOldWholeVersion = ReadIni(sVersionFile, “Version”, “WholeVersion”)

判断是否需要升级:

If lNewWholeVersion <= lOldWholeVersion Then
MsgBox “已是最新版本,无需升级。”
End If

如果有持更新内容,从返回内容中获取要升级的文件并下载:

Do While Not sUpdateFiles = “”
sNewFile = Left(sUpdateFiles, InStr(1, sUpdateFiles, “")1)sUpdateFiles=Right(sUpdateFiles,Len(sUpdateFiles)InStr(1,sUpdateFiles,"”))

如果是要下载exe文件,先用MoveFileEx函数去掉它的执行保护,这样就不会出现替换正在执行的文件而产生错误的问题:

If InStr(1, LCase(sNewFile), LCase(".exe")) Then
Call MoveFileEx(sNewFile, RndChr(6), 1)
End If

使用上面写好的升级控件下载文件,假设控件名为UserControlUpdate1:

With UserControlUpdate1

构造文件下载地址:

.URL = "http://xxx.cn/update/" & sNewFile 
.SaveFile = App.Path & "\" & sNewFile 
If .Init = True Then
lCurrentFileSize = .GetHeader("Content-Length")

开始下载文件:

.StartUpdate
End If
End With

Loop

以上是升级功能的思现思路及编码方法,在实际软件的编码中,除了实现文件下载的功能,还需要考虑其它辅助因素,比如在下地时,要将文件显示在界面中,告诉用户正在下载哪个文件,以及显示下载进度,以给用户一个更为直观和亲切的使用感受。

注:作者:wing qq:6465660
本书理论及功能、代码源于杀毒软件:“Ty2y杀毒软件”,作者授意,文章可自由转载,只需注明原出处即可,特此说明。

10-07 18:43