自动更新架构参考

时间:2023-02-10 05:59:25
About the AutoUpgrade Class Framework
http://www.devx.com/dotnet/Article/10045/0/page/4

http://www.sml.hw.ac.uk/computing/delphi_updatevir.html

自动更新架构参考 School of Management Computing Help
Delphi Applications: UpdateVir.exe
自动更新架构参考

UpdateVir.exe

Function: Reads Registry to determine current McAffee scan engine and virus database version; runs updated virus engine/database if required; optionally logs results to file. Lists current update files (superdat) on Heriot-Watt FTP site via built-in FTP connection and changes default update FTP site to Heriot-Watt university.

Current Version: 1.6

Author: Duncan Potter

Date: 8th October 2001

Location:sys:public

Download: updatevir.zip (246K)

自动更新架构参考自动更新架构参考

updatevir.pas (main body of code)

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, Buttons, nwbase, nwlib, ExtCtrls, FileCtrl, Registry,INIFiles;

type
  TForm1 = class(TForm)
    LblTitle: TLabel;
    MemoInfo: TMemo;
    BButtNext: TBitBtn;
    BButtCancel: TBitBtn;
    NWBase1: TNWBase;
    LblUserName: TLabel;
    Timer1: TTimer;
    LblCheckEngineVer: TLabel;
    Timer2: TTimer;
    PanelInfo: TPanel;
    LblFullName: TLabel;
    LblDetails: TLabel;
    LblEngineVersion: TLabel;
    LblEngineVerFound: TLabel;
    BButtHelp: TBitBtn;
    LblVirDef: TLabel;
    LblVirDefFound: TLabel;
    LblEngineUpdateVer: TLabel;
    LblVirDefInfo: TLabel;
    LblVirDefUpgrade: TLabel;
    LblCheckVirDef: TLabel;
    LblWinVersion: TLabel;
    procedure Startup(Sender: TObject);
    procedure ReadIni(Sender: TObject);
    procedure BButtCancelClick(Sender: TObject);
    procedure BButtNextClick(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure CheckStatus(Sender: TObject);
    procedure Timer2Timer(Sender: TObject);
    procedure BButtHelpClick(Sender: TObject);
    procedure FTPSiteChange(Sender: TObject);   

  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  Username: String;
  EngineVerFound: string; //That read from Registry
  EngineVerUpgrade: string; //Version of engine in upgrade (read from INI file)
  UpdateExe: string; //Superdat Executable that runs to upgrade scan engine and virus defs
  VirDefFound: string; //DAT file version read from Registry
  VirDefUpgrade: string;//Version of DAT file in the upgrade, read from INI file.
  RunUpdate: Boolean; // If True then update will run.
  WinVersion: String; //either 'W95' or 'NT4-2000'

implementation

uses help;
function CreateProcessAndWait(AppPath, AppParams: String; Visibility: word): DWord ; forward;
function CompareEngineVersion(EngineVerFound, EngineVerUpgrade: String): String; forward;
function CompareVirDefVersion(VirDefFound, VirDefUpgrade: String): String; forward;

{$R *.DFM}

{-----------------------------------------}
procedure TForm1.Startup(Sender: TObject);
begin
  username := whoami(0);
  RunUpdate := False; //Initialise
  LblUserName.Caption := UserName;
  LblUserName.Update;
  LblFullName.Caption := FullName(0,Username);
  LblFullName.Update;

  ReadIni(Sender); //Reads version of engine being upgraded.
  CheckStatus(Sender);//Check if update has already run.
  FTPSiteChange(Sender);//Change auto-update to HW FTP site
end;
{------------------------------------------------}
procedure TForm1.ReadIni(Sender: TObject);
{Reads INI file to find initial settings}
Var
AppINI: TIniFile; {Variable of type INI file required for "ReadSectionValues"}
INIFile: String; {Name of INI file}

begin
  try
     INIFile := ExtractFilePath(application.exename)+ 'updatevir.ini';
     if not FileExists(INIFile) then
        begin
           ShowMessage('Fatal Error: initialisation file ' + INIFile +' cannot be found');
           BButtCancelClick(Sender); //Quit
           exit;
        end;
     AppIni := TIniFile.Create(INIFile); {assign memory for TIniFile object}

    {Read the engine version to the application window}
     EngineVerUpgrade := trim(AppIni.ReadString('Engine','EngineUpgradeVer','engine unknown'));
     LblEngineUpdateVer.Caption := EngineVerUpgrade;
     {Read location of SuperDat update exe to be run}
     UpdateExe :=trim(AppIni.ReadString('Engine','UpdateExe','unknown'));
     {Read version of virus definitions in the upgrade }
     VirDefUpgrade :=trim(AppIni.ReadString('Engine','VirDefUpgrade','unknown'));
     LblVirDefUpgrade.Caption := VirDefUpgrade;

  finally
       AppIni.Free;
  end;
end;
{-----------------------------------------------}
procedure TForm1.BButtCancelClick(Sender: TObject);
{Close form, check if update has been allowed to run.}
begin
//     if EngineVerFound <> EngineVerUpgrade then  //Update required
 //       if not FoundMoreRecent then //skip this check if more recent engine detected
 //          if MessageDlg('Are you sure your virus scan engine is more recent than '+ EngineVerUpgrade+' ?', mtConfirmation, [mbYes, mbNo], 0) = mrYes then
//              ShowMessage('Okay. Please ignor this check.')
 //          else
 //             ShowMessage('No problem. This program will run again when you next login.');

     MemoInfo.Clear;
     MemoInfo.Update;
     Close;
     Application.Terminate;
end;

{---------------------------------------------------}
procedure TForm1.FTPSiteChange(Sender: TObject);
{On NT/2000 changes the FTP DAT auto-update path to the Heriot-Watt site}
var
Reg: TRegistry;
FTPLocationHW: String;  //Where McAfee should get it's FTP update info.
FTPLocationFound: String;  //Where McAfee looks for it's FTP update.

begin
   try
     Reg:=TRegistry.Create;
     Reg.RootKey:=HKey_Local_Machine; // Section to look for within the registry
     if not Reg.KeyExists('Software/McAfee/VirusScan/Tasks/Update') then exit //Not an NT machine
     else //An NT or W2000 machine with the required keys
      begin    //Read from Registry
        FTPLocationHW := 'ftp.hw.ac.uk/pub/internal/antivirus/update/';
        Reg.OpenKey('Software/McAfee/VirusScan/Tasks/Update',False);//False = Don't create
        if Reg.ValueExists('szUpdateFTPLocation') then FTPLocationFound:= trim(Reg.ReadString('szUpdateFTPLocation'));
        if lowercase(FTPLocationFound) <> lowercase(trim(FTPLocationHW)) then //change to HW site
          begin
             try
                 Reg.WriteString('szUpdateFTPLocation',FTPLocationHW); //Change to HW campus one.
             except
                begin
                  LblCheckVirDef.Caption :='Unable to change FTP location.' ;
                  LblCheckVirDef.Update;
                end;
             end; // of try-except
          end;
       end;
    finally
      Reg.CloseKey;
      Reg.Free;
    end;
end;
{---------------------------------------------------}
procedure TForm1.BButtNextClick(Sender: TObject);
{Runs the anti-virus update}
//var
//   UpdateExe: String;
begin
//     UpdateExe := 'n:/School/install/vscan4.03/autoupgrade/setup.exe';
     if not FileExists(UpdateExe) then
       begin
          ShowMessage('Cannot find ' + UpdateExe);
          exit;
       end;
     Form1.Visible := False;
     CreateProcessAndWait(UpdateExe,'',1);
     BButtNext.Visible := False;
     BButtCancel.Visible := False;
     LblFullName.Visible := False;
     Timer1.Enabled := True;  //startup countdown to exit
     MemoInfo.Clear;
     MemoInfo.Visible := False;

     LblCheckVirDef.Caption := 'Update is complete !';
     Form1.ClientHeight := 104;
     Form1.Visible := True;
end;
{-------------------------------------------------------}
function CreateProcessAndWait(AppPath, AppParams: String; Visibility: word): DWord;
var
  SI: TStartupInfo;
  PI: TProcessInformation;
  Proc: THandle;
begin
  FillChar(SI, SizeOf(SI), 0);
  SI.cb := SizeOf(SI);
  SI.wShowWindow := Visibility;
  if not CreateProcess(PChar(AppPath), PChar(AppParams), Nil, Nil, False,
                   Normal_Priority_Class, Nil, Nil, SI, PI) then
    raise Exception.CreateFmt('Failed to execute program.  Error Code %d',
                              [GetLastError]);
  Proc := PI.hProcess;
  CloseHandle(PI.hThread);
  if WaitForSingleObject(Proc, Infinite) <> Wait_Failed then
    GetExitCodeProcess(Proc, Result);
  CloseHandle(Proc);
end;
{----------------------------------------}
procedure TForm1.CheckStatus(Sender: TObject);
{Checks
 1) version of DAT files and Engine in Registry}
var
Reg: TRegistry;

begin
   try
     {Read version of engine and DAT files from Registry}
     Reg:=TRegistry.Create;
     Reg.RootKey:=HKey_Local_Machine; // Section to look for within the registry

     {Check if OS is Windows 95 which stores Virus Def version string differently}
     Reg.OpenKey('Software/Microsoft/Windows/CurrentVersion',False);//False = Don't create
     if Reg.ValueExists('ProductName') then WinVersion := 'W95' else WinVersion := 'NT4-2000';
     Reg.CloseKey;
     LblWinVersion.Caption := WinVersion;
     LblWinVersion.Update;

     if not Reg.KeyExists('Software/McAfee/VirusScan') then  //No registry key ... odd.
       begin
        EngineVerFound := 'unknown';
        LblEngineVerFound.Caption := 'Unknown';
       end
    else
      begin    //Read from Registry
        Reg.OpenKey('Software/McAfee/VirusScan',False);//False = Don't create
        if Reg.ValueExists('szEngineVer') then EngineVerFound:= trim(Reg.ReadString('szEngineVer'));
        LblEngineVerFound.Caption :=EngineVerFound;
        LblEngineVerFound.Update;

        if Reg.ValueExists('szVirDefVer') then VirDefFound := trim(Reg.ReadString('szVirDefVer'));
        //NT4 has format 4.0.4121
        //W95 has format 4121
        LblVirDefFound.Caption := VirDefFound;
        LblVirDefFound.Update;
      end;

   {Initial check to make sure value has real data, not 'unknown' etc.}
   if lowercase(EngineVerFound) = 'unknown' then
     begin
        LblCheckEngineVer.Caption:= 'Unknown scan engine.';
        LblCheckEngineVer.Update;
        BButtCancelClick(Sender); //quit without action
        exit;
     end;
   if length(EngineVerFound) <2 then //maybe no data or faulty data.
     begin
        LblCheckEngineVer.Caption:= 'Unknown scan engine.';
        LblCheckEngineVer.Update;
        BButtCancelClick(Sender); //quit without action
        exit;
     end;


   {Now take action depending on what version of engine was found:}
   if EngineVerFound = EngineVerUpgrade then //no need to update engine, same versions
     begin
       // Form1.ClientHeight := 112; //Reduce height of form
        LblCheckEngineVer.Caption := 'Status: Engine already updated, thank-you !';
        LblCheckEngineVer.Update;
     //   Timer1.Enabled := True; //Countdown to Exit
     end
   else
     begin
        if CompareEngineVersion(EngineVerFound, EngineVerUpgrade) = 'upgrade_yes' then
           begin
              RunUpdate := True; //Set global flag to run the update exe.
              MemoInfo.Visible := True;
              MemoInfo.Update;
              LblCheckEngineVer.Caption := 'Status: Engine update required';
              LblCheckEngineVer.Font.Color := clRed;
              LblCheckEngineVer.Update;
           end
        else //function must have detected a later version
           begin
//            Form1.ClientHeight := 112; //Reduce height of form
             LblCheckEngineVer.Caption := 'Status: Current engine is more recent.';
             LblCheckEngineVer.Update;
//             Timer1.Enabled := True; //Countdown to Exit
           end;
     end;


   {NT4-2000 code************** Now check version of virus definitions **********************
   NT4 has VirDefFound and VirDefUpgrade both in same format e.g. 4.0.4120 so next test will work:}
   if VirDefFound = VirDefUpgrade then //no need to update engine, same versions
     begin
 //       Form1.ClientHeight := 112; //Reduce height of form
        LblCheckVirDef.Caption := 'Status: Virus Definitions already updated, thank-you !';
        LblCheckVirDef.Update;
        if not RunUpdate then
          begin
             Form1.ClientHeight := 112; //Reduce height of form
             Timer1.Enabled := True; //Countdown to Exit, since engine doesn't need update either
          end;
     end
   else     //Call the procedure which determines if a later or earlier version is installed
     begin  // OR W95 is installed, in which case first test in IF block would also fail:
        if CompareVirDefVersion(VirDefFound, VirDefUpgrade) = 'upgrade_yes' then
           begin
              MemoInfo.Visible := True;
              MemoInfo.Update;
              LblCheckVirDef.Caption := 'Status: Virus Definition update required';
              LblCheckVirDef.Font.Color := clRed;
              LblCheckVirDef.Update;
           end
        else //function must have detected a later version so quit unless Engine needs update
           begin
             LblCheckVirDef.Caption := 'Status: Current virus definitions are more recent.';
             LblCheckVirDef.Update;
             if not RunUpdate then
               begin
                   Form1.ClientHeight := 112; //Hides the NEXT button, etc.
                   Timer1.Enabled := True;  //Start close procedure
               end;
           end;
     end;
 finally
    Reg.CloseKey;
    Reg.Free; //Free Registry memory.
 end;
end;
{-------------------------------------------------------}
function CompareEngineVersion(EngineVerFound, EngineVerUpgrade: String): String;
{Compares two version strings, higher numbers indicate later version}
{e.g.
 EngineVerFound       4.0.60
 EngineVerUpgrade     4.0.70
so break numbers into parts and compare each '.' delimited value}
var
E_VerF: array[1..3] of string; //Array holding characters in EngineVerFound
E_VerU: array[1..3] of string; //Array holding characters in EngineVerUpgrade
i,j: integer; //loop counter
bit, test: string;

begin
  if length(EngineVerFound)<2 then
    begin
     Result := 'upgrade_no';
     exit;
    end;

   j:=1;
   for i:=1 to length(EngineVerFound) do   //step through characters
     begin
       bit := copy(EngineVerFound,i,1);// Just a single character copied from the string
       if bit = '.' then  //we've completed one subversion of the engine version
         begin
            j:= j+1; //So next E_Ver[j] to be incremented will refer to next subversion number
         end
       else
         begin //Still building subversion string or at the end of the string.
            E_VerF[j] := E_VerF[j] +bit;
         end;
     end;

   {repeat for the Upgrade Engine ... slightly wasteful of code}
   j:=1;
   for i:=1 to length(EngineVerUpgrade) do   //step through characters
     begin
       bit := copy(EngineVerUpgrade,i,1);// Just a single character copied from the string
       if bit = '.' then  //we've completed one subversion of the engine version
         begin
            j:= j+1; //So next E_Ver[j] to be incremented will refer to next subversion number
         end
       else
         begin //Still building subversion string or at the end of the string.
            E_VerU[j] := E_VerU[j] +bit;
         end;
     end;
   {
   test :=  e_verU[1] +'-' +e_verU[2] +'-'+ e_verU[3];
   ShowMessage('Upgrade version was '+Test);

   test :=  e_verF[1] +'-' +e_verF[2] +'-'+ e_verF[3];
   ShowMessage('Detected version was '+Test);
   }
  
   {Now compare each version, most significant bit first. Convert to integers first}
   {If one subelement of Found version > that of Upgrade version then don't upgrade}
   Result := 'upgrade_yes'; //set a default
   for j:= 1 to 3 do
     begin
       // ShowMessage('Found: '+E_VerF[j]+'      Upgrade: '+E_VerU[j]);
        if StrToInt(E_VerF[j]) > StrToInt(E_VerU[j]) then //one subversion found > equiv. subversion upgrade
          begin
            Result := 'upgrade_no';
            exit;
          end;
     end;

end;
{-------------------------------------------------------}
function CompareVirDefVersion(VirDefFound, VirDefUpgrade: String): String;
{Compares two version strings of Virus Defs, higher numbers indicate later version}
{e.g.
 VirDefFound       4.0.60
 VirDefUpgrade     4.0.4104
so break numbers into parts and compare each '.' delimited value.
Windows 95 stores only the last number - 4104 - whereas NT4 and 2000 store 4.0.4104, for example.}
var
E_VerF: array[1..3] of string; //Array holding characters in VirDefFound
E_VerU: array[1..3] of string; //Array holding characters in VirDefUpgrade
i,j: integer; //loop counter
bit, test: string;

begin
  if length(VirDefFound)<2 then
    begin
     Result := 'upgrade_no';
     exit;
    end;

   {Split NT4/2000 Virus Def strings into three parts:}
   j:=1;
   for i:=1 to length(VirDefFound) do   //step through characters
     begin
       bit := copy(VirDefFound,i,1);// Just a single character copied from the string
       if bit = '.' then  //we've completed one subversion of the engine version
         begin
            j:= j+1; //So next E_Ver[j] to be incremented will refer to next subversion number
         end
       else
         begin //Still building subversion string or at the end of the string.
            E_VerF[j] := E_VerF[j] +bit;
         end;
     end;

   {repeat for the Upgrade Engine ... slightly wasteful of code}
   j:=1;
   for i:=1 to length(VirDefUpgrade) do   //step through characters
     begin
       bit := copy(VirDefUpgrade,i,1);// Just a single character copied from the string
       if bit = '.' then  //we've completed one subversion of the engine version
         begin
            j:= j+1; //So next E_Ver[j] to be incremented will refer to next subversion number
         end
       else
         begin //Still building subversion string or at the end of the string.
            E_VerU[j] := E_VerU[j] +bit;
         end;
     end;

    {Start W95 code, which above tests will not work for. This compares the final string of the
     upgrade virus definition with the VirDefFound string
     e.g. Last number of VirDefUpgrade (4.1.4014) is here '4014'
          VirDefFound for W95 is the single number '4014'
     The test thus compares the least significant bit of VirDefUpgrade, which isn't good but that's
     how McAffee store the virus definition version string.
     }
     if WinVersion = 'W95' then
          if StrToInt(E_VerU[3]) > StrToInt(VirDefFound) then
             begin
               ShowMessage(VirDefFound);
               Result := 'upgrade_yes';
               exit; //Don't process NT4-2000 code
             end
           else
             begin
                Result := 'upgrade_no';
                exit;
             end;


   {
   test :=  e_verU[1] +'-' +e_verU[2] +'-'+ e_verU[3];
   ShowMessage('Virus Def Upgrade version was '+Test);

   test :=  e_verF[1] +'-' +e_verF[2] +'-'+ e_verF[3];
   ShowMessage('Virus Def Detected version was '+Test);
   }

   {Now compare each version, most significant bit first. Convert to integers first}
   {If one subelement of Found version > that of Upgrade version then don't upgrade}
   Result := 'upgrade_yes'; //set a default
   for j:= 1 to 3 do
     begin
       // ShowMessage('Found: '+E_VerF[j]+'      Upgrade: '+E_VerU[j]);
        if StrToInt(E_VerF[j]) > StrToInt(E_VerU[j]) then //one subversion found > equiv. subversion upgrade
          begin
            Result := 'upgrade_no';
            exit;
          end;
     end;
 end;

{-------------------------------------------------------}
procedure TForm1.Timer1Timer(Sender: TObject);
begin
     BButtCancelClick(Sender);
end;

{-------------------------------------}
procedure TForm1.Timer2Timer(Sender: TObject);
{Flashes the NEXT button}
begin
     if BButtNext.Caption = 'NEXT' then
       begin
          BButtNext.Caption := '';
          BButtNext.Update;
       end
     else
       begin
          BButtNext.Caption := 'NEXT';
          BButtNext.Update;
       end;
end;
{-----------------------}
procedure TForm1.BButtHelpClick(Sender: TObject);
begin
     FrmHelp.ShowModal;    
end;
{---------------------------------}

 

end.

 

updatevir.ini

[Engine]
UpdateExePath=n:/School/install/vscan4.03/autoupgrade/
SearchMask=sdat*.exe
EngineUpgradeVer=4.1.50
VirDefUpgrade=4.0.4163

[Display]
Caption=School of Management    Updatevir.exe v1.6             Duncansoft 2001

[log]
loginfo=yes
logfile=s:/transfer/duncan/virlog.txt

[Help]
UpdateExePath  - where the Superdat update file should be placed
SearchMask - file filter used on above folder to locate the relevant sdat file.
EngineUpgradeVer- e.g 4.0.70, current version of scan engine
VirDefUpgrade - e.g. 4.0.4104, current version of virus definition database.

loginfo  - logs scan engine / DAT version info to a logfile
logfile  - full name of logfile, including path.