Search code examples
javajna

JNA call Win32 API method IShellFolder::GetUIObjectOf


I have a problem with calling this method IShellFolder::GetUIObject I don't know how to create Pointer to Array of Pointers as the 3rd argument of this function. In the documentation the header of this function is:

HRESULT GetUIObjectOf(
  HWND                  hwndOwner,
  UINT                  cidl,
  PCUITEMID_CHILD_ARRAY apidl,
  REFIID                riid,
  UINT                  *rgfReserved,
  void                  **ppv
);

This is my code:

String directory = "c:\\Users";
String file = "c:\\Users\\Duchon\\Downloads\\Baumüller Brno, s.r.o.PS43668.prpkg";
try {
   PointerByReference psfDesktopPTR = new PointerByReference();
   WinNT.HRESULT hResult = Shell32.INSTANCE.SHGetDesktopFolder(psfDesktopPTR);
   if (COMUtils.SUCCEEDED(hResult)) {
      IntByReference pcheaten = new IntByReference();
      PointerByReference ppidl = new PointerByReference();
      IntByReference pdwAttributes = new IntByReference();
      MyIShellFolder psfDesktop = MyIShellFolder.Converter.PointerToIShellFolder(psfDesktopPTR);
      hResult = psfDesktop.ParseDisplayName(null, null, new WString(file), pcheaten, ppidl, pdwAttributes);
      PointerByReference iContextMenuPtr = new PointerByReference();
      if (COMUtils.SUCCEEDED(hResult)) {
         Pointer[] ppidls = new Pointer[1];
         ppidls[0] = ppidl.getValue();
         hResult = psfDesktop.GetUIObjectOf(null, 1, ppidl.getValue(), new Guid.REFIID(IContextMenu.IID_IContextMenu), new IntByReference(), iContextMenuPtr);
         if (COMUtils.SUCCEEDED(hResult)) {
             // QueryIContextMenu ...
         }
      }
   }
}
catch (Exception e) {
   e.printStackTrace(System.err);
}

But I get invalid memory access exception. I need a solution for array of files, not only for one. Thank you very much.


Solution

  • When you get an Invalid Memory Access error, it's a clue that you need to properly allocate Native memory. Your code above only declares a Java-side Pointer array.

    Arrays in C use contiguous memory. This means you must allocate a single block of native memory large enough for the array; it is not enough to collect a bunch of individual allocations (which is what declaring a single Pointer variable in Java does).

    You have two primary options for allocating this block of native memory:

    Option 1. Use JNA's Memory class to explicitly allocate the size of memory you will need. If you allocating an array of Pointers, you will allocate like this: Memory m = new Memory(numberOfElements * Native.POINTER_SIZE); When you get the returned value into this memory you will use offsets to pull the appropriate pointer from the array, e.g., for the 0-indexed ith pointer, do Pointer p = m.getPointer(i * Native.POINTER_SIZE);

    Option 2. Create a Structure of the appropriate size (in this case, containing a single element which is a Pointer) and use Structure.toArray() to allocate the Structure array. So you could define:

        @FieldOrder ({"childId"})
        class PCUITEMID_CHILD extends Structure {
          public Pointer childId;
        }
    

    And then allocate the array

    PCUITEM_CHILD[] pcuItemIdArray = new PCUITEMID_CHILD().toArray(numberOfElements);
    

    Then you can pass this array variable, and access its result using traditional array syntax.

    Pointer p = pcuItemIdArray[0].childId;