Java 使用 JNA(Java Native Access) 调用 Windows API 您所在的位置:网站首页 sai未能调用windows的功能 Java 使用 JNA(Java Native Access) 调用 Windows API

Java 使用 JNA(Java Native Access) 调用 Windows API

2023-08-10 11:24| 来源: 网络整理| 查看: 265

博文目录

文章目录 JNA 调用 Windows API 真的非常简单环境使用DemoJNA Platform Demo, CreateToolhelp32Snapshot function (tlhelp32.h)JNA Demo, PrivateExtractIconsW function (winuser.h)

JNA 调用 Windows API 真的非常简单 环境

JNA GitHub JNA-Platform GitHub Readme

JNA GitHub GettingStarted

net.java.dev.jna jna 5.11.0 net.java.dev.jna jna-platform 5.11.0

jna 包是核心库, jna-platform 是对 Win32 常用 api 的大量封装, 我们常用的 api 在 jna-platform 里基本上都能找到, 一般引入该包即可

使用 JNA: This is the core artifact of JNA and contains only the binding library and the core helper classes.JNA Platform: This artifact holds cross-platform mappings and mappings for a number of commonly used platform functions, including a large number of Win32 mappings as well as a set of utility classes that simplify native access. The code is tested and the utility interfaces ensure that native memory management is taken care of correctly.

通过阅读 GettingStarted.md 了解了 JNA 大概的用法

定义一个继承自 com.sun.jna.Library 的接口(如 MyInterface)在该接口里定义一个自身类型的属性(如 INSTANCE), 通过 MyInterface INSTANCE = Native.load("库名字", MyInterface.class) 的方式赋值在该接口里定义要使用的库的方法函数(如 foo()), 注意参数对应在其他地方通过 MyInterface.INSTANCE.foo(); 来调用对应方法 package com.sun.jna.examples; import com.sun.jna.Library; import com.sun.jna.Native; import com.sun.jna.Platform; /** Simple example of JNA interface mapping and usage. */ public class HelloWorld { // This is the standard, stable way of mapping, which supports extensive // customization and mapping of Java to native types. public interface CLibrary extends Library { CLibrary INSTANCE = (CLibrary) Native.load((Platform.isWindows() ? "msvcrt" : "c"), CLibrary.class); void printf(String format, Object... args); } public static void main(String[] args) { CLibrary.INSTANCE.printf("Hello, World\n"); for (int i=0;i HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (INVALID_HANDLE_VALUE == hSnap) { return 0; } PROCESSENTRY32 pe = {sizeof(PROCESSENTRY32)}; for (BOOL ret = Process32First(hSnap, &pe); ret; ret = Process32Next(hSnap, &pe)) { if (strcmp(pe.szExeFile, processName) == 0) { CloseHandle(hSnap); return pe.th32ProcessID; } // cout WinNT.HANDLE handle = Kernel32.INSTANCE.CreateToolhelp32Snapshot(Tlhelp32.TH32CS_SNAPPROCESS, new WinDef.DWORD(0)); if (INVALID_HANDLE_VALUE == handle) { return 0; } return 0; }

两个 HANDLE 对象, 不是简单数据类型, 不能通过 == 来判断是否相同, 全局搜索发现好多 Utils 里面有如下的用法

HANDLE h = Kernel32.INSTANCE.FindFirstVolume(volumeUUID, 50); if (h == null || h.equals(WinBase.INVALID_HANDLE_VALUE)) { throw new Win32Exception(Native.getLastError()); }

我们也学一学

public static int getProcessIdByProcessName(String processName) { WinNT.HANDLE handle = Kernel32.INSTANCE.CreateToolhelp32Snapshot(Tlhelp32.TH32CS_SNAPPROCESS, new WinDef.DWORD(0)); if (null == handle || handle.equals(WinBase.INVALID_HANDLE_VALUE)) { throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); } return 0; }

继续, 发现 c/c++ 里有使用 sizeof 函数, java 里该怎么做呢, 经过一番百度和全局搜索, 发现在 Kernel32Util 里有 CreateToolhelp32Snapshot 函数的封装, 是用来获取进程中加载的模块的, 正好, 我们来参考下

// com.sun.jna.platform.win32.Kernel32Util#getModules public static List getModules(int processID) { HANDLE snapshot = Kernel32.INSTANCE.CreateToolhelp32Snapshot(Tlhelp32.TH32CS_SNAPMODULE, new DWORD(processID)); if (snapshot == null) { throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); } Win32Exception we = null; try { Tlhelp32.MODULEENTRY32W first = new Tlhelp32.MODULEENTRY32W(); if (!Kernel32.INSTANCE.Module32FirstW(snapshot, first)) { throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); } List modules = new ArrayList(); modules.add(first); Tlhelp32.MODULEENTRY32W next = new Tlhelp32.MODULEENTRY32W(); while (Kernel32.INSTANCE.Module32NextW(snapshot, next)) { modules.add(next); next = new Tlhelp32.MODULEENTRY32W(); } int lastError = Kernel32.INSTANCE.GetLastError(); // if we got a false from Module32Next, // check to see if it returned false because we're genuinely done // or if something went wrong. if (lastError != W32Errors.ERROR_SUCCESS && lastError != W32Errors.ERROR_NO_MORE_FILES) { throw new Win32Exception(lastError); } return modules; } catch (Win32Exception e) { we = e; throw we; // re-throw so finally block is executed } finally { try { closeHandle(snapshot); } catch(Win32Exception e) { if (we == null) { we = e; } else { we.addSuppressedReflected(e); } } if (we != null) { throw we; } } }

修改我们的代码并做优化如下

public static int getProcessIdByProcessName(String processName) { WinNT.HANDLE handle = Kernel32.INSTANCE.CreateToolhelp32Snapshot(Tlhelp32.TH32CS_SNAPPROCESS, new WinDef.DWORD(0)); if (null == handle || handle.equals(WinBase.INVALID_HANDLE_VALUE)) { throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); } Tlhelp32.PROCESSENTRY32 pe = new Tlhelp32.PROCESSENTRY32(); try { for (boolean success = Kernel32.INSTANCE.Process32First(handle, pe); success; success = Kernel32.INSTANCE.Process32Next(handle, pe)) { if (processName.equals(pe.szExeFile)) { Kernel32.INSTANCE.CloseHandle(handle); return pe.th32ProcessID.intValue(); } } } catch (Throwable cause) { throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); } Kernel32.INSTANCE.CloseHandle(handle); throw new RuntimeException("Process Not Exist"); }

发现 pe.szExeFile 不是 String, 而是 char[], 所以做一下转换 processName.equals(new String(pe.szExeFile)), 尝试运行发现全报 进程不存在, 不科学, 打印进程名字, 发现转换后的 String 有问题, 系统进程名字已经拿到了, 只是显示有问题, 再查代码发现, pe.szExeFile 的定义居然是长度 260, 怪不得, 看来还得处理下 在这里插入图片描述

// com.sun.jna.platform.win32.Tlhelp32.PROCESSENTRY32#szExeFile public char[] szExeFile = new char[WinDef.MAX_PATH]; // com.sun.jna.platform.win32.WinDef#MAX_PATH int MAX_PATH = 260;

最终的方法如下, 找到 pe.szExeFile 中的第一个 0, 然后使用 public String(char value[], int offset, int count) 截取部分并转换

public static int getProcessIdByProcessName(String processName) { WinNT.HANDLE handle = Kernel32.INSTANCE.CreateToolhelp32Snapshot(Tlhelp32.TH32CS_SNAPPROCESS, new WinDef.DWORD(0)); if (null == handle || handle.equals(WinBase.INVALID_HANDLE_VALUE)) { throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); } Tlhelp32.PROCESSENTRY32 pe = new Tlhelp32.PROCESSENTRY32(); try { for (boolean success = Kernel32.INSTANCE.Process32First(handle, pe); success; success = Kernel32.INSTANCE.Process32Next(handle, pe)) { int index = pe.szExeFile.length - 1; for (int i = 0; i index = i; break; } } if (processName.equals(new String(pe.szExeFile, 0, index))) { Kernel32.INSTANCE.CloseHandle(handle); return pe.th32ProcessID.intValue(); } } } catch (Throwable cause) { throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); } Kernel32.INSTANCE.CloseHandle(handle); throw new RuntimeException("Process Not Exist"); }

通过测试, 完全正确, 但是如果程序多开的话, 只能取到遍历时第一个进程的进程ID

JNA Demo, PrivateExtractIconsW function (winuser.h)

PrivateExtractIconsW function (winuser.h)

在这里插入图片描述

通过 Java 获取 exe 等文件内包含的高清 Icon 图片, 因为网上的两种方法, 拿到的 Icon 只是 1616 和 3232 的, 而现在的 Icon 一般都是很高清的, 有 6464, 96/96, 128128, 256*256 等尺寸, 这些高清图片不太好拿到

但是发现网上有一些工具是可以拿到高清大图的, 比如 IconViewer (安装后, 右键文件, 属性, Icons 面板里有全部尺寸的图片)

C# .exe和.dll文件图标资源提取工具

所以一定有办法拿到全尺寸图片的, 一番研究发现可能关键在于 PrivateExtractIconsW 这个函数, 不确定的参数可用 Object 代替

interface User32X extends Library { User32X INSTANCE = Native.load("user32", User32X.class, W32APIOptions.DEFAULT_OPTIONS); int PrivateExtractIconsW(String szFileName, int nIconIndex, int cxIcon, int cyIcon, Integer phicon, Object piconid, int nIcons, int flags); } public static void getAllSizeImageFromIconInAnyFile() { int size = User32X.INSTANCE.PrivateExtractIconsW("D:\\game\\虎牙直播\\HuyaClient\\Huya.exe", 0, 0, 0, null, null, 0, 0); System.out.println(size); }

效果是可以的, 能用, 且结果正确. 但是后续 c++ 解析 HICON 获取各尺寸图片的代码, 实在是模仿不来, 就先这样吧. 反正 JNA 调用的方法是明白了



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有