{$IFNDEF CLR}
{$IFNDEF EntityDAC}
{$I Dac.inc}
unit CRVersionChecker;
{$ENDIF}
{$ENDIF}

interface

uses
  Classes, SysUtils, Windows, WinInet, Math, Forms, StdCtrls, Controls,
  ExtCtrls, Registry, Graphics,
{$IFNDEF EntityDAC}
{$IFDEF CLR}
  System.Runtime.InteropServices, System.Reflection, Borland.Vcl.Variants,
{$ENDIF}
  CRTypes, CRFunctions, HelpUtils;
{$ELSE}
  EntityDAC.Common.Types, EntityDAC.Common.Functions, EntityDAC.Design.Common.HelpUtils;
{$ENDIF}

type
  TResultCode = (rcSuccess, rcFileNotFound, rcIncorrectSecurity, rcProductNotFound, rcInvalidUrl, rcNoPermissions);

  TFileVersion = record
    Major,
    Minor,
    Release,
    Build: integer;
    Edition: string;
  end;
  TDACInfo = record
    Name,
    Version,
    Edition: string;
  end;
  TDACVersion = record
    Name,
    CurrentVersion,
    NewVersion,
    Edition: string;
  end;

const
  ResultErrorMessages: array[TResultCode] of string = (
    'Operation completed successfully',
    'Cannot find a new installation file.',
    'Invalid login or password.',
    'The product is not found on the site.',
    'Invalid URL was generated.',
    'There is no access to the site or invalid login or password.'
  );

  DACUpdatesRegistryKey = {$IFNDEF EntityDAC}'Software\Devart\DAC\CheckUpdates'{$ELSE}'Software\Devart\EntityDAC\CheckUpdates'{$ENDIF};

type
  TVersionChecker = class
  public
    class function GetLatestVersion(const ProductName, ProductEdition, ProductVersion: string): string;
    class function VersionToInt(Version: string): Extended;
    class function GetFileVersion(FileName: string): TFileVersion;
    class procedure ShowDACUpdates; {$IFDEF CLR}static;{$ENDIF}
  end;

  TVersionCheckerThread = class (TThread)
  protected
    procedure Execute; override;
  public
    constructor Create;
  end;

  TCheckUpdatesForm = class (TForm)
  private
    pCaption: TPanel;
    pDACs: TPanel;
    pControls: TPanel;
    img: TImage;
    lbl: TLabel;
    bt: TButton;
    cb: TCheckBox;
    bevTop: TBevel;
    bevBottom: TBevel;
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure ButtonClick(Sender: TObject);
    procedure LabelClick(Sender: TObject);
    procedure LabelMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
    procedure PanelMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
  end;

{$I IdeConsts.inc}

implementation

var
  DACVersionArr: array of TDACVersion;
  DoNotShowDACList: TStringList;

procedure LoadDoNotShowDACList;
var
  Reg: TRegistry;
  TempList: TStringList;
  ii: integer;
begin
  Reg := TRegistry.Create(KEY_READ OR KEY_WRITE);
  TempList := TStringList.Create;
  try
    DoNotShowDACList.Clear;
    Reg.RootKey := HKEY_CURRENT_USER;
    if Reg.OpenKey(DACUpdatesRegistryKey, False) then begin
      Reg.GetValueNames(TempList);
      for ii := 0 to TempList.Count - 1 do
        DoNotShowDACList.Add(TempList[ii] + '=' + Reg.ReadString(TempList[ii]));
    end;
  finally
    TempList.Free;
    Reg.Free;
  end;
end;

procedure SaveDoNotShowDACList;
var
  Reg: TRegistry;
  ii: integer;
begin
  Reg := TRegistry.Create(KEY_READ OR KEY_WRITE);
  try
    Reg.RootKey := HKEY_CURRENT_USER;
    if Reg.OpenKey(DACUpdatesRegistryKey, True) then begin
      for ii := 0 to DoNotShowDACList.Count - 1 do
        Reg.WriteString(DoNotShowDACList.Names[ii], DoNotShowDACList.Values[DoNotShowDACList.Names[ii]]);
    end;
  finally
    Reg.Free;
  end;
end;

{ TVersionChecker }

class function TVersionChecker.GetLatestVersion(const ProductName, ProductEdition, ProductVersion: string): string;
var
  str: TStringList;
  Url, ResultString: string;
  ResultCode: TResultCode;

  function GetUrlContent(const Url: string): string;
  const
    BLOCK_SIZE = 512;
  var
    InetHandle: HINTERNET;
    UrlHandle: HINTERNET;
    Buffer: TBytes;
    BytesRead: DWORD;
    i: integer;
  begin
    Result := '';
    InetHandle := InternetOpen({$IFNDEF CLR}PChar{$ENDIF}(Url), INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
    if not Assigned(InetHandle) then
      RaiseLastOSError;
    try
      UrlHandle := InternetOpenUrl(InetHandle, {$IFNDEF CLR}PChar{$ENDIF}(URL), nil, 0, INTERNET_FLAG_RELOAD, 0);
      if not Assigned(UrlHandle) then
        RaiseLastOSError;
      try
        SetLength(Buffer, BLOCK_SIZE);
        repeat
          InternetReadFile(UrlHandle, Buffer, BLOCK_SIZE, BytesRead);
          for i := 0 to BytesRead - 1 do
            Result := Result + Chr(Buffer[i]);
        until BytesRead = 0;
      finally
        InternetCloseHandle(UrlHandle);
      end;
    finally
      InternetCloseHandle(InetHandle);
    end;
  end;
begin
  Url := 'http://devart.com/FileUpdateHandler.axd?op=0&product=' + ProductName + '&edition=' + ProductEdition + '&userversion=' + ProductVersion + '&cdata=' + IDEName;
  ResultString := GetUrlContent(Url);
  str := TStringList.Create;
  try
    ExtractStrings([';'], [], PChar(ResultString), str);
    ResultCode := rcInvalidUrl;
    try
      ResultCode := TResultCode(StrToInt(Copy(str[0], 3, Length(str[0]) - 2)));
    except
    end;
    if ResultCode <> rcSuccess then
      raise Exception.Create(ResultErrorMessages[ResultCode]);
    Result := Trim(Copy(str[1], 3, Length(str[1]) - 2));
  finally
    str.Free;
  end;
end;

class function TVersionChecker.VersionToInt(Version: string): Extended;
  function CountOfSubStr(SubString, Text: string): integer;
  begin
    Result := 0;
    while Pos(SubString, Text) > 0 do begin
      inc(Result);
      Delete(Text, 1, Pos(SubString, Text));
    end;
  end;
var
  i, k, cnt: integer;
begin
  Result := 0;
  cnt := CountOfSubStr('.', Version);
  for i := cnt downto 0 do begin
    TryStrToInt(Version, k);
    Result := Result + k*Power(10, i);
    k := Pos('.', Version);
    Delete(Version, 1, k);
  end;
end;

class function TVersionChecker.GetFileVersion(FileName: string): TFileVersion;
var
  VerBuf: {$IFDEF CLR}TBytes{$ELSE}Pointer{$ENDIF};
  InfoSize, Wnd, VerSize, EditionSize: DWORD;
{$IFDEF CLR}
  FI: TVSFixedFileInfo;
  PFI, EditionBuf: IntPtr;
{$ELSE}
  FI: PVSFixedFileInfo;
  EditionBuf: Pointer;
{$ENDIF}  
begin
  Result.Major := -1;
  Result.Edition := '';
  InfoSize := GetFileVersionInfoSize(PChar(FileName), Wnd);
  if InfoSize > 0 then begin
  {$IFDEF CLR}
    SetLength(VerBuf, InfoSize);
  {$ELSE}
    GetMem(VerBuf, InfoSize);
  {$ENDIF}
    try
      if GetFileVersionInfo(PChar(FileName), Wnd, InfoSize, VerBuf) then begin
        if VerQueryValue(VerBuf, '\', {$IFDEF CLR}PFI{$ELSE}Pointer(FI){$ENDIF}, VerSize) then begin
        {$IFDEF CLR}
          FI := TVSFixedFileInfo(Marshal.PtrToStructure(PFI, TypeOf(TVSFixedFileInfo)));
        {$ENDIF}
          Result.Major :=   FI.dwFileVersionMS shr 16;
          Result.Minor :=   FI.dwFileVersionMS and $FFFF;
          Result.Release := FI.dwFileVersionLS shr 16;
          Result.Build :=   FI.dwFileVersionLS and $FFFF;
        end;
        if VerQueryValue(VerBuf, PChar('\VarFileInfo\Translation'), {$IFDEF CLR}PFI{$ELSE}Pointer(FI){$ENDIF}, VerSize) then begin
        {$IFDEF CLR}
          FI := TVSFixedFileInfo(Marshal.PtrToStructure(PFI, TypeOf(TVSFixedFileInfo)));
        {$ENDIF}
          if VerQueryValue(VerBuf, PChar('\StringFileInfo\' + IntToHex(MakeLong(HiWord(FI.dwSignature), LoWord(FI.dwSignature)), 8) + '\ProductEdition'), EditionBuf, EditionSize) then
          {$IFDEF CLR}
            Result.Edition := Marshal.PtrToStringUni(EditionBuf);
          {$ELSE}
            Result.Edition := StrPas(PChar(EditionBuf));
          {$ENDIF}
        end;
      end;
    finally
    {$IFNDEF CLR}
      FreeMem(VerBuf);
    {$ENDIF}
    end;
  end;
end;

class procedure TVersionChecker.ShowDACUpdates;
const
  BorderSize = 12;
  DACPanelSize = 20;
var
  frm: TCheckUpdatesForm;
  pDACInfo: array of TPanel;
  lblDACNames, lblDACEditions, lblDACCurVer, lblDACNewVer: array of TLabel;
  i, DACVersionsNumber: integer;
begin
  DACVersionsNumber := Length(DACVersionArr);
  if DACVersionsNumber <= 0 then Exit;
  frm := nil;
  try
    frm := TCheckUpdatesForm.CreateNew(nil);
    frm.Position := poScreenCenter;
    frm.BorderStyle := bsSingle;
    frm.BorderIcons := [biSystemMenu];
    frm.FormStyle := fsStayOnTop;
    frm.Caption := 'Check Updates';
    frm.Width := 360;
    frm.OnClose := frm.FormClose;

    frm.pCaption := TPanel.Create(frm);
    frm.pCaption.Parent := frm;
    frm.pCaption.Width := frm.ClientWidth;
    frm.pCaption.Top := 0;
    frm.pCaption.Left := 0;
    frm.pCaption.Caption := '';
    frm.pCaption.BevelInner := bvNone;
    frm.pCaption.BevelOuter := bvNone;
    frm.pCaption.OnMouseMove := frm.PanelMouseMove;
    frm.img := TImage.Create(frm.pCaption);
    frm.img.Parent := frm.pCaption;
    frm.img.AutoSize := True;
    frm.img.Transparent := True;
  {$IFDEF CLR}
    frm.img.Picture.Bitmap.LoadFromResourceName('CHECKUPDATES', 'Devart.Dac.Design', Assembly.GetExecutingAssembly);
  {$ELSE}
    frm.img.Picture.Bitmap.LoadFromResourceName(HInstance, 'CHECKUPDATES');
  {$ENDIF}
    frm.img.Left := BorderSize;
    frm.img.Top := BorderSize;
    frm.pCaption.Height := frm.img.Height + BorderSize * 2;
    frm.lbl := TLabel.Create(frm.pCaption);
    frm.lbl.Parent := frm.pCaption;
    frm.lbl.AutoSize := False;
    frm.lbl.Width := frm.pCaption.Width - frm.img.Width - BorderSize * 2 - 12;
    frm.lbl.Height := frm.pCaption.Height - BorderSize * 2;
    frm.lbl.Top := BorderSize;
    frm.lbl.Left := frm.img.Width + BorderSize + 12;
    if DACVersionsNumber > 2 then
      frm.lbl.Caption := 'The new versions are available!'
    else
      frm.lbl.Caption := 'The new version is available!';
    frm.lbl.Font.Size := 14;
    frm.lbl.Alignment := taLeftJustify;
    frm.lbl.Layout := tlCenter;

    frm.pDACs := TPanel.Create(frm);
    frm.pDACs.Parent := frm;
    frm.pDACs.Width := frm.ClientWidth;
    frm.pDACs.Height := DACVersionsNumber * DACPanelSize + BorderSize * 2;
    frm.pDACs.Top := frm.pCaption.Top + frm.pCaption.Height;
    frm.pDACs.Left := 0;
    frm.pDACs.Caption := '';
    frm.pDACs.BevelInner := bvNone;
    frm.pDACs.BevelOuter := bvNone;
    frm.pDACs.OnMouseMove := frm.PanelMouseMove;
    frm.bevTop := TBevel.Create(frm.pDACs);
    frm.bevTop.Parent := frm.pDACs;
    frm.bevTop.Width := frm.pDACs.Width - BorderSize * 2;
    frm.bevTop.Height := 1;
    frm.bevTop.Top := 1;
    frm.bevTop.Left := BorderSize;
    frm.bevTop.Shape := bsTopLine;
    frm.bevBottom := TBevel.Create(frm.pDACs);
    frm.bevBottom.Parent := frm.pDACs;
    frm.bevBottom.Width := frm.pDACs.Width - BorderSize * 2;
    frm.bevBottom.Height := 1;
    frm.bevBottom.Top := frm.pDACs.Height - 1;
    frm.bevBottom.Left := BorderSize;
    frm.bevBottom.Shape := bsTopLine;

    SetLength(pDACInfo, DACVersionsNumber);
    SetLength(lblDACNames, DACVersionsNumber);
    SetLength(lblDACEditions, DACVersionsNumber);
    SetLength(lblDACCurVer, DACVersionsNumber);
    SetLength(lblDACNewVer, DACVersionsNumber);
    for i := 0 to DACVersionsNumber - 1 do begin
      pDACInfo[i] := TPanel.Create(frm.pDACs);
      pDACInfo[i].Parent := frm.pDACs;
      pDACInfo[i].Width := frm.pDACs.Width;
      pDACInfo[i].Height := DACPanelSize;
      pDACInfo[i].Left := BorderSize;
      pDACInfo[i].Top := BorderSize + i * DACPanelSize;
      pDACInfo[i].Caption := '';
      pDACInfo[i].BevelInner := bvNone;
      pDACInfo[i].BevelOuter := bvNone;
      pDACInfo[i].OnMouseMove := frm.PanelMouseMove;

      lblDACNames[i] := TLabel.Create(pDACInfo[i]);
      lblDACNames[i].Parent := pDACInfo[i];
      lblDACNames[i].Caption := DACVersionArr[i].Name;
      lblDACNames[i].Font.Style := [fsBold];
      lblDACNames[i].Left := BorderSize;
      lblDACNames[i].Alignment := taCenter;
      lblDACNames[i].Layout := tlCenter;

      lblDACEditions[i] := TLabel.Create(pDACInfo[i]);
      lblDACEditions[i].Parent := pDACInfo[i];
      lblDACEditions[i].Caption := DACVersionArr[i].Edition;
      if i = 0 then
        lblDACEditions[i].Left := {$IFNDEF EntityDAC}80{$ELSE}90{$ENDIF}
      else
        lblDACEditions[i].Left := {$IFNDEF EntityDAC}80{$ELSE}90{$ENDIF} + lblDACEditions[0].Width div 2 - lblDACEditions[i].Width div 2;
      lblDACEditions[i].Alignment := taCenter;
      lblDACEditions[i].Layout := tlCenter;

      lblDACCurVer[i] := TLabel.Create(pDACInfo[i]);
      lblDACCurVer[i].Parent := pDACInfo[i];
      lblDACCurVer[i].Caption := DACVersionArr[i].CurrentVersion;
      if i = 0 then
        lblDACCurVer[i].Left := 140
      else
        lblDACCurVer[i].Left := 140 + lblDACCurVer[0].Width div 2 - lblDACCurVer[i].Width div 2;
      lblDACCurVer[i].Alignment := taCenter;
      lblDACCurVer[i].Layout := tlCenter;

      lblDACNewVer[i] := TLabel.Create(pDACInfo[i]);
      lblDACNewVer[i].Parent := pDACInfo[i];
      lblDACNewVer[i].Caption := DACVersionArr[i].NewVersion;
      if i = 0 then
        lblDACNewVer[i].Left := 240
      else begin
        lblDACNewVer[i].Left := 240 + lblDACNewVer[0].Width div 2 - lblDACNewVer[i].Width div 2;
        lblDACNewVer[i].Font.Color := clBlue;
        lblDACNewVer[i].Font.Style := [fsUnderline];
        lblDACNewVer[i].Tag := {$IFDEF CLR}Variant(i){$ELSE}i{$ENDIF};
        lblDACNewVer[i].OnClick := frm.LabelClick;
        lblDACNewVer[i].OnMouseMove := frm.LabelMouseMove;
      end;
      lblDACNewVer[i].Alignment := taCenter;
      lblDACNewVer[i].Layout := tlCenter;
    end;

    frm.pControls := TPanel.Create(frm);
    frm.pControls.Parent := frm;
    frm.pControls.Width := frm.ClientWidth;
    frm.pControls.Height := 30 + BorderSize * 2;
    frm.pControls.Top := frm.pDACs.Top + frm.pDACs.Height;
    frm.pControls.Left := 0;
    frm.pControls.Caption := '';
    frm.pControls.BevelInner := bvNone;
    frm.pControls.BevelOuter := bvNone;
    frm.pControls.OnMouseMove := frm.PanelMouseMove;
    frm.cb := TCheckBox.Create(frm.pControls);
    frm.cb.Parent := frm.pControls;
    frm.cb.Width := 120;
    frm.cb.Caption := 'Don''t show again';
    frm.cb.Left := BorderSize;
    frm.cb.Top := frm.pControls.Height div 2 - frm.cb.Height div 2;
    frm.bt := TButton.Create(frm.pControls);
    frm.bt.Parent := frm.pControls;
    frm.bt.ModalResult := mrOk;
    frm.bt.Caption := 'OK';
    frm.bt.Height := 30;
    frm.bt.Width := 100;
    frm.bt.Left := frm.pControls.Width - frm.bt.Width - BorderSize;
    frm.bt.Top := frm.pControls.Height div 2 - frm.bt.Height div 2;
    frm.bt.OnClick := frm.ButtonClick;
    frm.ClientHeight := frm.pCaption.Height + frm.pDACs.Height + frm.pControls.Height;
    frm.ActiveControl := frm.bt;
    frm.Show;
  except
    if Assigned(frm) then
      FreeAndNil(frm);
  end;
end;

procedure TVersionCheckerThread.Execute;

  function GetProductNames: TStringArray;
  begin
  {$IFNDEF EntityDAC}
    SetLength(Result, 7);
    Result[0] := 'ODAC';
    Result[1] := 'SDAC';
    Result[2] := 'MyDAC';
    Result[3] := 'IBDAC';
    Result[4] := 'PgDAC';
    Result[5] := 'UniDAC';
    Result[6] := 'LiteDAC';
  {$ELSE}
    SetLength(Result, 1);
    Result[0] := 'EntityDAC';
  {$ENDIF}
  end;

  procedure GetDACUpdateList;
  var
    Reg: TRegistry;
    PackageList: TStringList;
    BplName, PackagePath, EnvVarName, LatestVersion: string;
    i, j, k, DACVersionsNumber: integer;
    FileVersion: TFileVersion;
    DACNames: TStringArray;
    DACInfoArr: array of TDACInfo;
  begin
    Reg := TRegistry.Create(KEY_READ OR KEY_WRITE);
    PackageList := TStringList.Create;
    try
      SetLength(DACInfoArr, 0);
      SetLength(DACNames, 0);
      Reg.RootKey := HKEY_CURRENT_USER;
      if Reg.OpenKey(IDERegistryKey + '\Known Packages', False) then begin
        Reg.GetValueNames(PackageList);
        SetLength(DACInfoArr, PackageList.Count);
        DACNames := GetProductNames;
        k := 0;
        for i := 0 to PackageList.Count - 1 do
          for j := Low(DACNames) to High(DACNames) do begin
            BplName := 'dcl' + DACNames[j] + IDEPackageSuffix + '.bpl';
            PackagePath := PackageList[i];
            if SameText(ExtractFileName(PackagePath), BplName) then begin
              if Pos('$(', PackagePath) = 1 then begin
                EnvVarName := Copy(PackagePath, 3, Pos(')\', PackagePath) - 3);
                PackagePath := StringReplace(PackagePath, '$(' + EnvVarName + ')', GetEnvironmentVariable(EnvVarName), []);
              end;
              if FileExists(PackagePath) then begin
                FileVersion := TVersionChecker.GetFileVersion(PackagePath);
                if FileVersion.Major <> -1 then begin
                  DACInfoArr[k].Name := DACNames[j];
                  DACInfoArr[k].Version := IntToStr(FileVersion.Major) + '.' + IntToStr(FileVersion.Minor) + '.' + IntToStr(FileVersion.Release);
                  DACInfoArr[k].Edition := FileVersion.Edition;
                  inc(k);
                end;
              end;
              Break;
            end;
          end;
        SetLength(DACInfoArr, k);
        if k > 0 then begin
          DACVersionsNumber := 0;
          SetLength(DACVersionArr, High(DACInfoArr) + 2);
          for i := 0 to High(DACInfoArr) do begin
            LatestVersion := TVersionChecker.GetLatestVersion(DACInfoArr[i].Name, DACInfoArr[i].Edition, DACInfoArr[i].Version);
            if (DoNotShowDACList.Count > 0) and (DoNotShowDACList.Values[DACInfoArr[i].Name] <> '') and
               (TVersionChecker.VersionToInt(LatestVersion) <= TVersionChecker.VersionToInt(DoNotShowDACList.Values[DACInfoArr[i].Name])) then
              Continue;
            if TVersionChecker.VersionToInt(LatestVersion) > TVersionChecker.VersionToInt(DACInfoArr[i].Version) then begin
              if DACVersionsNumber = 0 then begin
                DACVersionArr[DACVersionsNumber].Name := '';
                DACVersionArr[DACVersionsNumber].CurrentVersion := 'Current version';
                DACVersionArr[DACVersionsNumber].NewVersion := 'New version';
                DACVersionArr[DACVersionsNumber].Edition := 'Edition';
                Inc(DACVersionsNumber);
              end;
              DACVersionArr[DACVersionsNumber].Name := DACInfoArr[i].Name;
              DACVersionArr[DACVersionsNumber].CurrentVersion := DACInfoArr[i].Version;
              DACVersionArr[DACVersionsNumber].NewVersion := LatestVersion;
              DACVersionArr[DACVersionsNumber].Edition := DACInfoArr[i].Edition;
              Inc(DACVersionsNumber);
            end;
          end;
          SetLength(DACVersionArr, DACVersionsNumber);
        end;
      end;
    finally
      PackageList.Free;
      Reg.Free;
    end;
  end;

begin
  LoadDoNotShowDACList;
  GetDACUpdateList;
  if (Length(DACVersionArr) > 0) then
    Synchronize(TVersionChecker.ShowDACUpdates);
end;

constructor TVersionCheckerThread.Create;
begin
  inherited Create(True);

  FreeOnTerminate := True;
  Priority := tpLower;
  Resume;
end;

{ TCheckUpdatesForm }

procedure TCheckUpdatesForm.FormClose(Sender: TObject; var Action: TCloseAction);
var
  i: integer;
begin
  if cb.Checked then begin
    DoNotShowDACList.Clear;
    for i := Low(DACVersionArr) + 1 to High(DACVersionArr) do
      DoNotShowDACList.Add(DACVersionArr[i].Name + '=' + DACVersionArr[i].NewVersion);
    SaveDoNotShowDACList;
  end;
  Action := caFree;
end;

procedure TCheckUpdatesForm.ButtonClick(Sender: TObject);
begin
  Close;
end;

procedure TCheckUpdatesForm.LabelClick(Sender: TObject);
var
  UpdatesUrl: string;
begin
  if SameText(DACVersionArr[Integer(TLabel(Sender).Tag)].Edition, 'TRIAL') then
    UpdatesUrl := Format('http://www.devart.com/%s/download.html', [LowerCase(DACVersionArr[Integer(TLabel(Sender).Tag)].Name)])
  else
    UpdatesUrl := 'http://secure.devart.com';
  OpenUrl(UpdatesUrl);
end;

procedure TCheckUpdatesForm.LabelMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
begin
  TLabel(Sender).Font.Color := clOlive;
  TLabel(Sender).Cursor := crHandPoint;
end;

procedure TCheckUpdatesForm.PanelMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
var
  i: integer;

  procedure ProcessComponent(Component: TComponent);
  var
    j: integer;
  begin
    if (Component is TLabel) and (TLabel(Component).Tag > 0) then begin
      TLabel(Component).Font.Color := clBlue;
      TLabel(Component).Cursor := crDefault;
    end;
    for j := 0 to Component.ComponentCount - 1 do
      ProcessComponent(Component.Components[j]);
  end;
begin
  for i := 0 to Self.ComponentCount - 1 do
    ProcessComponent(Self.Components[i]);
end;

initialization
  DoNotShowDACList := TStringList.Create;
  TVersionCheckerThread.Create;

finalization
  DoNotShowDACList.Free;

end.
