Используйте SHGetFileInfo в Java с JNA

Я попытался перевести функцию SHGetFileInfo из Shell32 в Java с помощью JNA и использовал код С# и это в качестве ссылки.

В то время как в коде C# psfi.iIcon равен 432, в моем переведенном коде Java psfi.iIcon равен 0. Если я прав, для одного и того же файла они должны быть одинаковыми независимо от того, какой язык я использую, не так ли?

Мой Java-код:

public static void main(String[] args) {
    String path = "[PATH_TO_EXE]\\test.exe"; //Of course in my code I used the real path

    SHFILEINFO sfi = new SHFILEINFO();
    DWORD_PTR i = Shell32.INSTANCE.SHGetFileInfo(path, 0, sfi, sfi.size(), SHGFI.SysIconIndex);

    System.out.println(sfi.iIcon); //<-- Prints 0, should print 432
}

public static class SHGFI {
    static final int SysIconIndex = 0x000004000;
    static final int LargeIcon = 0x000000000;
    static final int UseFileAttributes = 0x000000010;
}

public interface Shell32 extends StdCallLibrary {
    Shell32 INSTANCE = Native.loadLibrary("shell32", Shell32.class, W32APIOptions.UNICODE_OPTIONS);
    DWORD_PTR SHGetFileInfo(String pszPath, int dwFileAttributes, SHFILEINFO psfi, int cbFileInfo, int uFlags);
}

public static class SHFILEINFO extends Structure {
    public HICON hIcon;
    public int iIcon;
    public DWORD dwAttributes;
    public char[] szDisplayName = new char[260];
    public char[] szTypeName = new char[80];

    @Override
    protected List<String> getFieldOrder() {
        return Arrays.asList("hIcon", "iIcon", "dwAttributes", "szDisplayName", "szTypeName");
    }
}

Есть ли что-то фундаментальное, что я сделал неправильно? Я новичок в функциях JNA и Windows

Вы пытались использовать флаг SHGFI_ICON (0x000000100)? Просто посмотреть на поведение.

LppEdd 17.03.2019 15:58

@LppEdd То же самое

Mano176 17.03.2019 16:05

Это довольно странно, вы уверены, что указываете на тот же ресурс?

LppEdd 17.03.2019 16:06

Ага. Просто exe, лежащий на моем рабочем столе

Mano176 17.03.2019 16:07

Вы выполняете это с правами администратора? Не обязательно, но стоит попробовать.

LppEdd 17.03.2019 16:11
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
2
5
117
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В разделе Примечания есть эта часть информации, которая может быть источником вашей проблемы.

You must initialize Component Object Model (COM) with CoInitialize or OleInitialize prior to calling SHGetFileInfo.

Это довольно простой вызов

CoInitialize(null);

Как указал DanielWiddis в комментариях, согласно документации

New applications should call CoInitializeEx instead of CoInitialize

А также

To close the COM library gracefully, each successful call to CoInitialize or CoInitializeEx, including those that return S_FALSE, must be balanced by a corresponding call to CoUninitialize

Пример

CoInitializeEx(null, 0);
CoUninitialize();
Приложения, разработанные сегодня, должны вызывать CoInitializeEx, а не CoInitialize. Также не забывайте сочетать каждый звонок CoInitialize с CoUninitialize!
Daniel Widdis 17.03.2019 22:57

@DanielWiddis спасибо за исправление! Обновил ответ.

LppEdd 17.03.2019 23:23

Пожалуйста. Однако для полноты вам нужно немного больше. Вы должны проверить возвращаемое значение CoInitializeEx. S_OK и S_FALSE оба означают, что COM был успешно инициализирован (и должен быть деинициализирован после его использования), но RPC_E_CHANGED_MODE означает, что он уже был инициализирован с другой моделью потоковой передачи, и вам нужно переключить флаг потока с 0 (значение по умолчанию и то, что вы указали) на 1 ( если он был инициализирован ранее) и повторите попытку.

Daniel Widdis 18.03.2019 03:34

(О чем вы упоминаете, ссылаясь на документы, кроме части «повторить»!)

Daniel Widdis 18.03.2019 03:45

@DanielWiddis Я не уверен, что вы можете изменить режим, некоторые вещи оболочки требуют STA.

Anders 18.03.2019 03:46

@anders нет, вы не можете изменить его, если он уже был инициализирован в другом режиме. Вот почему CoInitializeEx(null, 0); может потерпеть неудачу и вернуться RPC_E_CHANGED_MODE. В этом случае вам придется либо (а) принять сбой/неинициализацию и продолжить, а также довериться тому, что уже инициализированный COM не будет закрыт, пока вы его используете, либо (б) инициализировать себя, используя тот же режим. поскольку уже инициализирован, например, CoInitializeEx(null, 1); (который должен возвращать S_FALSE). Это гарантирует, что он останется инициализированным для вас.

Daniel Widdis 18.03.2019 06:40

@DanielWiddis Я имел в виду, что я не уверен, приемлемо ли использование MTA. Некоторые расширения оболочки могут аварийно завершать работу, если они не находятся в потоке STA.

Anders 18.03.2019 10:16

@ Андерс ах, я вижу это сейчас, когда перечитываю. Однако в этом случае инициализация все равно не удастся (RPC_E_CHANGED_MODE), и пользователь должен выполнить деинициализацию нет... это ключевой момент.

Daniel Widdis 18.03.2019 21:59

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