JNA - как получить первый элемент из SysTreeView - PullRequest
0 голосов
/ 03 октября 2018

Попробуйте использовать jna 4.5.2 и у вас возникли проблемы с SysTreeView.

Мой тестовый класс:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import com.sun.jna.Memory;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.platform.DesktopWindow;
import com.sun.jna.platform.WindowUtils;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.platform.win32.WinNT;
import com.sun.jna.win32.W32APIOptions;

public class SysTreeViewExample {

    static final int TV_FIRST = 0x1100;

    static final int TVM_GETCOUNT = TV_FIRST + 5;
    static final int TVM_GETNEXTITEM = TV_FIRST + 10;
    static final int TVM_GETITEMW = TV_FIRST + 62;

    static final int TVGN_ROOT = 0;

    static final int TVIF_TEXT = 1;
    static final int TVIF_CHILDREN = 64;


    //https://docs.microsoft.com/en-us/windows/desktop/api/commctrl/ns-commctrl-tagtvitemw
    public static class TVITEMW extends Structure {

        private static final int MEMSIZE = 260;

        public WinDef.UINT mask;
        public WinNT.HANDLE hItem;
        public WinDef.UINT state;
        public WinDef.UINT stateMask;
        public Pointer pszText = new Memory(MEMSIZE);
        public int cchTextMax = MEMSIZE;
        public int iImage;
        public int iSelectedImage;
        public int cChildren;
        public WinDef.LPARAM lParam;

        @Override
        protected List<String> getFieldOrder() {
            return Arrays.asList("mask", "hItem", "state", "stateMask", "pszText", "cchTextMax", "iImage", "iSelectedImage", "cChildren", "lParam");
        }
    }

    public interface User32Ext extends User32 {

        User32Ext INSTANCE = Native.loadLibrary("user32", User32Ext.class, W32APIOptions.DEFAULT_OPTIONS);

        LRESULT SendMessageW(HWND hWnd, int msg, WPARAM wParam, TVITEMW tvitemw);
    }

    public static void main(String[] args) {

        List<DesktopWindow> timeTrackerWindows = WindowUtils.getAllWindows(false).stream()//
                .filter(desktopWindow -> desktopWindow.getFilePath().contains("TimeTracker.exe"))//
                .collect(Collectors.toList());

        List<WinDef.HWND> sysTreeViewHwnds = timeTrackerWindows.stream()//
                .map(DesktopWindow::getHWND)//
                .flatMap(windowHwnd -> {

                    List<WinDef.HWND> sysTreeViews = new ArrayList<>();

                    char[] buff = new char[8 * 1024];

                    User32.INSTANCE.EnumChildWindows(windowHwnd, (windowChildHwnd, data) -> {

                        User32.INSTANCE.GetClassName(windowChildHwnd, buff, buff.length);

                        String className = Native.toString(buff);
                        if (className.contains("SysTreeView"))
                            sysTreeViews.add(windowChildHwnd);

                        return true;
                    }, null);

                    return sysTreeViews.stream();
                })//
                .collect(Collectors.toList());

        sysTreeViewHwnds.forEach(sysTreeViewHwnd -> {
            //https://docs.microsoft.com/en-us/windows/desktop/controls/tvm-getcount
            WinDef.LRESULT countItems = User32.INSTANCE.SendMessage(sysTreeViewHwnd, TVM_GETCOUNT, new WinDef.WPARAM(0), new WinDef.LPARAM(0));
            System.out.println(countItems);//return correct value

            //https://docs.microsoft.com/en-us/windows/desktop/controls/tvm-getnextitem
            WinDef.LRESULT handleFirstItem = User32.INSTANCE.SendMessage(sysTreeViewHwnd, TVM_GETNEXTITEM, new WinDef.WPARAM(TVGN_ROOT), null);

            TVITEMW tvitemw = new TVITEMW();
            tvitemw.hItem = new WinNT.HANDLE(handleFirstItem.toPointer());
            tvitemw.mask = new WinDef.UINT(TVIF_TEXT | TVIF_CHILDREN);

            //https://docs.microsoft.com/en-us/windows/desktop/controls/tvm-getitem
            WinDef.LRESULT isGetItem = User32Ext.INSTANCE.SendMessageW(sysTreeViewHwnd, TVM_GETITEMW, new WinDef.WPARAM(0), tvitemw);
            System.out.println(isGetItem);// return 0
        });
    }
}

Я не понимаю, что я делаю неправильно?Может быть, при попытке получить первый элемент SysTreeView

//https://docs.microsoft.com/en-us/windows/desktop/controls/tvm-
WinDef.LRESULT handleFirstItem = User32.INSTANCE.SendMessage(sysTreeViewHwnd, TVM_GETNEXTITEM, new WinDef.WPARAM(TVGN_ROOT), null);

Или при создании TVITEMW, или отправить сообщение TVM_GETITEMW.

Я использовал подсказки из JNA: передать указатель на структуру в функцию SendMessage из User32.dll как LPARAM , но это не сработало

Возможно, потребуется выделить память для TVITEMW в процессе владения SysTreeView, затем отправить сообщение TVM_GETITEMW, а затем забрать TVITEMW.Но как это сделать я не нашел.

Если я выполняю свой код, я получаю ошибку.

1 Ответ

0 голосов
/ 05 октября 2018

Мое решение: 1) VirtualAllocEx TVITEMW в процессе SysTreeView -> получить указатель для выделения памяти 2) WriteProcessMemory TVITEMW в выделенной памяти 3) SendMessage TVM_GETITEMW с указателем из 1 4) ReadProcessMemory указатель из 1 5) получить текст

...