Java JNA:PROCESSENTRY32.szExeFile返回" ???? ..."转换为Java字符串时

时间:2016-01-01 04:03:42

标签: java winapi jna

我是JNA的新手,在尝试我的第一个程序列出Windows上的所有进程时,我遇到了一些麻烦。出于某种原因,我得到以下输出:

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

进程标识符有效,并且在快照期间当前正在我的系统上运行,但由于某种原因,字符串已损坏。 StackOverflow上的其他几个类似的例子给了我相同的结果。我是否需要在最新版本的JNA中指定新的东西以使这样的程序起作用?

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

我的代码主要基于MSDN示例here

2 个答案:

答案 0 :(得分:1)

JNA使用Process32First\Next which is the ANSI version but you need to use the Unicode or UTF-16LE version which is Process32FirstW\NextW.这可能是JNA中的一个错误,因为它使用PROCESSENTRY32的Unicode版本,期望TCHAR szExeFile为UTF-16LE

你可以这样扩展Kernel32:

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

}

改变Processes.java:

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


原始答案与可悲的遗弃和过时的ANSI:
尝试

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

真正唯一的变化是将char []转换为byte [],因此可以指定“ANSI”。

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

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

我明白了:

[系统流程]:0
系统:4
SMSS.EXE:248
CSRSS.EXE:444
CSRSS.EXE:532
Wininit.exe将:540
Services.exe的:588
LSASS.EXE:596
...等

答案 1 :(得分:1)

您遗漏了Native.loadLibrary的选项,告诉JNA自动映射到Process32FirstWW32APIOptions.DEFAULT_OPTIONS会为您执行此操作)。了解JNA如何加载kernel32库。

由于{{1}的定义,JNA Process32First中包含的platform.jar的定义实际上与unicode(-W)版本一起使用结构,它使用Java PROCESSENTRY32作为文件名。您遇到垃圾的原因是“ANSI”版本的编码字节数组已被读入Java char数组。 char试图从该数组中读取数据,而不知道数据最初是编码的字节。