Я попытался перевести функцию 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
@LppEdd То же самое
Это довольно странно, вы уверены, что указываете на тот же ресурс?
Ага. Просто exe, лежащий на моем рабочем столе
Вы выполняете это с правами администратора? Не обязательно, но стоит попробовать.




В разделе Примечания есть эта часть информации, которая может быть источником вашей проблемы.
You must initialize Component Object Model (COM) with
CoInitializeorOleInitializeprior to callingSHGetFileInfo.
Это довольно простой вызов
CoInitialize(null);
Как указал DanielWiddis в комментариях, согласно документации
New applications should call
CoInitializeExinstead ofCoInitialize
А также
To close the COM library gracefully, each successful call to
CoInitializeorCoInitializeEx, including those that returnS_FALSE, must be balanced by a corresponding call toCoUninitialize
Пример
CoInitializeEx(null, 0);
CoUninitialize();
CoInitializeEx, а не CoInitialize. Также не забывайте сочетать каждый звонок CoInitialize с CoUninitialize!
@DanielWiddis спасибо за исправление! Обновил ответ.
Пожалуйста. Однако для полноты вам нужно немного больше. Вы должны проверить возвращаемое значение CoInitializeEx. S_OK и S_FALSE оба означают, что COM был успешно инициализирован (и должен быть деинициализирован после его использования), но RPC_E_CHANGED_MODE означает, что он уже был инициализирован с другой моделью потоковой передачи, и вам нужно переключить флаг потока с 0 (значение по умолчанию и то, что вы указали) на 1 ( если он был инициализирован ранее) и повторите попытку.
(О чем вы упоминаете, ссылаясь на документы, кроме части «повторить»!)
@DanielWiddis Я не уверен, что вы можете изменить режим, некоторые вещи оболочки требуют STA.
@anders нет, вы не можете изменить его, если он уже был инициализирован в другом режиме. Вот почему CoInitializeEx(null, 0); может потерпеть неудачу и вернуться RPC_E_CHANGED_MODE. В этом случае вам придется либо (а) принять сбой/неинициализацию и продолжить, а также довериться тому, что уже инициализированный COM не будет закрыт, пока вы его используете, либо (б) инициализировать себя, используя тот же режим. поскольку уже инициализирован, например, CoInitializeEx(null, 1); (который должен возвращать S_FALSE). Это гарантирует, что он останется инициализированным для вас.
@DanielWiddis Я имел в виду, что я не уверен, приемлемо ли использование MTA. Некоторые расширения оболочки могут аварийно завершать работу, если они не находятся в потоке STA.
@ Андерс ах, я вижу это сейчас, когда перечитываю. Однако в этом случае инициализация все равно не удастся (RPC_E_CHANGED_MODE), и пользователь должен выполнить деинициализацию нет... это ключевой момент.
Вы пытались использовать флаг SHGFI_ICON (0x000000100)? Просто посмотреть на поведение.