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

如何通过PID在Java中获取PowerShell的当前目录?

问题描述

需要在Java代码中通过PowerShell的PID获取其当前目录:

  • 当PowerShell实例处于E:\Temp目录时,获取E:\Temp
  • 执行cd Foo后,获取E:\Temp\Foo

现有代码仅对CMD有效,对PowerShell始终返回初始目录(测试环境:Windows 7),代码如下:

import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.WinNT;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.win32.StdCallLibrary;

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


public class Test {

    public interface NtDll extends StdCallLibrary {
        NtDll INSTANCE = com.sun.jna.Native.load("ntdll", NtDll.class);

        class PROCESS_BASIC_INFORMATION extends Structure {
            public int ExitStatus;
            public Pointer PebBaseAddress;
            public IntByReference AffinityMask;
            public int BasePriority;
            public IntByReference UniqueProcessId;
            public IntByReference InheritedFromUniqueProcessId;

            @Override
            protected List<String> getFieldOrder() {
                return Arrays.asList("ExitStatus", "PebBaseAddress", "AffinityMask", "BasePriority",
                        "UniqueProcessId", "InheritedFromUniqueProcessId");
            }
        }

        int NtQueryInformationProcess(WinNT.HANDLE ProcessHandle, int ProcessInformationClass,
                PROCESS_BASIC_INFORMATION ProcessInformation, int ProcessInformationLength, IntByReference ReturnLength);
    }

    public static void main(String[] args) {
        int pid = Integer.valueOf(args[0]);
        String currentDirectory = getProcessCurrentDirectory(pid);
        System.out.println("Current Directory: " + currentDirectory);
    }

    public static String getProcessCurrentDirectory(int pid) {
        WinNT.HANDLE processHandle =
                Kernel32.INSTANCE.OpenProcess(Kernel32.PROCESS_QUERY_INFORMATION | Kernel32.PROCESS_VM_READ, false, pid);
        if (processHandle == null) {
            System.err.println("Failed to open process");
            return null;
        }

        NtDll.PROCESS_BASIC_INFORMATION pbi = new NtDll.PROCESS_BASIC_INFORMATION();
        IntByReference retLen = new IntByReference();
        NtDll.INSTANCE.NtQueryInformationProcess(processHandle, 0, pbi, pbi.size(), retLen);

        Pointer pebAddress = pbi.PebBaseAddress;
        Pointer processParametersPointer = pebAddress.share(0x20);

        Pointer rtlUserProcParamsAddress = readPointer(processHandle, processParametersPointer);
        Pointer currentDirectoryPointer = rtlUserProcParamsAddress.share(0x38);

        int directoryLength = readShort(processHandle, currentDirectoryPointer.share(0x10));
        Pointer directoryPointer = readPointer(processHandle, currentDirectoryPointer.share(0x08));

        int bufferSize = Math.max(directoryLength + 2, 256);
        Memory buffer = new Memory(bufferSize);
        Kernel32.INSTANCE.ReadProcessMemory(processHandle, directoryPointer, buffer, (int) buffer.size(), null);

        String directory = buffer.getWideString(0);
        Kernel32.INSTANCE.CloseHandle(processHandle);
        return directory;
    }

    private static Pointer readPointer(WinNT.HANDLE processHandle, Pointer address) {
        Memory buffer = new Memory(Native.POINTER_SIZE);
        Kernel32.INSTANCE.ReadProcessMemory(processHandle, address, buffer, (int) buffer.size(), null);
        return buffer.getPointer(0);
    }

    private static int readShort(WinNT.HANDLE processHandle, Pointer address) {
        Memory buffer = new Memory(2); // size of short
        Kernel32.INSTANCE.ReadProcessMemory(processHandle, address, buffer, (int) buffer.size(), null);
        return buffer.getShort(0);
    }
}
解决方案

现有代码依赖读取进程PEB(进程环境块)中的原生工作目录,但PowerShell的当前目录是运行时维护的会话级变量,并非进程的原生工作目录,所以直接读PEB的方法对PowerShell无效。以下是两种可行的实现方式:

方法1:通过执行PowerShell命令获取目标进程的当前目录

通过构造PowerShell命令,根据PID定位目标进程并获取其会话内的当前目录,Java中通过执行外部命令实现:

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class PowerShellCwdGetter {
    public static String getPowerShellCurrentDirectory(int pid) throws Exception {
        // 构造命令:通过PID获取目标PowerShell进程,执行Get-Location返回当前目录
        String cmd = String.format("powershell -Command \"Get-Process -Id %d | ForEach-Object { $_.MainModule }; Get-Location\"", pid);
        
        Process process = Runtime.getRuntime().exec(cmd);
        // 适配Windows中文环境编码,可根据系统调整
        BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), "GBK"));
        
        String line;
        boolean skipExePath = true;
        String currentDir = null;
        while ((line = reader.readLine()) != null) {
            // 跳过第一行的powershell.exe路径,第二行即为当前目录
            if (skipExePath) {
                skipExePath = false;
                continue;
            }
            currentDir = line.trim();
            break;
        }
        
        process.waitFor();
        reader.close();
        return currentDir;
    }

    public static void main(String[] args) {
        if (args.length != 1) {
            System.err.println("使用方式: java PowerShellCwdGetter <PID>");
            System.exit(1);
        }
        int pid = Integer.parseInt(args[0]);
        try {
            String cwd = getPowerShellCurrentDirectory(pid);
            System.out.println("PowerShell当前目录: " + cwd);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

注意事项

  • 运行Java程序的用户需要拥有访问目标PowerShell进程的权限
  • 编码设置为GBK适配中文Windows系统,若为英文系统可改为UTF-8

方法2:通过WMI接口查询(进阶)

可以通过JNA调用Windows WMI接口,查询Win32_Process类并执行PowerShell命令获取当前目录,这种方式不需要启动额外进程,但实现复杂度较高,适合对性能有要求的场景。

原理说明

CMD的当前目录与进程原生工作目录绑定,因此读取PEB可以得到正确结果;而PowerShell基于.NET框架开发,其当前目录存储在会话状态(SessionState)中,与进程的原生工作目录相互独立,必须通过PowerShell自身的运行时接口才能获取真实的当前目录。


内容的提问来源于stack exchange,提问作者SilverCube

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 20:27:07