Ich bin neu bei JNA und beim Versuch, mit meinem ersten Programm alle Prozesse unter Windows aufzulisten, bin ich auf einige Probleme gestoßen. Aus irgendeinem Grund erhalte ich folgende Ausgabe:

[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?? ]
...

Die Prozesskennungen waren gültig und wurden derzeit während des Snapshots auf meinem System ausgeführt, aber aus irgendeinem Grund wurden die Zeichenfolgen beschädigt. Mehrere andere ähnliche Beispiele für StackOverflow haben zu demselben Ergebnis geführt. Muss ich in der neuesten Version von JNA etwas Neues angeben, damit ein solches Verfahren funktioniert?

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

Mein Code basiert stark auf dem MSDN-Beispiel hier.

4
Christopher O'Toole 1 Jän. 2016 im 07:03

2 Antworten

Beste Antwort

Es fehlen Optionen für Native.loadLibrary, um JNA anzuweisen, automatisch Process32FirstW zuzuordnen (W32APIOptions.DEFAULT_OPTIONS erledigt dies für Sie). Sehen Sie, wie JNA selbst die kernel32 - Bibliothek lädt.

Die in platform.jar von JNA enthaltene Definition von Process32First funktioniert aufgrund der Definition der PROCESSENTRY32 -Struktur nur mit der Unicode-Version (-W) , die Java char als Dateinamen verwendet. Der Grund, warum Sie Junk bekommen, ist, dass das codierte Byte-Array der "ANSI" -Version in ein Java char -Array eingelesen wurde. Native.toString() versucht, aus diesem Array zu lesen, ohne zu wissen, dass die Daten ursprünglich in Bytes codiert wurden.

1
technomage 3 Jän. 2016 im 13:11

Die JNA verwendet Process32First \ Next which ist die ANSI-Version, aber Sie müssen die Unicode- oder UTF-16LE-Version verwenden, die Process32FirstW \ NextW ist. Dies ist wahrscheinlich ein Fehler in JNA, da die Unicode-Version von PROCESSENTRY32 verwendet wird und {{X1} erwartet wird } für szExeFile als UTF-16LE

Sie können Kernel32 als solches erweitern:

Kernel32.java:

import com.sun.jna.Native;
import com.sun.jna.platform.win32.Tlhelp32;

public interface Kernel32 extends com.sun.jna.platform.win32.Kernel32 {
    Kernel32 INSTANCE = (Kernel32)Native.loadLibrary("kernel32", Kernel32.class, com.sun.jna.win32.W32APIOptions.DEFAULT_OPTIONS);

    boolean Process32FirstW(HANDLE hSnapshot, Tlhelp32.PROCESSENTRY32 lppe);
    boolean Process32NextW(HANDLE hSnapshot, Tlhelp32.PROCESSENTRY32 lppe);

}

Ändern Sie Processes.java als solches:

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

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


Ursprüngliche Antwort mit kläglich aufgegebenem und veraltetem ANSI:
Versuchen

import java.util.ArrayList;

import com.sun.jna.LastErrorException;
import com.sun.jna.Native;
import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.Tlhelp32;
import com.sun.jna.platform.win32.Tlhelp32.PROCESSENTRY32;
import com.sun.jna.platform.win32.WinDef.DWORD;
import com.sun.jna.platform.win32.WinNT.HANDLE;



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

        static class Process{
            public String pName;
            public int pID;
            Process(String pName,int pID){
                this.pName = pName;
                this.pID = pID;
            }
        }

        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
                {
                    byte[] bytes = new byte[entry.szExeFile.length*2];
                    for(int i=0;i<entry.szExeFile.length;i++) {
                       bytes[i*2+1] = (byte) (entry.szExeFile[i] >> 8);
                       bytes[i*2] = (byte) entry.szExeFile[i];
                    }
                    processes.add( new Process( Native.toString( bytes, "ANSI" ), entry.th32ProcessID.intValue() ) );
                }
                while( kernel.Process32Next( snapshot, entry ) );
            }
            finally
            {
                kernel.CloseHandle( snapshot );
            }

            return processes;
        }
    }

Die einzige Änderung besteht darin, char [] in Byte [] zu konvertieren, damit "ANSI" angegeben werden kann.

byte[] bytes = new byte[entry.szExeFile.length*2];
for(int i=0;i<entry.szExeFile.length;i++) {
   bytes[i*2+1] = (byte) (entry.szExeFile[i] >> 8);
   bytes[i*2] = (byte) entry.szExeFile[i];
}
processes.add( new Process( Native.toString( bytes, "ANSI" ), entry.th32ProcessID.intValue() ) );

Mit obiger Klasse in Main:

public static void main(String[] args) {

    ArrayList<Processes.Process> curProcesses = Processes.getSnapshot();
    for(Processes.Process curP : curProcesses){
        System.out.println(curP.pName + ":" + curP.pID);
    }
}

Ich bekomme:

[Systemprozess]: 0
System: 4
smss.exe: 248
csrss.exe: 444
csrss.exe: 532
wininit.exe: 540
services.exe: 588
lsass.exe: 596
...usw

1
Motes 1 Jän. 2016 im 22:39