Process 详解 1

Process 简述

  • public abstract class Processextends Object
  • ProcessBuilder.start 和 Runtime.exec  方法都可以开启一个本机进程,并返回 Process 子类的一个实例,Process实例可控制进程并获得相关信息。
  • Process 类提供了执行从进程输入、执行输出到进程、等待进程完成、检查进程的退出状态以及销毁(杀掉)进程的方法。 
  • 从JDK 1.5开始, ProcessBuilder.start()是创建Process的首选方式。 

获取子进程输入流

  • public abstract InputStream getInputStream() :获取子进程的输入流
  • 输入流获得由该 Process 对象表示的进程的标准输出流。 
  • 实现注意事项:对输入流进行缓冲是一个好主意。 
  • 返回连接到子进程正常输出的输入流。
public static void main(String[] args) {
    try {
        Runtime runtime = Runtime.getRuntime();
        Process process = runtime.exec("cmd /c dir %JAVA_HOME%",null);
        InputStream inputStream = process.getInputStream();
        InputStreamReader streamReader = new InputStreamReader(inputStream,"gbk");
        BufferedReader bufferedReader = new BufferedReader(streamReader);
        String readLine ;
        while ((readLine = bufferedReader.readLine())!=null){
            System.out.println(readLine);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}
  • runtime.exec("cmd /c dir %JAVA_HOME%",null),可以直接获取windows系统的环境变量值,输出结果如下:
 驱动器 D 中的卷是 program
 卷的序列号是 000C-81F5
 D:\Java\jdk1.8.0_101 的目录
2017/07/29 周六  10:42    <DIR>          .
2017/07/29 周六  10:42    <DIR>          ..
2017/07/29 周六  10:40    <DIR>          bin
2016/06/22 周三  01:42             3,244 COPYRIGHT
2017/07/29 周六  10:40    <DIR>          db
2017/07/29 周六  10:40    <DIR>          include
2017/07/29 周六  10:40         5,090,294 javafx-src.zip
2017/07/29 周六  10:40    <DIR>          jre
2017/07/29 周六  10:40    <DIR>          lib
2017/07/29 周六  10:40                40 LICENSE
2017/07/29 周六  10:40               159 README.html
2017/07/29 周六  10:40               528 release
2016/06/22 周三  01:42        21,251,669 src.zip
2017/07/29 周六  10:40           110,114 THIRDPARTYLICENSEREADME-JAVAFX.txt
2017/07/29 周六  10:40           177,094 THIRDPARTYLICENSEREADME.txt
               8 个文件     26,633,142 字节
               7 个目录 296,898,805,760 可用字节

Process finished with exit code 0

杀死子进程

  • public abstract void destroy() :杀掉子进程,强制终止此 Process 对象表示的子进程。 
  • public Process destroyForcibly():杀死子进程,此Process对象表示的子进程被强制终止。 此方法的默认实现调用destroy() ,因此可能不会强制终止进程。 强烈建议使用此类的具体实现来使用兼容实现覆盖此方法。这是1.8新增的方法,也是推荐方式
  • public boolean isAlive():测试这个Process代表的子 进程是否存活。 为true则表示此 Process对象表示的子进程尚未终止。 

不加cmd直接打开

  • 如下所示,Runtime 调用本地的PotPlayer程序播放了一个视频,返回Process对象,Process就代表着打开的PotPlayer子进程
  • 休眠5秒之后判断PotPlayer子进程是否活着,如果活着则杀死它,即关闭PotPlayer程序
  • 对于cmdStr这种没有加前缀"cmd /c"的写法,在杀死子进程时,是没有问题的,因为打开的时候,Process就直接记录的是PotPlayer.exe程序
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by Administrator on 2018/6/27 0027.
 * 杀进程测试类
 */
public class ProcessTest {
    public static void main(String[] args) {
        try {
            String cmdStr = "D:\\PotPlayer\\PotPlayerMini.exe E:/wmx/zl2.mp4";
            Runtime runtime = Runtime.getRuntime();
            Process process = runtime.exec(cmdStr);
            Thread.sleep(5000);
            if (process.isAlive()){
                process.destroy();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

加cmd打开程序

错误分析

  • cmdStr1、cmdStr2 :对于exe程序是不用加"cmd  /c"前缀就可以直接打开的,这种方式打开时,Process的destory()与destroyForcibly()方法能正常杀死子进程。而加了"cmd /c"前缀的cmdStr3、cmdStr4打开的文件或程序都是“杀不死”的
  • 然而对于其它的大部分的DOS指令是必须借助cmd.exe程序的,cmdStr3、cmdStr4中的cmd字符表示的就是windows系统目录中的程序“C:\\Windows\\System32\\cmd.exe”
  • 如下所示的cmdStr3、cmdStr4中要想打开普通的word文件,如txt、ppt、excel、pdf、png、mp4等等都是要借用cmd.exe程序才能打开的,否则会报错说找不到路径
  • cmdStr3、cmdStr4 这种直接指明参数文件,而没有指定打开此文件的程序时,默认会使用文件的关联程序进行打开,如下面的“E:/wmx/演讲稿.pptx”,如果关联的是Office则用Office打开,如果关联的是WPS,则用WPS打开
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by Administrator on 2018/6/27 0027.
 * 杀进程测试类
 */
public class ProcessTest {
    public static void main(String[] args) {
        try {
            String cmdStr1 = "D:\\PotPlayer\\PotPlayerMini.exe E:/wmx/zl2.mp4";
            String cmdStr2 = "D:\\PotPlayer\\GPaoPao.exe";
            String cmdStr3 = "cmd /c E:/wmx/演讲稿.pptx";
            String cmdStr4 = "cmd /c E:/wmx/zl2.mp4";

            Runtime runtime = Runtime.getRuntime();
            Process process = runtime.exec(cmdStr4);
            Thread.sleep(5000);
            if (process.isAlive()) {
                process.destroy();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
  • 加了"cmd  /c”前缀就表示会打开cmd.exe程序来执行后面的命令,所以cmdStr3、cmdStr4在执行之后任务管理器就会有cmd进程,于是当Process调用destory()或者destroyForcibly()杀子进程的时候,杀死的只是cmd.exe,而真正需要结束的是打开mp4文件的PotPlayer,或者是打开pptx文件的WPS程序

解决方式 1

  • 如果能准确知道打开文件的程序路径,则建议直接写上程序路径。如下所示,直接指定打开.docx文件的Office程序,这样结束子进程的时候就不会有问题了
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by Administrator on 2018/6/27 0027.
 * 杀进程测试类
 */
public class ProcessTest {
    public static void main(String[] args) {
        try {
            /** 当路径中有空格时,则应该程序和参数分开写*/
            String[] paramArr = new String[2];
            paramArr[0] = "C:\\Program Files (x86)\\Microsoft Office\\root\\Office16\\WINWORD.EXE";
            paramArr[1] = "E:\\wmx\\Map_in-depth.docx";
            Runtime runtime = Runtime.getRuntime();
            Process process = runtime.exec(paramArr);
            Thread.sleep(15000);
            if (process.isAlive()) {
                process.destroy();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

解决方式 2

  • 因为很多时候可能并不知道某个文件到底将来会用什么程序打开,如.docx文件可能是Office打开,也可能是WPS打开
  • 而且形如Office与WPS安装的路径也不一定知道或者路径不固定,此时采用杀进程树的方式来解决
  • 因为加"cmd /c"前缀使用cmd.exe程序来执行命令时,是先打开了cmd程序,然后打开的文件关联程序打开文件,后者属于cmd.exe程序的子进程,它们属于同一个进程树,而cmd提供了杀死整个进程树的命令,taskkill
  • taskkill /PID xxx /F /T:taskkill表示杀死任务,/PID表示按进程的pid进行关闭,xxx表示进程pid值,/F表示强制终止进程,/T表示终止指定的进程和由它启用的子进程。
  • 这种方式时借助JNA的API会更加方便,可以参考《JNA 理论详解 1》《JNA 实战 1
  • 使用JNA则先要导入它的两个开发包
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.WinNT;

import java.lang.reflect.Field;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;

/**
 * Created by Administrator on 2018/6/26 0026.
 * 系统工具类
 */
public class SystemUtils {

package com.lct.utils;

import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.WinNT;

import java.lang.reflect.Field;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;

/**
 * Created by Administrator on 2018/6/26 0026.
 * 系统工具类
 */
public class SystemUtils {

    /**
     * 杀死指定进程数,即包括process进程的所有子进程
     *
     * @param process
     */
    public static void killProcessTree(Process process) {
        try {
            if (process != null && process.isAlive()) {
                Field f = process.getClass().getDeclaredField("handle");
                f.setAccessible(true);
                long handl = f.getLong(process);
                Kernel32 kernel = Kernel32.INSTANCE;
                WinNT.HANDLE handle = new WinNT.HANDLE();
                handle.setPointer(Pointer.createConstant(handl));
                int ret = kernel.GetProcessId(handle);
                Long PID = Long.valueOf(ret);
                String cmd = "cmd /c taskkill /PID " + PID + " /F /T ";
                Runtime rt = Runtime.getRuntime();
                Process killPrcess = rt.exec(cmd);
                killPrcess.waitFor();
                killPrcess.destroyForcibly();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
import com.lct.utils.SystemUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by Administrator on 2018/6/27 0027.
 * 杀进程测试类
 */
public class ProcessTest {
    public static void main(String[] args) {
        try {
            String cmdStr4 = "cmd /c E:\\wmx\\Map_in-depth.docx";
            Runtime runtime = Runtime.getRuntime();
            Process process = runtime.exec(cmdStr4);
            /** 休眠5秒后关闭子进程树*/
            Thread.sleep(5000);
            SystemUtils.killProcessTree(process);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
  • 这样就能杀死打开.docx文件的程序了




猜你喜欢

转载自blog.csdn.net/wangmx1993328/article/details/80832181