Cannot run program through a Process after downloading it (Java)

928 views Asked by At

After downloading a program (NirCmd) like so:

        URL website = new URL(
                "https://copy.com/Q4qch6FBPZkrclxG/nircmd.exe?download=1");
        ReadableByteChannel rbc = Channels.newChannel(website.openStream());
        FileOutputStream fos = new FileOutputStream("nircmd.exe");

        fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);

And then running it like so:

Process process = Runtime.getRuntime().exec("nircmd.exe speak text Hi");

But it throws this exception:

java.io.IOException: Cannot run program "nircmd.exe": CreateProcess error=32, The process cannot access the file because it is being used by another process
at java.lang.ProcessBuilder.start(Unknown Source)
at java.lang.Runtime.exec(Unknown Source)
at java.lang.Runtime.exec(Unknown Source)
at java.lang.Runtime.exec(Unknown Source)
at Main.main(Main.java:18)

Caused by: java.io.IOException: CreateProcess error=32, The process cannot access the file because it is being used by another process
at java.lang.ProcessImpl.create(Native Method)
at java.lang.ProcessImpl.<init>(Unknown Source)
at java.lang.ProcessImpl.start(Unknown Source)
... 5 more
3

There are 3 answers

1
Lucas Baizer On

Fixed! Here's how I did it:

When it ran:

    URL website = new URL(
            "https://copy.com/Q4qch6FBPZkrclxG/nircmd.exe?download=1");
    ReadableByteChannel rbc = Channels.newChannel(website.openStream());
    FileOutputStream fos = new FileOutputStream("nircmd.exe");

    fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);

It needed a:

if(new File("nircmd.exe").exists())  {
    ... code here
}
0
user2864740 On

Close the OutputStream. Otherwise (at least in Windows) it maintains an exclusive write-lock on the underlying file resource for some undetermined duration.

Streams should be handled by calling close in a deterministic manner to avoid this very problem.

If close is not called then the stream will "be closed at some undetermined time in the future when/if the finalizer runs or the program exits". As seen, this non-deterministic behavior can lead to bugs and erratic behavior.

To make life easier, because it is very tedious to correctly wrap and call close methods, a try-with-resources statement can be used:

try (
  ReadableByteChannel rbc = Channels.newChannel(website.openStream());
  FileOutputStream fos = new FileOutputStream("nircmd.exe"))
) {
    fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
}

// Now nircmd.exe can be opened because fos.close()
// is GUARANTEED to have been called (by try-with-resources)
// and the underlying file is no longer [exclusively] opened.

While it isn't as important (in this case) to close the read channel it should be done for the sake of hubris, consistency, and practice.


Addendum:

Adding in a random File.exists check does not actually 'fix' the problem - although it may trigger a side-effect that makes it "appear to work" it is unreliable code.

The file does exist or the exception which reads "[the file that does exist] is being used by another process" would be different. The problem is that the file is still opened by the current process in a non-shared mode.


Is it necessary to close each nested OutputStream and Writer separately? is a fairly good at-a-glance-read and should be a good jumping off point to related questions.

1
Peter Pan On

You can use java lib "Apache Commons IO" to done it.

My Simple Code:

URL url = new URL("https://copy.com/Q4qch6FBPZkrclxG/nircmd.exe?download=1");
InputStream input = url.openStream();
String exef= "nircmd.exe";
FileOutputStream output = new FileOutputStream(exef);
IOUtils.copy(input, output);

Class "IOUtils" is a common tool for IO stream operation in commons-io jar.