Java8 要想读取和杀死进程,通过Java代码自身是无法搞定的,必须要利用JNI结合C++来做这个事情。
整个文章的代码在 https://gitee.com/zhangchao19890805/csdnBlog 上。克隆代码后进入blog185就是本文代码,其中 blog185vc 是C++代码,blog185java 是Java代码。
整个过程分成如下几步:
第一步:编写Java代码,预先定义 native 方法。
编写 ProcessDto 类,用于 C++ 向 Java 传递进程信息。
package zhangchao;
/**
* 传递进程信息的类
* @author zhangchao
*/
public class ProcessDto {
// 进程id
private Integer pid;
// 进程名称
private String name;
// 进程开始时间
private String startDateStr;
// 命令行参数
private String cmdline;
///
// setters/getters
//
@Override
public String toString() {
final StringBuffer sb = new StringBuffer("ProcessDto{");
sb.append("pid=").append(pid);
sb.append(", name='").append(name).append('\'');
sb.append(", startDateStr='").append(startDateStr).append('\'');
sb.append(", cmdline='").append(cmdline).append('\'');
sb.append('}');
return sb.toString();
}
public Integer getPid() {
return pid;
}
public void setPid(Integer pid) {
this.pid = pid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getStartDateStr() {
return startDateStr;
}
public String getCmdline() {
return cmdline;
}
public void setCmdline(String cmdline) {
this.cmdline = cmdline;
}
public void setStartDateStr(String startDateStr) {
this.startDateStr = startDateStr;
}
}
编写调用 JNI 的 ZcProcessJni 类
package zhangchao;
import java.util.ArrayList;
import java.util.List;
/**
* 调用JNI的类
*
* cd 进入D:\ws\csdnBlog\blog185\blog185java\target\classes>
* javah -jni zhangchao.ZcProcessJni 获取 zhangchao_ZcProcessJni.h 文件
*
* javap -s -p zhangchao.ZcProcessJni 获取 Java 方法的信息
*
* Compiled from "ZcProcessJni.java"
* public class zhangchao.ZcProcessJni {
* public static java.lang.String ERROR_LOAD_MESSAGE;
* descriptor: Ljava/lang/String;
* public java.util.List<zhangchao.ProcessDto> processList;
* descriptor: Ljava/util/List;
* public zhangchao.ZcProcessJni();
* descriptor: ()V
*
* public native void lookProcess();
* descriptor: ()V
*
* public native void killProcess(int);
* descriptor: (I)V
*
* private void callback(int, java.lang.String, java.lang.String, java.lang.String);
* descriptor: (ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
*
* static {};
* descriptor: ()V
* }
*
*
*
* @author zhangchao
*
*/
public class ZcProcessJni {
public static String ERROR_LOAD_MESSAGE = null;
// 存放若干进程信息
public List<ProcessDto> processList = new ArrayList<>();
/**
* 查找所有进程信息
*/
public native void lookProcess();
/**
* 杀死进程ID指定的进程
* @param pid 进程ID
*/
public native void killProcess(int pid);
/**
* C++ 调用 Java 的回调方法。用于返回查找到的进程信息。
* @param pid 进程ID
* @param name 进程名称
* @param startDateStr 进程启动时间
* @param cmdline 进程的命令行命令
*/
private void callback(int pid, String name, String startDateStr, String cmdline) {
ProcessDto dto = new ProcessDto();
dto.setPid(pid);
dto.setName(name);
dto.setStartDateStr(startDateStr);
dto.setCmdline(cmdline);
processList.add(dto);
}
static {
try {
// 加载dll文件
System.loadLibrary("blog185vc");
} catch (Throwable e) {
StringBuilder sb = new StringBuilder();
StackTraceElement[] stackTraceElements = e.getStackTrace();
if (null != stackTraceElements) {
for (StackTraceElement stackTraceElement : stackTraceElements) {
sb.append(stackTraceElement).append("\n");
}
}
ERROR_LOAD_MESSAGE = sb.toString();
}
}
}
Main 类包含 main 方法,查找视频软件 VLC 的进程,打印信息后杀死进程。
package zhangchao;
public class Main {
public static void main(String[] args) {
System.out.println("begin");
if (ZcProcessJni.ERROR_LOAD_MESSAGE != null &&
ZcProcessJni.ERROR_LOAD_MESSAGE.length() > 0) {
System.out.println("Error: " + ZcProcessJni.ERROR_LOAD_MESSAGE);
return;
}
ZcProcessJni zcProcessJni = new ZcProcessJni();
zcProcessJni.lookProcess();
if (null != zcProcessJni.processList && !zcProcessJni.processList.isEmpty()) {
for (ProcessDto item : zcProcessJni.processList) {
// 查找到视频播放软件VLC的进程,就打印出进程信息,并且杀死进程。
if ("vlc.exe".equalsIgnoreCase(item.getName())) {
System.out.println(item);
zcProcessJni.killProcess(item.getPid());
}
}
}
System.out.println("end");
}
}
第二步,利用 Java 工具为编写 C++ 代码做好准备。
编写完 Java 代码后,用 maven 的 compile 功能生成 class 文件。然后进入命令行窗口,通过 cd 命令进入 target/classes 文件夹。
执行如下命令:
javah -jni zhangchao.ZcProcessJni
classes 文件夹中会生成 zhangchao_ZcProcessJni.h 文件。
接着执行如下命令:
D:\ws\csdnBlog\blog185\blog185java\target\classes>javap -s -p zhangchao.ZcProcessJni
Compiled from "ZcProcessJni.java"
public class zhangchao.ZcProcessJni {
public static java.lang.String ERROR_LOAD_MESSAGE;
descriptor: Ljava/lang/String;
public java.util.List<zhangchao.ProcessDto> processList;
descriptor: Ljava/util/List;
public zhangchao.ZcProcessJni();
descriptor: ()V
public native void lookProcess();
descriptor: ()V
public native void killProcess(int);
descriptor: (I)V
private void callback(int, java.lang.String, java.lang.String, java.lang.String);
descriptor: (ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
static {};
descriptor: ()V
}
保存好 callback 方法的描述符 (ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
,这在 C++ 代码中会用到。
去 Java 的安装路径找到对应的 h 文件。以我电脑为例,我在 D:\program\Java\jdk1.8.0_291\include
找到 jni.h 文件。
去 D:\program\Java\jdk1.8.0_291\include\win32
找到 jni_md.h 文件。
第三步,编写 C++
打开 Visual Studio ,创建 DLL项目。项目名是 blog185vc。把 jni.h、jni_md.h、zhangchao_ZcProcessJni.h 三个文件放到 D:\ws\csdnBlog\blog185\blog185vc\lib\header 文件夹中。Visual Studio中点击【调试】,点击 blog185vc 属性调整。 在打开的对话窗中,左侧点击 【C/C++】,再点击【常规】。点击【附加目录包含】右侧向下箭头,再点击编辑,在弹出的对话窗中填写 D:\ws\csdnBlog\blog185\blog185vc\lib\header。
由于C++ 代码比较多,本文只写主要逻辑代码,读者可以阅读GIT中更加详细的代码。
#include <stdio.h>
#include<string.h>
#include <fstream>
#include "pch.h"
#include "jni.h"
#include "zhangchao_ZcProcessJni.h"
#include <windows.h>
#include "errhandlingapi.h"
#include <tlhelp32.h> //进程快照函数头文件
#include <stringapiset.h>
#include<tchar.h>
#include<Winternl.h>
#include "StringBuilder.h"
#include "Logger.h"
using namespace std;
typedef NTSTATUS(WINAPI* QT)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);//NtQueryInformationProcess
/*
写成一个函数,来获得所有的PEB结构体信息
*/
char* GetProcessCommandLine(HANDLE hProcess)
{
HMODULE hModule = 0;
QT NtQuery = { 0 };
hModule = LoadLibrary(L"Ntdll.dll"); //加载动态链接
if (hModule)
{
NtQuery = (QT)GetProcAddress(hModule, "NtQueryInformationProcess");
if (NtQuery == NULL)
return 0;
}
else
return 0;
PROCESS_BASIC_INFORMATION pi = { 0 };
NTSTATUS re = NtQuery(hProcess,
ProcessBasicInformation, &pi, sizeof(pi), NULL);
/*
在pi中,pi.PebBaseAddress指向PEB,那么这个指针指向的地址是哪个地址呢?
这个指针指向的地址,是word进程的地址
*/
if (!NT_SUCCESS(re))
{
// _tprintf(L"OK\n");
return 0;
}
PEB peb;
RTL_USER_PROCESS_PARAMETERS para;
ReadProcessMemory(hProcess, pi.PebBaseAddress, &peb, sizeof(peb), NULL); //将pi.PebBaseAddress的信息读取到peb中
ReadProcessMemory(hProcess, peb.ProcessParameters, ¶, sizeof(para), NULL);
wchar_t* CommandLine = new wchar_t[1024];
ReadProcessMemory(hProcess, para.CommandLine.Buffer, CommandLine, 1024 * 2, NULL);
int iSize = WideCharToMultiByte(CP_ACP, 0, CommandLine, -1, NULL, 0, NULL, NULL);
char* cmdline = new char[iSize + 10];
WideCharToMultiByte(CP_ACP, 0, CommandLine, -1, cmdline, iSize, NULL, NULL);
FreeLibrary(hModule);
return cmdline;
}
/*
* Class: zhangchao_ZcProcessJni
* Method: lookProcess
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_zhangchao_ZcProcessJni_lookProcess(JNIEnv* env, jobject obj)
{
// 获取Java对象中方法的ID
jclass cls = env->GetObjectClass(obj);
jmethodID mid = env->GetMethodID(cls, "callback", "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
if (NULL == mid) {
return;
}
PROCESSENTRY32W currentProcess; //存放快照进程信息的一个结构体
currentProcess.dwSize = sizeof(currentProcess); //在使用这个结构之前,先设置它的大小
HANDLE hProcess = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);//给系统内的所有进程拍一个快照
if (hProcess == INVALID_HANDLE_VALUE)
{
printf("CreateToolhelp32Snapshot()调用失败!\n");
return;
}
bool bMore = Process32FirstW(hProcess, ¤tProcess); //获取第一个进程信息
while (bMore)
{
HANDLE process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, currentProcess.th32ProcessID);
if (process != NULL) {
FILETIME ftCreation, ftExit, ftKernel, ftUser;
SYSTEMTIME stCreation, lstCreation;
if (GetProcessTimes(process, &ftCreation, &ftExit, &ftKernel, &ftUser)) {
FileTimeToSystemTime(&ftCreation, &stCreation);
SystemTimeToTzSpecificLocalTime(NULL, &stCreation, &lstCreation);
char nameArr[256];
int iSize = WideCharToMultiByte(CP_ACP, 0, currentProcess.szExeFile, -1, NULL, 0, NULL, NULL);
WideCharToMultiByte(CP_ACP, 0, currentProcess.szExeFile, -1, nameArr, iSize, NULL, NULL);
jstring nameStr = env->NewStringUTF(nameArr);
char timeArr[128];
sprintf_s(timeArr, "%4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d", lstCreation.wYear, lstCreation.wMonth, lstCreation.wDay,
lstCreation.wHour, lstCreation.wMinute, lstCreation.wSecond);
jstring timeStr = env->NewStringUTF(timeArr);
char* cmdline = GetProcessCommandLine(process);
jstring jsCmdline = env->NewStringUTF(cmdline);
env->CallVoidMethod(obj, mid, currentProcess.th32ProcessID, nameStr, timeStr, jsCmdline);
}
CloseHandle(process);
}
else {
//wcout << "PID=" << currentProcess.th32ProcessID << " PName=" << currentProcess.szExeFile << endl;
}
bMore = Process32NextW(hProcess, ¤tProcess); //遍历下一个
}
CloseHandle(hProcess); //清除hProcess句柄
}
/*
* Class: zhangchao_ZcProcessJni
* Method: killProcess
* Signature: (I)V
*/
JNIEXPORT void JNICALL Java_zhangchao_ZcProcessJni_killProcess(JNIEnv* env, jobject obj, jint pid)
{
unique_ptr<Logger> logger(new Logger());
unique_ptr<StringBuilder> sb{ new StringBuilder() };
logger->info("BEGIN killProcess");
PROCESSENTRY32W currentProcess; //存放快照进程信息的一个结构体
currentProcess.dwSize = sizeof(currentProcess); //在使用这个结构之前,先设置它的大小
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);//给系统内的所有进程拍一个快照
if (hSnapshot == INVALID_HANDLE_VALUE)
{
logger->info("CreateToolhelp32Snapshot()调用失败!");
return;
}
bool bMore = Process32FirstW(hSnapshot, ¤tProcess); //获取第一个进程信息
while (bMore) {
// HANDLE process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, currentProcess.th32ProcessID);
// 各种进程权限 https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocess
HANDLE process = OpenProcess(PROCESS_TERMINATE, FALSE, currentProcess.th32ProcessID);
if (process != NULL) {
if (pid == currentProcess.th32ProcessID) {
int terminateFlag = TerminateProcess(process, 0);
if (terminateFlag) {
logger->info(sb->clear()->append("Success TerminateProcess(process, 0); pid=")->append(pid));
}
else {
logger->info(sb->clear()->append("Fail TerminateProcess(process, 0); pid=")->append(pid));
}
}
CloseHandle(process);
}
else {
logger->info(sb->clear()->append("OpenProcess failed. process == NULL. pid=")->
append(pid)->append(" GetLastError()=")->append(GetLastError()));
}
bMore = Process32NextW(hSnapshot, ¤tProcess); //遍历下一个
}
CloseHandle(hSnapshot);
logger->info("END killProcess");
}