1. 程式人生 > >java動態執行程式碼或者第三方程式並返回pid,殺掉程序

java動態執行程式碼或者第三方程式並返回pid,殺掉程序

java動態執行程式碼或者第三方程式並返回pid,殺掉程序

使用java動態執行Java程式碼或者呼叫第三方軟體,如下程式碼即可

Process child = Runtime.getRuntime().exec(cmd);

只要寫好cmd命令即可,如何同時返回程序的pid呢,這樣可以準確的殺掉程序,這裡我們需要一個jar用於呼叫dll

網上,maven倉庫可下載,或者下載結尾原始碼

首先把jar buildpath引入,建立一個介面

import com.sun.jna.Library;
import com.sun.jna.Native;

import constant.OtherConstant;

public interface Kernel32 extends Library {
	public static Kernel32 INSTANCE = (Kernel32) Native.loadLibrary("kernel32", Kernel32.class);

	public long GetProcessId(Long hProcess);
}

呼叫方法

// 獲取pid
	public static long getProcessPID(Process child) {
		long pid = -1;
		Field field = null;
		try {
			field = child.getClass().getDeclaredField("handle");
			field.setAccessible(true);
			pid = Kernel32.INSTANCE.GetProcessId((Long) field.get(child));
		} catch (Exception e) {
			e.printStackTrace();
		}
		return pid;
	}

接下來實戰模擬一下,首先使用runtime執行class檔案,書寫程式碼,編譯成class,然後使用runtime執行它,示例程式碼:

package example;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

public class JavaProcess1 {
	public static void main(String[] args) {
		for (int i = 0; i < 100; i++) {
			System.out.println(i);
//			appendFile("C:\\RICO\\JavaProcess1.txt", "test:  " + i);
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

	// 追加檔案
	public static boolean appendFile(String filePath, String content) {
		FileWriter fw = null;
		try {
			// 如果檔案存在,則追加內容
			// 如果檔案不存在,則建立檔案
			File f = new File(filePath);
			fw = new FileWriter(f, true);
			PrintWriter pw = new PrintWriter(fw);
			pw.println(content);
			pw.flush();
			fw.flush();
			pw.close();
			fw.close();
			return true;
		} catch (IOException e) {
			e.printStackTrace();
			return false;
		}
	}
}

如何編譯java執行class詳見:https://blog.csdn.net/rico_zhou/article/details/79873344,注意有無package名的區別

接下來建立兩個工具類,一個是用於執行cmd,一個是執行緒擷取輸出流

執行類

package utils;

import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

import constant.OtherConstant;
import constant.SpiderConstant;

public class RunUtils {
	public static Map<String, Object> messageMap = new HashMap<String, Object>();

	// 執行程式碼有兩種方式,一種是通過反射動態執行,一種是另起程序,這裡選擇另起程序
	public static void run(String cmd, int flag) {
		long pid;
		try {
			Process child = Runtime.getRuntime().exec(cmd);
			messageMap.put(SpiderConstant.SPIDER_KEYWORD_PROCESSMAP_PROCESS, child);

			// 獲取此子程序的pid,只windows系統
			pid = getProcessPID(child);
			messageMap.put(SpiderConstant.SPIDER_KEYWORD_PROCESSMAP_PID, pid);

			// 獲取程式輸入流
			// OutputStream os = child.getOutputStream();
			// 正常輸出流和異常輸出流
			InputStream stdin = child.getInputStream();
			InputStream stderr = child.getErrorStream();
			// 啟動執行緒,獲取輸出流

			if (flag == 0) {
				ConsoleSimulator cs1 = new ConsoleSimulator(stdin, 0);
				ConsoleSimulator cs2 = new ConsoleSimulator(stderr, 1);
				Thread tIn = new Thread(cs1);
				Thread tErr = new Thread(cs2);
				tIn.start();
				tErr.start();
			}

			// // 啟動執行緒獲取輸出之後不在阻塞,直接返回,列印輸出以輪播形式推送前臺
			// messageMap.put(SpiderConstant.SPIDER_KEYWORD_PROCESSMAP_INPUTSTREAMTHREAD,
			// tIn);
			// messageMap.put(SpiderConstant.SPIDER_KEYWORD_PROCESSMAP_INPUTSTREAMCONSOLE,
			// cs1);
			// messageMap.put(SpiderConstant.SPIDER_KEYWORD_PROCESSMAP_ERRORSTREAMTHREAD,
			// tErr);
			// messageMap.put(SpiderConstant.SPIDER_KEYWORD_PROCESSMAP_ERRORSTREAMCONSOLE,
			// cs2);
			// 正在執行
			messageMap.put(SpiderConstant.SPIDER_KEYWORD_PROCESSMAP_RUNSTATUS, 0);
			// int result = child.waitFor();
			// tIn.join();
			// tErr.join();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	// 阻塞
	public static String[] run2(String cmd) {
		String returnPrintContent = null;
		String returnErrorContent = null;
		String[] returnContent = new String[2];
		try {

			Process child = Runtime.getRuntime().exec(cmd);

			// 獲取程式輸入流
			OutputStream os = child.getOutputStream();
			// 正常輸出流和異常輸出流
			InputStream stdin = child.getInputStream();
			InputStream stderr = child.getErrorStream();
			// 啟動執行緒

			ConsoleSimulator cs1 = new ConsoleSimulator(stdin, 0);
			ConsoleSimulator cs2 = new ConsoleSimulator(stderr, 1);
			Thread tIn = new Thread(cs1);
			Thread tErr = new Thread(cs2);
			tIn.start();
			tErr.start();
			int result = child.waitFor();
			tIn.join();
			tErr.join();
			returnPrintContent = cs1.getReturnPrintContent();
			returnErrorContent = cs2.getReturnErrorContent();
			// 處理中文亂碼,需更改伺服器端編碼
			// 0是全部資訊
			returnContent[0] = returnPrintContent;
			// 1是錯誤資訊
			returnContent[1] = returnErrorContent;
			return returnContent;
		} catch (Exception e) {
			e.printStackTrace();
			return returnContent;
		}
	}

	// 獲取pid
	public static long getProcessPID(Process child) {
		long pid = -1;
		Field field = null;
		try {
			field = child.getClass().getDeclaredField("handle");
			field.setAccessible(true);
			pid = Kernel32.INSTANCE.GetProcessId((Long) field.get(child));
		} catch (Exception e) {
			e.printStackTrace();
		}
		return pid;
	}
}

程式碼中常量資訊請下載原始碼檢視

輸出流執行緒

package utils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;

import constant.CodingConstant;
import constant.CommonSymbolicConstant;

//另起執行緒擷取程式流
public class ConsoleSimulator implements Runnable {
	public boolean isStop = false;
	public int INFO = 0;
	public int ERROR = 1;
	public InputStream is;
	public int type;
	public StringBuilder returnPrintContent = new StringBuilder();
	public StringBuilder returnErrorContent = new StringBuilder();
	public StringBuilder returnCommonContent = new StringBuilder();

	public ConsoleSimulator(InputStream is, int type) {
		this.is = is;
		this.type = type;
	}

	public void run() {
		InputStreamReader isr = null;
		try {
			isr = new InputStreamReader(is, CodingConstant.CODING_UTF_8);
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		BufferedReader reader = new BufferedReader(isr);
		String s;
		try {
			// 此地方執行python的時候阻塞,不知為何執行緒會停止
			while ((!isStop) && (s = reader.readLine()) != null) {
				if (s.length() != 0) {
					if (type == INFO) {
						System.out.println(s);
						returnPrintContent.append(s + CommonSymbolicConstant.LINEBREAK2);
					} else {
						returnErrorContent.append(s + CommonSymbolicConstant.LINEBREAK2);
					}
				}
			}
			isStop = true;

		} catch (IOException ex) {
			ex.printStackTrace();
		} finally {
			try {
				isr.close();
				reader.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	public String getReturnPrintContent() {
		return returnPrintContent.toString();
	}

	public String getReturnErrorContent() {
		return returnErrorContent.toString();
	}

	public String getReturnCommonContent() {
		return returnCommonContent.toString();
	}

	public void stop() {
		isStop = true;
	}

}

準備工作完成,開始測試,首先測試無阻塞不獲取輸出流,注意示例程式碼執行時長為100秒,我們在執行10秒後殺掉

package runkillprocess;

import constant.CMDConstant;
import constant.SpiderConstant;
import utils.RunUtils;

//使用runtime呼叫第三方程式
public class RunProcess {
	public static void main(String[] args) {
		// 執行無阻塞呼叫
		// 子程序動態執行java,class檔案
		long pid = runJavaProcess(1);
		System.out.println(pid);

		// 十秒後殺掉程序
		try {
			Thread.sleep(10000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		stopProcess(pid);

		// 執行阻塞呼叫

	}

	public static long runJavaProcess(int flag) {
		String cmd = "java -cp C:\\RICO example.JavaProcess1";
		RunUtils.run(cmd, flag);
		// 獲取pid
		return (long) RunUtils.messageMap.get("pid");
	}

	public static void stopProcess(long pid) {
		// 拼接命令
		String cmd = "taskkill /PID " + pid + " /F";
		// 執行命令
		String[] returnContent = RunUtils.run2(cmd);
	}
}

若不殺掉程序且不獲取輸出流,則情況如下:

我們可以看到主程式執行一下就結束了,獲取了動態執行的java進行pid為11304,開啟工作管理員你會發現11304的程序依然在進行,表明我執行成功。當我們選擇10秒後殺掉程序這種情況執行程式碼,注意管理器中的java程序會在十秒後結束,殺掉成功。

如果選擇讀取輸出流則控制檯將會獲取跟cmd執行class一樣的內容。

下面我們選擇一個第三方軟體試試。呼叫notepad++開啟一個檔案,10秒後殺掉

package runkillprocess;

import constant.CMDConstant;
import constant.SpiderConstant;
import utils.RunUtils;

//使用runtime呼叫第三方程式
public class RunProcess {
	public static void main(String[] args) {
		// 執行無阻塞呼叫
		// 子程序呼叫notepad++,開啟一個檔案
		long pid = runJavaProcess(1);
		System.out.println(pid);

		// 十秒後殺掉程序
		try {
			Thread.sleep(10000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		stopProcess(pid);

		// 執行阻塞呼叫

	}

	public static long runJavaProcess(int flag) {
		String cmd = "\"C:\\Program Files (x86)\\Notepad++\\notepad++.exe\" C:\\RICO\\icon2.txt";
		RunUtils.run(cmd, flag);
		// 獲取pid
		return (long) RunUtils.messageMap.get("pid");
	}

	public static void stopProcess(long pid) {
		// 拼接命令
		String cmd = "taskkill /PID " + pid + " /F";
		// 執行命令
		String[] returnContent = RunUtils.run2(cmd);
	}
}

完美執行。

github原始碼:https://github.com/ricozhou/runkillprocess