如何通过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
相关产品推荐
相关产品推荐

