1

I need to get PowerShell current directory in Java code using PowerShell PID. For example, if there is a PowerShell instance and a user is in E:\Temp directory I need to get E:\Temp. If the user does cd Foo, I need to get E:\Temp\Foo.

This is my code:

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);
    }
}

My code works only for CMD. However, it doesn't work for PowerShell always showing same initial directory (I tested in Windows 7). Could anyone say how to do it for PowerShell, if it is possible?

7
  • 1
    "However, it doesn't work for PowerShell always showing same initial directory" - that's expected, PowerShell 2.0 doesn't update [Environment]::CurrentDirectory when you change location. Try explicitly setting [Environment]::CurrentDirectory = "C:\some\existing\path" in PowerShell and run your java program again Commented Aug 6, 2024 at 10:00
  • There's nothing to fix, it's by-design behavior - 1 host process can host multiple runspaces, so there's no unambiguous "primary working directory". Commented Aug 6, 2024 at 12:00
  • @MathiasR.Jessen ChatGPT says it was fixed in 3.0, but I can' test as I have only Win 7 as I use Linux Commented Aug 6, 2024 at 12:04
  • Why are you arguing with me if ChatGPT has all the answers? :) FWIW behavior is still the same in Windows PowerShell 5.1 and PowerShell 7.4.3 on Windows Commented Aug 6, 2024 at 12:06
  • 1
    @MathiasR.Jessen We use JediTermFX. This is a terminal emulator for Java[FX]. So, it can be used in any OS. The problem is that we can't find a way to get current directory for Windows PowerShell. For example, it can be required if a user wants to split terminal, so we need to get his current directory to create a new terminal with the same location. Commented Aug 6, 2024 at 12:20

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.