unit Updater;
//{$A+,B-,D-,L-,M-,O+,Q-,R-,S-,X+,Y-}
(*
TChkUpdate 1.3
"Lets go get the net!"
-------- -------- -------- -------- -------- -------- -------- --------
Contents:
TChkUpdate - Component for checking wether an update of an application
is available on your homepage.
-------- -------- -------- -------- -------- -------- -------- --------
Copyrights (C) 1999 ved T Skovmand Eriksen
email: TSEriksen@cyberdude.com
ALL RIGHTS RESERVED
-------- -------- -------- -------- -------- -------- -------- --------
This source is freeware and may be used as follows:
* In development of in-house applications which will not be published.
* In development of freeware applications PROVIDED that credit is given
to me in the applications aboutbox or helpfile.
* In development of shareware and commercial applications PROVIDED that
I, Troels S Eriksen, recieve a free unlimited copy of all versions of
said application AND that credits are given to me in the applications
aboutbox or helpfile.
* In printed or electronic form (fx. in a book) or in a source-library
(fx. on CD) PROVIDED that I, Troels S Eriksen, recieve a royality-free
unlimited copy of said book or library.
* In any other way PROVIDED that I, Troels S Eriksen, give my explicit
permission to the suggested use.
By using this source you do acknowledge that the above restrictions apply to
you and your use of this component and that you will confirm by them.
-------- -------- -------- -------- -------- -------- -------- --------
Revision history:
TChkUpdate
01 - Component created.
02 - Ver. 1.1: Property for Version number check added.
03 - Internet Connection check added - doesn't seem to work, though...
04 - Ver. 1.2: AutoProxy property added thanks to Jura Kornienko.
05 - Minor bugfixes. Allows more loose syntax in ver/rev comments.
06 - Ver. 1.3: Background execute (Threaded) added.
07 - Threaded property added.
-------- -------- -------- -------- -------- -------- -------- --------
TChkUpdate Properties:
AutoProxy When TRUE uses IE's default proxy settings.
Homepage The URL to the main homepage of your company or
application. (Used by the OnFound method)
Revision Current revision. Is checked against the <!--rev #-->
comment on the webpage
Threaded When TRUE connects to the internet in a separate
thread for version/revision checking. When FALSE the
main thread is blocked while check occurs.
URL URL to an internet document containing the revision
comment to check for
Version Current version. Is checked against the <!--ver #.#-->
comment on the webpage
TChkUpdate Events
OnFound Triggered if the revision comment is greather than
the current revision. Use the Launch value to decide
wether the end-users default browser should open the
homepage.
OnNotFound Triggered if the above is not the true.
OnNoConnect Triggered if connection to the internet could not be
established or if the URL was not found.
TChkUpdate Methods:
Execute Connects to the url and checks the revision and version.
If Threaded is TRUE return value is always TRUE. If
Threaded is FALSE will only return TRUE if connection
to the server could be established and the URL found.
IsConnected Returns TRUE is an connection to the server defined
in the URL property may be established. FALSE will be
returned if no internet connection currently exists.
-------- -------- -------- -------- -------- -------- -------- --------
Requirements:
wininet.dll Windows 32bit Internet API library must be present
and working.
-------- -------- -------- -------- -------- -------- -------- --------
Thanks to:
Jura Kornienko Who had the ideas for rev. 04-06, made me sit up, add
a few lines of code and end up with a better component.
-------- -------- -------- -------- -------- -------- -------- --------
Bugs:
None reported - none fixed.
"It is not a bug, its an undisclosed feature for further extension of
this applications feasibility"
-------- -------- -------- -------- -------- -------- -------- --------
How to use:
First, create an webpage for your application. Somewhere within the
first kilobyte of this homepage, add a revision comment - like this:
"<!--rev #-->" and/or a version comment like this: "<!--ver #.##-->".
Everytime you update your app, increase the rev number (the #) or the
version number (the #.##) on this webpage.
Second, Drop a TChkUpdate component on your main form, enter the URL to
the webpage in the URL property and the current revision number in the
Revision property and the current version number in the version property.
Add some code to the OnFound / OnNotFound properties to inform the
end-user wether there is an update available or not. If you set the
variable Launch to true, the users browser will automatically show
the URL from the Homepage property. (Would be nice to ask the users
permission before launching anything).
If the homepage property points to an .zip or .exe-file - user should
automatically be asked to download/open the file directly (at least in
IE3 and later).
Then add some way that the user may call the Execute method (fx. a
button). Check the return value and inform the user if an internet
connection could not be made.
*)
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;
{ Import function from Wininet.dll - this has been left out of Inprises
(Borlands) official import unit. Shame, Shame, Shame...
}
function InternetAttemptConnect(dwReserved
Word):dword; stdcall;
{ Then the component itself
}
type
TUpdateEvent = procedure (Sender:TObject;var Launch:boolean) of object;
type
TChkUpdate = class(TComponent)
private
{ Private declarations }
fAutoProxy : boolean;
fURL : string;
fHomepage : string;
fRevision : integer;
fVersion : string;
fThreaded : boolean;
fOnFound : TUpdateEvent;
fOnNotFound : TNotifyEvent;
fOnNoConnect : TNotifyEvent;
// procedure SetVersion(Value:string);
// function GetVersion:string;
protected
procedure ChkResult(WebVer:double;WebRev:integer);
function ThreadExecute:boolean;
function BlockingExecute:boolean;
public
{ Methods }
constructor Create(AnOwner:TComponent); override;
function IsConnected:boolean;
function Execute:boolean;
published
{ Properties }
property AutoProxy : boolean read fAutoProxy write fAutoProxy;
property Homepage : string read fHomepage write fHomepage;
property Revision : integer read fRevision write fRevision;
property Threaded : boolean read fThreaded write fThreaded;
property URL : string read fURL write fURL;
property Version : string read fVersion write fVersion;
{ Events }
property OnFound : TUpdateEvent read fOnFound write fOnFound;
property OnNotFound : TNotifyEvent read fOnNotFound write fOnNotFound;
property OnNoConnect: TNotifyEvent read fOnNoConnect write fOnNoConnect;
end;
{ ------- -------- -------- -------- -------- -------- -------- ------- }
TChkResult = procedure(WebVer:double;WebRev:integer) of object;
{ ------- -------- -------- -------- -------- -------- -------- ------- }
TThreadedChecker = class(TThread)
private
{ Private declarations }
anURL : string;
aFlag : DWORD;
fVer : double;
fRev : integer;
fCallBack : TChkResult;
procedure ReturnResult;
protected
procedure Execute; override;
constructor Create(flags:integer;URL:string;Callback:TChkResult);
end;
procedure Register;
implementation
uses
Wininet, ShellApi;
{ ------- -------- -------- -------- -------- -------- -------- ------- }
procedure TThreadedChecker.ReturnResult;
begin
fCallBack(fVer,fRev);
end;
procedure TThreadedChecker.Execute;
type
tbuffer = array[0..1023] of char;
pbuffer = ^TBuffer;
var
Tmp : string;
Rev : integer;
Ver : double;
Launch : boolean;
buffer : pBuffer;
b : integer;
a : dword;
ihConnect,
iDocument : HINTERNET;
LocalDecSep: char;
begin
// Forbind til internettet
ihConnect:=InternetOpen('Modzilla/2.0 (Updater/Eriksen)', aFLAG, '', '', 0);
// 舃n dokument
iDocument:=InternetOpenURL(ihConnect,
pChar(anURL), NIL, $ffffffff, INTERNET_FLAG_RELOAD or
INTERNET_FLAG_DONT_CACHE or INTERNET_FLAG_RAW_DATA, 0);
// L鎠 f鴕ste kilobyte af dokument
Rev:=-1;
Ver:=-1;
if iDocument<>NIL then try
New(buffer);
InternetReadFile(iDocument, Buffer, sizeof(TBuffer), a);
Buffer^[a]:=#0;
// Indg錼 kommentaren "rev" i f鴕ste kb ?
Rev:=0;
a:=pos('<!--rev',AnsiLowercase(string(buffer^)));
if (a=0) then a:=pos('<!-- rev',AnsiLowercase(string(buffer^)));
if a>0 then begin
// Ja - afl鎠 revisionsnummer - det skal f鴏ge efter rev teksten
if UpCase(buffer[a+6])='V' then inc(a);
b:=a+6;
while buffer
in ['0'..'9',' '] do inc(b);
// Returner Sand hvis h鴍ere rev nr findes p?net
tmp:=Copy(string(Buffer^),a+7,b-a-6);
rev:=StrToIntDef(Trim(Tmp),0);
end;
// Indg錼 kommentaren "Ver" i f鴕ste kb ?
Ver:=0.0;
a:=pos('<!--ver',AnsiLowercase(string(buffer^)));
if (a=0) then a:=pos('<!-- ver',AnsiLowercase(string(buffer^)));
if a>0 then begin
// Ja - afl鎠 versionsnummer - det skal f鴏ge efter ver teksten
if UpCase(buffer[a+6])='R' then inc(a);
b:=a+6;
while buffer in ['0'..'9',' ','.',','] do inc(b);
// Returner Sand hvis h鴍ere ver nr findes p?net
LocalDecSep:=DecimalSeparator;
try
DecimalSeparator:='.';
tmp:=Copy(string(Buffer^),a+7,b-(a+6));
ver:=StrToFloat(trim(tmp));
except
ver:=0.0;
end;
DecimalSeparator:=LocalDecSep;
end;
// Er en opdatering tilg鎛gelig?
finally
internetCloseHandle(iDocument);
Dispose(buffer);
InternetCloseHandle(ihConnect);
end;
fVer:=Ver;
fRev:=Rev;
Synchronize(ReturnResult);
end;
constructor TThreadedChecker.Create(flags:integer;URL:string;Callback:TChkResult);
begin
inherited Create(FALSE);
anUrl:=URL;
aFlag:=flags;
fCallBack:=Callback;
end;
{ ------- -------- -------- -------- -------- -------- -------- ------- }
procedure Register;
begin
RegisterComponents('Samples', [TChkUpdate]);
end;
function Str2Float(value:string):double;
var
LocalDecSep : char;
begin
try
LocalDecSep:=DecimalSeparator;
try
DecimalSeparator:='.';
result:=StrToFloat(trim(value));
except
result:=0.0;
end;
finally
DecimalSeparator:=LocalDecSep;
end;
end;
{ ------- -------- -------- -------- -------- -------- -------- ------- }
{
procedure TChkUpdate.SetVersion(Value:string);
var
c : char;
begin
fVersion:= Value;
end;
function TChkUpdate.GetVersion:string;
var
c : char;
begin
c:=DecimalSeparator;
try
DecimalSeparator:='.';
result:=FormatFloat('0.00',fVersion);
finally
DecimalSeparator:=c;
end;
end;
}
{ ------- -------- -------- -------- -------- -------- -------- ------- }
function TChkUpdate.IsConnected:Boolean;
begin
result:=boolean(InternetAttemptConnect(0));
end;
procedure TChkUpdate.ChkResult;
var
launch : boolean;
LocalDecSep : char;
ver : double;
begin
ver:=Str2Float(version);
if (WebRev<0) and (WebVer<0) and (assigned(fOnNoConnect)) then
fOnNoConnect(SELF)
else begin
if ((WebRev>Revision) or (WebVer>Ver)) and (assigned(fOnFound)) then begin
Launch:=FALSE;
fOnFound(Self,Launch);
If (launch) and (trim(fHomepage)>'') then
ShellExecute(Application.Handle,'open',pChar(fHomepage),'','',
SW_SHOWNORMAL);
end else if assigned(fOnNotFound) then fOnNotFound(Self);
end;
end;
function TChkUpdate.ThreadExecute:Boolean;
var
flag : integer;
begin
Case fAutoProxy of
FALSE : Flag:=LOCAL_INTERNET_ACCESS;
TRUE : Flag:=INTERNET_OPEN_TYPE_PRECONFIG;
end;
With TThreadedChecker.Create(flag,fURL,ChkResult) do ;
result:=true;
end;
{
function TChkUpdate.BlockingExecute:Boolean;
type
tbuffer = array[0..1023] of char;
pbuffer = ^TBuffer;
var
Tmp : string;
Rev, Ver,
Launch : boolean;
buffer : pBuffer;
a : dword;
b : integer;
ihConnect,
iDocument : HINTERNET;
LocalDecSep: char;
begin
result:=false;
// Forbind til internettet
Case fAutoProxy of
FALSE : ihConnect:=InternetOpen('Modzilla/2.0 (Updater/Eriksen)',
LOCAL_INTERNET_ACCESS, '', '', 0);
TRUE : ihConnect:=InternetOpen('Modzilla/2.0 (Updater/Eriksen)',
INTERNET_OPEN_TYPE_PRECONFIG, '', '', 0);
end;
// 舃n dokument
iDocument:=InternetOpenURL(ihConnect,
pChar(fURL), NIL, $ffffffff, INTERNET_FLAG_RELOAD or
INTERNET_FLAG_DONT_CACHE or INTERNET_FLAG_RAW_DATA, 0);
// L鎠 f鴕ste kilobyte af dokument
if iDocument<>NIL then try
New(buffer);
InternetReadFile(iDocument, Buffer, sizeof(TBuffer), a);
Buffer^[a]:=#0;
// Indg錼 kommentaren "rev" i f鴕ste kb ?
Rev:=FALSE;
a:=pos('<!--rev',AnsiLowercase(string(buffer^)));
if (a=0) then a:=pos('<!-- rev',AnsiLowercase(string(buffer^)));
if a>0 then begin
// Ja - afl鎠 revisionsnummer - det skal f鴏ge efter rev teksten
if UpCase(buffer[a+6])='V' then inc(a);
b:=a+6;
while buffer in ['0'..'9',' '] do inc(b);
// Returner Sand hvis h鴍ere rev nr findes p?net
tmp:=Copy(string(Buffer^),a+7,b-a-6);
rev:=StrToIntDef(Trim(Tmp),0)>fRevision;
end;
// Indg錼 kommentaren "Ver" i f鴕ste kb ?
Ver:=FALSE;
a:=pos('<!--ver',AnsiLowercase(string(buffer^)));
if (a=0) then a:=pos('<!-- ver',AnsiLowercase(string(buffer^)));
if a>0 then begin
// Ja - afl鎠 versionsnummer - det skal f鴏ge efter ver teksten
if UpCase(buffer[a+6])='R' then inc(a);
b:=a+6;
while buffer in ['0'..'9',' ','.',','] do inc(b);
// Returner Sand hvis h鴍ere ver nr findes p?net
LocalDecSep:=DecimalSeparator;
try
DecimalSeparator:='.';
tmp:=Copy(string(Buffer^),a+7,b-(a+6));
ver:=StrToFloat(trim(tmp))>fVersion;
except
ver:=FALSE;
end;
DecimalSeparator:=LocalDecSep;
end;
// Er en opdatering tilg鎛gelig?
result:=(Rev or Ver);
// Vis h鎛delse
try
case result of
true : if assigned(fOnFound) then begin
Launch:=FALSE;
fOnFound(Self,Launch);
If (launch) and (trim(fHomepage)>'') then
ShellExecute(Application.Handle,'open',pChar(fHomepage),'','',
SW_SHOWNORMAL);
end;
false: if assigned(fOnNotFound) then
fOnNotFound(Self);
end;
finally
result:=true;
end;
finally
internetCloseHandle(iDocument);
Dispose(buffer);
InternetCloseHandle(ihConnect);
end;
if (not result) and (assigned(fOnNoConnect)) then
fOnNoConnect(SELF);
end;
}
function TChkUpdate.BlockingExecute:Boolean;
type
tbuffer = array[0..1023] of char;
pbuffer = ^TBuffer;
var
Tmp,s,s1 : string;
Rev, Ver,
Launch : boolean;
buffer : pBuffer;
a,l,p : dword;
b : integer;
ihConnect,
iDocument : HINTERNET;
LocalDecSep: char;
begin
result:=false;
// Forbind til internettet
Case fAutoProxy of
FALSE : ihConnect:=InternetOpen('Modzilla/2.0 (Updater/Eriksen)',
LOCAL_INTERNET_ACCESS, '', '', 0);
TRUE : ihConnect:=InternetOpen('Modzilla/2.0 (Updater/Eriksen)',
INTERNET_OPEN_TYPE_PRECONFIG, '', '', 0);
end;
// 舃n dokument
iDocument:=InternetOpenURL(ihConnect,
pChar(fURL), NIL, $ffffffff, INTERNET_FLAG_RELOAD or
INTERNET_FLAG_DONT_CACHE or INTERNET_FLAG_RAW_DATA, 0);
// L鎠 f鴕ste kilobyte af dokument
if iDocument<>NIL then try
New(buffer);
InternetReadFile(iDocument, Buffer, sizeof(TBuffer), l);
Buffer^[l]:=#0;
// Indg錼 kommentaren "rev" i f鴕ste kb ?
Rev:=FALSE;
s := '<!--version' ;
p:=pos(s,AnsiLowercase(string(buffer^)));
if p>0 then begin
while (buffer[p]<>'=') and (p<l) do inc(p);
inc(p);
while (buffer[p]<>'-') and (p<l) do begin
s1:=s1+buffer[p];
inc(p);
result := true;
end;
if result then version := s1;
end;
try
case result of
true : if assigned(fOnFound) then begin
Launch:=FALSE;
fOnFound(Self,Launch);
If (launch) and (trim(fHomepage)>'') then
ShellExecute(Application.Handle,'open',pChar(fHomepage),'','',
SW_SHOWNORMAL);
end;
false: if assigned(fOnNotFound) then
fOnNotFound(Self);
end;
finally
result:=true;
end;
finally
internetCloseHandle(iDocument);
Dispose(buffer);
InternetCloseHandle(ihConnect);
end;
if (not result) and (assigned(fOnNoConnect)) then
fOnNoConnect(SELF);
end;
function TChkUpdate.Execute:boolean;
begin
Case fThreaded of
TRUE : result:=ThreadExecute;
FALSE: result:=BlockingExecute;
end;
end;
{ ------- -------- -------- -------- -------- -------- -------- ------- }
constructor TChkUpdate.Create(AnOwner:TComponent);
begin
Inherited ;
URL :='http://webserver/application/revision.htm';
Homepage :='http://webserver/application/';
Revision :=1;
Version :='1.0';
AutoProxy:=FALSE;
Threaded :=FALSE;
end;
{ ------- -------- -------- -------- -------- -------- -------- ------- }
// Statisk kald til ikke-predef funktion i dll
const
winetdll = 'wininet.dll';
function InternetAttemptConnect; external winetdll name 'InternetAttemptConnect';
end.