14

I am looking for a Java tool/package/library that will allow me to force-kill a child process.

This tool/package/library must work on Windows platform (mandatory). Support for Linux/Unix is desired.

My Problem

My Java code creates a child process that will simply not react to the standard Java way for killing a child process: process.destroy(), and, since I do not have the child's source code, I cannot program it to better handle termination requests.

I have tried closing the child process' error input and output stream before calling destroy(), and for no effect.

I have even tried passing ctrlBreak signal (char=3) directly into child.getOutputStream(), and again have received the same results.

The workaround I have finally managed to find was to:

  1. Obtain the child's PID upon its creation This can be done in Windows by diffing the process lists before and after the child's creation (getRuntime().exec("tasklist /v"))

  2. Use the child's PID to issue a force kill system command
    in Windows: getRuntime().exec("taskkill /pid " + childPid + " /f")

But - this is complex code I have no desire to debug and maintain, plus the problem itself, I have no doubt, was previously encountered by many other java developers, which leads me to the hope that such a Java tool/package/library already exists.

I just don't know its name...

PS: My child process was created by Runtime.getRuntime().exec(cmd), but I get the same behaviour using a ProcessBuilder.

nwalke
  • 3,170
  • 6
  • 35
  • 60
Gilad Haimov
  • 5,767
  • 2
  • 26
  • 30
  • @user605111: if you want to *force* it, then what you're doing isn't bad. Java's *getRuntime().exec* is **guaranteed** to let you call native processes. On Un*xes a *kill -9* **guarantees** the instant death and releases of all the process's resources (or the Un*x is broken). I don't know about Windows' *taskkill* guarantees but executing *taskkill* on Windows and *kill -9* on all Un*xes (including OS X) ain't exactly a long piece of code to write nor to maintain. – SyntaxT3rr0r Feb 06 '11 at 13:29
  • @user605111: btw, **never ever** pass a String containing whitespaces to *getRuntime().exec*. Split on spaces yourselft and invoke the *getRuntime().exec(String[] array)* method (the one that accepts a String array). Otherwise you'll have a lot of unbelievable headaches on various Windows/Un*x/JVM versions. Make your call: *getRuntime().exec(("taskkill /pid " + childPid + " /f").split("[ ]++"))* and life is going to get better :) – SyntaxT3rr0r Feb 06 '11 at 13:32

3 Answers3

9

There is a leaner way to do this using Java JNA.

This works definitely for Windows and Linux, i assume that you can do the same for other platforms too.

The biggest problem of Java process handling is the lack of a method to get the process id of the process started with untime.getRuntime().exec().

Assuming you got the pid of a process, you always can start a kill -9 command in linux, or use similar ways to kill a process in windows.

Here is a way to get the process id natively for linux (borrowed from the selenium framework, :) ), and with the help of JNA this also can be done for windows (using native Windows API calls).

For this to work (for Windows) you first have to get the JNA Library at JAVA NATIVE ACCESS (JNA): Downloads or get it from maven

Look at the following code, which will get the pid of a (in this example windows) program (most of the code is actually debris to get a working java program going):

import com.sun.jna.*;
import java.lang.reflect.Field;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Main {

static interface Kernel32 extends Library {

    public static Kernel32 INSTANCE = (Kernel32) Native.loadLibrary("kernel32", Kernel32.class);

    public int GetProcessId(Long hProcess);
}

public static void main(String[] args) {
    try {
        Process p;

        if (Platform.isWindows())
            p = Runtime.getRuntime().exec("cmd /C ping msn.de");
        else if (Platform.isLinux())
            p = Runtime.getRuntime().exec("cmd /C ping msn.de");

        System.out.println("The PID: " + getPid(p));

        int x = p.waitFor();
        System.out.println("Exit with exitcode: " + x);

    } catch (Exception ex) {
        Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
    }
}

public static int getPid(Process p) {
    Field f;

    if (Platform.isWindows()) {
        try {
            f = p.getClass().getDeclaredField("handle");
            f.setAccessible(true);
            int pid = Kernel32.INSTANCE.GetProcessId((Long) f.get(p));
            return pid;
        } catch (Exception ex) {
            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
        }
    } else if (Platform.isLinux()) {
        try {
            f = p.getClass().getDeclaredField("pid");
            f.setAccessible(true);
            int pid = (Integer) f.get(p);
            return pid;
        } catch (Exception ex) {
            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    else{}
    return 0;
}
}

Hope this helps, ;)...

Reinhold
  • 546
  • 1
  • 3
  • 14
Stan
  • 106
  • 1
  • 2
  • For windows, you can do kill -9 with another Windows API call: BOOL WINAPI TerminateProcess(HANDLE hProcess,UINT uExitCode); – Stan May 17 '11 at 15:03
  • Very nice. Tested on Windows and it works. Little obvious bug: in getPid you never returns linux pid. – rafalmag Nov 01 '11 at 12:59
  • newer versions of JNA for Windows have undergone some changes, so now it looks smth. like: f = p.getClass().getDeclaredField("handle"); f.setAccessible(true); long hndl = f.getLong(p); Kernel32 kernel = Kernel32.INSTANCE; WinNT.HANDLE handle = new WinNT.HANDLE(); handle.setPointer(Pointer.createConstant(hndl)); pid = kernel.GetProcessId(handle); – Volodymyr Metlyakov Jan 14 '16 at 11:26
2

I had solved such problem in past using the same method you are suggesting here: use taskkill for windows and kill -9 for Unix.

On windows you can use alternatively WMI by either invoking script (VBS or JS) from Java or using one of interoperability libraries (JaWin, Jintegra, Jinterop etc.)

I do not think that this solution is so complicated as you are afraid. I think it is not more than 50 code lines.

Good luck.

AlexR
  • 114,158
  • 16
  • 130
  • 208
1

For windows using jna 3.5.1

try {
        Process p = Runtime.getRuntime.exec("notepad");

        Field f = p.getClass().getDeclaredField("handle");
        f.setAccessible(true);              
        long handLong = f.getLong(p);

            Kernel32 kernel = Kernel32.INSTANCE;

        WinNT.HANDLE handle = new WinNT.HANDLE();

        handle.setPointer(Pointer.createConstant(handLong));

        int pid = kernel.GetProcessId(handle);

        System.out.print(pid);
    } catch (Throwable e) {
}
Guillermo81
  • 189
  • 11