How can I handle spaces in arguments? - java

My app launches and loads the file if I do: myApp /file:c:\nospaces.asd from cmd but if I do myApp /file:c:\with spaces.asd it won't work because the program receives two arguments: myApp /file:c:\with and spaces.asd.
I know that I can do myApp "/file:c:\with spaces.asd" and it'll work like that from cmd. However this isn't a good solution because if I double click the .asd file (custom extension) (and select launch with my app) then main won't be getting the arguments as one line but as two arguments.
How can I go about fixing this issue so that my main will receive only one argument when double clicking the file?

You could join the arguments together if the file is not found. Something like this:
public static void main(String[] args) {
String fileName = joinArgumentsToValidFileName(args);
}
public static String joinArgumentsToValidFileName(String[] args) {
if(args.length == 0) {
return "";
}
String fileName = args[0];
int index = 1;
while(!new File(fileName).exists() && index < args.length) {
fileName += " " + args[index];
index++;
}
return fileName;
}
This assumes that the first argument (or the arguments) must be the file name. Any additional agruments could be evaluated by remembering the offset index somehow (not included in the code above).
But note: This is a not standard behavior of passing arguments to an application and could lead to cunfusion. So if you find a way to pass a "" wrapped file name argument, don't do this!

Double clicking to launch java would be affected by whats registered in windows related to the same i.e windows registry for jar-file launch command.
These are some similar questions asked on SO you can take a look at:
Running JAR file on Windows
java can run jar from cmd but not by double clicking
They mention the useful part which is ensuring you have the wildcard behind the executable in the registry c:\...\javaw.exe" -jar "%1" %

Related

Why java arguments behave differently in Windows and Linux?

I'm getting different results testing a simple java class on Windows cmd and wsl (ubuntu).
The java class:
public class PrintArgs {
public static void main(String[] args) {
System.out.println("Printing some arguments in this code: ");
// Loop through arguments passed and print them to standard output
for (int i = 0; i < args.length; i++) {
System.out.println("Argument " + (i + 1) + ": " + args[i]);
}
}
}
I use this test arguments:
java PrintArgs.java Test "Testing TestThis" 'Some arguments' ´More Arguments´
In cmd, the single quote doesn't group the arguments:
cmd results and java version
but in ubuntu, it does:
wsl results and java version
Any idea why this is happening?
The Java process will already get an array of strings as the argument (in anything C-based it's more like a **char, but that's close enough). It doesn't even see the quotes that group a single argument together, because those will already have been interpreted by the shell.
The shell (probably Bash in WSL and cmd.exe in the command window) is responsible for taking the single continuous string of what the user entered and splitting it into arguments (and expanding wildcards, where applicable, but that doesn't happen in this case).
Now Bash and cmd.exe have different rule about how quoting works, so they split the single string differently.

Packaging a jar with preconfigured command line arguments

I am wondering if there's a way to create a jar that includes some command line arguments in it, the arguments that are usually passed in the command line when one tries to start up the jar (these parameters are then passed on to the main function). Basically instead of starting my app with
java -jar myapp.jar "arg1" "arg2", I want to start my app with
java -jar myapp.jar
and have "arg1" and "arg2" passed to the main function.
The reason behind this is that I want to deploy this to different environments, and I want my jar to contain different parameters according to the environment it's being deployed at.
Maybe there's another way to achieve similar results ??
Cheers.
PS: Looking for a maven solution.
Edit: I'll add a complete example to make this a bit more clear:
Let's say I have 2 environments: "Production" and "Test". I want to run the jar in the same way no matter in what environment I deploy it. So I always want to run it with:
java -jar myapp.jar
But! In order for my 2 environments to run ok, I need the Production environment jar to start it's main method with an argument "prod" and I need the Test environment jar to start it's main method with an argument "test".
If I correctly understood your problem, in your main() you could define a simple logic to handle the case where you do not specify any input parameter; the logic could retrieve the desired values according to the correct platform/env.
As an example:
public class Test01
{
public static void main(String... aaa)
{
// Check input
if(aaa.length == 0) {
/* Insert logic to retrieve the value you want, depending on the platform/environment.
* A trivial example could be: */
aaa = new String[2];
aaa[0] = "First value";
aaa[1] = "Second value";
}
// Processing, e.g. print the 2 input values
System.out.println(aaa[0] + ", " + aaa[1]);
}
}
Fyi, I created a runnable jar using eclipse, and start the application by either
java -jar Test01.jar
or
java -jar Test01.jar arg1 arg2
Hope this helps!
One solution is to change main(String[] args) to get values from env var if they are not present in the passed arguments.
String user;
String password;
if(args.length < 2)
{
user = System.getenv("appUser");
password = System.getenv("appPassword");
} else {
user = args[0];
password = args[1];
}
You can also create another class with a main function that will call the real one.
public class CallerMyApp{
public void main(String[] args) {
String[] realArgs = {System.getenv("appUser"), System.getenv("appPassword")};
MyApp.main(realArgs);
}
}
Then to execute its something like
java -cp myapp.jar CallerMyApp

How to read and write text files from the main in java

The static method main, which receives an array of strings. The array should have two elements: the path where the files are located (at index 0), and the name of the files to process (at index 1). For example, if the name was “Walmart” then the program should use “Walmart.cmd” (from which it will read commands) and “Walmart.pro” (from which it will read/write products).
I don't want anyone to write the code for me because this is something I need to learn. However I've been reading this through and the wording is confusing. If someone could help me understand what it wants from me through pseudo-code or an algorithm it would be greatly appreciated.
Where I'm confused is how to initialize arg[0] and arg[1] and exactly
what they are being initialized to.
The main method's String array input argument consists of whatever String arguments you pass to the program's main method when you run the program. For example, here is a simple program that loops over args and prints a nice message with each argument's index and value on a separate line:
package com.example;
public class MainExample {
public static void main(String[] args) {
for (int i = 0; i < args.length; i++) {
System.out.printf("args[%d]=%s\n", i, args[i]);
}
}
}
Once you've compiled the program, you can run it on the command-line and pass it some arguments:
java -cp . com.example.MainExample eh? be sea 1 2 3 "multiple words"
Output:
args[0]=eh?
args[1]=be
args[2]=sea
args[3]=1
args[4]=2
args[5]=3
args[6]=multiple words
So lets explain to you
Create a class Inventory : if you don't know how to create a class google it just as is
The static method main: Every executable class in java (at least from the console) has the main method you should google java main method and propably in the same place you find it you will see the default arguments that it receives
When you learn about the default arguments of method main you will undertand about the 'args' that has to be on it
You will have t study the class String google it "java String class"
You will have to study the class File google it "java File class"
At the end everything else would be just logic and I beleave you have learned some at this point.
public class Inventory { // class inventory
public static void main(String[] args) // main method
{
if(args.length==2){ // check if args contains two elements
String filePath = args[0];
String fileName = args[1];
filePath+= System.getProperty("file.separator")+fileName;
File fileCMD = new File(filePath+".cmd");
//fileCMD.createNewFile();
File filePRO =new File(filePath+".pro");
//filePRO.createNewFile();
}
else {
//write the code to print the message Usage: java Inventory Incorrect number of parameters for a while and exit the program.
}
}
This is what I've understood. Basically you have to write a program to create two files, one called fileName.cmd and the other fileName.pro. You have to construct the path of the files using the arguments (input parameters of the main method) and system's file separator. If the arguments don't have two elements you have to print the 'invalid' message. That's it.
Where I'm confused is how to initialize arg[0] and arg[1] and exactly
what they are being initialized to.
You have to use command line to pass the arguments and launch the program , something like the following code in cmd or terminal:
java inventory thePath theFileName
That's how it get initialized.

How can I restart a Java application?

How can I restart a Java AWT application? I have a button to which I have attached an event handler. What code should I use to restart the application?
I want to do the same thing that Application.Restart() do in a C# application.
Of course it is possible to restart a Java application.
The following method shows a way to restart a Java application:
public void restartApplication()
{
final String javaBin = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java";
final File currentJar = new File(MyClassInTheJar.class.getProtectionDomain().getCodeSource().getLocation().toURI());
/* is it a jar file? */
if(!currentJar.getName().endsWith(".jar"))
return;
/* Build command: java -jar application.jar */
final ArrayList<String> command = new ArrayList<String>();
command.add(javaBin);
command.add("-jar");
command.add(currentJar.getPath());
final ProcessBuilder builder = new ProcessBuilder(command);
builder.start();
System.exit(0);
}
Basically it does the following:
Find the java executable (I used the java binary here, but that depends on your requirements)
Find the application (a jar in my case, using the MyClassInTheJar class to find the jar location itself)
Build a command to restart the jar (using the java binary in this case)
Execute it! (and thus terminating the current application and starting it again)
import java.io.File;
import java.io.IOException;
import java.lang.management.ManagementFactory;
public class Main {
public static void main(String[] args) throws IOException, InterruptedException {
StringBuilder cmd = new StringBuilder();
cmd.append(System.getProperty("java.home") + File.separator + "bin" + File.separator + "java ");
for (String jvmArg : ManagementFactory.getRuntimeMXBean().getInputArguments()) {
cmd.append(jvmArg + " ");
}
cmd.append("-cp ").append(ManagementFactory.getRuntimeMXBean().getClassPath()).append(" ");
cmd.append(Main.class.getName()).append(" ");
for (String arg : args) {
cmd.append(arg).append(" ");
}
Runtime.getRuntime().exec(cmd.toString());
System.exit(0);
}
}
Dedicated to all those who say it is impossible.
This program collects all information available to reconstruct the original commandline. Then, it launches it and since it is the very same command, your application starts a second time. Then we exit the original program, the child program remains running (even under Linux) and does the very same thing.
WARNING: If you run this, be aware that it never ends creating new processes, similar to a fork bomb.
Basically, you can't. At least not in a reliable way. However, you shouldn't need to.
The can't part
To restart a Java program, you need to restart the JVM. To restart the JVM you need to
Locate the java launcher that was used. You may try with System.getProperty("java.home") but there's no guarantee that this will actually point to the launcher that was used to launch your application. (The value returned may not point to the JRE used to launch the application or it could have been overridden by -Djava.home.)
You would presumably want to honor the original memory settings etc (-Xmx, -Xms, …) so you need to figure out which settings where used to start the first JVM. You could try using ManagementFactory.getRuntimeMXBean().getInputArguments() but there's no guarantee that this will reflect the settings used. This is even spelled out in the documentation of that method:
Typically, not all command-line options to the 'java' command are passed to the Java virtual machine. Thus, the returned input arguments may not include all command-line options.
If your program reads input from Standard.in the original stdin will be lost in the restart.
Lots of these tricks and hacks will fail in the presence of a SecurityManager.
The shouldn't need part
I recommend you to design your application so that it is easy to clean every thing up and after that create a new instance of your "main" class.
Many applications are designed to do nothing but create an instance in the main-method:
public class MainClass {
...
public static void main(String[] args) {
new MainClass().launch();
}
...
}
By using this pattern, it should be easy enough to do something like:
public class MainClass {
...
public static void main(String[] args) {
boolean restart;
do {
restart = new MainClass().launch();
} while (restart);
}
...
}
and let launch() return true if and only if the application was shut down in a way that it needs to be restarted.
Strictly speaking, a Java program cannot restart itself since to do so it must kill the JVM in which it is running and then start it again, but once the JVM is no longer running (killed) then no action can be taken.
You could do some tricks with custom classloaders to load, pack, and start the AWT components again but this will likely cause lots of headaches with regard to the GUI event loop.
Depending on how the application is launched, you could start the JVM in a wrapper script which contains a do/while loop, which continues while the JVM exits with a particular code, then the AWT app would have to call System.exit(RESTART_CODE). For example, in scripting pseudocode:
DO
# Launch the awt program
EXIT_CODE = # Get the exit code of the last process
WHILE (EXIT_CODE == RESTART_CODE)
The AWT app should exit the JVM with something other than the RESTART_CODE on "normal" termination which doesn't require restart.
Eclipse typically restarts after a plugin is installed. They do this using a wrapper eclipse.exe (launcher app) for windows. This application execs the core eclipse runner jar and if the eclipse java application terminates with a relaunch code, eclipse.exe restarts the workbench. You can build a similar bit of native code, shell script or another java code wrapper to achieve the restart.
Windows
public void restartApp(){
// This launches a new instance of application dirctly,
// remember to add some sleep to the start of the cmd file to make sure current instance is
// completely terminated, otherwise 2 instances of the application can overlap causing strange
// things:)
new ProcessBuilder("cmd","/c start /min c:/path/to/script/that/launches/my/application.cmd ^& exit").start();
System.exit(0);
}
/min to start script in minimized window
^& exit to close cmd window after finish
a sample cmd script could be
#echo off
rem add some sleep (e.g. 10 seconds) to allow the preceding application instance to release any open resources (like ports) and exit gracefully, otherwise the new instance could fail to start
sleep 10
set path=C:\someFolder\application_lib\libs;%path%
java -jar application.jar
sleep 10 sleep for 10 seconds
Just adding information which is not present in other answers.
If procfs /proc/self/cmdline is available
If you are running in an environment which provides procfs and therefore has the /proc file system available (which means this is not a portable solution), you can have Java read /proc/self/cmdline in order to restart itself, like this:
public static void restart() throws IOException {
new ProcessBuilder(getMyOwnCmdLine()).inheritIO().start();
}
public static String[] getMyOwnCmdLine() throws IOException {
return readFirstLine("/proc/self/cmdline").split("\u0000");
}
public static String readFirstLine(final String filename) throws IOException {
try (final BufferedReader in = new BufferedReader(new FileReader(filename))) {
return in.readLine();
}
}
On systems with /proc/self/cmdline available, this probably is the most elegant way of how to "restart" the current Java process from Java. No JNI involved, and no guessing of paths and stuff required. This will also take care of all JVM options passed to the java binary. The command line will be exactly identical to the one of the current JVM process.
Many UNIX systems including GNU/Linux (including Android) nowadays have procfs However on some like FreeBSD, it is deprecated and being phased out. Mac OS X is an exception in the sense that it does not have procfs. Windows also does not have procfs. Cygwin has procfs but it's invisible to Java because it's only visible to applications using the Cygwin DLLs instead of Windows system calls, and Java is unaware of Cygwin.
Don't forget to use ProcessBuilder.inheritIO()
The default is that stdin / stdout / stderr (in Java called System.in / System.out / System.err) of the started Process are set to pipes which allow the currently running process to communicate with the newly started process. If you want to restart the current process, this is most likely not what you want. Instead you would want that stdin / stdout / stderr are the same as those of the current VM. This is called inherited. You can do so by calling inheritIO() of your ProcessBuilder instance.
Pitfall on Windows
A frequent use case of a restart() function is to restart the application after an update. The last time I tried this on Windows this was problematic. When overwrote the application's .jar file with the new version, the application started to misbehave and giving exceptions about the .jar file. I'm just telling, in case this is your use case. Back then I solved the issue by wrapping the application in a batch file and using a magic return value from System.exit() that I queried in the batch file and had the batch file restart the application instead.
Although this question is old and answered, I've stumbled across a problem with some of the solutions and decided to add my suggestion into the mix.
The problem with some of the solutions is that they build a single command string. This creates issues when some parameters contain spaces, especially java.home.
For example, on windows, the line
final String javaBin = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java";
Might return something like this:C:\Program Files\Java\jre7\bin\java
This string has to be wrapped in quotes or escaped due to the space in Program Files. Not a huge problem, but somewhat annoying and error prone, especially in cross platform applications.
Therefore my solution builds the command as an array of commands:
public static void restart(String[] args) {
ArrayList<String> commands = new ArrayList<String>(4 + jvmArgs.size() + args.length);
List<String> jvmArgs = ManagementFactory.getRuntimeMXBean().getInputArguments();
// Java
commands.add(System.getProperty("java.home") + File.separator + "bin" + File.separator + "java");
// Jvm arguments
for (String jvmArg : jvmArgs) {
commands.add(jvmArg);
}
// Classpath
commands.add("-cp");
commands.add(ManagementFactory.getRuntimeMXBean().getClassPath());
// Class to be executed
commands.add(BGAgent.class.getName());
// Command line arguments
for (String arg : args) {
commands.add(arg);
}
File workingDir = null; // Null working dir means that the child uses the same working directory
String[] env = null; // Null env means that the child uses the same environment
String[] commandArray = new String[commands.size()];
commandArray = commands.toArray(commandArray);
try {
Runtime.getRuntime().exec(commandArray, env, workingDir);
System.exit(0);
} catch (IOException e) {
e.printStackTrace();
}
}
If you realy need to restart your app, you could write a separate app the start it...
This page provides many different examples for different scenarios:
http://www.rgagnon.com/javadetails/java-0014.html
Similar to Yoda's 'improved' answer, but with further improvements (both functional, readability, and testability). It's now safe to run, and restarts for as as many times as the amount of program arguments given.
No accumulation of JAVA_TOOL_OPTIONS options.
Automatically finds main class.
Inherits current stdout/stderr.
public static void main(String[] args) throws Exception {
if (args.length == 0)
return;
else
args = Arrays.copyOf(args, args.length - 1);
List<String> command = new ArrayList<>(32);
appendJavaExecutable(command);
appendVMArgs(command);
appendClassPath(command);
appendEntryPoint(command);
appendArgs(command, args);
System.out.println(command);
try {
new ProcessBuilder(command).inheritIO().start();
} catch (IOException ex) {
ex.printStackTrace();
}
}
private static void appendJavaExecutable(List<String> cmd) {
cmd.add(System.getProperty("java.home") + File.separator + "bin" + File.separator + "java");
}
private static void appendVMArgs(Collection<String> cmd) {
Collection<String> vmArguments = ManagementFactory.getRuntimeMXBean().getInputArguments();
String javaToolOptions = System.getenv("JAVA_TOOL_OPTIONS");
if (javaToolOptions != null) {
Collection<String> javaToolOptionsList = Arrays.asList(javaToolOptions.split(" "));
vmArguments = new ArrayList<>(vmArguments);
vmArguments.removeAll(javaToolOptionsList);
}
cmd.addAll(vmArguments);
}
private static void appendClassPath(List<String> cmd) {
cmd.add("-cp");
cmd.add(ManagementFactory.getRuntimeMXBean().getClassPath());
}
private static void appendEntryPoint(List<String> cmd) {
StackTraceElement[] stackTrace = new Throwable().getStackTrace();
StackTraceElement stackTraceElement = stackTrace[stackTrace.length - 1];
String fullyQualifiedClass = stackTraceElement.getClassName();
String entryMethod = stackTraceElement.getMethodName();
if (!entryMethod.equals("main"))
throw new AssertionError("Entry point is not a 'main()': " + fullyQualifiedClass + '.' + entryMethod);
cmd.add(fullyQualifiedClass);
}
private static void appendArgs(List<String> cmd, String[] args) {
cmd.addAll(Arrays.asList(args));
}
V1.1 Bugfix: null pointer if JAVA_TOOL_OPTIONS is not set
Example:
$ java -cp Temp.jar Temp a b c d e
[/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java, -cp, Temp.jar, Temp, a, b, c, d]
[/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java, -cp, Temp.jar, Temp, a, b, c]
[/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java, -cp, Temp.jar, Temp, a, b]
[/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java, -cp, Temp.jar, Temp, a]
[/usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java, -cp, Temp.jar, Temp]
$
I was researching the subject myself when came across this question.
Regardless of the fact that the answer is already accepted, I would still like to offer an alternative approach for completeness. Specifically, Apache Ant served as a very flexible solution.
Basically, everything boils down to an Ant script file with a single Java execution task (refer here and here) invoked from a Java code (see here). This Java code, which can be a method launch, could be a part of the application that needs to be restarted. The application needs to have a dependency on the Apache Ant library (jar).
Whenever application needs to be restarted, it should call method launch and exit the VM. The Ant java task should have options fork and spawn set to true.
Here is an example of an Ant script:
<project name="applaucher" default="launch" basedir=".">
<target name="launch">
<java classname="package.MasinClass" fork="true" spawn="true">
<jvmarg value="-splash:splash.jpg"/>
<jvmarg value="-D other VM params"/>
<classpath>
<pathelement location="lib-1.jar" />
...
<pathelement location="lib-n.jar" />
</classpath>
</java>
</target>
</project>
The code for the launch method may look something like this:
public final void launch(final String antScriptFile) {
/* configure Ant and execute the task */
final File buildFile = new File(antScriptFile);
final Project p = new Project();
p.setUserProperty("ant.file", buildFile.getAbsolutePath());
final DefaultLogger consoleLogger = new DefaultLogger();
consoleLogger.setErrorPrintStream(System.err);
consoleLogger.setOutputPrintStream(System.out);
consoleLogger.setMessageOutputLevel(Project.MSG_INFO);
p.addBuildListener(consoleLogger);
try {
p.fireBuildStarted();
p.init();
final ProjectHelper helper = ProjectHelper.getProjectHelper();
p.addReference("ant.projectHelper", helper);
helper.parse(p, buildFile);
p.executeTarget(p.getDefaultTarget());
p.fireBuildFinished(null);
} catch (final BuildException e) {
p.fireBuildFinished(e);
}
/* exit the current VM */
System.exit(0);
}
A very convenient thing here is that the same script is used for initial application start up as well as for restarts.
Old question and all of that. But this is yet another way that offers some advantages.
On Windows, you could ask the task scheduler to start your app again for you. This has the advantage of waiting a specific amount of time before the app is restarted. You can go to task manager and delete the task and it stops repeating.
SimpleDateFormat hhmm = new SimpleDateFormat("kk:mm");
Calendar aCal = Calendar.getInstance();
aCal.add(Calendar.SECOND, 65);
String nextMinute = hhmm.format(aCal.getTime()); //Task Scheduler Doesn't accept seconds and won't do current minute.
String[] create = {"c:\\windows\\system32\\schtasks.exe", "/CREATE", "/F", "/TN", "RestartMyProg", "/SC", "ONCE", "/ST", nextMinute, "/TR", "java -jar c:\\my\\dev\\RestartTest.jar"};
Process proc = Runtime.getRuntime().exec(create, null, null);
System.out.println("Exit Now");
try {Thread.sleep(1000);} catch (Exception e){} // just so you can see it better
System.exit(0);
System.err.println("Someone is Restarting me...");
setVisible(false);
try {
Thread.sleep(600);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
setVisible(true);
I guess you don't really want to stop the application, but to "Restart" it. For that, you could use this and add your "Reset" before the sleep and after the invisible window.

Launching java classes via windows drag-and-drop

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?

Categories

Resources