I am trying to install Java using the silent mode and also specify an installation directory that contains spaces. When I do this it pops up the "Windows Installer" dialog box indicating one of the parameters is incorrect. If I use the short path name it works correctly, but I really would prefer not to use the short directory name because that is the value that gets stored in the Registry.
The command I want to use...
jre-6u39-windows-i586.exe /s INSTALLDIR="C:\Program Files (x86)\Java"
This pops up the Windows Installer dialog box.
When I use...
jre-6u39-windows-i586.exe /s INSTALLDIR=C:\Progra~2\Java
This works.
NOTE: "Program Files (x86)" is just an example. This is installed at client sites and they choose the install directory, therefore we have to be able to support any directory they may specify.
Any idea how I can do a silent install but still use the long path name?
UPDATE:
I thought I would share the final solution. One cool thing I found that I wanted to share is that you can suppress the auto-reboot of install and it returns an exit code of 3010. Therefore you can defer the reboot to another time. Here is the code (rewritten a bit to eliminate a bunch of our own abstraction)
public bool InstallJava(string installPath, string logFile)
{
bool rebootRequired = false;
string fullLogFileName = Path.Combine(logFile, "JavaInstall.log");
string arguments = string.Format("/s /v\"/qn REBOOT=Suppress INSTALLDIR=\\\"{0}\\\" STATIC=1 /L \\\"{1}\\\"\"", installPath, fullLogFileName);
ProcessStartInfo startInfo = new ProcessStartInfo { RedirectStandardError = true, RedirectStandardOutput = true, RedirectStandardInput = true, UseShellExecute = false, CreateNoWindow = true,
FileName = "jre-7u25-windows-x64.exe", Arguments = arguments };
var process = Process.Start(startInfo);
process.WaitForExit();
if (process.ExitCode == 3010)
rebootRequired = true;
else if (process.ExitCode != 0)
{
// This just looks through the list of error codes and returns the appropriate message
string expandedMessage = ExpandExitCode(StringResources.JAVA_INSTALL_ERROR, process.ExitCode, fullLogFileName);
throw new Exception(expandedMessage);
}
return rebootRequired;
}
i recall encountering this issue before....
You need to use quotes when passing paths to the installer if the
paths have spaces. Because the path arg is already in quotes, you
need to escape each quote with a '\' so it gets passed through. So
the command would be
j2re.exe /s /v"/qn INSTALLDIR=\"C:\Program Files\JRE\""
reference :
http://docs.oracle.com/javase/1.5.0/docs/guide/deployment/deployment-guide/silent.html
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4966488
Related
I successfully added an item to the context menu of folders (that's the easy part) but my program is jar extension when I try to open it via context menu error appears: This program will not work on your computer or something like this, so I tried in command value write this:
Java -jar path.jar "%1"
Results were the same. My workaround is bat file to open jar file xD but there are two problems:
An unpleasant black cmd window pops up every time
Paths with spaces don't work (the program is given null?)
How to do it so I can rid of this bat?
I think, this is much more of a Windows than a java problem, so more of a generic approach:
Don't try to directly run your java code. Instead: write a script (for example using PowerShell that gets called).
And within that script, you could be doing things such as:
searching the windows machine for a java installation
checking/translating relative paths to "correct" absolute values
to then, finally invoke some java.exe with correct parameters
Here is my simple code :
param (
[string]$p1
)
function Find-JavaExe {
[CmdletBinding()]
param ()
$JavaExeSuffix = 'bin\java.exe'
if ($env:JAVAHOME -eq $null) {
Write-Debug "`$env:JAVAHOME doesn't exist, going to look elsewhere"
}
else {
$JavaHomeBasedPath = Join-Path $env:JAVAHOME $JavaExeSuffix
Write-Debug "Testing for $JavaHomeBasedPath, based on `$env:JAVAHOME"
if (Test-Path $JavaHomeBasedPath) {
Write-Debug "Found $JavaExePath"
return $JavaExePath
}
}
$RegistrySearchPaths = #('HKLM:\SOFTWARE\JavaSoft\Java Runtime Environment\', 'HKLM:\SOFTWARE\Wow6432Node\JavaSoft\Java Runtime Environment\')
$JavaExePath = $RegistrySearchPaths |
where { Test-Path $_ } |
% {
$CurrentVersion = (Get-ItemProperty $_).CurrentVersion
Write-Debug "Current Java version is $CurrentVersion, based on $($_)"
$VersionKey = Join-Path $_ $CurrentVersion
$JavaHomeBasedPath = Join-Path (Get-ItemProperty $VersionKey).JavaHome $JavaExeSuffix
Write-Debug "Testing for $JavaHomeBasedPath, based on $VersionKey\JavaHome"
if (Test-Path $JavaHomeBasedPath) { $JavaHomeBasedPath }
} |
select -First 1
if ($JavaExePath -ne $null) {
Write-Debug "Found $JavaExePath"
return $JavaExePath
}
}
$path_java = Find-JavaExe
$java_param = " -jar "
$path_prog =
$ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath('.\')
$program_name = "\ResizeProgram.jar "
$command = $path_java + $java_param + $path_prog + $program_name + """$p1"""
iex $command
it works fine and handles paths with spaces
but I have another problem when running it from comtextmenu of the folder, prompt appears saying:
This program will not work on your computer; again.
In regedit I wrote simple:
path.ps1 "%1"
First let me say that this question is not a duplicate of Use Process.Start with parameters AND spaces in path. I am using the System.Diagnostics.Process to start a cmd.exe window and then running java in that window. Except I want the java command to be run based on their installed Java path, as the PATH environment variable is unreliable and doesn't seem to get set very often when Java is installed. So I replaced the "java" in my arguments for the Process with the actual Java path, but now I'm getting this error:
'C:\Program' is not recognized as an internal or external command,
operable program or batch file.
Clearly this is because there are spaces in the name, but I am properly quoting the path and using escape characters to create those quotes. Here is the code used to run the cmd.exe:
System.Diagnostics.Process process = new System.Diagnostics.Process();
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
//startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal;
startInfo.FileName = "cmd.exe";
if (chbPath.Checked) startInfo.Arguments = "/C \"" + javaPath + "\\bin\\java.exe\" -Djava.library.path=\"lib\\natives-win\" -jar SecondDimension.jar " + chbWindowed.Checked.ToString();
else startInfo.Arguments = "/C java -Djava.library.path=\"lib\\natives-win\" -jar SecondDimension.jar " + chbWindowed.Checked.ToString();
process.StartInfo = startInfo;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardError = true;
process.Start();
this.Visible = false;
process.WaitForExit();
Console.WriteLine(process.StandardError.ReadToEnd());
Application.Exit();
If chbPath.Checked = false, it runs the command with the java command set with PATH. Which works fine for me, but doesn't for people who haven't ever tried to run java from the command line. But when I check chbPath, then I get the error listed above. Can anyone help with this? This is really annoying and I should have been done with this hours ago, but of course a SPACE....a SINGLE SPACE is stopping me from progressing....ARGHHH!!!
Edit:
Also here is the code for my path finder, which I pulled off another post here:
String javaKey = "SOFTWARE\\JavaSoft\\Java Runtime Environment";
using (var baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64).OpenSubKey(javaKey))
{
String currentVersion = baseKey.GetValue("CurrentVersion").ToString();
using (var homeKey = baseKey.OpenSubKey(currentVersion))
return homeKey.GetValue("JavaHome").ToString();
}
First you could use the property ProcessStartInfo.WorkingDirectory to set the working folder for the Java process, then, because your program is in a different directory you need to change the path to this program.
You could set an Environment variable and use that variable to complete the path to the program or directly include the name of the program in the environment variable
Environment.SetEnvironmentVariable("JAVA_PRG", #"d:\temp"); // Whatever
Process process = new Process();
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.WorkingDirectory = Path.Combine(javaPath, "bin");
startInfo.FileName = "cmd.exe";
if (chbPath.Checked)
startInfo.Arguments = "/C java.exe .... -jar %JAVA_PRG%\SecondDimension.jar ";
Ok, so I was able to fix it. Apparently I don't need to run a cmd window to run the java command, because using diagnostics.process to start a progarm this way always creates a console window. So I just changed the GetJavaInstallationPath() code to return the path to the actual java executable, and then just set StartInfo.FileName = GetJavaInstallationPath(); which pretty much solved all my problems. This way the working directory stays in the game's directory and I still get the console window I wanted. So I guess I was trying too hard. :) Here's the fixed code:
private void btnLaunch_Click(object sender, EventArgs e)
{
System.Diagnostics.Process process = new System.Diagnostics.Process();
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
startInfo.FileName = GetJavaInstallationPath();
startInfo.Arguments = "-Djava.library.path=\"lib\\natives-win\" -jar SecondDimension.jar " + chbWindowed.Checked.ToString();
process.StartInfo = startInfo;
process.Start();
this.Visible = false;
process.WaitForExit();
Application.Exit();
}
I have an executable Jar file and to keep it simple, I want to make it so that you can simply double click it on the desktop and it will run. I've tried this:
if(args.length == 0){
String path = Main.class.getProtectionDomain().getCodeSource().getLocation().getPath();
String decodedPath = URLDecoder.decode(path, "UTF-8");
Runtime rt = Runtime.getRuntime();
Process pr = rt.exec("java -jar \"" + decodedPath + "\" -arg");
System.out.println("java -jar \"" + decodedPath + "\" -arg");
}
To no avail. I assumed that if I told the program to check for the "-arg" argument and it wasn't there, then it would asssume the program was run from the executable, not being called from the command line. So is there a way to make the program open a command prompt and then run itself within it, killing the previous program?
As to "run on double click", this is OS dependent.
You can "run a jar" at the command line using:
java -jar the.jar
This requires that the jar has a META-INF/MANIFEST.MF and that this manifest file has a Main-Class entry, the argument being the class where your main() method is. For instance:
Main-Class: org.foobar.mypackage.Foo
What I have done for a similar problem is that I have made a separate GUI program in a JAR file with some JTextFields for input and a JButton for confirmation. When the button gets clicked, it calls the main method in my other class with those values in a String array to start that program and close the GUI form with frame.setVisible(false). I suggest doing something like that, but it's dependent on what type of program you're developing.
You could also just pass the necessary command-line flags directly into the JRE at runtime! I just figured this out a couple weeks ago, but you can access the java.library.path and change it to match necessary library paths through reflection by just putting this code in the front of your main method.
try{
System.setProperty("java.library.path", path);
Field fieldSysPath = ClassLoader.class.getDeclaredField( "sys_paths" );
fieldSysPath.setAccessible( true );
fieldSysPath.set( null, null );
}catch(Exception ex){
// just exit and tell user that there was an error or something similar
}
Anyway, I hope that this was helpful. You can also do many similar things by similar code.
I am building a setup in install4j which will be run for each client of a marketing agency. There is one installer, but the user can run it more than once, specifying a different clientId value at the installation time. In the end, I would like to end up with a directory structure like this:
on Mac:
/Applications/MYPRODUCTNAME-clientID1/
/Applications/MYPRODUCTNAME-clientID2/
/Applications/MYPRODUCTNAME-clientID3/
on Windows:
/Program Files/MYPRODUCTNAME-clientID1/
/Program Files/MYPRODUCTNAME-clientID2/
/Program Files/MYPRODUCTNAME-clientID3/
Where the IDs are entered at installation time, in independent installer runs. The IDs are not known in advance - I can't build as many installers as there are IDs.
Ideally, on Mac, I would also prefer to change the name of the launcher file, so that it can be easily discerned from the others in Spotlight search.
I've been playing with Directory Resolver - no luck, especially on Mac which seams to produce a broken launcher on every attempt to change its directory structure.
Any help will be greatly appreciated!
You can change the installation directory by calling
context.setInstallationDirectory(...);
in a "Run script" action or any code snippet in install4j.
Changing launcher names at runtime is not directly supported by install4j.
I ended up doing something like this:
At activation of the Location window:
systemInstallPath = context.getVariable( "sys.programFilesDir" ); // if Windows
if( systemInstallPath == null || systemInstallPath.isEmpty() ) // assume Mac
systemInstallPath = "/Applications";
context.setInstallationDirectory( new File( systemInstallPath ) );
Then at activation of Installation window:
final Boolean confirmedUpdate = context.getBooleanVariable("sys.confirmedUpdateInstallation");
if( confirmedUpdate == null || !confirmedUpdate ) {
final File originalInstallDir = context.getInstallationDirectory();
final String clientId = ( String )context.getVariable( "clientId" );
final File clientInstallDir = new File( originalInstallDir, "MYPRODUCTNAME-" + clientId );
context.setInstallationDirectory( clientInstallDir );
}
That did the trick.
I have a java class file with a main method. In Windows, I would like to be able to drag files onto a desktop icon/short/etc that would call supply the filenames to my main method. Basically, I want to allow users to drag-and-drop files at program execution instead of having type them on the command line.
Any thoughts?
To build on daub815's answer, in Windows, you can use a batch file to pass
arguments to another command. In this case, we'll use the java launcher to
launch your class with the main method.
I did a quick Google search on how to do write a batch file to take multiple arguments,
and found a page with a batch file to pass arguments to another command. Adapting from
the example, here is what you can do:
#ECHO OFF
:Loop
IF "%1" == "" GOTO Done
java YourClass %1
SHIFT
GOTO Loop
:Done
Save the above file as a batch file (with a ".bat" extension), and then you can drag-and-drop
files onto it, and it will be passed as arguments.
Also, you can call the batch file from the command line and pass arguments as well.
Edit: It appears that the batch file will not work with quoted arguments which contain spaces. Using a workaround presented in the site I've linked to will split the spaces contained in the quoted full path of the file into separate arguments, so that won't work either. If anyone has a good idea how to fix this, please either edit this entry, or post another answer. I will make this a community wiki.
PhiLho's answer works perfectly if you pack the classes in an executable JAR file (it's how you're meant to do it anyway) and make a .reg file that looks like the one below. Then just double-click that .reg file to merge it into the registry and you're good to go. This lets you both double-click a JAR file to run it, and starting it by Drag & Drop.
Do remember to change the path to where your Java executable is installed.
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\.jar]
#="jarfile"
[HKEY_CLASSES_ROOT\jarfile\DefaultIcon]
#="C:\\Java\\jdk1.7.0\\bin\\java.exe,1"
[HKEY_CLASSES_ROOT\jarfile\shell\open]
#="Run Java Program"
[HKEY_CLASSES_ROOT\jarfile\shell\open\command]
#="\"C:\\Java\\jdk1.7.0\\bin\\java.exe\" -jar \"%1\" %*"
[HKEY_CLASSES_ROOT\jarfile\shellex\DropHandler]
#="{86C86720-42A0-1069-A2E8-08002B30309D}"
OK, I made it work... The base knowledge is to use DropHandler UUID in the registry. I made a base setting, as follow:
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\.class]
#="JavaClass"
[HKEY_CLASSES_ROOT\JavaClass\DefaultIcon]
#="C:\\Java\\jdk1.6.0_05\\bin\\java.exe,1"
[HKEY_CLASSES_ROOT\JavaClass\shell\open]
#="Run Java class"
[HKEY_CLASSES_ROOT\JavaClass\shell\open\command]
#="\"C:\\Java\\jdk1.6.0_05\\bin\\java.exe\" \"%1\" %*"
[HKEY_CLASSES_ROOT\JavaClass\shellex\DropHandler]
#="{86C86720-42A0-1069-A2E8-08002B30309D}"
and... it didn't work!
I just forgot that java.exe wants a class name, not a file name! But I see no way to do that in the registry.
Fortunately, there is a workaround, which still need a script file if we want to be generic, to work on any/all class files (with static main function, of course!). Not batch, I avoid them when I can. I chose to use WSH, as it should be available on any modern Windows system. I also chose to make a JS script, it could have been a VB script as well.
So I made the following script (LaunchJavaClass.js):
if (WScript.Arguments.count() == 0)
{
WScript.StdOut.Write("No parameters");
WScript.Quit(1);
}
var className = WScript.Arguments.Item(0);
//~ WScript.StdOut.Write(className + "\n");
var m = className.match(/^(.*)\\(.+?)\.class$/);
if (m == null)
{
WScript.StdOut.Write("Not a class file");
WScript.Quit(1);
}
var classPath = m[1];
className = m[2];
//~ WScript.StdOut.Write(classPath + " >>> " + className + "\n");
var params = new Array();
for (i = 1; i < WScript.Arguments.count(); i++)
{
params[params.length] = WScript.Arguments.Item(i);
}
var cmd = "cmd /c cd /D " + classPath +
" & C:/Java/jdk1.6.0_05/bin/java.exe " +
className + " " + params.join(" ");
//~ WScript.StdOut.Write(cmd + "\n");
var shell = WScript.CreateObject("WScript.Shell");
//~ var exec = shell.Exec(cmd); // Can be used to get stdout
shell.Run(cmd, 0);
I left some output, not useful in this context, but usable for debugging (run with cscript).
Of course, the path to the JRE must be adjusted.
And I changed the command in the registry, as follow:
[HKEY_CLASSES_ROOT\JavaClass\shell\open\command]
#="\wscript -b "D:\\_PhiLhoSoft\\WSH\\LaunchJavaClass.js\" %1 %*"
Of course, adjust path, and keep the above other lines.
Now, if I drag'n'drop some files to a .class file, it gets the short file paths as arguments of the main() function.
import java.io.*;
class TestDnD
{
public static void main(String[] args)
{
Writer output = null;
try
{
output = new BufferedWriter(new FileWriter(new File("LogFile.txt")));
for (String arg : args)
{
output.write(arg + "\n");
}
}
catch (IOException ioe)
{
ioe.printStackTrace();
return;
}
finally
{
try { output.close(); } catch (IOException e) {}
}
}
}
I think the first version of the .reg file can be used for something else, eg. to drag'n'drop on .jar files (adapting it, of course).
This technique has limited use: we rarely make one-class programs in Java! But it looked like a good and interesting challenge, so I didn't resist to solve it. Note: you can add stuff like -Djava.ext.dirs="some path;another path" if you ever need to use external libraries (in jar files).
Adding onto Adiel A. If you create a batch file, which launches your a Java window using Swing. You would have the user drop the files onto that window. You could then be able to root through those dropped files.
So there's no way to have windows itself pass the args into main() via drag and drop?