Я пытаюсь контролировать панель задач, чтобы я мог показать прогресс некоторой давней задачи в приложении Javafx. Для общения с Winapi я хочу использовать новый API Java FFM, который должен заменить JNI на один день. API: < /p>
jextract --output target/generated-sources/jextract -t "taskbar_test.gen" -l :shell32 -l :Explorerframe -l :ole32 -I "C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\shared" -I "C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\um" -I "C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\km" -I "C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\km\crt" "C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\um\ShObjIdl_core.h"
В коде ниже вы можете найти полное приложение с моей попыткой в конечном итоге функции вызова setprogressvalue . Моя проблема заключается в том, что я не могу успешно вызвать функцию hrinit , которая должна быть вызвана для инициализации itaskbarlist .
package taskbar_test;
import com.sun.glass.ui.Window;
import javafx.application.Application;
import javafx.stage.Stage;
import taskbar_test.gen.CLSID;
import taskbar_test.gen.IID;
import taskbar_test.gen.ITaskbarList;
import taskbar_test.gen.ITaskbarList3;
import taskbar_test.gen.ITaskbarList3Vtbl;
import taskbar_test.gen.ITaskbarListVtbl;
import taskbar_test.gen.ShObjIdl_core_h;
import java.lang.foreign.Arena;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.ValueLayout;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.Executors;
public class FxWinTaskbar extends Application {
public static final String GUID_FORMAT = "{%s}";
// CLSID of ITaskbarList3
public static final String CLSID_CONST = "56FDF344-FD6D-11d0-958A-006097C9A090";
// IID of ITaskbarList3
public static final String IID_ITASKBAR_LIST = "56FDF342-FD6D-11d0-958A-006097C9A090";
public static final String IID_ITASKBAR_LIST_3 = "EA1AFB91-9E28-4B86-90E9-9E9F8A5EEFAF";
@Override
public void start(Stage stage) throws Exception {
var button = new javafx.scene.control.Button("Click Me");
button.setOnAction(e -> handleClick());
var root = new javafx.scene.layout.StackPane(button);
var scene = new javafx.scene.Scene(root, 300, 200);
stage.setTitle("JavaFX Stage with Button");
stage.setScene(scene);
stage.show();
}
void handleClick() {
long rawHandle = Window.getWindows().getFirst().getRawHandle();
Executors.newSingleThreadExecutor().submit(() -> {
try (var arena = Arena.ofConfined()) {
// 1. Initialize variables
// https://learn.microsoft.com/en-us/windo ... ng#remarks
// The CLSID format is {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}.
var clsidString = arena.allocateFrom(GUID_FORMAT.formatted(CLSID_CONST), StandardCharsets.UTF_16LE);
var iidITaskbarList = arena.allocateFrom(GUID_FORMAT.formatted(IID_ITASKBAR_LIST), StandardCharsets.UTF_16LE);
var iidITaskbarList3 = arena.allocateFrom(GUID_FORMAT.formatted(IID_ITASKBAR_LIST_3), StandardCharsets.UTF_16LE);
var clsid = CLSID.allocate(arena);
var iidTaskbarList = IID.allocate(arena);
var iidTaskbarList3 = IID.allocate(arena);
var taskbarPtrToPtr = arena.allocate(ShObjIdl_core_h.C_POINTER);
var taskbar3PtrToPtr = arena.allocate(ShObjIdl_core_h.C_POINTER);
MemorySegment windowHandle = arena.allocate(ValueLayout.ADDRESS, rawHandle);
// 2. Initialize COM
int hr = ShObjIdl_core_h.CoInitializeEx(MemorySegment.NULL, ShObjIdl_core_h.COINIT_MULTITHREADED());
if (hr != ShObjIdl_core_h.S_OK()) {
throw new RuntimeException("CoInitialize failed with error code: " + hr);
}
// 3. Create CLSID and IIDs
hr = ShObjIdl_core_h.CLSIDFromString(clsidString, clsid);
if (hr != ShObjIdl_core_h.S_OK()) {
throw new RuntimeException("CLSIDFromString failed with error code: " + hr);
}
hr = ShObjIdl_core_h.IIDFromString(iidITaskbarList, iidTaskbarList);
if (hr != ShObjIdl_core_h.S_OK()) {
throw new RuntimeException("IIDFromString failed with error code: " + hr);
}
hr = ShObjIdl_core_h.IIDFromString(iidITaskbarList3, iidTaskbarList3);
if (hr != ShObjIdl_core_h.S_OK()) {
throw new RuntimeException("IIDFromString failed with error code: " + hr);
}
// 4. Create instance of ITaskbarList
hr = ShObjIdl_core_h.CoCreateInstance(clsid, MemorySegment.NULL, ShObjIdl_core_h.CLSCTX_ALL(), iidTaskbarList, taskbarPtrToPtr);
if (hr != ShObjIdl_core_h.S_OK()) {
if (hr == ShObjIdl_core_h.REGDB_E_CLASSNOTREG()) {
System.out.println("COM class is not registered!");
}
throw new RuntimeException("CoCreateInstance failed with error code: " + hr);
}
// CoCreateInstance returns pointer to pointer to ITaskbarList so here we obtain the "inner" pointer
var taskbarPtr = taskbarPtrToPtr.get(ValueLayout.ADDRESS, 0);
// Use reinterpret method to have access to the actual ITaskbarList instance
var taskbarListInstance = ITaskbarList.reinterpret(taskbarPtr, arena, _ -> {
System.out.println("Some cleanup...");
});
// 5. Obtain lpVtbl pointer from ITaskbarList
MemorySegment taskbarListVtblPtr = ITaskbarList.lpVtbl(taskbarListInstance);
// Use reinterpret method to have access to the actual ITaskbarListVtbl instance
MemorySegment taskbarListVtbl = ITaskbarListVtbl.reinterpret(taskbarListVtblPtr, arena, _ -> {
System.out.println("Some cleanup...");
});
// 6. Get pointer to function HrInit to initialize ITaskbarList
// https://learn.microsoft.com/en-us/windo ... ist-hrinit
// Initializes the taskbar list object. This method must be called before any other ITaskbarList methods can be called.
MemorySegment functionHrInitPtr = ITaskbarListVtbl.HrInit(taskbarListVtbl);
hr = ITaskbarListVtbl.HrInit.invoke(functionHrInitPtr, taskbarListVtbl);
if (hr != ShObjIdl_core_h.S_OK()) {
throw new RuntimeException("HrInit failed with error code: " + hr);
}
// 7. Create instance of ITaskbarList3
hr = ShObjIdl_core_h.CoCreateInstance(clsid, MemorySegment.NULL, ShObjIdl_core_h.CLSCTX_ALL(), iidTaskbarList3, taskbar3PtrToPtr);
if (hr != ShObjIdl_core_h.S_OK()) {
if (hr == ShObjIdl_core_h.REGDB_E_CLASSNOTREG()) {
System.out.println("COM class is not registered!");
}
throw new RuntimeException("CoCreateInstance failed with error code: " + hr);
}
// 8. Obtain a pointer to the instance
var taskbar3Ptr = taskbar3PtrToPtr.get(ValueLayout.ADDRESS, 0);
// Use reinterpret method to have access to the actual ITaskbarList3 instance
var taskbarList3Instance = ITaskbarList3.reinterpret(taskbar3Ptr, arena, _ -> {
System.out.println("Some cleanup...");
});
// 9. Obtain lpVtbl pointer from ITaskbarList3
MemorySegment taskbarList3VtblPtr = ITaskbarList3.lpVtbl(taskbarList3Instance);
// Use reinterpret method to have access to the actual ITaskbarList3Vtbl instance
MemorySegment taskbarList3Vtbl = ITaskbarList3Vtbl.reinterpret(taskbarList3VtblPtr, arena, _ -> {
System.out.println("Some cleanup...");
});
// 10. Set progress state to indeterminate
MemorySegment functionSetProgressStatePtr = ITaskbarList3Vtbl.SetProgressState(taskbarList3Vtbl);
hr = ITaskbarList3Vtbl.SetProgressState.invoke(functionSetProgressStatePtr, taskbarList3Vtbl, windowHandle, ShObjIdl_core_h.TBPF_INDETERMINATE());
if (hr != ShObjIdl_core_h.S_OK()) {
throw new RuntimeException("SetProgressState failed with error code: " + hr);
}
} catch (Throwable ex) {
ex.printStackTrace();
} finally {
ShObjIdl_core_h.CoUninitialize();
}
});
}
public static void main(String[] args) {
launch(args);
}
}
Я не могу вызвать функцию setprogressstate непосредственно на интерфейсе itaskbarlist3 , потому что сгенерированные источники не имеют возможности сделать это. Вместо этого я должен вручную получить структуру vtbl и вызвать функцию в этой структуре. Вызовая функция hrinit потерпит неудачу, потому что это доступ к неправильной памяти. /> < /p>
Изменить: я применил предложения из комментариев. Теперь существует только один экземпляр itaskbarlist3 , и все функции вызываются на нем. Я также расширил код, чтобы моделировать некоторый прогресс, чтобы увидеть, может ли он установить прогресс. Код, кажется, работает, но, к сожалению, панель задач по-прежнему без каких-либо изменений.package taskbar_test;
import com.sun.glass.ui.Window;
import javafx.application.Application;
import javafx.stage.Stage;
import taskbar_test.gen.CLSID;
import taskbar_test.gen.IID;
import taskbar_test.gen.ITaskbarList3;
import taskbar_test.gen.ITaskbarList3Vtbl;
import taskbar_test.gen.ShObjIdl_core_h;
import java.lang.foreign.Arena;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.ValueLayout;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.Executors;
public class FxWinTaskbar extends Application {
public static final String GUID_FORMAT = "{%s}";
// CLSID of ITaskbarList3
public static final String CLSID_CONST = "56FDF344-FD6D-11d0-958A-006097C9A090";
// IID of ITaskbarList3
public static final String IID_ITASKBAR_LIST_3 = "EA1AFB91-9E28-4B86-90E9-9E9F8A5EEFAF";
@Override
public void start(Stage stage) throws Exception {
var button = new javafx.scene.control.Button("Click Me");
button.setOnAction(e -> handleClick());
var root = new javafx.scene.layout.StackPane(button);
var scene = new javafx.scene.Scene(root, 300, 200);
stage.setTitle("JavaFX Stage with Button");
stage.setScene(scene);
stage.show();
}
void handleClick() {
long rawHandle = Window.getWindows().getFirst().getRawHandle();
Executors.newSingleThreadExecutor().submit(() -> {
try (var arena = Arena.ofConfined()) {
// 1. Initialize variables
// https://learn.microsoft.com/en-us/windo ... ng#remarks
// The CLSID format is {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}.
var clsidString = arena.allocateFrom(GUID_FORMAT.formatted(CLSID_CONST), StandardCharsets.UTF_16LE);
var iidITaskbarList3 = arena.allocateFrom(GUID_FORMAT.formatted(IID_ITASKBAR_LIST_3), StandardCharsets.UTF_16LE);
var clsid = CLSID.allocate(arena);
var iidTaskbarList3 = IID.allocate(arena);
var taskbar3PtrToPtr = arena.allocate(ShObjIdl_core_h.C_POINTER);
MemorySegment windowHandle = arena.allocate(ValueLayout.ADDRESS, rawHandle);
// 2. Initialize COM
int hr = ShObjIdl_core_h.CoInitializeEx(MemorySegment.NULL, ShObjIdl_core_h.COINIT_MULTITHREADED());
if (hr != ShObjIdl_core_h.S_OK()) {
throw new RuntimeException("CoInitialize failed with error code: " + hr);
}
// 3. Create CLSID and IIDs
hr = ShObjIdl_core_h.CLSIDFromString(clsidString, clsid);
if (hr != ShObjIdl_core_h.S_OK()) {
throw new RuntimeException("CLSIDFromString failed with error code: " + hr);
}
hr = ShObjIdl_core_h.IIDFromString(iidITaskbarList3, iidTaskbarList3);
if (hr != ShObjIdl_core_h.S_OK()) {
throw new RuntimeException("IIDFromString failed with error code: " + hr);
}
// 4. Create instance of ITaskbarList3
hr = ShObjIdl_core_h.CoCreateInstance(clsid, MemorySegment.NULL, ShObjIdl_core_h.CLSCTX_ALL(), iidTaskbarList3, taskbar3PtrToPtr);
if (hr != ShObjIdl_core_h.S_OK()) {
if (hr == ShObjIdl_core_h.REGDB_E_CLASSNOTREG()) {
System.out.println("COM class is not registered!");
}
throw new RuntimeException("CoCreateInstance failed with error code: " + hr);
}
// 5. Obtain a pointer to the instance
var taskbar3Ptr = taskbar3PtrToPtr.get(ValueLayout.ADDRESS, 0);
// Use reinterpret method to have access to the actual ITaskbarList3 instance
var taskbarList3Instance = taskbar3Ptr.reinterpret(ITaskbarList3.sizeof());
// 6. Obtain lpVtbl pointer from ITaskbarList3
MemorySegment taskbarList3VtblPtr = ITaskbarList3.lpVtbl(taskbarList3Instance);
// Use reinterpret method to have access to the actual ITaskbarList3Vtbl instance
MemorySegment taskbarList3Vtbl = taskbarList3VtblPtr.reinterpret(ITaskbarList3Vtbl.sizeof());
// https://learn.microsoft.com/en-us/windo ... ist-hrinit
// Initializes the taskbar list object. This method must be called before any other ITaskbarList methods can be called.
MemorySegment functionHrInitPtr = ITaskbarList3Vtbl.HrInit(taskbarList3Vtbl);
hr = ITaskbarList3Vtbl.HrInit.invoke(functionHrInitPtr, taskbarList3Instance);
if (hr != ShObjIdl_core_h.S_OK()) {
throw new RuntimeException("HrInit failed with error code: " + hr);
}
// 7. Set progress state to indeterminate
MemorySegment functionSetProgressStatePtr = ITaskbarList3Vtbl.SetProgressState(taskbarList3Vtbl);
hr = ITaskbarList3Vtbl.SetProgressState.invoke(functionSetProgressStatePtr, taskbarList3Instance, windowHandle, ShObjIdl_core_h.TBPF_INDETERMINATE());
if (hr != ShObjIdl_core_h.S_OK()) {
throw new RuntimeException("SetProgressState failed with error code: " + hr);
}
// 8. Simulate some progress
for (int i = 0; i < 100; i+=20) {
System.out.println("Progress is: " + i);
MemorySegment functionSetProgressValuePtr = ITaskbarList3Vtbl.SetProgressValue(taskbarList3Vtbl);
hr = ITaskbarList3Vtbl.SetProgressValue.invoke(functionSetProgressValuePtr, taskbarList3Instance, windowHandle, i, 100);
if (hr != ShObjIdl_core_h.S_OK()) {
throw new RuntimeException("SetProgressValue failed with error code: " + hr);
}
Thread.sleep(500);
}
// 9. Reset progress state
hr = ITaskbarList3Vtbl.SetProgressState.invoke(functionSetProgressStatePtr, taskbarList3Instance, windowHandle, ShObjIdl_core_h.TBPF_INDETERMINATE());
if (hr != ShObjIdl_core_h.S_OK()) {
throw new RuntimeException("SetProgressState failed with error code: " + hr);
}
} catch (Throwable ex) {
ex.printStackTrace();
} finally {
ShObjIdl_core_h.CoUninitialize();
}
});
}
public static void main(String[] args) {
launch(args);
}
}
Подробнее здесь: https://stackoverflow.com/questions/793 ... and-winapi
Панель задач управления в Windows от Java с использованием FFM и Winapi ⇐ JAVA
-
- Похожие темы
- Ответы
- Просмотры
- Последнее сообщение