Search code examples
javawinapijna

Java JNA: PROCESSENTRY32.szExeFile returns "????..." when converted to a Java string


I'm new to JNA and upon attempting my first program to list all processes on Windows I've run into a bit of trouble. For some reason, I get the following output:

[pid = 0, name = ???????? ]
[pid = 4, name = ???????? ]
[pid = 364, name = ???????? ]
[pid = 516, name = ????e??? ]
[pid = 648, name = ?????e?? ]
[pid = 668, name = ????ee?? ]
[pid = 708, name = ???????? ]
[pid = 732, name = ????e??? ]
[pid = 740, name = ???ee??? ]
[pid = 796, name = ???????? ]
[pid = 880, name = ?????e?? ]
...

The process identifiers were valid and were currently running on my system during the snapshot, but for some reason the strings got corrupted. Several other similar examples on StackOverflow gave me the same result. Do I need to specify something new in the latest version of JNA to get such a procedure to work?

    public class Processes 
    {
        private static final Kernel32 kernel = ( Kernel32 )Native.loadLibrary( Kernel32.class );

        public static ArrayList<Process> getSnapshot( ) throws LastErrorException
        {
            ArrayList<Process> processes = new ArrayList<Process>( );
            HANDLE snapshot = null;

            try
            {
                snapshot = kernel.CreateToolhelp32Snapshot( Tlhelp32.TH32CS_SNAPPROCESS, new DWORD( 0 ) );
                PROCESSENTRY32 entry = new PROCESSENTRY32( );
                kernel.Process32First( snapshot, entry );

                do
                {
                    processes.add( new Process( Native.toString( entry.szExeFile ), entry.th32ProcessID.intValue() ) );
                }
                while( kernel.Process32Next( snapshot, entry ) );
            }
            finally
            {
                kernel.CloseHandle( snapshot );
            }

            return processes;
        }
    }

My code is based heavily off of the MSDN example featured here.


Solution

  • You're missing options to Native.loadLibrary to tell JNA to automatically map to Process32FirstW (W32APIOptions.DEFAULT_OPTIONS will do that for you). See how JNA itself loads the kernel32 library.

    The definition of Process32First included with JNA's platform.jar will actually only work with the unicode (-W) version due to the definition of the PROCESSENTRY32 structure, which uses Java char for the filename. The reason you're getting junk is that the encoded byte array of the "ANSI" version has been read into a Java char array. Native.toString() is attempting to read from that array, unaware that the data was originally encoded bytes.