Implémentation de l'interface COM IContextMenu à l'aide de JNA

Aug 25 2020

J'ai besoin de tous les éléments du menu Shell de l'Explorateur Windows. J'implémente avec jna l'interface d'objet IShellFolder COM. Mais maintenant, j'ai un problème avec la mise en œuvre de l'interface IContextMenu pour interroger le menu contextuel. Lorsque j'appelle la fonction QueryContextMenu, le résultat de HResult est 0 comme true, mais le HMenu est vide lorsque j'appelle la fonction GetMenuItemCount et le résultat est 0.

Alors pouvez-vous me dire où j'ai un bug. Merci beaucoup.

Ceci est mon code pour 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 }));
            }
        };
    }
}

}

Et voici mon code pour appeler le 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 */

Réponses

DanielWiddis Aug 25 2020 at 03:18

Vous ne lisez peut-être pas ce que vous pensez du IContextMenuVtbl. Ce code indique que vous ne collectez que 3 pointeurs de fonction:

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

Cependant, cette interface (comme la plupart des interfaces COM) s'étend IUnknown, donc obtient automatiquement les méthodes QueryInterfaceà l'index 0, AddRefà l'index 1 et Releaseà l'index 2. Ce sont donc les trois méthodes que vous semblez appeler, bien qu'avec les mauvais arguments.

À partir du fichier d'en-tête , les index de pointeur continuent:

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

Vous pouvez soit lire les 6 pointeurs de fonction et référencer les 3 derniers, soit tenter de lire à un décalage de 3 pointeurs en utilisant votre numérotation existante (mais dans l'ordre inverse!)

Je pense que vous devriez également ajouter un extends IUnknownà votre définition d'interface.