diff --git a/clean_build.bat b/clean_build.bat index 406ba993..4363182b 100644 --- a/clean_build.bat +++ b/clean_build.bat @@ -43,6 +43,8 @@ if exist bin\Debug ( copy %B_QT_FULLPATH%\bin\Qt5Core.dll bin\Release\ > NUL copy ..\ext\openssl\windows\x64\bin\* bin\Release\ > NUL copy ..\res\openssl\barrier.conf bin\Release\ > NUL + mkdir bin\Release\platforms + copy %B_QT_FULLPATH%\plugins\platforms\qwindows.dll bin\Release\platforms\ > NUL ) else ( echo Remember to copy supporting binaries and confiuration files! ) diff --git a/dist/inno/barrier.iss b/dist/inno/barrier.iss new file mode 100644 index 00000000..ec774131 --- /dev/null +++ b/dist/inno/barrier.iss @@ -0,0 +1,74 @@ +#define MyAppName "Barrier" +#define MyAppVersion "1.9" +#define MyAppPublisher "Debauchee Open Source Group" +#define MyAppURL "https://github.com/debauchee/barrier/wiki" +#define MyAppExeName "barrier.exe" +#define MyAppServiceName "Barrier" +#define MyAppServiceExe "barrierd.exe" +#define MyAppServiceDesc "Manages the Barrier background processes." + +[Setup] +AppId={{41036EA6-3F7A-4803-8AE0-469E5E91EFCC} +AppName={#MyAppName} +AppVersion={#MyAppVersion} +AppVerName={#MyAppName} {#MyAppVersion} +AppPublisher={#MyAppPublisher} +AppPublisherURL={#MyAppURL} +AppSupportURL={#MyAppURL} +AppUpdatesURL={#MyAppURL} +DefaultDirName={pf}\{#MyAppName} +DisableProgramGroupPage=yes +LicenseFile=E:\Projects\vs\barrier-release.git\res\License.rtf +OutputDir=E:\Projects\vs\barrier-release.git\build\installer\bin +OutputBaseFilename=BarrierSetup +SetupIconFile=E:\Projects\vs\barrier-release.git\res\barrier.ico +Compression=lzma +SolidCompression=yes +ArchitecturesInstallIn64BitMode=x64 ia64 + +#include "scripts\lang\english.iss" + +[Tasks] +Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked + +[Files] +Source: "E:\Projects\vs\barrier-release.git\build\bin\Release\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs +; NOTE: Don't use "Flags: ignoreversion" on any shared system files + +[Icons] +Name: "{commonprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}" +Name: "{commondesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon + +[Run] +Filename: {sys}\sc.exe; Parameters: "create {#MyAppServiceName} start= auto binPath= ""{app}\{#MyAppServiceExe}"""; Flags: runhidden +Filename: {sys}\sc.exe; Parameters: "description {#MyAppServiceName} ""{#MyAppServiceDesc}"""; Flags: runhidden +Filename: {sys}\sc.exe; Parameters: "start {#MyAppServiceName}"; Flags: runhidden +Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent + +[UninstallDelete] +Type: files; Name: "{app}\barrierd.log" + +[UninstallRun] +Filename: {sys}\sc.exe; Parameters: "stop {#MyAppServiceName}"; Flags: runhidden +Filename: {sys}\sc.exe; Parameters: "delete {#MyAppServiceName}"; Flags: runhidden + +[CustomMessages] +DependenciesDir="redist" + +; shared code for installing the products +#include "scripts\products.iss" +#include "scripts\products\stringversion.iss" +#include "scripts\products\winversion.iss" +#include "scripts\products\msiproduct.iss" +#include "scripts\products\vcredist2017.iss" + +[Code] +function InitializeSetup(): boolean; +begin + // initialize windows version + initwinversion(); + + vcredist2017('14'); // min allowed version is 14.0 + + Result := true; +end; diff --git a/dist/inno/scripts/isxdl/english.ini b/dist/inno/scripts/isxdl/english.ini new file mode 100644 index 00000000..0546ae49 --- /dev/null +++ b/dist/inno/scripts/isxdl/english.ini @@ -0,0 +1,49 @@ +[strings] +; General +100=File download +101=Do you want to cancel the download? +102=%1 (%2 of %3) +103=%1 KB +104=%1 KB of %2 KB (%3%) + +; Status information +110=Getting file information... +111=Redirecting to %1 +112=Sending request... +113=Resolving %1 +114=Connected to %1 +115=Receiving... +116=Connecting to %1 + +; Error messages +120=Error connecting to the internet.\n\n%1 +121=Error opening %1.\n\nThe server returned status code %2. +122=Error reading URL.\n\n%1 +123=Error writing file %1.\n\n%2 +124=Error opening file %1.\n\n%2 +125='%1' is an invalid URL. +126=Error opening %1.\n\n%2 +127=Error sending request.\n\n%1 +128=Unsupported protocol. Only HTTP and FTP protocols are supported. +129=Failed to connect to %1.\n\n%2 +130=Failed to query status code.\n\n%1 +131=Error requesting file.\n\n%1 + +; Other +144=About... +146=Download +147=Setup is now downloading additional files to your computer. + +; labels +160=File: +161=Speed: +162=Status: +163=Elapsed Time: +164=Remaining Time: +165=Current File: +166=Overall Progress: +167=Cancel +168=OK +169=User Name and Password +170=User Name: +171=Password: diff --git a/dist/inno/scripts/isxdl/isxdl.dll b/dist/inno/scripts/isxdl/isxdl.dll new file mode 100644 index 00000000..d227bcad Binary files /dev/null and b/dist/inno/scripts/isxdl/isxdl.dll differ diff --git a/dist/inno/scripts/isxdl/isxdl.iss b/dist/inno/scripts/isxdl/isxdl.iss new file mode 100644 index 00000000..3c25d6d5 --- /dev/null +++ b/dist/inno/scripts/isxdl/isxdl.iss @@ -0,0 +1,14 @@ +[Files] +Source: "scripts\isxdl\isxdl.dll"; Flags: dontcopy + +[Code] +procedure isxdl_AddFile(URL, Filename: PAnsiChar); +external 'isxdl_AddFile@files:isxdl.dll stdcall'; + +function isxdl_DownloadFiles(hWnd: Integer): Integer; +external 'isxdl_DownloadFiles@files:isxdl.dll stdcall'; + +function isxdl_SetOption(Option, Value: PAnsiChar): Integer; +external 'isxdl_SetOption@files:isxdl.dll stdcall'; + +[Setup] diff --git a/dist/inno/scripts/lang/english.iss b/dist/inno/scripts/lang/english.iss new file mode 100644 index 00000000..fa32fc06 --- /dev/null +++ b/dist/inno/scripts/lang/english.iss @@ -0,0 +1,18 @@ +[Languages] +Name: "en"; MessagesFile: "compiler:Default.isl" + +[CustomMessages] +;http://www.microsoft.com/globaldev/reference/lcid-all.mspx +en.lcid=1033 +en.depdownload_msg=The following applications are required before setup can continue:%n%n%1%nDownload and install now? +en.depdownload_memo_title=Download dependencies +en.depinstall_memo_title=Install dependencies +en.depinstall_title=Installing dependencies +en.depinstall_description=Please wait while Setup installs dependencies on your computer. +en.depinstall_status=Installing %1... +en.depinstall_missing=%1 must be installed before setup can continue. Please install %1 and run Setup again. +en.depinstall_error=An error occured while installing the dependencies. Please restart the computer and run the setup again or install the following dependencies manually:%n + +en.isxdl_langfile= + +[Files] diff --git a/dist/inno/scripts/products.iss b/dist/inno/scripts/products.iss new file mode 100644 index 00000000..63cf4d1d --- /dev/null +++ b/dist/inno/scripts/products.iss @@ -0,0 +1,6 @@ +#include "isxdl\isxdl.iss" + +[Code] +#include "products.pas" + +[Setup] diff --git a/dist/inno/scripts/products.pas b/dist/inno/scripts/products.pas new file mode 100644 index 00000000..d114c41e --- /dev/null +++ b/dist/inno/scripts/products.pas @@ -0,0 +1,329 @@ +{ + --- TYPES AND VARIABLES --- +} +type + TProduct = record + File: String; + Title: String; + Parameters: String; + ForceSuccess : boolean; + InstallClean : boolean; + MustRebootAfter : boolean; + end; + + InstallResult = (InstallSuccessful, InstallRebootRequired, InstallError); + +var + installMemo, downloadMessage: string; + products: array of TProduct; + delayedReboot, isForcedX86: boolean; + DependencyPage: TOutputProgressWizardPage; + +procedure AddProduct(filename, parameters, title, size, url: string; forceSuccess, installClean, mustRebootAfter : boolean); +{ + Adds a product to the list of products to download. + Parameters: + filename: the file name under which to save the file + parameters: the parameters with which to run the file + title: the product title + size: the file size + url: the URL to download from + forceSuccess: whether to continue in case of setup failure + installClean: whether the product needs a reboot before installing + mustRebootAfter: whether the product needs a reboot after installing +} +var + path: string; + i: Integer; +begin + installMemo := installMemo + '%1' + title + #13; + + path := ExpandConstant('{src}{\}') + CustomMessage('DependenciesDir') + '\' + filename; + if not FileExists(path) then begin + path := ExpandConstant('{tmp}{\}') + filename; + + if not FileExists(path) then begin + isxdl_AddFile(url, path); + + downloadMessage := downloadMessage + '%1' + title + ' (' + size + ')' + #13; + end; + end; + + i := GetArrayLength(products); + SetArrayLength(products, i + 1); + products[i].File := path; + products[i].Title := title; + products[i].Parameters := parameters; + products[i].ForceSuccess := forceSuccess; + products[i].InstallClean := installClean; + products[i].MustRebootAfter := mustRebootAfter; +end; + +function SmartExec(product : TProduct; var resultcode : Integer): boolean; +{ + Executes a product and returns the exit code. + Parameters: + product: the product to install + resultcode: the exit code +} +begin + if (LowerCase(Copy(product.File, Length(product.File) - 2, 3)) = 'exe') then begin + Result := Exec(product.File, product.Parameters, '', SW_SHOWNORMAL, ewWaitUntilTerminated, resultcode); + end else begin + Result := ShellExec('', product.File, product.Parameters, '', SW_SHOWNORMAL, ewWaitUntilTerminated, resultcode); + end; +end; + +function PendingReboot: boolean; +{ + Checks whether the machine has a pending reboot. +} +var names: String; +begin + if (RegQueryMultiStringValue(HKEY_LOCAL_MACHINE, 'SYSTEM\CurrentControlSet\Control\Session Manager', 'PendingFileRenameOperations', names)) then begin + Result := true; + end else if ((RegQueryMultiStringValue(HKEY_LOCAL_MACHINE, 'SYSTEM\CurrentControlSet\Control\Session Manager', 'SetupExecute', names)) and (names <> '')) then begin + Result := true; + end else begin + Result := false; + end; +end; + +function InstallProducts: InstallResult; +{ + Installs the downloaded products +} +var + resultCode, i, productCount, finishCount: Integer; +begin + Result := InstallSuccessful; + productCount := GetArrayLength(products); + + if productCount > 0 then begin + DependencyPage := CreateOutputProgressPage(CustomMessage('depinstall_title'), CustomMessage('depinstall_description')); + DependencyPage.Show; + + for i := 0 to productCount - 1 do begin + if (products[i].InstallClean and (delayedReboot or PendingReboot())) then begin + Result := InstallRebootRequired; + break; + end; + + DependencyPage.SetText(FmtMessage(CustomMessage('depinstall_status'), [products[i].Title]), ''); + DependencyPage.SetProgress(i, productCount); + + while true do begin + // set 0 as used code for shown error if SmartExec fails + resultCode := 0; + if SmartExec(products[i], resultCode) then begin + // setup executed; resultCode contains the exit code + if (products[i].MustRebootAfter) then begin + // delay reboot after install if we installed the last dependency anyways + if (i = productCount - 1) then begin + delayedReboot := true; + end else begin + Result := InstallRebootRequired; + end; + break; + end else if (resultCode = 0) or (products[i].ForceSuccess) then begin + finishCount := finishCount + 1; + break; + end else if (resultCode = 3010) then begin + // Windows Installer resultCode 3010: ERROR_SUCCESS_REBOOT_REQUIRED + delayedReboot := true; + finishCount := finishCount + 1; + break; + end; + end; + + case MsgBox(FmtMessage(SetupMessage(msgErrorFunctionFailed), [products[i].Title, IntToStr(resultCode)]), mbError, MB_ABORTRETRYIGNORE) of + IDABORT: begin + Result := InstallError; + break; + end; + IDIGNORE: begin + break; + end; + end; + end; + + if Result <> InstallSuccessful then begin + break; + end; + end; + + // only leave not installed products for error message + for i := 0 to productCount - finishCount - 1 do begin + products[i] := products[i+finishCount]; + end; + SetArrayLength(products, productCount - finishCount); + + DependencyPage.Hide; + end; +end; + +{ + -------------------- + INNO EVENT FUNCTIONS + -------------------- +} + +function PrepareToInstall(var NeedsRestart: boolean): String; +{ + Before the "preparing to install" page. + See: http://www.jrsoftware.org/ishelp/index.php?topic=scriptevents +} +var + i: Integer; + s: string; +begin + delayedReboot := false; + + case InstallProducts() of + InstallError: begin + s := CustomMessage('depinstall_error'); + + for i := 0 to GetArrayLength(products) - 1 do begin + s := s + #13 + ' ' + products[i].Title; + end; + + Result := s; + end; + InstallRebootRequired: begin + Result := products[0].Title; + NeedsRestart := true; + + // write into the registry that the installer needs to be executed again after restart + RegWriteStringValue(HKEY_CURRENT_USER, 'SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce', 'InstallBootstrap', ExpandConstant('{srcexe}')); + end; + end; +end; + +function NeedRestart : boolean; +{ + Checks whether a restart is needed at the end of install + See: http://www.jrsoftware.org/ishelp/index.php?topic=scriptevents +} +begin + Result := delayedReboot; +end; + +function UpdateReadyMemo(Space, NewLine, MemoUserInfoInfo, MemoDirInfo, MemoTypeInfo, MemoComponentsInfo, MemoGroupInfo, MemoTasksInfo: String): String; +{ + Just before the "ready" page. + See: http://www.jrsoftware.org/ishelp/index.php?topic=scriptevents +} +var + s: string; +begin + if downloadMessage <> '' then + s := s + CustomMessage('depdownload_memo_title') + ':' + NewLine + FmtMessage(downloadMessage, [Space]) + NewLine; + if installMemo <> '' then + s := s + CustomMessage('depinstall_memo_title') + ':' + NewLine + FmtMessage(installMemo, [Space]) + NewLine; + + if MemoDirInfo <> '' then + s := s + MemoDirInfo + NewLine + NewLine; + if MemoGroupInfo <> '' then + s := s + MemoGroupInfo + NewLine + NewLine; + if MemoTasksInfo <> '' then + s := s + MemoTasksInfo; + + Result := s +end; + +function NextButtonClick(CurPageID: Integer): boolean; +{ + At each "next" button click + See: http://www.jrsoftware.org/ishelp/index.php?topic=scriptevents +} +begin + Result := true; + + if CurPageID = wpReady then begin + if downloadMessage <> '' then begin + // change isxdl language only if it is not english because isxdl default language is already english + if (ActiveLanguage() <> 'en') then begin + ExtractTemporaryFile(CustomMessage('isxdl_langfile')); + isxdl_SetOption('language', ExpandConstant('{tmp}{\}') + CustomMessage('isxdl_langfile')); + end; + //isxdl_SetOption('title', FmtMessage(SetupMessage(msgSetupWindowTitle), [CustomMessage('appname')])); + + //if SuppressibleMsgBox(FmtMessage(CustomMessage('depdownload_msg'), [FmtMessage(downloadMessage, [''])]), mbConfirmation, MB_YESNO, IDYES) = IDNO then + // Result := false + //else if + if isxdl_DownloadFiles(StrToInt(ExpandConstant('{wizardhwnd}'))) = 0 then + Result := false; + end; + end; +end; + +{ + ----------------------------- + ARCHITECTURE HELPER FUNCTIONS + ----------------------------- +} + +function IsX86: boolean; +{ + Gets whether the computer is x86 (32 bits). +} +begin + Result := isForcedX86 or (ProcessorArchitecture = paX86) or (ProcessorArchitecture = paUnknown); +end; + +function IsX64: boolean; +{ + Gets whether the computer is x64 (64 bits). +} +begin + Result := (not isForcedX86) and Is64BitInstallMode and (ProcessorArchitecture = paX64); +end; + +function IsIA64: boolean; +{ + Gets whether the computer is IA64 (Itanium 64 bits). +} +begin + Result := (not isForcedX86) and Is64BitInstallMode and (ProcessorArchitecture = paIA64); +end; + +function GetString(x86, x64, ia64: String): String; +{ + Gets a string depending on the computer architecture. + Parameters: + x86: the string if the computer is x86 + x64: the string if the computer is x64 + ia64: the string if the computer is IA64 +} +begin + if IsX64() and (x64 <> '') then begin + Result := x64; + end else if IsIA64() and (ia64 <> '') then begin + Result := ia64; + end else begin + Result := x86; + end; +end; + +function GetArchitectureString(): String; +{ + Gets the "standard" architecture suffix string. + Returns either _x64, _ia64 or nothing. +} +begin + if IsX64() then begin + Result := '_x64'; + end else if IsIA64() then begin + Result := '_ia64'; + end else begin + Result := ''; + end; +end; + +procedure SetForceX86(value: boolean); +{ + Forces the setup to use X86 products +} +begin + isForcedX86 := value; +end; diff --git a/dist/inno/scripts/products/msiproduct.iss b/dist/inno/scripts/products/msiproduct.iss new file mode 100644 index 00000000..35a8d234 --- /dev/null +++ b/dist/inno/scripts/products/msiproduct.iss @@ -0,0 +1,49 @@ +[Code] +#ifdef UNICODE + #define AW "W" +#else + #define AW "A" +#endif + +type + INSTALLSTATE = Longint; +const + INSTALLSTATE_INVALIDARG = -2; // An invalid parameter was passed to the function. + INSTALLSTATE_UNKNOWN = -1; // The product is neither advertised or installed. + INSTALLSTATE_ADVERTISED = 1; // The product is advertised but not installed. + INSTALLSTATE_ABSENT = 2; // The product is installed for a different user. + INSTALLSTATE_DEFAULT = 5; // The product is installed for the current user. + +function MsiQueryProductState(szProduct: string): INSTALLSTATE; +external 'MsiQueryProductState{#AW}@msi.dll stdcall'; + +function MsiEnumRelatedProducts(szUpgradeCode: string; nReserved: dword; nIndex: dword; szProductCode: string): integer; +external 'MsiEnumRelatedProducts{#AW}@msi.dll stdcall'; + +function MsiGetProductInfo(szProductCode: string; szProperty: string; szValue: string; var nvalueSize: dword): integer; +external 'MsiGetProductInfo{#AW}@msi.dll stdcall'; + +function msiproduct(productID: string): boolean; +begin + Result := MsiQueryProductState(productID) = INSTALLSTATE_DEFAULT; +end; + +function msiproductupgrade(upgradeCode: string; minVersion: string): boolean; +var + productCode, version: string; + valueSize: dword; +begin + SetLength(productCode, 39); + Result := false; + + if (MsiEnumRelatedProducts(upgradeCode, 0, 0, productCode) = 0) then begin + SetLength(version, 39); + valueSize := Length(version); + + if (MsiGetProductInfo(productCode, 'VersionString', version, valueSize) = 0) then begin + Result := compareversion(version, minVersion) >= 0; + end; + end; +end; + +[Setup] diff --git a/dist/inno/scripts/products/stringversion.iss b/dist/inno/scripts/products/stringversion.iss new file mode 100644 index 00000000..4cb114f7 --- /dev/null +++ b/dist/inno/scripts/products/stringversion.iss @@ -0,0 +1,62 @@ +[Code] +function stringtoversion(var temp: String): Integer; +var + part: String; + pos1: Integer; + +begin + if (Length(temp) = 0) then begin + Result := -1; + Exit; + end; + + pos1 := Pos('.', temp); + if (pos1 = 0) then begin + Result := StrToInt(temp); + temp := ''; + end else begin + part := Copy(temp, 1, pos1 - 1); + temp := Copy(temp, pos1 + 1, Length(temp)); + Result := StrToInt(part); + end; +end; + +function compareinnerversion(var x, y: String): Integer; +var + num1, num2: Integer; + +begin + num1 := stringtoversion(x); + num2 := stringtoversion(y); + if (num1 = -1) and (num2 = -1) then begin + Result := 0; + Exit; + end; + + if (num1 < 0) then begin + num1 := 0; + end; + if (num2 < 0) then begin + num2 := 0; + end; + + if (num1 < num2) then begin + Result := -1; + end else if (num1 > num2) then begin + Result := 1; + end else begin + Result := compareinnerversion(x, y); + end; +end; + +function compareversion(versionA, versionB: String): Integer; +var + temp1, temp2: String; + +begin + temp1 := versionA; + temp2 := versionB; + Result := compareinnerversion(temp1, temp2); +end; + +[Setup] diff --git a/dist/inno/scripts/products/vcredist2017.iss b/dist/inno/scripts/products/vcredist2017.iss new file mode 100644 index 00000000..da20e031 --- /dev/null +++ b/dist/inno/scripts/products/vcredist2017.iss @@ -0,0 +1,32 @@ +; requires Windows 10, Windows 7 Service Pack 1, Windows 8, Windows 8.1, Windows Server 2003 Service Pack 2, Windows Server 2008 R2 SP1, Windows Server 2008 Service Pack 2, Windows Server 2012, Windows Vista Service Pack 2, Windows XP Service Pack 3 +; http://www.visualstudio.com/en-us/downloads/ + +[CustomMessages] +vcredist2017_title=Visual C++ 2017 Redistributable +vcredist2017_title_x64=Visual C++ 2017 64-Bit Redistributable + +vcredist2017_size=13.7 MB +vcredist2017_size_x64=14.5 MB + +[Code] +const + vcredist2017_url = 'http://download.microsoft.com/download/1/f/e/1febbdb2-aded-4e14-9063-39fb17e88444/vc_redist.x86.exe'; + vcredist2017_url_x64 = 'http://download.microsoft.com/download/3/b/f/3bf6e759-c555-4595-8973-86b7b4312927/vc_redist.x64.exe'; + + vcredist2017_upgradecode = '{65E5BD06-6392-3027-8C26-853107D3CF1A}'; + vcredist2017_upgradecode_x64 = '{36F68A90-239C-34DF-B58C-64B30153CE35}'; + +procedure vcredist2017(minVersion: string); +begin + if (not IsIA64()) then begin + if (not msiproductupgrade(GetString(vcredist2017_upgradecode, vcredist2017_upgradecode_x64, ''), minVersion)) then + AddProduct('vcredist2017' + GetArchitectureString() + '.exe', + '/passive /norestart', + CustomMessage('vcredist2017_title' + GetArchitectureString()), + CustomMessage('vcredist2017_size' + GetArchitectureString()), + GetString(vcredist2017_url, vcredist2017_url_x64, ''), + false, false, false); + end; +end; + +[Setup] diff --git a/dist/inno/scripts/products/winversion.iss b/dist/inno/scripts/products/winversion.iss new file mode 100644 index 00000000..e1aff98b --- /dev/null +++ b/dist/inno/scripts/products/winversion.iss @@ -0,0 +1,49 @@ +[Code] +var + WindowsVersion: TWindowsVersion; + +procedure initwinversion(); +begin + GetWindowsVersionEx(WindowsVersion); +end; + +function exactwinversion(MajorVersion, MinorVersion: integer): boolean; +begin + Result := (WindowsVersion.Major = MajorVersion) and (WindowsVersion.Minor = MinorVersion); +end; + +function minwinversion(MajorVersion, MinorVersion: integer): boolean; +begin + Result := (WindowsVersion.Major > MajorVersion) or ((WindowsVersion.Major = MajorVersion) and (WindowsVersion.Minor >= MinorVersion)); +end; + +function maxwinversion(MajorVersion, MinorVersion: integer): boolean; +begin + Result := (WindowsVersion.Major < MajorVersion) or ((WindowsVersion.Major = MajorVersion) and (WindowsVersion.Minor <= MinorVersion)); +end; + +function exactwinspversion(MajorVersion, MinorVersion, SpVersion: integer): boolean; +begin + if exactwinversion(MajorVersion, MinorVersion) then + Result := WindowsVersion.ServicePackMajor = SpVersion + else + Result := true; +end; + +function minwinspversion(MajorVersion, MinorVersion, SpVersion: integer): boolean; +begin + if exactwinversion(MajorVersion, MinorVersion) then + Result := WindowsVersion.ServicePackMajor >= SpVersion + else + Result := true; +end; + +function maxwinspversion(MajorVersion, MinorVersion, SpVersion: integer): boolean; +begin + if exactwinversion(MajorVersion, MinorVersion) then + Result := WindowsVersion.ServicePackMajor <= SpVersion + else + Result := true; +end; + +[Setup]