Implement a new external updater tool, stick that updater.exe as resource to the main executable, release and run it when the user clicks "Download build xyz". The updater itself posts WM_CLOSE messages to all Heidi instances, waits until all instances are gone, and then replaces the .exe with the downloaded one, after having copied a backup file to your temp directory.

This commit is contained in:
Ansgar Becker
2010-02-16 23:58:48 +00:00
parent 08c947bfd1
commit a43edc883f
13 changed files with 413 additions and 204 deletions

View File

@@ -153,6 +153,7 @@ cd /d "%base_dir%\packages\%package_dir%\"
brcc32 ..\..\res\version.rc
brcc32 ..\..\res\icon.rc
brcc32 ..\..\res\manifest.rc
brcc32 ..\..\res\updater.rc
"%compiler%" %params% -e"%base_dir%\out" heidisql.dpr
if not %errorlevel% == 0 goto end

View File

@@ -1,91 +0,0 @@
unit helpers;
interface
type
procedure ExtractUpdater;
procedure UpdateItWith(const _file: String);
implementation
{$R updater.res}
{**
Extract the updater from resource.
}
procedure ExtractUpdater;
var
ri: HRSRC;
rh: THandle;
bf: PChar;
ms: TMemoryStream;
begin
// look for resource UPDATER
ri := FindResource( HInstance, 'UPDATER', 'EXE' );
// define the Handle
rh := LoadResource( HInstance, ri );
if ( rh <> 0 ) then
begin
// alloc memory space
ms := TMemoryStream.Create();
try
ms.Clear();
bf := LockResource( rh );
// load the resource on memory stream
ms.WriteBuffer( bf[0], SizeOfResource( HInstance, ri ) );
ms.Seek( 0, 0 );
// save the resource like executable
ms.SaveToFile( ExtractFilePath( Application.ExeName ) + 'updater.exe' );
finally
// free memory
UnlockResource( rh );
FreeResource( rh );
ms.Free();
end;
end;
end;
{**
Update the current application with the file.
@param _file the file that will replace the current application
}
procedure UpdateItWith(const _file: String);
var
app: String;
begin
// sanitize: try to remove the oldest updater
DeleteFile( PAnsiChar( ExtractFilePath( Application.ExeName ) + 'updater.exe' ) );
// retrive the application name without extension
app := Copy( Application.Exename, 1, ( Length( Application.Exename ) - 4 ) );
// sanitize: try to remove the oldest file
DeleteFile( PAnsiChar( app + '.old' ) );
// verify the file existence
if ( FileExists( _file ) ) then
begin
// copy to current application folder the newest file
CopyFile( PChar( _file ), PChar( app + '.new' ), False );
// extract the updater from current application resource
ExtractUpdater();
// call the updater
WinExec( PAnsiChar( '"' + ExtractFilePath( Application.ExeName ) + 'updater.exe" "' + app + '"' ), 0 );
// stop running the current application
Application.Terminate();
end;
end;

View File

@@ -1,3 +0,0 @@
@echo off
brcc32 updater.rc updater.res
ren updater.RES updater.res

View File

@@ -1,54 +0,0 @@
program updater;
{$APPTYPE CONSOLE}
uses
SysUtils, Windows;
const
_maxloop = 10;
procedure Update;
var
_currentfilename: String;
_oldfilename: String;
_newfilename: String;
_minloop: Integer;
begin
if ( ParamCount > 0 ) then
begin
_currentfilename := ParamStr(1) + '.exe';
_oldfilename := ParamStr(1) + '.old';
_newfilename := ParamStr(1) + '.new';
// try to delete the old file
if ( FileExists( _oldfilename ) ) then
begin
DeleteFile( PAnsiChar( _oldfilename ) );
end;
// try to execute
_minloop := 0;
repeat
// try to rename the current filename
if ( RenameFile( _currentfilename, _oldfilename ) ) then
begin
// rename the new file with the current filename
RenameFile( _newfilename, _currentfilename );
// call again the application
WinExec( PAnsiChar( _currentfilename ), 0 );
Exit;
end;
// if doesn't rename is because the application still running, so
// wait for 2 seconds and try again
Sleep( 2000 );
_minloop := _minloop + 1;
until ( _minloop <= _maxloop );
end;
end;
begin
Update();
end.

View File

Binary file not shown.

View File

@@ -41,6 +41,7 @@ uses
{$R ..\..\res\icon.RES}
{$R ..\..\res\version.RES}
{$R ..\..\res\manifest.RES}
{$R ..\..\res\updater.RES}
var
DoStop, prefAllowMultipleInstances: Boolean;

View File

@@ -23,6 +23,7 @@
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="'$(Base)'!=''">
<BRCC_OutputDir>..\..\res</BRCC_OutputDir>
<DCC_DependencyCheckOutputName>heidisql.exe</DCC_DependencyCheckOutputName>
</PropertyGroup>
<PropertyGroup Condition="'$(Cfg_1)'!=''">
@@ -45,7 +46,6 @@
<DCC_Description>HeidiSQL</DCC_Description>
</PropertyGroup>
<PropertyGroup Condition="'$(Cfg_2)'!=''">
<BRCC_OutputDir>..\..\res</BRCC_OutputDir>
<DCC_MapFile>3</DCC_MapFile>
<DCC_DependencyCheckOutputName>..\..\out\heidisql.exe</DCC_DependencyCheckOutputName>
<Version>7.0</Version>
@@ -198,6 +198,9 @@
<DCCReference Include="..\..\source\searchreplace.pas">
<Form>frmSearchReplace</Form>
</DCCReference>
<RcCompile Include="..\..\res\updater.rc">
<Form>updater.res</Form>
</RcCompile>
<RcCompile Include="..\..\res\icon.rc">
<Form>icon.res</Form>
</RcCompile>

BIN
res/updater.exe Normal file
View File

Binary file not shown.

View File

273
res/updater/updater.dpr Normal file
View File

@@ -0,0 +1,273 @@
program updater;
{ A window which terminates running HeidiSQL instances and moves the downloaded update file to
its desired directory. Avoids to use any VCL unit, to keep the executable small. }
uses
Windows, Messages;
var
WClass: TWndClass;
BackupPath, AppPath, DownloadPath: String;
hAppHandle, HLabel: HWND;
AppMsg: TMsg;
const
AppName = 'HeidiSQL';
WindowPadding = 10;
WindowWidth = 600;
WindowHeight = 80;
QuitTimeout = 20000; // We long we're gracefully waiting for a window to be gone, in milliseconds
TerminatedCheck = 200; // Interval between WM_CLOSE posts to a window, in milliseconds
PathDelim = '\';
{ We don't include SysUtils unit, so we need to implement our own versions of some basic functions here }
function FileExists(Filename: String): Boolean;
var
Find: THandle;
Data: TWin32FindData;
begin
Find := FindFirstFile(PChar(Filename), Data);
Result := Find <> INVALID_HANDLE_VALUE;
end;
function IntToStr(Value: Int64): String;
var
Minus : Boolean;
begin
Result := '';
if Value = 0 then
Result := '0';
Minus := Value < 0;
if Minus then
Value := -Value;
while Value > 0 do begin
Result := Char((Value mod 10) + Integer('0')) + Result;
Value := Value div 10;
end;
if Minus then
Result := '-' + Result;
end;
function ExtractFileName(const FileName: string): string;
var
i: Integer;
begin
for i:=Length(Filename) downto 0 do
if Filename[i] = PathDelim then
break;
Result := Copy(FileName, i+1, MaxInt);
end;
function ExtractFilePath(const FileName: string): string;
var
i: Integer;
begin
for i:=Length(Filename) downto 0 do
if Filename[i] = PathDelim then
break;
Result := Copy(FileName, 1, i);
end;
procedure Status(Text: String; IsError: Boolean=False);
begin
// Display status message on label
SendMessage(HLabel, WM_SETTEXT, 1, Integer(PChar(Text)) );
UpdateWindow(hLabel);
if IsError then begin
Sleep(4000);
Halt(1);
end;
end;
function EnumAllInstances(Wnd: HWND; LParam: LPARAM): Bool; stdcall;
var
WndTitle, Hint: String;
i, p, MajorVer, Code, WaitTime: Integer;
begin
// Callback function which passes one window handle
// EnumWindows will stop processing if we return false
Result := True;
if IsWindowVisible(Wnd) and
((GetWindowLong(Wnd, GWL_HWNDPARENT) = 0) or
(HWND(GetWindowLong(Wnd, GWL_HWNDPARENT)) = GetDesktopWindow)) then begin
SetLength(WndTitle, 256);
for i:=1 to 256 do
WndTitle[i] := ' ';
GetWindowText(Wnd, PChar(WndTitle), 256);
// Check if this is a HeidiSQL window, must contain "HeidiSQL 1.2.3.4"
p := Pos(AppName, WndTitle);
Val(WndTitle[p+Length(AppName)+1], MajorVer, Code);
if Code <> 0 then MajorVer := 0;
if (p > 0) and (MajorVer <> 0) then begin
Hint := 'Closing "'+WndTitle+'"';
Status(Hint);
PostMessage(Wnd, WM_CLOSE, 0, 0);
WaitTime := 0;
while WaitTime < QuitTimeout do begin
Sleep(TerminatedCheck);
Inc(WaitTime, TerminatedCheck);
Status(Hint + ', wait time left: '+IntToStr((QuitTimeout - WaitTime) div 1000)+' seconds.');
if not IsWindow(Wnd) then
break;
end;
if IsWindow(Wnd) then begin
Status('Error: Could not terminate session '+WndTitle, true);
Result := False;
end;
end;
end;
end;
// Callback function for Timer
procedure FormShow(wnd: HWND; uMsg: UINT; idEvent: UINT; dwTime: DWORD); stdcall;
var
SUInfo: TStartupInfo;
ProcInfo: TProcessInformation;
begin
KillTimer(hAppHandle, 0);
AppPath := Paramstr(1);
DownloadPath := ParamStr(2);
// Paremeter syntax check
if (AppPath = '') or (DownloadPath = '') then begin
Status('Syntax: '+ExtractFilename(Paramstr(0))+' OldFile.exe NewFile.exe'+#13#10+
'Please don''t execute this file directly.', True);
end;
if (not FileExists(AppPath)) or (not FileExists(DownloadPath)) then
Status('Error: Either target file "'+AppPath+'" or download file "'+DownloadPath+'" does not exist.', True);
// Terminate running instances
Status('Close running '+AppName+' instances ...');
EnumWindows(@EnumAllInstances, 0);
// Backup old .exe to working directory
Status('Creating backup of old file ...');
BackupPath := ExtractFilepath(Paramstr(0))+ExtractFilename(AppPath)+'.backup.exe';
if FileExists(BackupPath) then
DeleteFile(PChar(BackupPath));
if not MoveFile(PChar(AppPath), PChar(BackupPath)) then
Status('Failed to create backup file "'+BackupPath+'" from "'+AppPath+'"', True)
else
Status('Success.');
// Move update file to final path
Status('Moving downloaded file to desired directory ...');
if not MoveFile(PChar(DownloadPath), PChar(AppPath)) then
Status('Failed to move file "'+DownloadPath+'" to "'+AppPath+'"', True)
else begin
Status('Success. Restarting '+AppName+' now ...');
FillChar(SUInfo, SizeOf(SUInfo), #0);
SUInfo.cb := SizeOf(SUInfo);
SUInfo.dwFlags := STARTF_USESHOWWINDOW;
SUInfo.wShowWindow := SW_SHOWNORMAL;
CreateProcess(
nil,
PChar(AppPath),
nil,
nil,
False,
CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS,
nil,
PChar(ExtractFilePath(AppPath)),
SUInfo,
ProcInfo
);
end;
PostQuitMessage(0);
end;
function WindowProc(hWnd, msg, wpr, lpr: Longint): Longint; stdcall;
var
x, y: integer;
Font: HFont;
begin
// Custom window procedure
case msg of
WM_CREATE: begin
// Center window
x := GetSystemMetrics(SM_CXSCREEN);
y := GetSystemMetrics(SM_CYSCREEN);
MoveWindow(hWnd,
(x div 2) - (WindowWidth div 2),
(y div 2) - (WindowHeight div 2),
WindowWidth,
WindowHeight,
true);
// Create status label
HLabel := CreateWindow(
'STATIC', // Class name
'Status:', // Label's text
WS_VISIBLE or WS_CHILD or SS_LEFT, // Styles
WindowPadding, // X pos
WindowPadding, // Y pos
WindowWidth - 2*WindowPadding, // Width
WindowHeight - 2*WindowPadding, // Height
hWnd, // Parent hwnd
0, // ID
hAppHandle, // HInstance of program
nil // Params for main window
);
// Cosmetics
Font := Createfont(-11, 0, 0, 0, 0, 0, 0, 0, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, 'Tahoma');
SendMessage(HLabel, WM_SETFONT, Font, 1);
SetBkColor(hwnd, COLOR_BTNFACE+1);
end;
WM_SHOWWINDOW: SetTimer(hAppHandle, 0, 200, @FormShow);
WM_DESTROY: PostQuitMessage(0);
end;
Result := DefWindowProc(hWnd, msg, wpr, lpr);
end;
// Main program goes here
begin
// Define window class
WClass.hInstance := hInstance;
WClass.style := CS_HREDRAW or CS_VREDRAW;
WClass.hIcon := LoadIcon(hInstance, IDI_WINLOGO);
WClass.lpfnWndProc := @WindowProc;
WClass.hbrBackground := COLOR_BTNFACE+1;
WClass.lpszClassName := 'WndClass';
WClass.hCursor := LoadCursor(0, IDC_ARROW);
WClass.cbClsExtra := 0;
WClass.cbWndExtra := 0;
WClass.lpszMenuName := '';
RegisterClass(WClass);
// Create form
hAppHandle := CreateWindow(
WClass.lpszClassName,
AppName+' Updater',
WS_POPUPWINDOW or WS_CAPTION or WS_VISIBLE,
100, // Default x + y coordinates, will be centered in WM_CREATE
100,
WindowWidth,
WindowHeight,
0,
0,
hInstance,
nil
);
// Message loop
while GetMessage(AppMsg, 0, 0, 0) do begin
TranslateMessage(AppMsg);
DispatchMessage(AppMsg);
end;
ExitCode := AppMsg.wParam;
end.

110
res/updater/updater.dproj Normal file
View File

@@ -0,0 +1,110 @@
 <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectGuid>{24E2AAD6-CDE7-46EC-95BD-79028373B17A}</ProjectGuid>
<ProjectVersion>12.0</ProjectVersion>
<MainSource>updater.dpr</MainSource>
<Config Condition="'$(Config)'==''">Debug</Config>
<DCC_DCCCompiler>DCC32</DCC_DCCCompiler>
</PropertyGroup>
<PropertyGroup Condition="'$(Config)'=='Base' or '$(Base)'!=''">
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="'$(Config)'=='Release' or '$(Cfg_1)'!=''">
<Cfg_1>true</Cfg_1>
<CfgParent>Base</CfgParent>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="'$(Config)'=='Debug' or '$(Cfg_2)'!=''">
<Cfg_2>true</Cfg_2>
<CfgParent>Base</CfgParent>
<Base>true</Base>
</PropertyGroup>
<PropertyGroup Condition="'$(Base)'!=''">
<DCC_ExeOutput>..\</DCC_ExeOutput>
<DCC_UsePackage>vcl;rtl;vclx;vclimg;vclactnband;dbrtl;vcldb;vcldbx;bdertl;vcltouch;xmlrtl;dsnap;dsnapcon;TeeUI;TeeDB;Tee;vclib;ibxpress;adortl;IndyCore;IndySystem;IndyProtocols;inet;intrawebdb_100_140;Intraweb_100_140;VclSmp;vclie;websnap;webdsnap;inetdb;inetdbbde;inetdbxpress;soaprtl;vclribbon;dbexpress;DbxCommonDriver;DataSnapIndy10ServerTransport;DataSnapProviderClient;DbxClientDriver;DataSnapServer;DBXInterBaseDriver;DBXMySQLDriver;dbxcds;DBXFirebirdDriver;DBXSybaseASEDriver;DBXSybaseASADriver;DBXOracleDriver;DBXMSSQLDriver;DBXInformixDriver;DBXDb2Driver;madBasic_;madDisAsm_;madExcept_;SynEditR;VirtualTreesR</DCC_UsePackage>
<DCC_DependencyCheckOutputName>..\updater.exe</DCC_DependencyCheckOutputName>
<DCC_ImageBase>00400000</DCC_ImageBase>
<DCC_UnitAlias>WinTypes=Windows;WinProcs=Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE;$(DCC_UnitAlias)</DCC_UnitAlias>
<DCC_Platform>x86</DCC_Platform>
<DCC_N>false</DCC_N>
<DCC_S>false</DCC_S>
<DCC_K>false</DCC_K>
<DCC_E>false</DCC_E>
<DCC_F>false</DCC_F>
</PropertyGroup>
<PropertyGroup Condition="'$(Cfg_1)'!=''">
<DCC_LocalDebugSymbols>false</DCC_LocalDebugSymbols>
<DCC_Define>RELEASE;$(DCC_Define)</DCC_Define>
<DCC_SymbolReferenceInfo>0</DCC_SymbolReferenceInfo>
<DCC_DebugInformation>false</DCC_DebugInformation>
</PropertyGroup>
<PropertyGroup Condition="'$(Cfg_2)'!=''">
<DCC_Define>DEBUG;$(DCC_Define)</DCC_Define>
</PropertyGroup>
<ItemGroup>
<DelphiCompile Include="updater.dpr">
<MainSource>MainSource</MainSource>
</DelphiCompile>
<BuildConfiguration Include="Base">
<Key>Base</Key>
</BuildConfiguration>
<BuildConfiguration Include="Debug">
<Key>Cfg_2</Key>
<CfgParent>Base</CfgParent>
</BuildConfiguration>
<BuildConfiguration Include="Release">
<Key>Cfg_1</Key>
<CfgParent>Base</CfgParent>
</BuildConfiguration>
</ItemGroup>
<Import Project="$(BDS)\Bin\CodeGear.Delphi.Targets" Condition="Exists('$(BDS)\Bin\CodeGear.Delphi.Targets')"/>
<ProjectExtensions>
<Borland.Personality>Delphi.Personality.12</Borland.Personality>
<Borland.ProjectType/>
<BorlandProject>
<Delphi.Personality>
<Source>
<Source Name="MainSource">updater.dpr</Source>
</Source>
<Parameters>
<Parameters Name="UseLauncher">False</Parameters>
<Parameters Name="LoadAllSymbols">True</Parameters>
<Parameters Name="LoadUnspecifiedSymbols">False</Parameters>
</Parameters>
<VersionInfo>
<VersionInfo Name="IncludeVerInfo">False</VersionInfo>
<VersionInfo Name="AutoIncBuild">False</VersionInfo>
<VersionInfo Name="MajorVer">1</VersionInfo>
<VersionInfo Name="MinorVer">0</VersionInfo>
<VersionInfo Name="Release">0</VersionInfo>
<VersionInfo Name="Build">0</VersionInfo>
<VersionInfo Name="Debug">False</VersionInfo>
<VersionInfo Name="PreRelease">False</VersionInfo>
<VersionInfo Name="Special">False</VersionInfo>
<VersionInfo Name="Private">False</VersionInfo>
<VersionInfo Name="DLL">False</VersionInfo>
<VersionInfo Name="Locale">1031</VersionInfo>
<VersionInfo Name="CodePage">1252</VersionInfo>
</VersionInfo>
<VersionInfoKeys>
<VersionInfoKeys Name="CompanyName"/>
<VersionInfoKeys Name="FileDescription"/>
<VersionInfoKeys Name="FileVersion">1.0.0.0</VersionInfoKeys>
<VersionInfoKeys Name="InternalName"/>
<VersionInfoKeys Name="LegalCopyright"/>
<VersionInfoKeys Name="LegalTrademarks"/>
<VersionInfoKeys Name="OriginalFilename"/>
<VersionInfoKeys Name="ProductName"/>
<VersionInfoKeys Name="ProductVersion">1.0.0.0</VersionInfoKeys>
<VersionInfoKeys Name="Comments"/>
</VersionInfoKeys>
<Excluded_Packages>
<Excluded_Packages Name="D:\heidisql\trunk\components\pngcomponents\build\PngComponentsD.bpl">File D:\heidisql\trunk\components\pngcomponents\build\PngComponentsD.bpl not found</Excluded_Packages>
<Excluded_Packages Name="$(BDS)\bin\dcloffice2k140.bpl">Microsoft Office 2000 Sample Automation Server Wrapper Components</Excluded_Packages>
<Excluded_Packages Name="$(BDS)\bin\dclofficexp140.bpl">Microsoft Office XP Sample Automation Server Wrapper Components</Excluded_Packages>
</Excluded_Packages>
</Delphi.Personality>
</BorlandProject>
<ProjectFileVersion>12</ProjectFileVersion>
</ProjectExtensions>
</Project>

View File

@@ -61,6 +61,7 @@ object frmUpdateCheck: TfrmUpdateCheck
Height = 25
Anchors = [akLeft, akRight, akBottom]
Caption = 'Download nightly build'
ElevationRequired = True
ModalResult = 1
TabOrder = 0
OnClick = btnBuildClick

View File

@@ -217,8 +217,11 @@ end;
procedure TfrmUpdateCheck.btnBuildClick(Sender: TObject);
var
Download: TDownLoadURL;
ScriptFile: Textfile;
ExeName, ScriptFilename, ScriptContent: String;
ExeName, UpdaterFilename: String;
ResInfoblockHandle: HRSRC;
ResHandle: THandle;
ResPointer: PChar;
Stream: TMemoryStream;
begin
Download := TDownLoadURL.Create(Self);
Download.URL := BuildURL;
@@ -239,60 +242,25 @@ begin
if not FileExists(Download.Filename) then
Raise Exception.Create('Downloaded file not found: '+Download.Filename);
// The Visual Basic Script code which kills this exe and moves the
// downloaded file to the application directory.
// This file moving can fail due to several reasons. Especially in Vista
// where users are normally not admins, they'll get a "Permission denied".
// However, the script does several write attempts and quits with a clear
// error message if it couldn't move the file.
// TODO: move this code to a seperate file for easier debugging
Status('Update in progress ...');
ScriptContent := ''' This is a temporary script which shall update your ' + APPNAME + CRLF +
''' with a nightly build.' + CRLF +
CRLF +
'ExeName = "'+ExeName+'"' + CRLF +
'DownloadFileName = "'+Download.Filename+'"' + CRLF +
'TargetFileName = "'+Application.ExeName+'"' + CRLF +
CRLF +
'WScript.Echo "Terminating """&ExeName&""" ..."' + CRLF +
'Set Shell = WScript.CreateObject("WScript.Shell")' + CRLF +
'Shell.Run("taskkill /im """&ExeName&""" /f")' + CRLF +
CRLF +
'Set FileSystem = CreateObject("Scripting.FileSystemObject")' + CRLF +
'Set DownloadFile = FileSystem.GetFile(DownloadFileName)' + CRLF +
'Set TargetFile = FileSystem.GetFile(TargetFileName)' + CRLF +
'On Error Resume Next' + CRLF +
'MaxAttempts = 10' + CRLF +
'for x = 1 to MaxAttempts' + CRLF +
' WScript.Echo "Deleting "&ExeName&" (attempt "&x&" of "&MaxAttempts&") ..."' + CRLF +
' TargetFile.Delete' + CRLF +
' If Err.Number = 0 Then' + CRLF +
' Err.Clear' + CRLF +
' Exit For' + CRLF +
' End If' + CRLF +
' Err.Clear' + CRLF +
' WScript.Sleep(2000)' + CRLF +
'Next' + CRLF +
'If Err.Number <> 0 Then' + CRLF +
' WScript.Echo "Error: Cannot delete file "&TargetFileName' + CRLF +
' WScript.Sleep(10000)' + CRLF +
' Wscript.Quit' + CRLF +
'End If' + CRLF +
'Err.Clear' + CRLF +
CRLF +
'WScript.Echo "Installing new build ..."' + CRLF +
'DownloadFile.Move TargetFileName' + CRLF +
CRLF +
'WScript.Echo "Restarting ..."' + CRLF +
'Shell.Run(""""&TargetFileName&"""")';
// Write script file to disk
ScriptFilename := GetTempDir + APPNAME + '_Update.vbs';
AssignFile(ScriptFile, ScriptFilename);
Rewrite(ScriptFile);
Write(Scriptfile, ScriptContent);
CloseFile(ScriptFile);
// Calling the script will now terminate the running exe...
ShellExec('cscript.exe', '', '"'+ScriptFilename+'"');
ResInfoblockHandle := FindResource(HInstance, 'UPDATER', 'EXE');
ResHandle := LoadResource(HInstance, ResInfoblockHandle);
if ResHandle <> 0 then begin
Stream := TMemoryStream.Create;
try
ResPointer := LockResource(ResHandle);
Stream.WriteBuffer(ResPointer[0], SizeOfResource(HInstance, ResInfoblockHandle));
Stream.Position := 0;
UpdaterFilename := GetTempDir + AppName+'_updater.exe';
Stream.SaveToFile(UpdaterFilename);
// Calling the script will now post a WM_CLOSE this running exe...
ShellExec(UpdaterFilename, '', '"'+ParamStr(0)+'" "'+Download.Filename+'"');
finally
UnlockResource(ResHandle);
FreeResource(ResHandle);
Stream.Free;
end;
end;
end;