App Deployment#
Static deployment (Windows)#
windeployqt
and found under QtFolder/bin/windeployqt
Build your project under
Release
mode- Open a command prompt (requires privileges if Qt files are inside a protected path)
cd qtfolder/5.x.x/msvc*/bin
(or add it to path to use with a command prompt anywhere)
Windeployqt
takes as argument the directory where your .exe lies, or the .exe directly. It will scan for the dependencies.windeployqt "path\to\exe"
Qt may fail finding some of your dependencies. Try to run the program and, if needed, add them manually next to your .exe.
You can use CFF Explorer to select your .exe and have a view on required .dll files and their path. Sadly, no automatic system exists to copy the required files automatically. Also, the list of dependencies may not include everything (some .dll will require other .dll …).
- It will gather the required Qt dependencies into given folder. You can select an app, a library and a plugin directory for a cleaner work tree :
windeployqt "path\to\exe" -dir "path\to\distrib" -libdir "path\to\distrib\lib" -plugindir "path\to\distrib\plugin"
If you modify the plugin dir, you may need to add it to
PATH
, since your app won’t know where the files are now.
Turn it into an installer#
Install it
Launch
Inno Setup Compiler
and selectCreate a new empty script file using the Script Wizard
Finish the Wizard, then you will have your script that can be edited
If you compile then run, your
setup.exe
file is created and you will be able to install your program
License .rtf#
following license file
is the (L)GPL for open-source projects.InnoSetup
, link it with the command LicenseFile=path\to\license\license.rtf
under [SETUP]
tag.Add a dependency installer#
following script
propose the installation of your runnable and, if cancelled, will prevent the installation of your app.DetectAndInstallPrerequisites
function, along with the [Files]
section to include the source file) : 1#define MyAppName "Slicer"
2#define MyAppVersion "1.0"
3#define MyAppPublisher "SPL Group"
4#define MyAppURL "https://www.hevs.ch/"
5#define MyAppExeName "Slicer.exe"
6#define MyAppAssocName "Point Cloud Samples"
7#define MyAppAssocExt ".pcs"
8#define MyAppAssocKey StringChange(MyAppAssocName, " ", "") + MyAppAssocExt
9
10[Setup]
11; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications.
12; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
13AppId={{547814E9-1BD4-4A91-9511-9FEF1892A9EF}
14AppName={#MyAppName}
15AppVersion={#MyAppVersion}
16;AppVerName={#MyAppName} {#MyAppVersion}
17AppPublisher={#MyAppPublisher}
18AppPublisherURL={#MyAppURL}
19AppSupportURL={#MyAppURL}
20AppUpdatesURL={#MyAppURL}
21DefaultDirName={autopf}\{#MyAppName}
22ChangesAssociations=yes
23DefaultGroupName={#MyAppName}
24AllowNoIcons=yes
25; Uncomment the following line to run in non administrative install mode (install for current user only.)
26;PrivilegesRequired=lowest
27OutputDir=C:\Users\Axel\Desktop\slicer_qt\install
28OutputBaseFilename=slicer
29Compression=lzma
30SolidCompression=yes
31WizardStyle=modern
32
33[Languages]
34Name: "english"; MessagesFile: "compiler:Default.isl"
35Name: "french"; MessagesFile: "compiler:Languages\French.isl"
36Name: "german"; MessagesFile: "compiler:Languages\German.isl"
37
38[Tasks]
39Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
40
41[Files]
42Source: "C:\Users\Axel\Desktop\slicer_qt\bin\deploy\*"; DestDir: "{tmp}"; Flags: dontcopy
43Source: "C:\Users\Axel\Desktop\slicer_qt\bin\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
44Source: "C:\Users\Axel\Desktop\slicer_qt\bin\deploy\qt\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
45Source: "C:\Users\Axel\Desktop\slicer_qt\bin\dlls\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
46; NOTE: Don't use "Flags: ignoreversion" on any shared system files
47
48[Registry]
49Root: HKA; Subkey: "Software\Classes\{#MyAppAssocExt}\OpenWithProgids"; ValueType: string; ValueName: "{#MyAppAssocKey}"; ValueData: ""; Flags: uninsdeletevalue
50Root: HKA; Subkey: "Software\Classes\{#MyAppAssocKey}"; ValueType: string; ValueName: ""; ValueData: "{#MyAppAssocName}"; Flags: uninsdeletekey
51Root: HKA; Subkey: "Software\Classes\{#MyAppAssocKey}\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\{#MyAppExeName},0"
52Root: HKA; Subkey: "Software\Classes\{#MyAppAssocKey}\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#MyAppExeName}"" ""%1"""
53Root: HKA; Subkey: "Software\Classes\Applications\{#MyAppExeName}\SupportedTypes"; ValueType: string; ValueName: ".myp"; ValueData: ""
54
55[Icons]
56Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
57Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
58
59[Run]
60Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
61
62[Code]
63const
64QuitMessageReboot = 'The installation needs to be restarted';
65QuitMessageError = 'Prerequistes installation aborted';
66
67function GetString(const x86, x64: String): String;
68begin
69if IsX64 then begin
70 Result := x64;
71end else begin
72 Result := x86;
73end;
74end;
75
76function CompareVersion(const Version1, Version2: String): Integer;
77var
78Position, Number1, Number2: Integer;
79begin
80Result := 0;
81while (Version1 <> '') or (Version2 <> '') do begin
82 Position := Pos('.', Version1);
83 if Position > 0 then begin
84 Number1 := StrToIntDef(Copy(Version1, 1, Position - 1), 0);
85 Delete(Version1, 1, Position);
86 end else if Version1 <> '' then begin
87 Number1 := StrToIntDef(Version1, 0);
88 Version1 := '';
89 end else begin
90 Number1 := 0;
91 end;
92 Position := Pos('.', Version2);
93 if Position > 0 then begin
94 Number2 := StrToIntDef(Copy(Version2, 1, Position - 1), 0);
95 Delete(Version2, 1, Position);
96 end else if Version2 <> '' then begin
97 Number2 := StrToIntDef(Version2, 0);
98 Version2 := '';
99 end else begin
100 Number2 := 0;
101 end;
102 if Number1 < Number2 then begin
103 Result := -1;
104 break;
105 end else if Number1 > Number2 then begin
106 Result := 1;
107 break;
108 end;
109end;
110end;
111
112function MsiEnumRelatedProducts(UpgradeCode: String; Reserved, Index: DWORD; ProductCode: String): Integer;
113external 'MsiEnumRelatedProductsW@msi.dll stdcall';
114function MsiGetProductInfo(ProductCode, PropertyName, Value: String; var ValueSize: DWORD): Integer;
115external 'MsiGetProductInfoW@msi.dll stdcall';
116function IsMsiProductInstalled(const UpgradeCode, MinVersion: String): Boolean;
117var
118ProductCode, Version: String;
119ValueSize: DWORD;
120begin
121SetLength(ProductCode, 39);
122Result := False;
123if MsiEnumRelatedProducts(UpgradeCode, 0, 0, ProductCode) = 0 then begin
124 SetLength(Version, 39);
125 ValueSize := Length(Version);
126 if MsiGetProductInfo(ProductCode, 'VersionString', Version, ValueSize) = 0 then begin
127 Result := CompareVersion(Version, MinVersion) >= 0;
128 end;
129end;
130end;
131
132
133(*** Install the prerequistes ***)
134function DetectAndInstallPrerequisites: Boolean;
135var
136ResultCode: Integer;
137InstallOk: Boolean;
138begin
139Result := True;
140
141(*** Check VC_REDIST ***)
142 (*** Check if already exists, else install ***)
143// https://support.microsoft.com/en-US/help/2977003/the-latest-supported-visual-c-downloads
144if not IsMsiProductInstalled(GetString('{65E5BD06-6392-3027-8C26-853107D3CF1A}', '{36F68A90-239C-34DF-B58C-64B30153CE35}'), '14.28.29325') then
145begin
146 (*** Does not exists - install ***)
147 ExtractTemporaryFile('vcredist2015_2017_2019_x64.exe');
148 InstallOk := Exec(ExpandConstant('{tmp}\vcredist2015_2017_2019_x64.exe'), '-install "' + ExpandConstant('{tmp}') + '"', '', SW_SHOW, ewWaitUntilTerminated, ResultCode);
149 (*** Result code gives indication if install succeeded - try with message boxes to determine ok codes ***)
150 if not ResultCode = 0 then
151 begin
152 Result := False
153 exit;
154 end;
155end;
156if not IsMsiProductInstalled(GetString('{1F4F1D2A-D9DA-32CF-9909-48485DA06DD5}', '{5B75F761-BAC8-33BC-A381-464DDDD813A3}'), '10.0.40219') then
157begin
158 (*** Does not exists - install ***)
159 ExtractTemporaryFile('vcredist2010_x64.exe');
160 InstallOk := Exec(ExpandConstant('{tmp}\vcredist2010_x64.exe'), '-install "' + ExpandConstant('{tmp}') + '"', '', SW_SHOW, ewWaitUntilTerminated, ResultCode);
161 (*** Result code gives indication if install succeeded - try with message boxes to determine ok codes ***)
162 if not ResultCode = 0 then
163 begin
164 Result := False
165 exit;
166 end;
167end;
168
169 (*** To display a message to the user : MsgBox('Code value : ' + IntToStr(ResultCode) + ' / install NOT Ok', mbError, MB_OK); ***)
170end;
171
172(*** Callback when install will be launched ***)
173function PrepareToInstall(var NeedsRestart: Boolean): String;
174var
175ChecksumBefore, ChecksumAfter: String;
176begin
177ChecksumBefore := MakePendingFileRenameOperationsChecksum;
178(*** Check prerequistes ***)
179if DetectAndInstallPrerequisites then begin
180 ChecksumAfter := MakePendingFileRenameOperationsChecksum;
181 if ChecksumBefore <> ChecksumAfter then begin
182 NeedsRestart := True;
183 Result := QuitMessageReboot;
184 end;
185end else
186 Result := QuitMessageError;
187end;
Add downloadable dependencies#
VC Redistributables
files to reduce your installer size).following script
propose an automatic internet downloading and installation of dependencies such as VC Redistributables
, that you can enable by uncommenting the corresponding #define
.InitializeSetup
function) : 1// comment out dependency defines to disable installing them
2//#define UseMsi45
3
4//#define UseDotNet11
5//#define UseDotNet20
6//#define UseDotNet35
7//#define UseDotNet40Client
8//#define UseDotNet40Full
9//#define UseDotNet45
10//#define UseDotNet46
11//#define UseDotNet47
12//#define UseDotNet48
13
14// requires netcorecheck.exe and netcorecheck_x64.exe (see download link below)
15//#define UseNetCoreCheck
16#ifdef UseNetCoreCheck
17#define UseNetCore31
18#define UseNetCore31Asp
19#define UseNetCore31Desktop
20#define UseDotNet50
21#define UseDotNet50Asp
22#define UseDotNet50Desktop
23#endif
24
25#define UseMsiProductCheck
26#ifdef UseMsiProductCheck
27//#define UseVC2005
28//#define UseVC2008
29#define UseVC2010
30//#define UseVC2012
31//#define UseVC2013
32#define UseVC2015To2019
33#endif
34
35// requires dxwebsetup.exe (see download link below)
36//#define UseDirectX
37
38//#define UseSql2008Express
39//#define UseSql2012Express
40//#define UseSql2014Express
41//#define UseSql2016Express
42//#define UseSql2017Express
43//#define UseSql2019Express
44
45#define MyAppName "Slicer"
46#define MyAppVersion "1.0"
47#define MyAppPublisher "SPL Group"
48#define MyAppURL "https://www.hevs.ch/"
49#define MyAppExeName "Slicer.exe"
50#define MyAppAssocName "Point Cloud Samples"
51#define MyAppAssocExt ".pcs"
52#define MyAppAssocKey StringChange(MyAppAssocName, " ", "") + MyAppAssocExt
53
54[Setup]
55; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications.
56; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
57AppId={{547814E9-1BD4-4A91-9511-9FEF1892A9EF}
58AppName={#MyAppName}
59AppVersion={#MyAppVersion}
60;AppVerName={#MyAppName} {#MyAppVersion}
61AppPublisher={#MyAppPublisher}
62AppPublisherURL={#MyAppURL}
63AppSupportURL={#MyAppURL}
64AppUpdatesURL={#MyAppURL}
65DefaultDirName={autopf}\{#MyAppName}
66ChangesAssociations=yes
67DefaultGroupName={#MyAppName}
68AllowNoIcons=yes
69; Uncomment the following line to run in non administrative install mode (install for current user only.)
70;PrivilegesRequired=lowest
71OutputDir=C:\Users\Axel\Desktop\slicer_qt\install
72OutputBaseFilename=slicer
73Compression=lzma
74SolidCompression=yes
75WizardStyle=modern
76
77// remove next line if you only deploy 32-bit binaries and dependencies
78ArchitecturesInstallIn64BitMode=x64
79
80[Languages]
81Name: "english"; MessagesFile: "compiler:Default.isl"
82Name: "french"; MessagesFile: "compiler:Languages\French.isl"
83Name: "german"; MessagesFile: "compiler:Languages\German.isl"
84
85[Tasks]
86Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
87
88[Files]
89//Source: "C:\Users\Axel\Desktop\slicer_qt\bin\deploy\*"; DestDir: "{tmp}"; Flags: dontcopy
90Source: "C:\Users\Axel\Desktop\slicer_qt\bin\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
91Source: "C:\Users\Axel\Desktop\slicer_qt\bin\deploy\qt\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
92Source: "C:\Users\Axel\Desktop\slicer_qt\bin\dlls\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
93; NOTE: Don't use "Flags: ignoreversion" on any shared system files
94
95[Registry]
96Root: HKA; Subkey: "Software\Classes\{#MyAppAssocExt}\OpenWithProgids"; ValueType: string; ValueName: "{#MyAppAssocKey}"; ValueData: ""; Flags: uninsdeletevalue
97Root: HKA; Subkey: "Software\Classes\{#MyAppAssocKey}"; ValueType: string; ValueName: ""; ValueData: "{#MyAppAssocName}"; Flags: uninsdeletekey
98Root: HKA; Subkey: "Software\Classes\{#MyAppAssocKey}\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\{#MyAppExeName},0"
99Root: HKA; Subkey: "Software\Classes\{#MyAppAssocKey}\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#MyAppExeName}"" ""%1"""
100Root: HKA; Subkey: "Software\Classes\Applications\{#MyAppExeName}\SupportedTypes"; ValueType: string; ValueName: ".myp"; ValueData: ""
101
102[Icons]
103Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
104Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
105
106[Run]
107Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
108
109// shared code for installing the dependencies
110[Code]
111// types and variables
112type
113TDependency = record
114 Filename: String;
115 Parameters: String;
116 Title: String;
117 URL: String;
118 Checksum: String;
119 ForceSuccess: Boolean;
120 InstallClean: Boolean;
121 RebootAfter: Boolean;
122end;
123InstallResult = (InstallSuccessful, InstallRebootRequired, InstallError);
124var
125MemoInstallInfo: String;
126Dependencies: array of TDependency;
127DelayedReboot, ForceX86: Boolean;
128DownloadPage: TDownloadWizardPage;
129procedure AddDependency(const Filename, Parameters, Title, URL, Checksum: String; const ForceSuccess, InstallClean, RebootAfter: Boolean);
130var
131Dependency: TDependency;
132I: Integer;
133begin
134MemoInstallInfo := MemoInstallInfo + #13#10 + '%1' + Title;
135Dependency.Filename := Filename;
136Dependency.Parameters := Parameters;
137Dependency.Title := Title;
138if FileExists(ExpandConstant('{tmp}{\}') + Filename) then begin
139 Dependency.URL := '';
140end else begin
141 Dependency.URL := URL;
142end;
143Dependency.Checksum := Checksum;
144Dependency.ForceSuccess := ForceSuccess;
145Dependency.InstallClean := InstallClean;
146Dependency.RebootAfter := RebootAfter;
147I := GetArrayLength(Dependencies);
148SetArrayLength(Dependencies, I + 1);
149Dependencies[I] := Dependency;
150end;
151function IsPendingReboot: Boolean;
152var
153Value: String;
154begin
155Result := RegQueryMultiStringValue(HKEY_LOCAL_MACHINE, 'SYSTEM\CurrentControlSet\Control\Session Manager', 'PendingFileRenameOperations', Value) or
156 (RegQueryMultiStringValue(HKEY_LOCAL_MACHINE, 'SYSTEM\CurrentControlSet\Control\Session Manager', 'SetupExecute', Value) and (Value <> ''));
157end;
158function InstallProducts: InstallResult;
159var
160ResultCode, I, ProductCount: Integer;
161begin
162Result := InstallSuccessful;
163ProductCount := GetArrayLength(Dependencies);
164MemoInstallInfo := SetupMessage(msgReadyMemoTasks);
165if ProductCount > 0 then begin
166 DownloadPage.Show;
167 for I := 0 to ProductCount - 1 do begin
168 if Dependencies[I].InstallClean and (DelayedReboot or IsPendingReboot) then begin
169 Result := InstallRebootRequired;
170 break;
171 end;
172 DownloadPage.SetText(Dependencies[I].Title, '');
173 DownloadPage.SetProgress(I + 1, ProductCount);
174 while True do begin
175 ResultCode := 0;
176 if ShellExec('', ExpandConstant('{tmp}{\}') + Dependencies[I].Filename, Dependencies[I].Parameters, '', SW_SHOWNORMAL, ewWaitUntilTerminated, ResultCode) then begin
177 if Dependencies[I].RebootAfter then begin
178 // delay reboot after install if we installed the last dependency anyways
179 if I = ProductCount - 1 then begin
180 DelayedReboot := True;
181 end else begin
182 Result := InstallRebootRequired;
183 MemoInstallInfo := Dependencies[I].Title;
184 end;
185 break;
186 end else if (ResultCode = 0) or Dependencies[I].ForceSuccess then begin
187 break;
188 end else if ResultCode = 3010 then begin
189 // Windows Installer ResultCode 3010: ERROR_SUCCESS_REBOOT_REQUIRED
190 DelayedReboot := True;
191 break;
192 end;
193 end;
194 case SuppressibleMsgBox(FmtMessage(SetupMessage(msgErrorFunctionFailed), [Dependencies[I].Title, IntToStr(ResultCode)]), mbError, MB_ABORTRETRYIGNORE, IDIGNORE) of
195 IDABORT: begin
196 Result := InstallError;
197 MemoInstallInfo := MemoInstallInfo + #13#10 + ' ' + Dependencies[I].Title;
198 break;
199 end;
200 IDIGNORE: begin
201 MemoInstallInfo := MemoInstallInfo + #13#10 + ' ' + Dependencies[I].Title;
202 break;
203 end;
204 end;
205 end;
206 if Result <> InstallSuccessful then begin
207 break;
208 end;
209 end;
210 DownloadPage.Hide;
211end;
212end;
213// Inno Setup event functions
214procedure InitializeWizard;
215begin
216DownloadPage := CreateDownloadPage(SetupMessage(msgWizardPreparing), SetupMessage(msgPreparingDesc), nil);
217end;
218function PrepareToInstall(var NeedsRestart: Boolean): String;
219begin
220DelayedReboot := False;
221case InstallProducts of
222 InstallError: begin
223 Result := MemoInstallInfo;
224 end;
225 InstallRebootRequired: begin
226 Result := MemoInstallInfo;
227 NeedsRestart := True;
228 // write into the registry that the installer needs to be executed again after restart
229 RegWriteStringValue(HKEY_CURRENT_USER, 'SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce', 'InstallBootstrap', ExpandConstant('{srcexe}'));
230 end;
231end;
232end;
233function NeedRestart: Boolean;
234begin
235Result := DelayedReboot;
236end;
237function UpdateReadyMemo(const Space, NewLine, MemoUserInfoInfo, MemoDirInfo, MemoTypeInfo, MemoComponentsInfo, MemoGroupInfo, MemoTasksInfo: String): String;
238begin
239Result := '';
240if MemoUserInfoInfo <> '' then begin
241 Result := Result + MemoUserInfoInfo + Newline + NewLine;
242end;
243if MemoDirInfo <> '' then begin
244 Result := Result + MemoDirInfo + Newline + NewLine;
245end;
246if MemoTypeInfo <> '' then begin
247 Result := Result + MemoTypeInfo + Newline + NewLine;
248end;
249if MemoComponentsInfo <> '' then begin
250 Result := Result + MemoComponentsInfo + Newline + NewLine;
251end;
252if MemoGroupInfo <> '' then begin
253 Result := Result + MemoGroupInfo + Newline + NewLine;
254end;
255if MemoTasksInfo <> '' then begin
256 Result := Result + MemoTasksInfo;
257end;
258if MemoInstallInfo <> '' then begin
259 if MemoTasksInfo = '' then begin
260 Result := Result + SetupMessage(msgReadyMemoTasks);
261 end;
262 Result := Result + FmtMessage(MemoInstallInfo, [Space]);
263end;
264end;
265function NextButtonClick(const CurPageID: Integer): Boolean;
266var
267I, ProductCount: Integer;
268Retry: Boolean;
269begin
270Result := True;
271if (CurPageID = wpReady) and (MemoInstallInfo <> '') then begin
272 DownloadPage.Show;
273 ProductCount := GetArrayLength(Dependencies);
274 for I := 0 to ProductCount - 1 do begin
275 if Dependencies[I].URL <> '' then begin
276 DownloadPage.Clear;
277 DownloadPage.Add(Dependencies[I].URL, Dependencies[I].Filename, Dependencies[I].Checksum);
278 Retry := True;
279 while Retry do begin
280 Retry := False;
281 try
282 DownloadPage.Download;
283 except
284 if GetExceptionMessage = SetupMessage(msgErrorDownloadAborted) then begin
285 Result := False;
286 I := ProductCount;
287 end else begin
288 case SuppressibleMsgBox(AddPeriod(GetExceptionMessage), mbError, MB_ABORTRETRYIGNORE, IDIGNORE) of
289 IDABORT: begin
290 Result := False;
291 I := ProductCount;
292 end;
293 IDRETRY: begin
294 Retry := True;
295 end;
296 end;
297 end;
298 end;
299 end;
300 end;
301 end;
302 DownloadPage.Hide;
303end;
304end;
305// architecture helper functions
306function IsX64: Boolean;
307begin
308Result := not ForceX86 and Is64BitInstallMode;
309end;
310function GetString(const x86, x64: String): String;
311begin
312if IsX64 then begin
313 Result := x64;
314end else begin
315 Result := x86;
316end;
317end;
318function GetArchitectureSuffix: String;
319begin
320Result := GetString('', '_x64');
321end;
322function GetArchitectureTitle: String;
323begin
324Result := GetString(' (x86)', ' (x64)');
325end;
326function CompareVersion(const Version1, Version2: String): Integer;
327var
328Position, Number1, Number2: Integer;
329begin
330Result := 0;
331while (Version1 <> '') or (Version2 <> '') do begin
332 Position := Pos('.', Version1);
333 if Position > 0 then begin
334 Number1 := StrToIntDef(Copy(Version1, 1, Position - 1), 0);
335 Delete(Version1, 1, Position);
336 end else if Version1 <> '' then begin
337 Number1 := StrToIntDef(Version1, 0);
338 Version1 := '';
339 end else begin
340 Number1 := 0;
341 end;
342 Position := Pos('.', Version2);
343 if Position > 0 then begin
344 Number2 := StrToIntDef(Copy(Version2, 1, Position - 1), 0);
345 Delete(Version2, 1, Position);
346 end else if Version2 <> '' then begin
347 Number2 := StrToIntDef(Version2, 0);
348 Version2 := '';
349 end else begin
350 Number2 := 0;
351 end;
352 if Number1 < Number2 then begin
353 Result := -1;
354 break;
355 end else if Number1 > Number2 then begin
356 Result := 1;
357 break;
358 end;
359end;
360end;
361#ifdef UseNetCoreCheck
362// source code: https://github.com/dotnet/deployment-tools/tree/master/src/clickonce/native/projects/NetCoreCheck
363function IsNetCoreInstalled(const Version: String): Boolean;
364var
365ResultCode: Integer;
366begin
367if not FileExists(ExpandConstant('{tmp}{\}') + 'netcorecheck' + GetArchitectureSuffix + '.exe') then begin
368 ExtractTemporaryFile('netcorecheck' + GetArchitectureSuffix + '.exe');
369end;
370Result := ShellExec('', ExpandConstant('{tmp}{\}') + 'netcorecheck' + GetArchitectureSuffix + '.exe', Version, '', SW_HIDE, ewWaitUntilTerminated, ResultCode) and (ResultCode = 0);
371end;
372#endif
373#ifdef UseMsiProductCheck
374function MsiEnumRelatedProducts(UpgradeCode: String; Reserved, Index: DWORD; ProductCode: String): Integer;
375external 'MsiEnumRelatedProductsW@msi.dll stdcall';
376function MsiGetProductInfo(ProductCode, PropertyName, Value: String; var ValueSize: DWORD): Integer;
377external 'MsiGetProductInfoW@msi.dll stdcall';
378function IsMsiProductInstalled(const UpgradeCode, MinVersion: String): Boolean;
379var
380ProductCode, Version: String;
381ValueSize: DWORD;
382begin
383SetLength(ProductCode, 39);
384Result := False;
385if MsiEnumRelatedProducts(UpgradeCode, 0, 0, ProductCode) = 0 then begin
386 SetLength(Version, 39);
387 ValueSize := Length(Version);
388 if MsiGetProductInfo(ProductCode, 'VersionString', Version, ValueSize) = 0 then begin
389 Result := CompareVersion(Version, MinVersion) >= 0;
390 end;
391end;
392end;
393#endif
394
395function InitializeSetup: Boolean;
396var
397Version: String;
398begin
399#ifdef UseMsi45
400// https://www.microsoft.com/en-US/download/details.aspx?id=8483
401if not GetVersionNumbersString(ExpandConstant('{sys}{\}msi.dll'), Version) or (CompareVersion(Version, '4.5') < 0) then begin
402 AddDependency('msi45' + GetArchitectureSuffix + '.msu',
403 '/quiet /norestart',
404 'Windows Installer 4.5',
405 GetString('https://download.microsoft.com/download/2/6/1/261fca42-22c0-4f91-9451-0e0f2e08356d/Windows6.0-KB942288-v2-x86.msu', 'https://download.microsoft.com/download/2/6/1/261fca42-22c0-4f91-9451-0e0f2e08356d/Windows6.0-KB942288-v2-x64.msu'),
406 '', False, False, False);
407end;
408#endif
409#ifdef UseDotNet11
410// https://www.microsoft.com/en-US/download/details.aspx?id=26
411if not IsDotNetInstalled(net11, 0) then begin
412 AddDependency('dotnetfx11.exe',
413 '/q',
414 '.NET Framework 1.1',
415 'https://download.microsoft.com/download/a/a/c/aac39226-8825-44ce-90e3-bf8203e74006/dotnetfx.exe',
416 '', False, False, False);
417end;
418// https://www.microsoft.com/en-US/download/details.aspx?id=33
419if not IsDotNetInstalled(net11, 1) then begin
420 AddDependency('dotnetfx11sp1.exe',
421 '/q',
422 '.NET Framework 1.1 Service Pack 1',
423 'https://download.microsoft.com/download/8/b/4/8b4addd8-e957-4dea-bdb8-c4e00af5b94b/NDP1.1sp1-KB867460-X86.exe',
424 '', False, False, False);
425end;
426#endif
427#ifdef UseDotNet20
428// https://www.microsoft.com/en-US/download/details.aspx?id=1639
429if not IsDotNetInstalled(net20, 2) then begin
430 AddDependency('dotnetfx20' + GetArchitectureSuffix + '.exe',
431 '/lang:enu /passive /norestart',
432 '.NET Framework 2.0 Service Pack 2',
433 GetString('https://download.microsoft.com/download/c/6/e/c6e88215-0178-4c6c-b5f3-158ff77b1f38/NetFx20SP2_x86.exe', 'https://download.microsoft.com/download/c/6/e/c6e88215-0178-4c6c-b5f3-158ff77b1f38/NetFx20SP2_x64.exe'),
434 '', False, False, False);
435end;
436#endif
437#ifdef UseDotNet35
438// https://www.microsoft.com/en-US/download/details.aspx?id=22
439if not IsDotNetInstalled(net35, 1) then begin
440 AddDependency('dotnetfx35.exe',
441 '/lang:enu /passive /norestart',
442 '.NET Framework 3.5 Service Pack 1',
443 'https://download.microsoft.com/download/0/6/1/061f001c-8752-4600-a198-53214c69b51f/dotnetfx35setup.exe',
444 '', False, False, False);
445end;
446#endif
447#ifdef UseDotNet40Client
448// https://www.microsoft.com/en-US/download/details.aspx?id=24872
449if not IsDotNetInstalled(net4client, 0) and not IsDotNetInstalled(net4full, 0) then begin
450 AddDependency('dotNetFx40_Client_setup.exe',
451 '/lcid ' + IntToStr(GetUILanguage) + ' /passive /norestart',
452 '.NET Framework 4.0 Client',
453 'https://download.microsoft.com/download/7/B/6/7B629E05-399A-4A92-B5BC-484C74B5124B/dotNetFx40_Client_setup.exe',
454 '', False, False, False);
455end;
456#endif
457#ifdef UseDotNet40Full
458// https://www.microsoft.com/en-US/download/details.aspx?id=17718
459if not IsDotNetInstalled(net4full, 0) then begin
460 AddDependency('dotNetFx40_Full_setup.exe',
461 '/lcid ' + IntToStr(GetUILanguage) + ' /passive /norestart',
462 '.NET Framework 4.0',
463 'https://download.microsoft.com/download/1/B/E/1BE39E79-7E39-46A3-96FF-047F95396215/dotNetFx40_Full_setup.exe',
464 '', False, False, False);
465end;
466#endif
467#ifdef UseDotNet45
468// https://www.microsoft.com/en-US/download/details.aspx?id=42643
469if not IsDotNetInstalled(net452, 0) then begin
470 AddDependency('dotnetfx45.exe',
471 '/lcid ' + IntToStr(GetUILanguage) + ' /passive /norestart',
472 '.NET Framework 4.5.2',
473 'https://download.microsoft.com/download/B/4/1/B4119C11-0423-477B-80EE-7A474314B347/NDP452-KB2901954-Web.exe',
474 '', False, False, False);
475end;
476#endif
477#ifdef UseDotNet46
478// https://www.microsoft.com/en-US/download/details.aspx?id=53345
479if not IsDotNetInstalled(net462, 0) then begin
480 AddDependency('dotnetfx46.exe',
481 '/lcid ' + IntToStr(GetUILanguage) + ' /passive /norestart',
482 '.NET Framework 4.6.2',
483 'https://download.microsoft.com/download/D/5/C/D5C98AB0-35CC-45D9-9BA5-B18256BA2AE6/NDP462-KB3151802-Web.exe',
484 '', False, False, False);
485end;
486#endif
487#ifdef UseDotNet47
488// https://support.microsoft.com/en-US/help/4054531
489if not IsDotNetInstalled(net472, 0) then begin
490 AddDependency('dotnetfx47.exe',
491 '/lcid ' + IntToStr(GetUILanguage) + ' /passive /norestart',
492 '.NET Framework 4.7.2',
493 'https://download.microsoft.com/download/0/5/C/05C1EC0E-D5EE-463B-BFE3-9311376A6809/NDP472-KB4054531-Web.exe',
494 '', False, False, False);
495end;
496#endif
497#ifdef UseDotNet48
498// https://dotnet.microsoft.com/download/dotnet-framework/net48
499if not IsDotNetInstalled(net48, 0) then begin
500 AddDependency('dotnetfx48.exe',
501 '/lcid ' + IntToStr(GetUILanguage) + ' /passive /norestart',
502 '.NET Framework 4.8',
503 'https://download.visualstudio.microsoft.com/download/pr/7afca223-55d2-470a-8edc-6a1739ae3252/c9b8749dd99fc0d4453b2a3e4c37ba16/ndp48-web.exe',
504 '', False, False, False);
505end;
506#endif
507#ifdef UseNetCore31
508// https://dotnet.microsoft.com/download/dotnet-core/3.1
509if not IsNetCoreInstalled('Microsoft.NETCore.App 3.1.12') then begin
510 AddDependency('netcore31' + GetArchitectureSuffix + '.exe',
511 '/lcid ' + IntToStr(GetUILanguage) + ' /passive /norestart',
512 '.NET Core Runtime 3.1.12' + GetArchitectureTitle,
513 GetString('https://go.microsoft.com/fwlink/?linkid=2155262', 'https://go.microsoft.com/fwlink/?linkid=2155351'),
514 '', False, False, False);
515end;
516#endif
517#ifdef UseNetCore31Asp
518// https://dotnet.microsoft.com/download/dotnet-core/3.1
519if not IsNetCoreInstalled('Microsoft.AspNetCore.App 3.1.12') then begin
520 AddDependency('netcore31asp' + GetArchitectureSuffix + '.exe',
521 '/lcid ' + IntToStr(GetUILanguage) + ' /passive /norestart',
522 'ASP.NET Core Runtime 3.1.12' + GetArchitectureTitle,
523 GetString('https://go.microsoft.com/fwlink/?linkid=2155260', 'https://go.microsoft.com/fwlink/?linkid=2155349'),
524 '', False, False, False);
525end;
526#endif
527#ifdef UseNetCore31Desktop
528// https://dotnet.microsoft.com/download/dotnet-core/3.1
529if not IsNetCoreInstalled('Microsoft.WindowsDesktop.App 3.1.12') then begin
530 AddDependency('netcore31desktop' + GetArchitectureSuffix + '.exe',
531 '/lcid ' + IntToStr(GetUILanguage) + ' /passive /norestart',
532 '.NET Desktop Runtime 3.1.12' + GetArchitectureTitle,
533 GetString('https://go.microsoft.com/fwlink/?linkid=2155261', 'https://go.microsoft.com/fwlink/?linkid=2155350'),
534 '', False, False, False);
535end;
536#endif
537#ifdef UseDotNet50
538// https://dotnet.microsoft.com/download/dotnet/5.0
539if not IsNetCoreInstalled('Microsoft.NETCore.App 5.0.3') then begin
540 AddDependency('dotnet50' + GetArchitectureSuffix + '.exe',
541 '/lcid ' + IntToStr(GetUILanguage) + ' /passive /norestart',
542 '.NET Runtime 5.0.3' + GetArchitectureTitle,
543 GetString('https://go.microsoft.com/fwlink/?linkid=2155348', 'https://go.microsoft.com/fwlink/?linkid=2155259'),
544 '', False, False, False);
545end;
546#endif
547#ifdef UseDotNet50Asp
548// https://dotnet.microsoft.com/download/dotnet/5.0
549if not IsNetCoreInstalled('Microsoft.AspNetCore.App 5.0.3') then begin
550 AddDependency('dotnet50asp' + GetArchitectureSuffix + '.exe',
551 '/lcid ' + IntToStr(GetUILanguage) + ' /passive /norestart',
552 'ASP.NET Core Runtime 5.0.3' + GetArchitectureTitle,
553 GetString('https://go.microsoft.com/fwlink/?linkid=2155346', 'https://go.microsoft.com/fwlink/?linkid=2155257'),
554 '', False, False, False);
555end;
556#endif
557#ifdef UseDotNet50Desktop
558// https://dotnet.microsoft.com/download/dotnet/5.0
559if not IsNetCoreInstalled('Microsoft.WindowsDesktop.App 5.0.3') then begin
560 AddDependency('dotnet50desktop' + GetArchitectureSuffix + '.exe',
561 '/lcid ' + IntToStr(GetUILanguage) + ' /passive /norestart',
562 '.NET Desktop Runtime 5.0.3' + GetArchitectureTitle,
563 GetString('https://go.microsoft.com/fwlink/?linkid=2155347', 'https://go.microsoft.com/fwlink/?linkid=2155258'),
564 '', False, False, False);
565end;
566#endif
567#ifdef UseVC2005
568// https://www.microsoft.com/en-US/download/details.aspx?id=26347
569if not IsMsiProductInstalled(GetString('{86C9D5AA-F00C-4921-B3F2-C60AF92E2844}', '{A8D19029-8E5C-4E22-8011-48070F9E796E}'), '8.0.61000') then begin
570 AddDependency('vcredist2005' + GetArchitectureSuffix + '.exe',
571 '/q',
572 'Visual C++ 2005 Service Pack 1 Redistributable' + GetArchitectureTitle,
573 GetString('https://download.microsoft.com/download/8/B/4/8B42259F-5D70-43F4-AC2E-4B208FD8D66A/vcredist_x86.EXE', 'https://download.microsoft.com/download/8/B/4/8B42259F-5D70-43F4-AC2E-4B208FD8D66A/vcredist_x64.EXE'),
574 '', False, False, False);
575end;
576#endif
577#ifdef UseVC2008
578// https://www.microsoft.com/en-US/download/details.aspx?id=26368
579if not IsMsiProductInstalled(GetString('{DE2C306F-A067-38EF-B86C-03DE4B0312F9}', '{FDA45DDF-8E17-336F-A3ED-356B7B7C688A}'), '9.0.30729.6161') then begin
580 AddDependency('vcredist2008' + GetArchitectureSuffix + '.exe',
581 '/q',
582 'Visual C++ 2008 Service Pack 1 Redistributable' + GetArchitectureTitle,
583 GetString('https://download.microsoft.com/download/5/D/8/5D8C65CB-C849-4025-8E95-C3966CAFD8AE/vcredist_x86.exe', 'https://download.microsoft.com/download/5/D/8/5D8C65CB-C849-4025-8E95-C3966CAFD8AE/vcredist_x64.exe'),
584 '', False, False, False);
585end;
586#endif
587#ifdef UseVC2010
588// https://www.microsoft.com/en-US/download/details.aspx?id=26999
589if not IsMsiProductInstalled(GetString('{1F4F1D2A-D9DA-32CF-9909-48485DA06DD5}', '{5B75F761-BAC8-33BC-A381-464DDDD813A3}'), '10.0.40219') then begin
590 AddDependency('vcredist2010' + GetArchitectureSuffix + '.exe',
591 '/passive /norestart',
592 'Visual C++ 2010 Service Pack 1 Redistributable' + GetArchitectureTitle,
593 GetString('https://download.microsoft.com/download/1/6/5/165255E7-1014-4D0A-B094-B6A430A6BFFC/vcredist_x86.exe', 'https://download.microsoft.com/download/1/6/5/165255E7-1014-4D0A-B094-B6A430A6BFFC/vcredist_x64.exe'),
594 '', False, False, False);
595end;
596#endif
597#ifdef UseVC2012
598// https://www.microsoft.com/en-US/download/details.aspx?id=30679
599if not IsMsiProductInstalled(GetString('{4121ED58-4BD9-3E7B-A8B5-9F8BAAE045B7}', '{EFA6AFA1-738E-3E00-8101-FD03B86B29D1}'), '11.0.61030') then begin
600 AddDependency('vcredist2012' + GetArchitectureSuffix + '.exe',
601 '/passive /norestart',
602 'Visual C++ 2012 Update 4 Redistributable' + GetArchitectureTitle,
603 GetString('https://download.microsoft.com/download/1/6/B/16B06F60-3B20-4FF2-B699-5E9B7962F9AE/VSU_4/vcredist_x86.exe', 'https://download.microsoft.com/download/1/6/B/16B06F60-3B20-4FF2-B699-5E9B7962F9AE/VSU_4/vcredist_x64.exe'),
604 '', False, False, False);
605end;
606#endif
607#ifdef UseVC2013
608//ForceX86 := True; // force 32-bit install of next dependencies
609// https://support.microsoft.com/en-US/help/4032938
610if not IsMsiProductInstalled(GetString('{B59F5BF1-67C8-3802-8E59-2CE551A39FC5}', '{20400CF0-DE7C-327E-9AE4-F0F38D9085F8}'), '12.0.40664') then begin
611 AddDependency('vcredist2013' + GetArchitectureSuffix + '.exe',
612 '/passive /norestart',
613 'Visual C++ 2013 Update 5 Redistributable' + GetArchitectureTitle,
614 GetString('https://download.visualstudio.microsoft.com/download/pr/10912113/5da66ddebb0ad32ebd4b922fd82e8e25/vcredist_x86.exe', 'https://download.visualstudio.microsoft.com/download/pr/10912041/cee5d6bca2ddbcd039da727bf4acb48a/vcredist_x64.exe'),
615 '', False, False, False);
616end;
617//ForceX86 := False; // disable forced 32-bit install again
618#endif
619#ifdef UseVC2015To2019
620// https://support.microsoft.com/en-US/help/2977003/the-latest-supported-visual-c-downloads
621if not IsMsiProductInstalled(GetString('{65E5BD06-6392-3027-8C26-853107D3CF1A}', '{36F68A90-239C-34DF-B58C-64B30153CE35}'), '14.28.29325') then begin
622 AddDependency('vcredist2019' + GetArchitectureSuffix + '.exe',
623 '/passive /norestart',
624 'Visual C++ 2015-2019 Redistributable' + GetArchitectureTitle,
625 GetString('https://aka.ms/vs/16/release/vc_redist.x86.exe', 'https://aka.ms/vs/16/release/vc_redist.x64.exe'),
626 '', False, False, False);
627end;
628#endif
629#ifdef UseDirectX
630// https://www.microsoft.com/en-US/download/details.aspx?id=35
631ExtractTemporaryFile('dxwebsetup.exe');
632AddDependency('dxwebsetup.exe',
633 '/q',
634 'DirectX Runtime',
635 'https://download.microsoft.com/download/1/7/1/1718CCC4-6315-4D8E-9543-8E28A4E18C4C/dxwebsetup.exe',
636 '', True, False, False);
637#endif
638#ifdef UseSql2008Express
639// https://www.microsoft.com/en-US/download/details.aspx?id=30438
640if not RegQueryStringValue(HKLM, 'SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQLServer\CurrentVersion', 'CurrentVersion', Version) or (CompareVersion(Version, '10.50.4000') < 0) then begin
641 AddDependency('sql2008express' + GetArchitectureSuffix + '.exe',
642 '/QS /IACCEPTSQLSERVERLICENSETERMS /ACTION=INSTALL /FEATURES=SQL /INSTANCENAME=MSSQLSERVER',
643 'SQL Server 2008 R2 Service Pack 2 Express',
644 GetString('https://download.microsoft.com/download/0/4/B/04BE03CD-EAF3-4797-9D8D-2E08E316C998/SQLEXPR32_x86_ENU.exe', 'https://download.microsoft.com/download/0/4/B/04BE03CD-EAF3-4797-9D8D-2E08E316C998/SQLEXPR_x64_ENU.exe'),
645 '', False, False, False);
646end;
647#endif
648#ifdef UseSql2012Express
649// https://www.microsoft.com/en-US/download/details.aspx?id=56042
650if not RegQueryStringValue(HKLM, 'SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQLServer\CurrentVersion', 'CurrentVersion', Version) or (CompareVersion(Version, '11.0.7001') < 0) then begin
651 AddDependency('sql2012express' + GetArchitectureSuffix + '.exe',
652 '/QS /IACCEPTSQLSERVERLICENSETERMS /ACTION=INSTALL /FEATURES=SQL /INSTANCENAME=MSSQLSERVER',
653 'SQL Server 2012 Service Pack 4 Express',
654 GetString('https://download.microsoft.com/download/B/D/E/BDE8FAD6-33E5-44F6-B714-348F73E602B6/SQLEXPR32_x86_ENU.exe', 'https://download.microsoft.com/download/B/D/E/BDE8FAD6-33E5-44F6-B714-348F73E602B6/SQLEXPR_x64_ENU.exe'),
655 '', False, False, False);
656end;
657#endif
658#ifdef UseSql2014Express
659// https://www.microsoft.com/en-US/download/details.aspx?id=57473
660if not RegQueryStringValue(HKLM, 'SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL12.MSSQLSERVER\MSSQLServer\CurrentVersion', 'CurrentVersion', Version) or (CompareVersion(Version, '12.0.6024') < 0) then begin
661 AddDependency('sql2014express' + GetArchitectureSuffix + '.exe',
662 '/QS /IACCEPTSQLSERVERLICENSETERMS /ACTION=INSTALL /FEATURES=SQL /INSTANCENAME=MSSQLSERVER',
663 'SQL Server 2014 Service Pack 3 Express',
664 GetString('https://download.microsoft.com/download/3/9/F/39F968FA-DEBB-4960-8F9E-0E7BB3035959/SQLEXPR32_x86_ENU.exe', 'https://download.microsoft.com/download/3/9/F/39F968FA-DEBB-4960-8F9E-0E7BB3035959/SQLEXPR_x64_ENU.exe'),
665 '', False, False, False);
666end;
667#endif
668#ifdef UseSql2016Express
669// https://www.microsoft.com/en-US/download/details.aspx?id=56840
670if not RegQueryStringValue(HKLM, 'SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL13.MSSQLSERVER\MSSQLServer\CurrentVersion', 'CurrentVersion', Version) or (CompareVersion(Version, '13.0.5026') < 0) then begin
671 AddDependency('sql2016express' + GetArchitectureSuffix + '.exe',
672 '/QS /IACCEPTSQLSERVERLICENSETERMS /ACTION=INSTALL /FEATURES=SQL /INSTANCENAME=MSSQLSERVER',
673 'SQL Server 2016 Service Pack 2 Express',
674 'https://download.microsoft.com/download/3/7/6/3767D272-76A1-4F31-8849-260BD37924E4/SQLServer2016-SSEI-Expr.exe',
675 '', False, False, False);
676end;
677#endif
678#ifdef UseSql2017Express
679// https://www.microsoft.com/en-US/download/details.aspx?id=55994
680if not RegQueryStringValue(HKLM, 'SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL14.MSSQLSERVER\MSSQLServer\CurrentVersion', 'CurrentVersion', Version) or (CompareVersion(Version, '14') < 0) then begin
681 AddDependency('sql2017express' + GetArchitectureSuffix + '.exe',
682 '/QS /IACCEPTSQLSERVERLICENSETERMS /ACTION=INSTALL /FEATURES=SQL /INSTANCENAME=MSSQLSERVER',
683 'SQL Server 2017 Express',
684 'https://download.microsoft.com/download/5/E/9/5E9B18CC-8FD5-467E-B5BF-BADE39C51F73/SQLServer2017-SSEI-Expr.exe',
685 '', False, False, False);
686end;
687#endif
688#ifdef UseSql2019Express
689// https://www.microsoft.com/en-US/download/details.aspx?id=101064
690if not RegQueryStringValue(HKLM, 'SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL15.MSSQLSERVER\MSSQLServer\CurrentVersion', 'CurrentVersion', Version) or (CompareVersion(Version, '15') < 0) then begin
691 AddDependency('sql2019express' + GetArchitectureSuffix + '.exe',
692 '/QS /IACCEPTSQLSERVERLICENSETERMS /ACTION=INSTALL /FEATURES=SQL /INSTANCENAME=MSSQLSERVER',
693 'SQL Server 2019 Express',
694 'https://download.microsoft.com/download/7/f/8/7f8a9c43-8c8a-4f7c-9f92-83c18d96b681/SQL2019-SSEI-Expr.exe',
695 '', False, False, False);
696end;
697#endif
698Result := True;
699end;