Implementacja interfejsu COM IContextMenu przy użyciu JNA

Aug 25 2020

Potrzebuję wszystkich elementów z menu powłoki Eksploratora Windows. Wdrażam z jna interfejs obiektu COM IShellFolder. Ale teraz mam problem z implementacją interfejsu IContextMenu do odpytywania menu kontekstowego. Kiedy wywołuję funkcję QueryContextMenu, wynik HResult jest równy 0, jak prawda, ale HMenu jest puste, gdy wywołuję funkcję GetMenuItemCount, a wynikiem jest 0.

Czy możesz mi powiedzieć, gdzie mam błąd. Dziękuję Ci bardzo.

To jest mój kod dla IContextMenu:

public interface IContextMenu {
Guid.IID IID_IContextMenu = new Guid.IID("{000214E4-0000-0000-C000-000000000046}");

WinNT.HRESULT QueryContextMenu(WinDef.HMENU hMenu, int indexMenu, int idCmdFirst, int idCmdLast, int uFlags);
WinNT.HRESULT InvokeCommand(Pointer pici);
WinNT.HRESULT GetCommandString(IntByReference idCmd, int uType, IntByReference pReserved, WTypes.LPSTR pszName, int cchMax);

public static class Converter {
    public Converter() {
    }

    public static IContextMenu PointerToIContextMenu(PointerByReference ptr) {
        final Pointer interfacePointer = ptr.getValue();
        final Pointer vTablePointer = interfacePointer.getPointer(0);
        final Pointer[] vTable = new Pointer[3];
        vTablePointer.read(0, vTable, 0, 3);

        return new IContextMenu() {
            @Override
            public WinNT.HRESULT QueryContextMenu(WinDef.HMENU hMenu, int indexMenu, int idCmdFirst, int idCmdLast, int uFlags) {
                Function f = Function.getFunction(vTable[2], Function.ALT_CONVENTION);
                return new WinNT.HRESULT(f.invokeInt(new Object[] { interfacePointer, hMenu.getPointer(), indexMenu, idCmdFirst, idCmdLast, uFlags }));
            }

            @Override
            public WinNT.HRESULT InvokeCommand(Pointer pici) {
                Function f = Function.getFunction(vTable[1], Function.ALT_CONVENTION);
                return new WinNT.HRESULT(f.invokeInt(new Object[]{ interfacePointer, pici }));
            }

            @Override
            public WinNT.HRESULT GetCommandString(IntByReference idCmd, int uType, IntByReference pReserved, WTypes.LPSTR pszName, int cchMax) {
                Function f = Function.getFunction(vTable[0], Function.ALT_CONVENTION);
                return new WinNT.HRESULT(f.invokeInt(new Object[] { interfacePointer, idCmd, uType, pReserved, pszName, cchMax }));
            }
        };
    }
}

}

A to jest mój kod do wywołania QueryContextMenu:

IContextMenu contextMenu = null;
PointerByReference contextMenuPtr = new PointerByReference();
Pointer ppidlsPointer = new Memory(Native.POINTER_SIZE * ppidls.length);
ppidlsPointer.setPointer(0, ppidls[0].getValue());
hResult = psfParentFolder.GetUIObjectOf(null, 1, ppidls[0].getPointer(), new Guid.REFIID(IContextMenu.IID_IContextMenu), new IntByReference(0), contextMenuPtr);
if (!COMUtils.SUCCEEDED(hResult))
   return false;

/* End Section */

/* Begin Get Context Menu Section */

contextMenu = IContextMenu.Converter.PointerToIContextMenu(contextMenuPtr);
WinDef.HMENU hMenu = User32Ex.INSTANCE.CreatePopupMenu();
hResult = contextMenu.QueryContextMenu(hMenu, 0, 1, 30, 0x00000004);
if (!COMUtils.SUCCEEDED(hResult)) {
   int error = Native.getLastError();
   return false;
}

int count = User32Ex.INSTANCE.GetMenuItemCount(hMenu.getPointer());
if (count > 0) {
}

/* End Section */

Odpowiedzi

DanielWiddis Aug 25 2020 at 03:18

Możesz nie czytać, co myślisz z IContextMenuVtbl. Ten kod wskazuje, że zbierasz tylko 3 wskaźniki funkcji:

final Pointer[] vTable = new Pointer[3];
vTablePointer.read(0, vTable, 0, 3);

Jednak ten interfejs (jak większość interfejsów COM) rozszerza się IUnknown, więc automatycznie pobiera metody QueryInterfaceo indeksie 0, AddRefindeksie 1 i Releaseindeksie 2. Są to więc trzy metody, które wydają się wywoływać, chociaż mają niewłaściwe argumenty.

Z pliku nagłówkowego indeksy wskaźników są kontynuowane:

  • 3: QueryContextMenu
  • 4: InvokeCommand
  • 5: GetCommandString

Możesz wczytać wszystkie 6 wskaźników funkcji i odwołać się do ostatnich 3 lub spróbować odczytać z przesunięciem o 3 wskaźniki, używając istniejącej numeracji (ale w odwrotnej kolejności!)

Uważam, że powinieneś również dodać extends IUnknowndo swojej definicji interfejsu.