I have a software which requires the default browser installed on user computer.
Is there a way that I can get it?
Thanks
An solution that correctly works on modern versions of Windows cannot be based on association with http protocol, as that's no longer reliable. It should rather be based on a solution like the answer by #GregT to How to determine the Windows default browser (at the top of the start menu).
So something like:
function GetBrowserCommand: string;
var
UserChoiceKey: string;
HtmlProgId: string;
begin
UserChoiceKey :=
'Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.html\UserChoice';
if RegQueryStringValue(HKCU, UserChoiceKey, 'ProgId', HtmlProgId) then
begin
Log(Format('ProgID to registered for .html is [%s].', [HtmlProgId]));
if RegQueryStringValue(HKCR, HtmlProgId + '\shell\open\command', '', Result) then
begin
Log(Format('Command for ProgID [%s] is [%s].', [HtmlProgId, Result]));
end;
end;
{ Fallback for old version of Windows }
if Result = '' then
begin
if RegQueryStringValue(HKCR, 'http\shell\open\command', '', Result) then
begin
Log(Format('Command registered for http: [%s].', [Result]));
end;
end;
end;
If you want to extract browser path from the command, use a code like:
function ExtractProgramPath(Command: string): string;
var
P: Integer;
begin
if Copy(Command, 1, 1) = '"' then
begin
Delete(Command, 1, 1);
P := Pos('"', Command);
end
else P := 0;
if P = 0 then
begin
P := Pos(' ', Command);
end;
Result := Copy(Command, 1, P - 1);
end;
(based on Executing UninstallString in Inno Setup)
Take this:
function GetBrowser() : String;
var
RegistryEntry: String;
Browser: String;
Limit: Integer ;
begin
if RegQueryStringValue(HKEY_CLASSES_ROOT, 'http\shell\open\command', '', RegistryEntry) then
begin
Limit := Pos('.exe' ,RegistryEntry)+ Length('.exe');
Browser := Copy(RegistryEntry, 1, Limit );
MsgBox('Your browser: ' + Browser , mbInformation, MB_OK);
end;
end;
Related
Is it possible to get an output of an Exec'ed executable?
I want to show the user an info query page, but show the default value of MAC address in the input box. Is there any other way to achieve this?
Yes, use redirection of the standard output to a file:
[Code]
function NextButtonClick(CurPage: Integer): Boolean;
var
TmpFileName, ExecStdout: string;
ResultCode: integer;
begin
if CurPage = wpWelcome then begin
TmpFileName := ExpandConstant('{tmp}') + '\ipconfig_results.txt';
Exec('cmd.exe', '/C ipconfig /ALL > "' + TmpFileName + '"', '', SW_HIDE,
ewWaitUntilTerminated, ResultCode);
if LoadStringFromFile(TmpFileName, ExecStdout) then begin
MsgBox(ExecStdout, mbInformation, MB_OK);
{ do something with contents of file... }
end;
DeleteFile(TmpFileName);
end;
Result := True;
end;
Note that there may be more than one network adapter, and consequently several MAC addresses to choose from.
I had to do the same (execute command line calls and get the result) and came up with a more general solution.
It also fixes strange bugs if quoted paths are used in the actual calls by using the /S flag for cmd.exe.
// Exec with output stored in result.
// ResultString will only be altered if True is returned.
function ExecWithResult(
Filename, Params, WorkingDir: String; ShowCmd: Integer;
Wait: TExecWait; var ResultCode: Integer; var ResultString: String): Boolean;
var
TempFilename: String;
Command: String;
ResultStringAnsi: AnsiString;
begin
TempFilename := ExpandConstant('{tmp}\~execwithresult.txt');
// Exec via cmd and redirect output to file.
// Must use special string-behavior to work.
Command :=
Format('"%s" /S /C ""%s" %s > "%s""', [
ExpandConstant('{cmd}'), Filename, Params, TempFilename]);
Result :=
Exec(ExpandConstant('{cmd}'), Command, WorkingDir, ShowCmd, Wait, ResultCode);
if not Result then
Exit;
LoadStringFromFile(TempFilename, ResultStringAnsi); // Cannot fail
// See https://stackoverflow.com/q/20912510/850848
ResultString := ResultStringAnsi;
DeleteFile(TempFilename);
// Remove new-line at the end
if (Length(ResultString) >= 2) and
(ResultString[Length(ResultString) - 1] = #13) and
(ResultString[Length(ResultString)] = #10) then
Delete(ResultString, Length(ResultString) - 1, 2);
end;
Usage:
Success :=
ExecWithResult('ipconfig', '/all', '', SW_HIDE, ewWaitUntilTerminated,
ResultCode, ExecStdout) and
(ResultCode = 0);
The result can also be loaded into a TStringList object to get all lines:
Lines := TStringList.Create;
Lines.Text := ExecStdout;
// ... some code ...
Lines.Free;
Want to download files based on contents of first download using Inno Download Plugin (IDP). How to do that?
Here is my code
[Code]
procedure InitializeWizard();
var
line: string;
line2: string;
url: string;
appname: string;
begin
idpAddFile('http://download.website.com/files.txt', ExpandConstant('{tmp}\files.txt'));
idpDownloadAfter(wpReady);
TryGetFileLine(expandConstant('{tmp}\files.txt'), 0, line);
TryGetFileLine(expandConstant('{tmp}\files.txt'), 1, line2);
url := line;
appname := line2;
idpAddFile(url, ExpandConstant('{tmp}\'+appname));
idpDownloadAfter(wpReady);
end;
Here second file starts downloading before the first file finishes. So how to make it one after another?
Tell IDP to only download the list initially. Then wait for the download to finish (for that see Running a program after it is downloaded in Code section in Inno Setup) and based on the results, create new download list and restart the download.
var
ListDownloaded: Boolean;
procedure InitializeWizard();
begin
idpAddFile('http://www.example.com/files.txt', ExpandConstant('{tmp}\files.txt'));
idpDownloadAfter(wpReady);
ListDownloaded := False;
end;
function NextButtonClick(CurPageID: Integer): Boolean;
var
Url, AppName: string;
begin
Result := True;
if CurPageID = IDPForm.Page.ID then
begin
if not ListDownloaded then
begin
TryGetFileLine(ExpandConstant('{tmp}\files.txt'), 0, Url);
TryGetFileLine(ExpandConstant('{tmp}\files.txt'), 1, AppName);
idpClearFiles;
idpAddFile(Url, ExpandConstant('{tmp}\' + AppName));
idpFormActivate(nil); { This restarts the download }
Result := False;
ListDownloaded := True;
end;
end;
end;
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;
Is it possible to get an output of an Exec'ed executable?
I want to show the user an info query page, but show the default value of MAC address in the input box. Is there any other way to achieve this?
Yes, use redirection of the standard output to a file:
[Code]
function NextButtonClick(CurPage: Integer): Boolean;
var
TmpFileName, ExecStdout: string;
ResultCode: integer;
begin
if CurPage = wpWelcome then begin
TmpFileName := ExpandConstant('{tmp}') + '\ipconfig_results.txt';
Exec('cmd.exe', '/C ipconfig /ALL > "' + TmpFileName + '"', '', SW_HIDE,
ewWaitUntilTerminated, ResultCode);
if LoadStringFromFile(TmpFileName, ExecStdout) then begin
MsgBox(ExecStdout, mbInformation, MB_OK);
{ do something with contents of file... }
end;
DeleteFile(TmpFileName);
end;
Result := True;
end;
Note that there may be more than one network adapter, and consequently several MAC addresses to choose from.
I had to do the same (execute command line calls and get the result) and came up with a more general solution.
It also fixes strange bugs if quoted paths are used in the actual calls by using the /S flag for cmd.exe.
// Exec with output stored in result.
// ResultString will only be altered if True is returned.
function ExecWithResult(
Filename, Params, WorkingDir: String; ShowCmd: Integer;
Wait: TExecWait; var ResultCode: Integer; var ResultString: String): Boolean;
var
TempFilename: String;
Command: String;
ResultStringAnsi: AnsiString;
begin
TempFilename := ExpandConstant('{tmp}\~execwithresult.txt');
// Exec via cmd and redirect output to file.
// Must use special string-behavior to work.
Command :=
Format('"%s" /S /C ""%s" %s > "%s""', [
ExpandConstant('{cmd}'), Filename, Params, TempFilename]);
Result :=
Exec(ExpandConstant('{cmd}'), Command, WorkingDir, ShowCmd, Wait, ResultCode);
if not Result then
Exit;
LoadStringFromFile(TempFilename, ResultStringAnsi); // Cannot fail
// See https://stackoverflow.com/q/20912510/850848
ResultString := ResultStringAnsi;
DeleteFile(TempFilename);
// Remove new-line at the end
if (Length(ResultString) >= 2) and
(ResultString[Length(ResultString) - 1] = #13) and
(ResultString[Length(ResultString)] = #10) then
Delete(ResultString, Length(ResultString) - 1, 2);
end;
Usage:
Success :=
ExecWithResult('ipconfig', '/all', '', SW_HIDE, ewWaitUntilTerminated,
ResultCode, ExecStdout) and
(ResultCode = 0);
The result can also be loaded into a TStringList object to get all lines:
Lines := TStringList.Create;
Lines.Text := ExecStdout;
// ... some code ...
Lines.Free;