Получить выбранный элемент файла в проводнике Windows с помощью JNA

Я пишу альтернативный вариант OSX Quick-look для Windows с использованием Java, и у меня возникли проблемы с тем, как получить активный выбор файла в активном окне проводника, ниже моя попытка:

    @Override
    public void nativeKeyReleased(NativeKeyEvent e) {
        System.out.println("key up:"
                + NativeKeyEvent.getKeyText(e.getKeyCode()));
        if (e.getKeyCode() == NativeKeyEvent.VK_SPACE) {
            System.out.println("Space detected! intercept active window");
            char[] buffer = new char[MSWindowConstants.MAX_TITLE_LENGTH * 2];
            User32DLL.GetWindowTextW(User32DLL.GetForegroundWindow(),
                    buffer, MSWindowConstants.MAX_TITLE_LENGTH);
            System.out.println("Active window title: "
                    + Native.toString(buffer));

            PointerByReference pointer = new PointerByReference();
            User32DLL.GetWindowThreadProcessId(
                    User32DLL.GetForegroundWindow(), pointer);
            Pointer process = Kernel32.OpenProcess(
                    Kernel32.PROCESS_QUERY_INFORMATION
                            | Kernel32.PROCESS_VM_READ, false,
                    pointer.getValue());
            Psapi.GetModuleBaseNameW(process, null, buffer,
                    MSWindowConstants.MAX_TITLE_LENGTH);
            System.out.println("Active window process: "
                    + Native.toString(buffer));

            if(MSWindowConstants.SHELL_PROCESS_NAME.equals(Native.toString(buffer))){
                System.out.println("shell focused! intercept selection");
                // retrieve selected FileItems and get the path ...

                //Ole32.INSTANCE

            }



        }

Класс MSEnumeration:

public class MSEnumeration {



public static class Psapi {
    static {
        Native.register("psapi");
    }

    public static native int GetModuleBaseNameW(Pointer hProcess,
            Pointer hmodule, char[] lpBaseName, int size);
}

public static class Kernel32 {
    static {
        Native.register("kernel32");
    }
    public static int PROCESS_QUERY_INFORMATION = 0x0400;
    public static int PROCESS_VM_READ = 0x0010;

    public static native int GetLastError();

    public static native Pointer OpenProcess(int dwDesiredAccess,
            boolean bInheritHandle, Pointer pointer);
}

public static class User32DLL {
    static {
        Native.register("user32");
    }

    public static native int GetWindowThreadProcessId(HWND hWnd,
            PointerByReference pref);

    public static native HWND GetForegroundWindow();

    public static native int GetWindowTextW(HWND hWnd, char[] lpString,
            int nMaxCount);
}

//  public static class Shell32DLL{
//      static {
//          Native.register("shell32");
//      }
//      
//      public static native Shell32 Windows();
//  }
//  
//  public static class SHDocVwDLL{
//      static {
//          Native.register("shdocvw");
//      }
//      
//      public static native ShellWindows ShellWindows();
//      
//  }

}

Я был смущен тем, как реализовать следующее в JNA:

Получить текущий выбор в WindowsExplorer из приложения C#?

IntPtr handle = GetForegroundWindow();

List<string> selected = new List<string>();
var shell = new Shell32.Shell();
foreach(SHDocVw.InternetExplorer window in shell.Windows())
{
    if (window.HWND == (int)handle)
    {
        Shell32.FolderItems items =     ((Shell32.IShellFolderViewDual2)window.Document).SelectedItems();
        foreach(Shell32.FolderItem item in items)
        {
            selected.Add(item.Path);
        }
    }
}

Как я могу перевести это в вызовы JNA?

Я посмотрел на JNA'S Shell32 и COM(классы Ole32), но это все равно никуда меня не привело.

Единственный обходной путь, о котором я могу подумать сейчас, - это скомпилировать данный C# в отдельный исполняемый файл, который принимает аргументы и возвращает пути к файлам, но мне не очень нравится идея встраивания другого исполняемого файла в Java.

РЕДАКТИРОВАТЬ:

Некоторый прогресс:

public static final String CLSID_ShellWindows = "9BA05972-F6A8-11CF-A442-00A0C90A8F39";

public static final String IID_IShellWindows = "85CB6900-4D95-11CF-960C-0080C7F4EE85";

HRESULT hr = Ole32.INSTANCE
                        .CoCreateInstance(
                                GUID.fromString(CLSID_ShellWindows),
                                null,
                                WTypes.CLSCTX_ALL,
                                GUID.fromString(IID_IShellWindows),
                                p);

                System.out.println("result:" + W32Errors.SUCCEEDED(hr)
                        + "raw:" + hr.toString());

но результат никогда не верен по некоторым причинам...

1 ответ

Я отказался от JNA(ну, для этой конкретной задачи) и использовал вместо него com4j (с отличной документацией).

Сначала вы должны сгенерировать коды для DLL, которую вы хотите, в этом случае это shell32.dll, с помощью tlbimp.jar от com4j

Этот пример может быть немного устаревшим, но я все равно выложу его здесь

if (isExplorer(getHWNDProcessName(hwnd))) {
        IWebBrowser2 browser = getIWebBrowser2(hwnd);
        IShellFolderViewDual3 view = getIShellFolderViewDual3(browser);
        if (view != null && browser.visible()) {

            lastHWND = hwnd;
            FolderItems items = view.selectedItems();
            ArrayList<Path> paths = new ArrayList<>(items.count());
            for (Com4jObject object : items) {
                FolderItem item = object.queryInterface(FolderItem.class);
                if (item != null) {
                    paths.add(Paths.get(item.path()));
                    // this is for example only, do not create a new File just to get length
                    System.out.println("file: " + item.path() + " length: "
                            + new File(item.path()).length() + " type:" + item.type());
                }
            }
        }
    }


// some methods used in the example...

public static IWebBrowser2 getIWebBrowser2(HWND hWnd) {
    // TODO this can be potentially optimized
    IShellWindows windows = ClassFactory.createShell().windows()
            .queryInterface(IShellWindows.class);
    for (Com4jObject window : windows) {
        IWebBrowser2 browser = window.queryInterface(IWebBrowser2.class);
        if (browser.hwnd() == getHWNDValue(hWnd))
            return browser;
    }
    return null;
}

public static IShellFolderViewDual3 getIShellFolderViewDual3(IWebBrowser2 browser) {
    if (browser == null)
        return null;
    return browser.document().queryInterface(IShellFolderViewDual3.class);
}

Примечание: некоторые вызовы методов могут отсутствовать, я опубликовал только важные части о том, как получить выбранные элементы.

ВАЖНЫЙ

Вам понадобятся оба Shell32.dll а также Shdocvw.dll для этого, так что вы хотите сделать, это сгенерировать коды дважды с разными DLL

java -jar tlbimp.jar -o wsh -p test.wsh %WINDIR%\system32\Shell32.dll
java -jar tlbimp.jar -o wsh -p test.wsh %WINDIR%\system32\Shdocvw.dll

Так что мы можем иметь IWebBrowser2 и другие полезные вещи для работы, список того, что вы можете сделать с этим классом, см. в документации

Другие вопросы по тегам