编程语言

img Raptor
博客专家

软件的版本更新检查实现

发表于2004/10/31 20:18:00  4760人阅读

分类: Borland(Delphi/BCB...)

软件的自动更新检查

[Mental Studio]猛禽[Blog]

还是在“PIA-MyPhotoGallery”中,为了能让使用者及时知道软件的更新版本发布,我增加了自动更新检查功能。鉴于这种功能具有很好的实用价值,所以写本文说明此功能的实现。

要实现更新检查,需要解决两个方面的问题:

1、通过Internet获取最新发布的版本号;

2、取得当前程序的版本,并与取得的最新版本相比较。

如果检查到有新版本发布,则打开下载页面(至于直接下载更新本文暂不讨论)。

对于第一个问题,最简单的解决办法就是在网站上发布新版本软件的同时,发布一个记录着版本号的文件。在软件进行版本检查时(比如程序启动时),通过Internet下载此文件,并读出最新的版本号。

注意:此方法仅适用于简单更新的情况,对于像杀毒软件的病毒库这样增量更新的情况,这种简单方法是不合适的,通常还需要有相应的服务端程序配合才行。

要通过Internet下载文件有很多方法,比如用WinINet API或现成的控件都可以。本文以Indy的TIdHTTP控件为例。

TIdHTTP控件的用法非常简单,但是直接使用它下载会有一个问题:程序会被阻塞着,直到文件被下载或连接超时(比如网络未连接)。所以必须将它放到线程中处理。

在PIA-MyPhotoGallery中,我用的代码如下:

//---------------------------------------------------------------------------
//  Get new version thread
class TGetNewVersionThread : public TThread
{
private:
    AnsiString      FURL;
    TMFileVersion * FVer;
protected:
    void __fastcall Execute( );
public:
    __fastcall TGetNewVersionThread( AnsiString aURL )
        : TThread( true ), FURL( aURL ), FVer( new TMFileVersion( ) )
    {
        FreeOnTerminate = true;
    }
    __fastcall ~TGetNewVersionThread( ) { delete FVer; }
    __property TMFileVersion * Version  = { read=FVer };
};
//---------------------------------------------------------------------------
//  TGetNewVersionThread
//---------------------------------------------------------------------------
void __fastcall TGetNewVersionThread::Execute( )
{
    boost::scoped_ptr webConn( new TIdHTTP( NULL ) );
    boost::scoped_ptr ss( new TStringList( ) );
    try {
        ss->Text = webConn->Get( FURL );
    }
    catch ( ... )
    {
        ss->Text = "";
    }
    AnsiString s = ss->Values["piapg"];
    if ( s != "" )
        FVer->VerStr = s;
}
//---------------------------------------------------------------------------

这段代码很简单:创建一个线程,在线程里创建一个TIdHTTP实例,然后下载URL对应的文件,最后从中读出“piapg”的版本号。为了偷懒,我用了boost库里的smart pointer--scoped_ptr。

这个线程类的使用方法如下:

//---------------------------------------------------------------------------
//  在程序启动时执行:
    if ( PIAPGCfg->AutoUpd )  //  如果选择了“检查更新”选项则执行检查
    {
        if ( SplashDlg )  //  如果有splash,则在其中显示提示文本
        {
            SplashDlg->labProgress->Caption = "正在检查新版本...";
            SplashDlg->labProgress->Refresh( );
        }
        //  创建检查新版本的线程
        TGetNewVersionThread * pThread = new TGetNewVersionThread( "http://mental.mentsu.com/update.txt" );
        pThread->OnTerminate = GetNewVersionDone;
        pThread->Resume( );
    }
//---------------------------------------------------------------------------
//  版本文件下载完成或超时
void __fastcall TMainForm::GetNewVersionDone(TObject * Sender)
{
    TGetNewVersionThread * pThread = dynamic_cast( Sender );
    boost::scoped_ptr fv( new TMFileVersion( ) );
    fv->GetVersionFromFile( Application->ExeName );  //  读取当前程序的版本
    if ( ( pThread->Version->Compare( fv.get( ) ) > 0 )  //  如果有新版本,则提示
        && ( Application->MessageBox( "发现更新版本的程序,是否现在更新?",
        "新版本检查", MB_YESNO | MB_ICONINFORMATION ) == IDYES ) )
    {
        ShellExecute( NULL, "open", "http://mental.mentsu.com", NULL, NULL, SW_SHOW );
        PostQuitMessage( 0 );
    }
}
//---------------------------------------------------------------------------

此代码的功能详见其中的注释。

再来看第二个问题:程序版本的问题。

在上面的代码中,用到了一个类:TMFileVersion。这是我以前用DELPHI写的一个用于处理可执行文件版本号的类。实现代码如下:

    TMFileVersion = class
    private
        FMajor   : Integer;
        FMinor   : Integer;
        FRelease : Integer;
        FBuild   : Integer;
        Function  GetVerStr : String;
        Procedure SetVerStr( aVerStr : String );
    public
        constructor Create;
        destructor Destroy; override;
        Procedure GetVersionFromFile( aFileName : String );
        Function  Compare( aVer : TMFileVersion ) : Integer;
        Property VerStr : String read GetVerStr write SetVerStr;
    End;
{ TMFileVersion }
//  init
constructor TMFileVersion.Create;
Begin
    Inherited;
    FMajor   := 0;
    FMinor   := 0;
    FRelease := 0;
    FBuild   := 0;
End;
destructor TMFileVersion.Destroy;
Begin
    Inherited;
End;
//  Get version info from a file
Procedure TMFileVersion.GetVersionFromFile( aFileName : String );
Type
    PVS_FIXEDFILEINFO = ^VS_FIXEDFILEINFO;
Var
    h : Cardinal;        // a handle, ignore
    nSize : Cardinal;    // version info size
    pData : Pointer;     // version info data
    pffiData : PVS_FIXEDFILEINFO;  // fixed file info data
    nffiSize : Cardinal; // fixed file info size
Begin
    FMajor   := 0;
    FMinor   := 0;
    FRelease := 0;
    FBuild   := 0;
    If ( FileExists( aFileName ) ) Then
        FBuild := 1;
    nSize := GetFileVersionInfoSize( PChar( aFileName ), h );
    If ( nSize = 0 ) Then
        Exit;
    GetMem( pData, nSize );
    Try
        GetFileVersionInfo( PChar( aFileName ), h, nSize, pData );
        If ( VerQueryValue( pData, '/', Pointer( pffiData ), nffiSize ) ) Then
        Begin
            FMajor   := ( pffiData^.dwFileVersionMS ) SHR 16;
            FMinor   := ( pffiData^.dwFileVersionMS ) AND $FFFF;
            FRelease := ( pffiData^.dwFileVersionLS ) SHR 16;
            FBuild   := ( pffiData^.dwFileVersionLS ) AND $FFFF;
        End;
    Finally
        FreeMem( pData );
    End;
End;
//  Compare two version info
Function TMFileVersion.Compare( aVer : TMFileVersion ) : Integer;
Var
    n1, n2 : Cardinal;
Begin
    n1 := ( FMajor SHL 16 ) OR FMinor;
    With aVer Do
        n2 := ( FMajor SHL 16 ) OR FMinor;
    If ( n1 > n2 ) Then
        Result := 1
    Else If ( n1 < n2 ) Then
        Result := -1
    Else
    Begin
        n1 := ( FRelease SHL 16 ) OR FBuild;
        With aVer Do
            n2 := ( FRelease SHL 16 ) OR FBuild;
        If ( n1 > n2 ) Then
            Result := 1
        Else IF ( n1 < n2 ) Then
            Result := -1
        Else
            Result := 0;
    End;
End;
//  Get/Set property - VerStr
Function TMFileVersion.GetVerStr : String;
Begin
    Result := Format( '%d,%.02d,%d,%.02d', [FMajor, FMinor, FRelease, FBuild] );
End;
Procedure TMFileVersion.SetVerStr( aVerStr : String );
Var
    sTemp : TStrings;
Begin
    FMajor   := 0;
    FMinor   := 0;
    FRelease := 0;
    FBuild   := 0;
    sTemp := TStringList.Create;
    Try
        sTemp.CommaText := aVerStr;
        Try
            FMajor   := StrToInt( sTemp.Strings[0] );
            FMinor   := StrToInt( sTemp.Strings[1] );
            FRelease := StrToInt( sTemp.Strings[2] );
            FBuild   := StrToInt( sTemp.Strings[3] );
        Except
            //  Do nothing
        End;
    Finally
        sTemp.Free;
    End;
End;

解决了这两个问题,自动更新检查的功能也就解决了。

BTW:为方便使用,已经改用DELPHI重写并封装为一个VCL控件

[Mental Studio]猛禽 Oct.30-04

阅读全文
0 0

相关文章推荐

img
取 消
img