0

I basically want to convert a command line program into a gui program using Swing. As soon as the user presses an appropriate button, a corresponding command should be passed by the GUI to the command line program. If we can do this without showing the command line program then it would be a complete replacement of that program.

I have been trying to search this on the internet for the past two days now and I only found the Runtime.getRuntime().exec(cmd) command useful to open the command prompt and open that command line program, but as the commands can't be further passed on to the prompt, no further action can be performed on that program. Please Help.

mKorbel
  • 109,525
  • 20
  • 134
  • 319
Sukhmeet Singh
  • 29
  • 1
  • 11

1 Answers1

3

I would actually avoid passing through the command prompt and directly invoke your command line program. All you have to do is to find where your program is located.

A few things to take into consideration:

  • You should execute your command outside the EDT (Event dispatching thread) to avoid GUI-freeze (SwingWorker will do a very good job at it)
  • Rather use ProcessBuilder than RuntimeExec

Here is an example of such code which invokes the command java -version (this is only for the example of using the ProcessBuilder API):

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.concurrent.ExecutionException;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.UIManager;

public class TestRuntimeExec {

    private JButton executeButton;

    protected void initUI() {
        final JFrame frame = new JFrame();
        frame.setTitle(TestRuntimeExec.class.getSimpleName());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        executeButton = new JButton("Clik me to execute command");
        executeButton.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                doWork();
            }
        });
        frame.add(executeButton, BorderLayout.SOUTH);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    protected void doWork() {
        SwingWorker<String, Void> worker = new SwingWorker<String, Void>() {
            @Override
            protected String doInBackground() throws Exception {
                ProcessBuilder builder = new ProcessBuilder(System.getProperty("java.home") + "/bin/java", "-version");
                builder.redirectErrorStream(true);
                Process process = builder.start();
                ConsoleReader consoleReader = new ConsoleReader(process.getInputStream());
                consoleReader.start();
                int waitFor = process.waitFor();
                consoleReader.join();
                switch (waitFor) {
                case 0:
                    return consoleReader.getResult();
                default:
                    throw new RuntimeException("Failed to execute " + builder.command() + " \nReturned message: "
                            + consoleReader.getResult());
                }
            }

            @Override
            protected void done() {
                try {
                    showCommandResult(get());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    e.printStackTrace();
                    showCommandError(e);
                }
            }
        };
        worker.execute();
    }

    protected void showCommandError(ExecutionException e) {
        JOptionPane.showMessageDialog(executeButton, e.getMessage(), "An error has occured", JOptionPane.ERROR_MESSAGE);
    }

    protected void showCommandResult(String commandResult) {
        JOptionPane.showMessageDialog(executeButton, commandResult);
    }

    public static void main(String[] args) {
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (Exception e) {
            e.printStackTrace();
        }
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new TestRuntimeExec().initUI();
            }
        });
    }

    public static class ConsoleReader extends Thread {
        private InputStream is;

        private StringWriter sw;

        ConsoleReader(InputStream is) {
            this.is = is;
            sw = new StringWriter();
        }

        @Override
        public void run() {
            try {
                int c;
                while ((c = is.read()) != -1) {
                    sw.write(c);
                }
            } catch (IOException e) {
                ;
            }
        }

        String getResult() {
            return sw.toString();
        }
    }
}
Guillaume Polet
  • 47,259
  • 4
  • 83
  • 117
  • Thank you sir for that help, but there is another part to it, that I need to send further commands to the same program and each command would affect the penultimate stage of the program which was created due to the last command sent. In short, I want the program to remain opened until the next command be sent through another button. – Sukhmeet Singh Jun 12 '13 at 09:19
  • @SukhmeetSingh `Process` also has an `OutputStream` (`getOutputStream()`) to which you can send data. Of course the code above needs to be reorganized to take this into account (especially the two lines `int waitFor = process.waitFor(); consoleReader.join();`). You will also need to keep a reference to the `Process` so that you can reuse it constantly every time you send a command. – Guillaume Polet Jun 12 '13 at 09:24
  • Thanx again sir. Sir whenever I try to use the ConsoleReader class in my program it says it should be declared in a file named ConsoleReader.java, I cant understand what is wrong. It is obviously a library class, but then I see that you have used it in your own program. I am a beginner here in the field of swing so pardon me and please help me. I can understand the code and how it is working though. At least tell me where to find some place on the internet or some book which I can study in detail. – Sukhmeet Singh Jun 12 '13 at 12:28
  • @SukhmeetSingh Here I have declared it as a public static inner class. It is probably better if you move it to its own file (ConsoleReader.java) and copy the code of it (just drop the `static`). – Guillaume Polet Jun 12 '13 at 12:40
  • sir I think I should clarify that I am working on a third party command line program which does its own work in a prompt. When I use builder.directory() and take the input from the InputStream through the process started, it shows only directories and thus whenever I invoke the file(.exe file) through builder.command("jtag.exe") [builder is ProcessBuilder object] it says file not found. What is going wrong over here sir? – Sukhmeet Singh Jun 13 '13 at 12:55
  • sir, I have been trying to work on the problem using ProcessBuilder but the very main thing, i.e., the execution of the .exe is not happening. As I said I have tried changing the directory and executing the file from there by giving the direct command of execution. It is not working. What I cant figure out is that how to give commands in the ProcessBuilder? What are the valid commands that we can pass in the string? [I am talking about this string ---> System.getProperty("java.home") + "/bin/java", "-version" – Sukhmeet Singh Jun 17 '13 at 03:20
  • @SukhmeetSingh You need to provide either the full path to the command (like `C:/Program Files/MyProgram.exe`, or `C:/Users/MyUser/MyFolder/MyProgram.exe`, on Windows) or make sure that the path where the program is located is set in the `PATH` environment variable. It can be either an executable or a script file. If on Unix/Linux, make sure to make it executable (`chmod a+x the_script.sh`). See also this [answer](http://stackoverflow.com/a/9368686/928711) – Guillaume Polet Jun 17 '13 at 08:07
  • sir I tried this right away and it gave me this error as soon as the process was started using the instance of ProcessBuilder ----> java.io.IOException:Cannot run program "f:/msg.vbs":CreateProcess error=193, %1 is not valid Win32 application <---- where msg.vbs is a small VB script file which works very well and is undoubtedly a win32 application. – Sukhmeet Singh Jun 17 '13 at 09:02
  • @SukhmeetSingh VBS scripts is a script, and therefore it is absolutely not a valid Win32 application. To run it, you need to pass it to the VB-script-interpreter (your command is actually `full-path-to-vbs.exe` and you have a second argument which is `f:/msg.vbs`. See also this [answer](http://stackoverflow.com/a/9577151/928711). – Guillaume Polet Jun 17 '13 at 09:09
  • sir, thank you sir, I randomly picked a file to test and that too a VBS, although I got to learn another thing which is good. But sir, the problem still remains. I tried to open Photoshop.exe using it, it executed after around 30 seconds and Photoshop showed up. When I executed the jtag.exe, nothing showed up and has not shown up for 10 minutes now. I know I wanted nothing to show up but the problem is that I haven't commanded it to do so. (Jtag.exe is a command-line program which shows up a prompt when I double click its icon) – Sukhmeet Singh Jun 17 '13 at 10:22
  • @SukhmeetSingh let's stop this discussion in comments as it is becoming way too unproductive nor useful for future readers. I would suggest that you post a new question on SO with on a specific aspect of your problem. FWIW: double-clicking an icon is far different from executing a command with ProcessBuilder. – Guillaume Polet Jun 17 '13 at 10:27