Java JNA获取桌面项位置

标签 java position icons desktop jna

目标

获取桌面项目/图标的桌面坐标。

尝试

我已经获得了 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 是桌面项目的正确 ListView )。传递的 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;

问题

WriteProcessMemory 调用经常失败,错误代码为 299 ERROR_PARTIAL_COPY,即使没有失败,返回的 POINT 也始终为 (0,0)。我希望问题出在 MyUser32MyKernel32 中的 SendMessage/VirtualAllocEx 方法声明中,或者出在我身上没有正确理解将哪个对象/指针传递给 VirtualAllocExWriteProcessMemory

我做了很多研究并弄清楚了它应该如何在 C/C++ 中工作,但我找不到任何使用 JNA 的有效代码示例。

感谢您表现出兴趣并努力帮助您完成我的消息。

最佳答案

问题是 com.sun.jna.Native.getNativeSize(Class) 不是在这种情况下使用的正确函数。使用 32 位 JVM 时,问题是可见的(64 位 VM 不可见)。

对于结构,上述函数假定它们通过引用(指向结构的指针)传递,因此该函数返回 Native.POINTER_SIZE 的值。在 64 位 VM 上,这正好匹配 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获取桌面项位置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55485540/

相关文章:

java - 仅从 URL 获取 id

java - com.sun.star.uno 不存在错误

JavaScript/HTML : How to position object to right of page?

android - android 无法设置窗口图标

flutter - 如何在 Flutter 中为 AppBar 添加图标

java - 如何在拖放过程中显示图标

java - Hadoop 类型转换

java - 当某些模块未编译时如何在 IntelliJ 中运行单元测试?

CSS 问题 : Links on page becoming unclickable when other element is positioned "absolute"

jquery - 加载时将 div 保持在屏幕底部,但在向下滚动时保持在原始位置