Java JNA:PROCESSENTRY32.szExeFile returns "????..." 转换为 Java 字符串时
Java JNA: PROCESSENTRY32.szExeFile returns "????..." when converted to a Java string
我是 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?? ]
...
进程标识符是有效的,并且在快照期间当前 运行 在我的系统上,但由于某种原因,字符串已损坏。 Whosebug 上的其他几个类似示例给了我相同的结果。我是否需要在最新版本的 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;
}
}
我的代码主要基于 here.
中的 MSDN 示例
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() ) );
在主要 class 以上:
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
System:4
smss.exe:248
csrss.exe:444
csrss.exe:532
wininit.exe:540
services.exe:588
lsass.exe:596
...等等
您缺少 Native.loadLibrary
选项来告诉 JNA 自动映射到 Process32FirstW
(W32APIOptions.DEFAULT_OPTIONS
会为您完成)。查看 JNA 本身如何加载 kernel32
库。
JNA 的 platform.jar
中包含的 Process32First
的定义实际上 仅 由于定义而与 unicode (-W
) 版本一起使用PROCESSENTRY32
结构的一部分,它使用 Java char
作为文件名。你变得垃圾的原因是 "ANSI" 版本的编码字节数组已被读入 Java char
数组。 Native.toString()
正在尝试从该数组中读取数据,但没有意识到数据最初是编码字节。
我是 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?? ]
...
进程标识符是有效的,并且在快照期间当前 运行 在我的系统上,但由于某种原因,字符串已损坏。 Whosebug 上的其他几个类似示例给了我相同的结果。我是否需要在最新版本的 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;
}
}
我的代码主要基于 here.
中的 MSDN 示例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() ) );
在主要 class 以上:
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
System:4
smss.exe:248
csrss.exe:444
csrss.exe:532
wininit.exe:540
services.exe:588
lsass.exe:596
...等等
您缺少 Native.loadLibrary
选项来告诉 JNA 自动映射到 Process32FirstW
(W32APIOptions.DEFAULT_OPTIONS
会为您完成)。查看 JNA 本身如何加载 kernel32
库。
JNA 的 platform.jar
中包含的 Process32First
的定义实际上 仅 由于定义而与 unicode (-W
) 版本一起使用PROCESSENTRY32
结构的一部分,它使用 Java char
作为文件名。你变得垃圾的原因是 "ANSI" 版本的编码字节数组已被读入 Java char
数组。 Native.toString()
正在尝试从该数组中读取数据,但没有意识到数据最初是编码字节。