You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在不修改目标进程的前提下监控ProcessBuilder启动进程的文件写入路径

可以实现,以下是具体方案

你可以通过Java结合JNA调用Windows API,在不修改目标进程的前提下监控其生成的文件路径,核心思路是获取目标进程PID后,监控文件系统的创建事件并匹配进程ID。

步骤说明

  1. 获取目标进程PID:通过Java API或反射拿到你启动的子进程ID。
  2. 监控文件系统事件:用JNA调用Windows的ReadDirectoryChangesWAPI捕获文件创建事件。
  3. 匹配进程ID:对每个新创建的文件,调用Windows API获取其创建进程的PID,与目标PID匹配则记录路径。

代码实现

1. JNA接口定义(Kernel32.java)

import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.WString;
import com.sun.jna.platform.win32.WinBase;
import com.sun.jna.platform.win32.WinNT;
import com.sun.jna.ptr.IntByReference;

import java.util.Arrays;
import java.util.List;

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

    int FILE_INFO_BY_HANDLE_CLASS_FileProcessIdsUsageInformation = 9;

    WinNT.HANDLE OpenProcess(int dwDesiredAccess, boolean bInheritHandle, int dwProcessId);

    boolean ReadDirectoryChangesW(
            WinNT.HANDLE hDirectory,
            Pointer lpBuffer,
            int nBufferLength,
            boolean bWatchSubtree,
            int dwNotifyFilter,
            IntByReference lpBytesReturned,
            WinBase.OVERLAPPED lpOverlapped,
            WinBase.LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
    );

    WinNT.HANDLE CreateFileW(
            WString lpFileName,
            int dwDesiredAccess,
            int dwShareMode,
            Pointer lpSecurityAttributes,
            int dwCreationDisposition,
            int dwFlagsAndAttributes,
            WinNT.HANDLE hTemplateFile
    );

    boolean GetFileInformationByHandleEx(
            WinNT.HANDLE hFile,
            int FileInformationClass,
            Pointer lpFileInformation,
            int dwBufferSize
    );

    boolean CloseHandle(WinNT.HANDLE hObject);

    class FILE_PROCESS_IDS_USAGE_INFORMATION extends Structure {
        public long NumberOfProcessIdsInList;
        public long[] ProcessIdList = new long[1];

        @Override
        protected List<String> getFieldOrder() {
            return Arrays.asList("NumberOfProcessIdsInList", "ProcessIdList");
        }
    }
}

2. 文件监控实现(FileMonitor.java)

import com.sun.jna.platform.win32.WinNT;
import com.sun.jna.ptr.IntByReference;

import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;

public class FileMonitor {
    public static void monitorForProcess(long targetPid, String directory) {
        // 打开监控目录的句柄
        WinNT.HANDLE dirHandle = Kernel32.INSTANCE.CreateFileW(
                new WString(directory),
                WinNT.FILE_LIST_DIRECTORY,
                WinNT.FILE_SHARE_READ | WinNT.FILE_SHARE_WRITE | WinNT.FILE_SHARE_DELETE,
                null,
                WinNT.OPEN_EXISTING,
                WinNT.FILE_FLAG_BACKUP_SEMANTICS,
                null
        );

        if (dirHandle.equals(WinNT.INVALID_HANDLE_VALUE)) {
            throw new RuntimeException("无法打开监控目录句柄");
        }

        ByteBuffer buffer = ByteBuffer.allocate(4096);
        IntByReference bytesReturned = new IntByReference();

        // 后台线程持续监控
        new Thread(() -> {
            while (true) {
                boolean success = Kernel32.INSTANCE.ReadDirectoryChangesW(
                        dirHandle,
                        Pointer.nativeValue(buffer),
                        buffer.capacity(),
                        true,
                        WinNT.FILE_NOTIFY_CHANGE_FILE_NAME | WinNT.FILE_NOTIFY_CHANGE_CREATION,
                        bytesReturned,
                        null,
                        null
                );

                if (!success) break;

                buffer.position(0);
                while (buffer.remaining() > 0) {
                    int action = buffer.getInt();
                    int fileNameLength = buffer.getShort() & 0xFFFF;
                    byte[] fileNameBytes = new byte[fileNameLength];
                    buffer.get(fileNameBytes);
                    String fileName = new String(fileNameBytes, StandardCharsets.UTF_16LE).trim();
                    String fullPath = directory + "\\" + fileName;

                    // 打开文件获取创建进程PID
                    WinNT.HANDLE fileHandle = Kernel32.INSTANCE.CreateFileW(
                            new WString(fullPath),
                            WinNT.GENERIC_READ,
                            WinNT.FILE_SHARE_READ,
                            null,
                            WinNT.OPEN_EXISTING,
                            0,
                            null
                    );

                    if (!fileHandle.equals(WinNT.INVALID_HANDLE_VALUE)) {
                        Kernel32.FILE_PROCESS_IDS_USAGE_INFORMATION info = new Kernel32.FILE_PROCESS_IDS_USAGE_INFORMATION();
                        boolean infoSuccess = Kernel32.INSTANCE.GetFileInformationByHandleEx(
                                fileHandle,
                                Kernel32.FILE_INFO_BY_HANDLE_CLASS_FileProcessIdsUsageInformation,
                                info.getPointer(),
                                info.size()
                        );

                        if (infoSuccess && info.NumberOfProcessIdsInList > 0 && info.ProcessIdList[0] == targetPid) {
                            System.out.println("目标进程生成的文件路径: " + fullPath);
                        }

                        Kernel32.INSTANCE.CloseHandle(fileHandle);
                    }

                    // 缓冲区对齐处理
                    int padding = (4 - (fileNameLength % 4)) % 4;
                    if (buffer.remaining() >= padding) {
                        buffer.position(buffer.position() + padding);
                    } else {
                        break;
                    }
                }
                buffer.clear();
            }
            Kernel32.INSTANCE.CloseHandle(dirHandle);
        }).start();
    }
}

3. 主程序调用

import java.lang.reflect.Field;

public class Main {
    public static void main(String[] args) throws Exception {
        final String cmd = "your_command_here"; // 替换为实际要执行的命令
        final ProcessBuilder processBuilder = new ProcessBuilder("cmd", "/c", cmd);
        final Process process = processBuilder.start();

        // 获取子进程PID
        long pid;
        if (System.getProperty("java.version").startsWith("1.8")) {
            // Java 8及以下通过反射获取PID
            try {
                Field pidField = process.getClass().getDeclaredField("pid");
                pidField.setAccessible(true);
                pid = (long) pidField.get(process);
            } catch (Exception e) {
                throw new RuntimeException("获取PID失败", e);
            }
        } else {
            // Java 9+直接调用API
            pid = process.pid();
        }

        // 监控C盘根目录及所有子目录(可根据实际情况调整监控范围)
        FileMonitor.monitorForProcess(pid, "C:\\");

        // 等待子进程执行完成
        process.waitFor();
    }
}

注意事项

  • 权限要求:必须以管理员权限运行Java程序,否则无法正常获取进程信息或监控目录。
  • 监控范围:如果不确定目标进程的文件生成路径,建议缩小监控范围(比如用户目录、临时目录),避免性能损耗。
  • 边缘情况:若目标进程创建文件后立即删除,可能会错过捕获,可考虑结合进程的文件句柄监控优化。
  • JNA依赖:需要引入JNA及JNA Platform依赖,Maven配置示例:
    <dependency>
        <groupId>net.java.dev.jna</groupId>
        <artifactId>jna</artifactId>
        <version>5.13.0</version>
    </dependency>
    <dependency>
        <groupId>net.java.dev.jna</groupId>
        <artifactId>jna-platform</artifactId>
        <version>5.13.0</version>
    </dependency>
    

内容的提问来源于stack exchange,提问作者carrj-nm

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 22:27:03