mirror of
https://github.com/HeidiSQL/HeidiSQL.git
synced 2026-03-13 09:24:25 +08:00
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:
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
@echo off
|
||||
brcc32 updater.rc updater.res
|
||||
ren updater.RES updater.res
|
||||
@@ -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.
|
||||
Binary file not shown.
@@ -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;
|
||||
|
||||
@@ -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
BIN
res/updater.exe
Normal file
Binary file not shown.
273
res/updater/updater.dpr
Normal file
273
res/updater/updater.dpr
Normal 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
110
res/updater/updater.dproj
Normal 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>
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user