Implementation of IContextMenu COM interface using JNA

1

I need all items from Windows Explorer Shell Menu. I am implementing with jna the IShellFolder COM object interface. But now I have a problem with implementation the interface IContextMenu for query the context menu. When I invoke the QueryContextMenu function the result of HResult is 0 like true, but the HMenu has been empty when I invoke function GetMenuItemCount and the result is 0.

So can you tell me where I have a bug. Thank you very much.

This is my code for 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 }));
            }
        };
    }
}

}

And this is my code to invoke the 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 */
java
winapi
com
jna
vtable
asked on Stack Overflow Aug 24, 2020 by xasd89 • edited Aug 24, 2020 by Daniel Widdis

1 Answer

0

You may not be reading what you think from the IContextMenuVtbl. This code indicates you're only collecting 3 function pointers:

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

However, this interface (as most COM interfaces) extends IUnknown, so automatically gets the methods QueryInterface at index 0, AddRef at index 1, and Release at index 2. So those are the three methods you appear to be calling, although with the wrong arguments.

From the header file, the pointer indices continue:

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

You can either read in all 6 function pointers and reference the last 3, or attempt to read at an offset of 3 pointers using your existing numbering (but in reverse order!)

I believe you should also add an extends IUnknown to your interface definition.

answered on Stack Overflow Aug 24, 2020 by Daniel Widdis • edited Aug 24, 2020 by Remy Lebeau

User contributions licensed under CC BY-SA 3.0