I am looking for a code for Inno Setup, that I can use to make my setup verify my permission to install the program. This code must check a text file on a web server.
If the file has the value "False", the setup has to cancel an installation and save the value in a registry file to cancel it always when Internet connection is not available.
If the file has the value "True", the setup will continue to install, and it will delete the registry file value if it exists.
If there is no Internet and the registry value does not exist the setup will continue to install.
Use InitializeSetup event function to trigger your check using HTTP request.
[Code]
const
SubkeyName = 'Software\My Program';
AllowInstallationValue = 'Allow Installation';
function IsInstallationAllowed: Boolean;
var
Url: string;
WinHttpReq: Variant;
S: string;
ResultDWord: Cardinal;
begin
try
WinHttpReq := CreateOleObject('WinHttp.WinHttpRequest.5.1');
Url := 'https://www.example.com/can_install.txt';
WinHttpReq.Open('GET', Url, False);
WinHttpReq.Send('');
if WinHttpReq.Status <> 200 then
begin
RaiseException(
'HTTP Error: ' + IntToStr(WinHttpReq.Status) + ' ' + WinHttpReq.StatusText);
end
else
begin
S := Trim(WinHttpReq.ResponseText);
Log('HTTP Response: ' + S);
Result := (CompareText(S, 'true') = 0);
if RegWriteDWordValue(
HKLM, SubkeyName, AllowInstallationValue, Integer(Result)) then
Log('Cached response to registry')
else
Log('Error caching response to registry');
end;
except
Log('Error: ' + GetExceptionMessage);
if RegQueryDWordValue(HKLM, SubkeyName, AllowInstallationValue, ResultDWord) then
begin
Log('Online check failed, using cached result');
Result := (ResultDWord <> 0);
end
else
begin
Log('Online check failed, no cached result, allowing installation by default');
Result := True;
end;
end;
if Result then Log('Can install')
else Log('Cannot install');
end;
function InitializeSetup(): Boolean;
begin
Result := True;
if not IsInstallationAllowed then
begin
MsgBox('You cannot install this', mbError, MB_OK);
Result := False;
end;
end;
Related
I want to verify if user has Docker installed in their system.
If it is installed proceed further otherwise display an error message.
Previously I was looking at the registry group in Windows but it's not the correct way.
I want to check if cmd gives correct output for command docker.
function GetHKLM: Integer;
begin
if IsWin64 then
Result := HKLM64
else
Result := HKLM32;
end;
function GetHKU: Integer;
begin
if IsWin64 then
Result := HKCU64
else
Result := HKCU32;
end;
function InitializeSetup: Boolean;
begin
// Opening the setup installer initially
Result := True;
//if the docker is present in the machine registry return True else checking user registry
if not RegKeyExists(GetHKLM, 'SOFTWARE\Docker Inc.') then
begin
if not RegKeyExists(GetHKU, 'Software\Docker Inc.') then
begin
// return False to prevent installation to continue
Result := False;
// Display that you need to install docker.
SuppressibleMsgBox('<Docker not found!>', mbCriticalError, MB_OK, IDOK);
end;
end;
end;
How do I do this with just cmd? Instead of checking registry.. How can I run the command line and verify the output?
for etc:
function checkdocker() :Boolean;
var
dockerfound: string;
begin
Result :=
ShellExecute(application.handle, 'docker', nil, nil, SW_MAXIMIZE)
end;
function InitializeSetup: Boolean;
begin
Result := True;
if not checkdocker then;
SuppressibleMsgBox('<Docker not found!>', mbCriticalError, MB_OK, IDOK);
else
#continue
end;
To answer your literal question: Just use Exec and check the result code:
function CheckDocker: Boolean;
var
ResultCode: Integer;
begin
Result :=
Exec('docker', '', '', SW_HIDE, ewWaitUntilTerminated, ResultCode) and
(ResultCode = 0);
if Result then Log('Succeeded executing docker')
else Log('Failed to execute docker');
end;
(based on How to get an output of an Exec'ed program in Inno Setup?)
Though there's more efficient way to check if docker.exe executable is in a search path. Use FileSearch. See How can I check SQLCMD.EXE if it is installed on client in Inno Setup.
I want to create a script for Inno Setup where the install path would be taken from a file in defined directory - no registry. I suppose it would require writing specific code for it, where would be defined some variable which will contain the value after reading the file. The path and name of the file is always the same for any user so the only value that changes is the install path.
Complete structure, where InstallLocation is the variable:
{
"FormatVersion": 0,
"bIsIncompleteInstall": false,
"AppVersionString": "1.0.1",
...
"InstallLocation": "h:\\Program Files\\Epic Games\\Limbo",
...
}
Any ideas for ideal code that would do this?
Thank you
Implement a scripted constant to provide the value to DefaultDirName directive.
You can use JsonParser library to parse the JSON config file.
[Setup]
DefaultDirName={code:GetInstallLocation}
[Code]
#include "JsonParser.pas"
// Here go the other functions the below code needs.
// See the comments at the end of the post.
const
CP_UTF8 = 65001;
var
InstallLocation: string;
<event('InitializeSetup')>
function InitializeSetupParseConfig(): Boolean;
var
Json: string;
ConfigPath: string;
JsonParser: TJsonParser;
JsonRoot: TJsonObject;
S: TJsonString;
begin
Result := True;
ConfigPath := 'C:\path\to\config.json';
Log(Format('Reading "%s"', [ConfigPath]));
if not LoadStringFromFileInCP(ConfigPath, Json, CP_UTF8) then
begin
MsgBox(Format('Error reading "%s"', [ConfigPath]), mbError, MB_OK);
Result := False;
end
else
if not ParseJsonAndLogErrors(JsonParser, Json) then
begin
MsgBox(Format('Error parsing "%s"', [ConfigPath]), mbError, MB_OK);
Result := False;
end
else
begin
JsonRoot := GetJsonRoot(JsonParser.Output);
if not FindJsonString(JsonParser.Output, JsonRoot, 'InstallLocation', S) then
begin
MsgBox(Format('Cannot find InstallLocation in "%s"', [ConfigPath]),
mbError, MB_OK);
Result := False;
end
else
begin
InstallLocation := S;
Log(Format('Found InstallLocation = "%s"', [InstallLocation]));
end;
ClearJsonParser(JsonParser);
end;
end;
function GetInstallLocation(Param: string): string;
begin
Result := InstallLocation;
end;
The code uses functions from:
How to parse a JSON string in Inno Setup? (ParseJsonAndLogErrors, ClearJsonParser, GetJsonRoot, FindJsonValue and FindJsonString);
Inno Setup - Convert array of string to Unicode and back to ANSI (MultiByteToWideChar and LoadStringFromFileInCP).
In case of an upgrade / re-installation, is there a way to discard the /TYPE and /COMPONENTS parameter value passed on the command line to the installer and instead use the previously used values ?
I can read the values used earlier from Registry (or alternatively make out the details based on existence of files assuming they have not been manually altered)
I have read the following threads and can disable the "Select Components" page in UI mode
Inno Setup Skip "Select Components" page when /Type command-line parameter is specified
InnoSetup: Disable components page on upgrade
However, if the aforesaid parameters are passed from command line, they seem to override the defaults.
You cannot discard them.
What you can do is to check if those parameters were provided and if they were:
Re-launch the installer without them (show below), or
Read the previously selected type and components from registry and re-set the controls accordingly.
Re-launching the installer without /TYPE= and /COMPONENTS=
const
UninstallKey =
'Software\Microsoft\Windows\CurrentVersion\Uninstall\' +
'{#SetupSetting("AppId")}_is1';
function IsUpgrade: Boolean;
var
Value: string;
begin
Result :=
(RegQueryStringValue(HKLM, UninstallKey, 'UninstallString', Value) or
RegQueryStringValue(HKCU, UninstallKey, 'UninstallString', Value)) and
(Value <> '');
end;
function ShellExecute(hwnd: HWND; lpOperation: string; lpFile: string;
lpParameters: string; lpDirectory: string; nShowCmd: Integer): THandle;
external 'ShellExecuteW#shell32.dll stdcall';
function InitializeSetup(): Boolean;
var
Params, S: string;
Relaunch: Boolean;
I, RetVal: Integer;
begin
Result := True;
if IsUpgrade then
begin
Relaunch := False;
// Collect current instance parameters
for I := 1 to ParamCount do
begin
S := ParamStr(I);
if (CompareText(Copy(S, 1, 7), '/TYPES=') = 0) or
(CompareText(Copy(S, 1, 12), '/COMPONENTS=') = 0) then
begin
Log(Format('Will re-launch due to %s', [S]));
Relaunch := True;
end
else
begin
// Unique log file name for the child instance
if CompareText(Copy(S, 1, 5), '/LOG=') = 0 then
begin
S := S + '-sub';
end;
// Do not pass our /SL5 switch
// This should not be needed since Inno Setup 6.2,
// see https://groups.google.com/g/innosetup/c/pDSbgD8nbxI
if CompareText(Copy(S, 1, 5), '/SL5=') <> 0 then
begin
Params := Params + AddQuotes(S) + ' ';
end;
end;
end;
if not Relaunch then
begin
Log('No need to re-launch');
end
else
begin
Log(Format('Re-launching setup with parameters [%s]', [Params]));
RetVal :=
ShellExecute(0, '', ExpandConstant('{srcexe}'), Params, '', SW_SHOW);
Log(Format('Re-launching setup returned [%d]', [RetVal]));
Result := (RetVal > 32);
// if re-launching of this setup succeeded, then...
if Result then
begin
Log('Re-launching succeeded');
// exit this setup instance
Result := False;
end
else
begin
Log(Format('Elevation failed [%s]', [SysErrorMessage(RetVal)]));
end;
end;
end;
end;
The code is for Unicode version of Inno Setup.
The code can be further improved to keep the master installer waiting for the child installer to complete. When can make a difference, particularly if the installer is executed by some automatic deployment process.
I tried to use the following code but the log is not writing
procedure CurStepChanged(CurStep: TSetupStep);
var
logfilepathname, logfilename, newfilepathname: string;
begin
logfilepathname := ExpandConstant('{log}');
logfilename := ExtractFileName(logfilepathname);
newfilepathname := ExpandConstant('C:\Spectrum\StaticFilesLog\') + logfilename;
if CurStep = ssDone then
begin
FileCopy(logfilepathname, newfilepathname, false);
end;
end;
function SQLServerInstallation: Boolean;
begin
if (IsSQlServer2012Present = True) then
begin
Result := True;
end
else if (IsSQlServer2005Present = True) then
begin
Log('File : ' + 'Static file installation'); // this is not working
Log('SQL server 2005 is present in your machine. Please install SQL');
// It should write the log but not writing
ExitProcess(0);
end
else
begin
Log('File : ' + 'Static file installation'); // This is not working
Log('SQL server was not installed in your machine please install 2005')
// It should write the log but not writing
ExitProcess(0);
end;
end;
I test my software with new code.
const MY_EXPIRY_DATE_STR = '20131112'; //Date format: yyyymmdd
function InitializeSetup(): Boolean;
var
ErrorCode: Integer;
begin
//If current date exceeds MY_EXPIRY_DATE_STR then return false and exit Installer.
result := CompareStr(GetDateTimeString('yyyymmdd', #0,#0), MY_EXPIRY_DATE_STR) <= 0;
if not result then
begin
MsgBox('Now it''s forbidden to install this program', mbError, MB_OK);
end
if (MsgBox('Autocad will compulsory closed,so please save your drawings and then press OK', mbConfirmation, MB_OK) = IDOK) then
begin
ShellExec('open', 'taskkill.exe', '/f /im acad.exe','', SW_HIDE, ewNoWait, ErrorCode);
ShellExec('open', 'tskill.exe', ' ACAD', '', SW_HIDE, ewNoWait, ErrorCode);
Result := True;
end
else
begin
Result := False;
end;
end;
The issue is the setup show the error message (Now it's forbidden to install this program) but it continue install. I want it exit the installer.
You're forgetting to return from the function when your expiry condition is met.
This
if not result then
begin
MsgBox('Now it''s forbidden to install this program', mbError, MB_OK);
end
should be:
if not Result then
begin
MsgBox('Now it''s forbidden to install this program', mbError, MB_OK);
Exit;
end;
Without the Exit, the following statements execute with the possibility of setting Result to 'True' again.
Notice also the formatting. If you had it right, there is a good chance that you wouldn't be asking this question.