Inno Setup custom dialog with per user or per machine installation - javafx

I'm using Inno Setup (http://www.jrsoftware.org/isinfo.php) to create native bundle for my JavaFX application.
I'd like to create a custom step where ask the user if want a "per user" or "per machine" installation in order to permit both the unprivileged user, and the administrator to install the software.
It's this possible with Inno Setup? And if yes can you provide a trace to follow?
Take a look at this screenshot

Inno Setup 6
Inno Setup 6 has a built-in support for non-administrative install mode.
Basically, you can simply set PrivilegesRequiredOverridesAllowed:
[Setup]
PrivilegesRequiredOverridesAllowed=commandline dialog
Inno Setup 5
There's no such simple solution, in the previous versions of Inno Setup.
The easiest what you can do is to set PrivilegesRequired directive to none (undocumented value):
[Setup]
PrivilegesRequired=none
This will allow the installer to be run by an unprivileged user. It will install for him/her only.
For a privileged user, the Windows will typically detect that executable is an installer and it will popup a UAC prompt. It will install for all users afterwards.
For details see Make Inno Setup installer request privileges elevation only when needed
To make the installer install to "application data", when run by an unprivileged user, you can do:
[Setup]
DefaultDirName={code:GetDefaultDirName}
[Code]
function GetDefaultDirName(Param: string): string;
begin
if IsAdminLoggedOn then
begin
Result := ExpandConstant('{pf}\My Program');
end
else
begin
Result := ExpandConstant('{userappdata}\My Program');
end;
end;
If you really want the user to select, where to install to (though I do not think it is really necessary to allow the Administrator to install for him/herself), you can do this instead of the above DefaultDirName:
[Code]
var
OptionPage: TInputOptionWizardPage;
procedure InitializeWizard();
begin
OptionPage :=
CreateInputOptionPage(
wpWelcome,
'Choose installation options', 'Who should this application be installed for?',
'Please select whether you wish to make this software available for all users ' +
'or just yourself.',
True, False);
OptionPage.Add('&Anyone who uses this computer');
OptionPage.Add('&Only for me');
if IsAdminLoggedOn then
begin
OptionPage.Values[0] := True;
end
else
begin
OptionPage.Values[1] := True;
OptionPage.CheckListBox.ItemEnabled[0] := False;
end;
end;
function NextButtonClick(CurPageID: Integer): Boolean;
begin
if CurPageID = OptionPage.ID then
begin
if OptionPage.Values[1] then
begin
{ override the default installation to program files ({pf}) }
WizardForm.DirEdit.Text := ExpandConstant('{userappdata}\My Program')
end
else
begin
WizardForm.DirEdit.Text := ExpandConstant('{pf}\My Program');
end;
end;
Result := True;
end;

Related

How can you add :// to your file? [duplicate]

How do I register a custom protocol with Windows so that when clicking a link in an email or on a web page my application is opened and the parameters from the URL are passed to it?
Go to Start then in Find type regedit -> it should open Registry editor
Click Right Mouse on HKEY_CLASSES_ROOT then New -> Key
In the Key give the lowercase name by which you want urls to be called (in my case it will be testus://sdfsdfsdf) then Click Right Mouse on testus -> then New -> String Value and add URL Protocol without value.
Then add more entries like you did with protocol ( Right Mouse New -> Key ) and create hierarchy like testus -> shell -> open -> command and inside command change (Default) to the path where .exe you want to launch is, if you want to pass parameters to your exe then wrap path to exe in "" and add "%1" to look like: "c:\testing\test.exe" "%1"
To test if it works go to Internet Explorer (not Chrome or Firefox) and enter testus:have_you_seen_this_man this should fire your .exe (give you some prompts that you want to do this - say Yes) and pass into args testus://have_you_seen_this_man.
Here's sample console app to test:
using System;
namespace Testing
{
class Program
{
static void Main(string[] args)
{
if (args!= null && args.Length > 0)
Console.WriteLine(args[0]);
Console.ReadKey();
}
}
}
Hope this saves you some time.
The MSDN link is nice, but the security information there isn't complete. The handler registration should contain "%1", not %1. This is a security measure, because some URL sources incorrectly decode %20 before invoking your custom protocol handler.
PS. You'll get the entire URL, not just the URL parameters. But the URL might be subject to some mistreatment, besides the already mentioned %20->space conversion. It helps to be conservative in your URL syntax design. Don't throw in random // or you'll get into the mess that file:// is.
If anyone wants a .reg file for creating the association, see below:
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\duck]
"URL Protocol"=""
[HKEY_CLASSES_ROOT\duck\shell]
[HKEY_CLASSES_ROOT\duck\shell\open]
[HKEY_CLASSES_ROOT\duck\shell\open\command]
#="\"C:\\Users\\duck\\source\\repos\\ConsoleApp1\\ConsoleApp1\\bin\\Debug\\net6.0\\ConsoleApp1.exe\" \"%1\""
Pasted that into notepad, the file -> save as -> duck.reg, and then run it. After running it, when you type duck://arg-here into chrome, ConsoleApp1.exe will run with "arg-here" as an argument. Double slashes are required for the path to the exe and double quotes must be escaped.
Tested and working on Windows 11 with Edge (the chrome version) and Chrome
There is an npm module for this purpose.
link :https://www.npmjs.com/package/protocol-registry
So to do this in nodejs you just need to run the code below:
First Install it
npm i protocol-registry
Then use the code below to register you entry file.
const path = require('path');
const ProtocolRegistry = require('protocol-registry');
console.log('Registering...');
// Registers the Protocol
ProtocolRegistry.register({
protocol: 'testproto', // sets protocol for your command , testproto://**
command: `node ${path.join(__dirname, './index.js')} $_URL_`, // $_URL_ will the replaces by the url used to initiate it
override: true, // Use this with caution as it will destroy all previous Registrations on this protocol
terminal: true, // Use this to run your command inside a terminal
script: false
}).then(async () => {
console.log('Successfully registered');
});
Then suppose someone opens testproto://test
then a new terminal will be launched executing :
node yourapp/index.js testproto://test
It also supports all other operating system.

Inno Setup: Make desktop icon 'Run as administrator' [duplicate]

I'm creating an installer using Inno Setup. As part of the install process I'm installing Tomcat. On Windows 7 I suffer from the problem described here:
http://blog.paulbouwer.com/2010/10/23/the-case-of-the-annoying-tomcat-6-monitor/
I can fix it by manually setting the 'Run as administrator' on tomcat7w.exe (the issue and the root cause is the same for tomcat7 as well), but I don't know how to do it through Inno Setup.
I'm finding threads that explain running some_program.exe as administrator, but here the program is started when the Tomcat service starts (e.g. on machine start-up), so I need a way to flag it using Inno Setup to 'Run as administrator' rather than actually run it.
You can add a Registry entry in [Registry] Section that will set run as Administrator as a default action for running this app.
Example:
Root: "HKLM"; Subkey: "SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers"; \
ValueType: String; ValueName: "{app}\tomcat7w.exe"; ValueData: "RUNASADMIN"; \
Flags: uninsdeletekeyifempty uninsdeletevalue; MinVersion: 0,6.1
If you really want to set the "Run as administrator" flag of the shortcut (as opposite to forcing the target application run with administrator privileges), you can use this code:
[Icons]
Name: "{userdesktop}\My Program"; Filename: "{app}\MyProg.exe"; \
AfterInstall: SetElevationBit('{userdesktop}\My Program.lnk')
[Code]
procedure SetElevationBit(Filename: string);
var
Buffer: string;
Stream: TStream;
begin
Filename := ExpandConstant(Filename);
Log('Setting elevation bit for ' + Filename);
Stream := TFileStream.Create(FileName, fmOpenReadWrite);
try
Stream.Seek(21, soFromBeginning);
SetLength(Buffer, 1);
Stream.ReadBuffer(Buffer, 1);
Buffer[1] := Chr(Ord(Buffer[1]) or $20);
Stream.Seek(-1, soFromCurrent);
Stream.WriteBuffer(Buffer, 1);
finally
Stream.Free;
end;
end;
This is based on:
LinkFlags in [MS-SHLLINK]: Shell Link (.LNK) Binary File Format;
How to create a Run As Administrator shortcut using Powershell;
How can I use JScript to create a shortcut that uses "Run as Administrator"
Tested on Unicode version of Inno Setup. But it should, even more naturally, work on Ansi version too, though you should use Unicode version anyway.
If you want to allow user to execute the program at the end of the installation using a postinstall entry in [Run] section, you will of course need to explicitly request the elevation.
If the installer runs with Administrator privileges, you can simply add runascurrentuser flag:
[Run]
Filename: "{app}\MyProg.exe"; Description: "Launch application"; \
Flags: postinstall nowait skipifsilent runascurrentuser
If the installer runs without Administrator privileges, set Verb parameter to runas (for that you also need shellexec flag):
[Run]
Filename: "{app}\MyProg.exe"; Verb: runas; Description: "Launch application"; \
Flags: postinstall nowait skipifsilent shellexec
Though, make sure you have a very good reason to run your application with Administrator privileges. User applications should not need Administrator privileges. If they need it, it's usually a sign of a bad design. One common (bad) reason to want an application to run with Administrator privileges, is that the application needs to write to its installation folder.
See Application does not work when installed with Inno Setup
Add the runascurrentuser flag attribute to the [Run] section
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: runascurrentuser nowait postinstall skipifsilent;

Delphi Indy HTTPS detect the newest file from a web server

What is the correct approach in trying to detect the name of the newest file add into a web server? I'm trying to download a file using Indy HTTPS.The filename is dynamically generated so I first need to determine what is the name of the newest file that exists on the server.
I use the following code to download the file from the webserver:
function getIndyFile_from_https(const fileURL, FileName: String): boolean;
var http:TIdHttp;
s:TFileStream;
i:integer;
FProgress: Integer;
FBytesToTransfer: Int64;
FOnChange: TNotifyEvent;
IOHndl: TIdSSLIOHandlerSocketOpenSSL;
protocol_url,fullurl,url_fara_protocol,host:string;
begin
http:=TIdHTTP.Create(nil);
IOHndl := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
http.Request.BasicAuthentication := True;
//Request.Accept:='text/html,*/*';
//Request.AcceptEncoding:='utf-8';
http.HandleRedirects := True;
http.IOHandler := IOHndl;
http.ReadTimeout := 30000;
//http.OnWork := HTTPWork;
//http.OnWorkBegin := HTTPWorkBegin;
//http.OnWorkEnd := HTTPWorkEnd;
try
s:=TFileStream.Create(filename, fmcreate);
try
http.Get(fileUrl,s);
result:=true;
finally
s.free;
end;
finally
http.Free;
end;
end;
There is no standard HTTP command which retrieves a file list.
Some specific server installation may return file lists for given path / directory URLs, but then you need to parse the HTTP response programmatically.
You need server-side logic to handle this issue. In response to a given static URL that your client can request, the server needs to either:
keep track of the newest file and return that filename, if not the file itself.
provide a directory listing that describes all available files and their timestamps. You can then parse the listing and request the newest file.

QtIFW Always create registry entry

I'm currently using the Qt-Installer-Framework to create a setup for my application. Everything works fine for now except one thing:
If I install it to any location but C:\Program Files\MyApp, the installer won't create the registry entry for Programs and Features!
Is there a way to tell the installer to always do this?
Edit:
After trying out vairous different combinations, I do know now where the problem comes from:
If I try to install as current user only (set the AllUsers variable to false), it will always work and create an entry in HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{GUID}.
If I install for all users, however, it will try to create a key in HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{GUID}. This will only work, if the installer has to elevate it's operations during installation (because I chose a directory I need admin rights for).
So, the error is: The installer won't elevate itself to create the "global" registry entry and thus fails to create it. Any ideas on how to fix it?
Here the link which has answer to this question.
Add the following line to your component's package xml file:
#<RequiresAdminRights>true</RequiresAdminRights>#
And use this line in your script file:
#component.addElevatedOperation("Execute", "someCommand");#
instead of
#component.addOperation("Execute", "someCommand");#
There is boolean installer.gainAdminRights() to gain elevated privileges during runtime but you will have to add it to an installer script (in packages meta directory) something alike
function Component()
{
if (!installer.isInstaller())
{
if (allUsers && installer.gainAdminRights())
{
//Set registry global
} else {
//Set registry local
}
}
}

Passing JVM option to forked test in SBT

I am trying to use a JVM option in a forked test, which has been set externally to SBT prior to its launch. I'm also setting additional JVM options like so:
javaOptions in ThisBuild ++= List("-Xmx3072m")
to my understanding, based on the SBT documentation the JVM options provided to the SBT process should be available to the forked process:
By default, a forked process uses the same Java and Scala versions being used for the build and the working directory and JVM options of the current process.
However, I can't seem to retrieve those "external" JVM options in the forked tests, i.e. System.getProperty("foo") will always return null. Given that I am trying to pass along a password, I can't set it directly in the build file. My questions therefore are:
is there an SBT task / key to access the JVM options passed to the JVM in which SBT is running? That way I would attempt to add the key to the javaOptions
is there any other means by which to pass external Java Options to a forked test?
You may control your options with testGrouping. Below copy'n'paste from one of my projects. It properly handles hierarchical projects and root project without tests too. Options are merged from javaOptions in run and test.options file. This allow modify arguments without project reloading. That project has load time more then minute. So I use test.options to fast switch between production and debug mode with -Xrunjdwp:transport=dt_...
testGrouping in Test <<= (definedTests in Test, javaOptions in run, baseDirectory in LocalRootProject) map { (tests, javaOptions, baseDirectory) ⇒
if (tests.nonEmpty) {
val testOptionsFile = baseDirectory / "test.options"
val externalOptions = if (testOptionsFile.exists()) {
val source = scala.io.Source.fromFile(testOptionsFile)
val options = source.getLines().toIndexedSeq
source.close()
options
} else Nil
tests map { test ⇒
new Tests.Group(
name = test.name,
tests = Seq(test),
// runPolicy = Tests.InProcess)
runPolicy = Tests.SubProcess(javaOptions = javaOptions ++ externalOptions))
}
} else {
Seq(new Tests.Group(
name = "Empty",
tests = Seq(),
runPolicy = Tests.InProcess))
}
},

Resources