views:

853

answers:

4

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?

A: 

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.

daub815
I deleted my post, to clean up things. That was a misunderstanding becuase I haven't read the question well. Stupid me.
Adeel Ansari
+2  A: 

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.

coobird
I'll try this, thanks!
Are you sure it wouldn't run. I guess it will run, but with a few arguments. Then we can merge/concatenate all arguments back to one in our Java program. What you say?
Adeel Ansari
A: 

So there's no way to have windows itself pass the args into main() via drag and drop?

+1  A: 

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).

PhiLho