Java JNA获取桌面项目位置
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java JNA获取桌面项目位置相关的知识,希望对你有一定的参考价值。
Goal
获取桌面项/图标的桌面坐标。
Attempt
我已经获得了SysListView32窗口句柄,其中包含使用以下内容的桌面图标:
HWND hWnd_Progman = User32.INSTANCE.FindWindow("Progman", "Program Manager");
HWND hWnd_SHELLDLL_DefView = User32.INSTANCE.FindWindowEx(hWnd_Progman, null, "SHELLDLL_DefView", null);
HWND hWnd_SysListView32 = User32.INSTANCE.FindWindowEx(hWnd_SHELLDLL_DefView, null, "SysListView32", "FolderView");
我已经获得了桌面项目数:
LRESULT result = User32.INSTANCE.SendMessage(hWnd_SysListView32, LVM_GETITEMCOUNT, new WPARAM(), new LPARAM());
long desktopIconCount = result.longValue();
我已设置桌面项位置(验证SysListView32是桌面项的右侧列表视图)。传递的x和y坐标对应于从最左侧监视器的左上角到桌面项目的左上角的偏移量。码:
int itemIndex = 0; // Allows 0 to desktopIconCount - 1.
int x = ...;
int y = ...;
LRESULT res = User32.INSTANCE.SendMessage(hWnd_SysListView32, LVM_SETITEMPOSITION, new WPARAM(itemIndex), new LPARAM((x & 0xFFFF) | (y << 16)));
现在,要获取桌面项目位置,需要将LVM_GETITEMPOSITION
发送到SysListView32并包含指向可以将位置写入的地址的指针。但是,此指针必须是属于SysListView32的进程的内存中的有效地址。所以我试图做的是:
- 获取属于SysListView32的进程。
- 在该过程中分配内存。
- 在此内存中写入
POINT
对象(用于项目位置)。 - 使用指向此分配内存的指针将
LVM_GETITEMPOSITION
发送到SysListView32。 - 从内存中读取这个
POINT
对象。此时,进程应该已将桌面项位置写入其中。
我用以下代码尝试了这个:
// Get the SysListView32 process handle.
IntByReference processIdRef = new IntByReference();
User32.INSTANCE.GetWindowThreadProcessId(hWnd_SysListView32, processIdRef);
HANDLE procHandle = Kernel32.INSTANCE.OpenProcess(
Kernel32.PROCESS_VM_OPERATION | Kernel32.PROCESS_VM_WRITE | Kernel32.PROCESS_VM_READ,
false, processIdRef.getValue());
// Allocate memory in the SysView32 process.
int pointSize = Native.getNativeSize(POINT.class)); // 8 bytes.
LPVOID pMem = MyKernel32.INSTANCE.VirtualAllocEx(procHandle, new LPVOID(), new SIZE_T(pointSize),
MyKernel32.MEM_COMMIT, MyKernel32.PAGE_READWRITE);
// Put some POINT-sized object in the process its memory.
boolean success = Kernel32.INSTANCE.WriteProcessMemory(
procHandle, pMem.getPointer(), pMem.getPointer(), pointSize, null);
if(!success) {
System.out.println("Write error = " + Kernel32.INSTANCE.GetLastError());
System.exit(1);
}
// Send the LVM_GETITEMPOSITION message to the SysListView32.
int itemIndex = 0; // Allows 0 to desktopIconCount - 1.
LRESULT res = MyUser32.INSTANCE.SendMessage(
hWnd_SysListView32, LVM_GETITEMPOSITION, new WPARAM(itemIndex), pMem.getPointer());
System.out.println("Message result = " + res.longValue());
// Read the earlier POINT-sized written memory.
POINT point = new POINT();
success = Kernel32.INSTANCE.ReadProcessMemory(
procHandle, pMem.getPointer(), point.getPointer(), pointSize, null);
if(!success) {
System.out.println("Read error = " + Kernel32.INSTANCE.GetLastError());
System.exit(1);
}
System.out.println("Point found: x=" + pos.x + ", y=" + pos.y);
在这里,MyUser32
创建如下:
interface MyUser32 extends User32 {
static MyUser32 INSTANCE =
(MyUser32) Native.load("user32", MyUser32.class, W32APIOptions.DEFAULT_OPTIONS);
LRESULT SendMessage(HWND hWnd, int msg, WPARAM wParam, Pointer pointer);
}
和MyKernel32
创建如下:
interface MyKernel32 extends Kernel32 {
static final MyKernel32 INSTANCE =
(MyKernel32) Native.load("kernel32", MyKernel32.class, W32APIOptions.DEFAULT_OPTIONS);
static int MEM_COMMIT = 0x1000;
static int PAGE_READWRITE = 0x04;
LPVOID VirtualAllocEx(HANDLE hProcess, LPVOID lpAddress, SIZE_T dwSize, int flAllocationType, int flProtect);
}
为完成,使用以下附加静态值:
static final int LVM_FIRST = 0x1000;
static final int LVM_GETITEMCOUNT = LVM_FIRST + 4;
static final int LVM_SETITEMPOSITION = LVM_FIRST + 15;
static final int LVM_GETITEMPOSITION = LVM_FIRST + 16;
Problem
WriteProcessMemory
调用经常失败,错误代码为299 ERROR_PARTIAL_COPY
,即使没有失败,返回的POINT始终为(0,0)。我希望问题出现在SendMessage
或VirtualAllocEx
中的MyUser32
/ MyKernel32
方法声明中,或者我不能正确理解传递给VirtualAllocEx
或WriteProcessMemory
的对象/指针。
我已经做了很多研究并弄清楚它应该如何在C / C ++中工作,但我找不到任何使用JNA的工作代码示例。
感谢您表现出兴趣并尝试提供帮助,如果您通过我的消息一直这样做。
问题是com.sun.jna.Native.getNativeSize(Class)不是在这种情况下使用的正确函数。使用32位JVM时,问题是可见的(64位虚拟机不可见)。
对于结构,上面提到的函数假定它们通过引用传递(指向结构的指针),因此函数返回Native.POINTER_SIZE的值。在64位虚拟机上,运气匹配POINT结构的大小。在32位VM上,Native.POINTER_SIZE是4字节,因此只能保存结果结构的一部分。
最相关的部分:要确定JNA中结构的大小,请使用Structure#size函数。在这种情况下,使用ReadProcessMemory的最终参数也很有帮助。该函数返回读取的字节数并显示差异(4对8)。
进一步的评论:记得释放你分配的内存,并关闭收到的进程句柄。
这是完整的可运行样本(仅缺少导入,使用JNA 5.2测试):
public class Test {
private interface Kernel32 extends com.sun.jna.platform.win32.Kernel32 {
Kernel32 INSTANCE = Native.load("kernel32", Kernel32.class, W32APIOptions.DEFAULT_OPTIONS);
public Pointer VirtualAllocEx(HANDLE hProcess, Pointer lpAddress, SIZE_T dwSize, int flAllocationType, int flProtect);
public boolean VirtualFreeEx(HANDLE hProcess, Pointer lpAddress, SIZE_T dwSize, int dwFreeType);
int MEM_COMMIT = 0x00001000;
int MEM_RESERVE = 0x00002000;
int MEM_RESET = 0x00080000;
int MEM_RESET_UNDO = 0x1000000;
int MEM_LARGE_PAGES = 0x20000000;
int MEM_PHYSICAL = 0x00400000;
int MEM_TOP_DOWN = 0x00100000;
int MEM_COALESCE_PLACEHOLDERS = 0x00000001;
int MEM_PRESERVE_PLACEHOLDER = 0x00000002;
int MEM_DECOMMIT = 0x4000;
int MEM_RELEASE = 0x8000;
}
private static final int LVM_FIRST = 0x1000;
private static final int LVM_GETITEMCOUNT = LVM_FIRST + 4;
private static final int LVM_GETITEMPOSITION = LVM_FIRST + 16;
public static void main(String[] args) throws IOException, InterruptedException {
// Find the HWND for the "desktop" list view
HWND hWnd_Progman = User32.INSTANCE.FindWindow("Progman", "Program Manager");
HWND hWnd_SHELLDLL_DefView = User32.INSTANCE.FindWindowEx(hWnd_Progman, null, "SHELLDLL_DefView", null);
HWND hWnd_SysListView32 = User32.INSTANCE.FindWindowEx(hWnd_SHELLDLL_DefView, null, "SysListView32", "FolderView");
// Fetch the icon count
int itemCount = User32.INSTANCE.SendMessage(hWnd_SysListView32, LVM_GETITEMCOUNT, new WPARAM(), new LPARAM()).intValue();
System.out.println("Desktop Icons: " + itemCount);
// Get the SysListView32 process handle.
IntByReference processIdRef = new IntByReference();
User32.INSTANCE.GetWindowThreadProcessId(hWnd_SysListView32, processIdRef);
HANDLE procHandle = Kernel32.INSTANCE.OpenProcess(
Kernel32.PROCESS_VM_OPERATION | Kernel32.PROCESS_VM_WRITE | Kernel32.PROCESS_VM_READ,
false, processIdRef.getValue());
// Allocate memory in the SysView32 process.
int pointSize = new POINT().size(); // 8 bytes.
Pointer pMem = Kernel32.INSTANCE.VirtualAllocEx(procHandle, null, new SIZE_T(pointSize),
Kernel32.MEM_COMMIT, Kernel32.PAGE_READWRITE);
for (int i = 0; i < itemCount; i++) {
// Send the LVM_GETITEMPOSITION message to the SysListView32.
LRESULT res = User32.INSTANCE.SendMessage(
hWnd_SysListView32, LVM_GETITEMPOSITION, new WPARAM(i), new LPARAM(Pointer.nativeValue(pMem)));
if(res.intValue() != 1) {
throw new IllegalStateException("Message sending failed");
}
// Read the earlier POINT-sized written memory.
POINT point = new POINT();
IntByReference read = new IntByReference();
boolean success = Kernel32.INSTANCE.ReadProcessMemory(
procHandle, pMem, point.getPointer(), pointSize, read);
if (!success) {
System.out.println("Read error = " + Kernel32.INSTANCE.GetLastError());
System.exit(1);
}
point.read();
System.out.println("Point found: x=" + point.x + ", y=" + point.y);
}
// Release allocated memory
Kernel32.INSTANCE.VirtualFreeEx(procHandle, pMem, new SIZE_T(0), Kernel32.MEM_RELEASE);
// Close Process Handle
Kernel32.INSTANCE.CloseHandle(procHandle);
}
}
以上是关于Java JNA获取桌面项目位置的主要内容,如果未能解决你的问题,请参考以下文章