Invalid parameter implementing GetMenuItemInfoW with JNA

1

I am invoking the win32 API function GetMenuItemInfoW. When I invoke the function I it returns false, and Native.getLastError() returns 87:

ERROR_INVALID_PARAMETER 87 (0x57) The parameter is incorrect.

I think that my error is in my implementation of the structure MenuItemInfoW:

@Structure.FieldOrder({"cbSize", "fMask", "fType", "fState", "wId", "hSubMenu", "hBmpChecked", "hBmpUnchecked", "dwItemData", "dwTypeData", "cch", "hbmpItem"})
public class MENUITEMINFOW extends Structure {
   public int cbSize;
   public int fMask;
   public int fType;
   public int fState;
   public int wId;
   public Pointer hSubMenu;
   public Pointer hBmpChecked;
   public Pointer hBmpUnchecked;
   public WinDef.ULONGByReference dwItemData;
   public WString dwTypeData;
   public int cch;
   public Pointer hbmpItem;

   public MENUITEMINFOW() {
       super();
   }

   public MENUITEMINFOW(Pointer pointer) {
      super(pointer);
   }
}

And this is my code for invoking the function:

MENUITEMINFOW menuiteminfow = new MENUITEMINFOW();
menuiteminfow.fMask = 0x00000040 | 0x00000080 | 0x00000004 | 0x00000002;
menuiteminfow.fType = 0x00000000;
menuiteminfow.cch = 256;
menuiteminfow.dwTypeData = new WString(String.join("", Collections.nCopies(256, " ")));
menuiteminfow.cbSize = Native.getNativeSize(menuiteminfow.getClass());

WinDef.BOOL result = User32Ex.INSTANCE.GetMenuItemInfoW(hMenu.getPointer(), 0, true, menuiteminfow.getPointer());
if (!result.booleanValue()) {
   int errorCode = Native.getLastError();
   System.out.println("Error Code: " + errorCode);
}

I have edited my editing my code for your tip, but I get the same error 87. This is my new code:

MENUITEMINFOW menuiteminfow = new MENUITEMINFOW();                                                                                                 
menuiteminfow.fMask = 0x00000040 | 0x00000080 | 0x00000004 | 0x00000002;                                                                           
menuiteminfow.fType = 0x00000000;                                                                                                                  
menuiteminfow.cch = 0;                                                                                                                             
menuiteminfow.dwTypeData = Pointer.NULL;                                                                                                           
menuiteminfow.cbSize = Native.getNativeSize(menuiteminfow.getClass());                                                                             
                                                                                                                                               
WinDef.BOOL result = User32Ex.INSTANCE.GetMenuItemInfoW(hMenu.getPointer(), 
new WinDef.UINT(0), new WinDef.BOOL(true), menuiteminfow.getPointer());
if (!result.booleanValue()) {                                                                                                                      
   int errorCode = Native.getLastError();                                                                                                         
   System.out.println("Error Code: " + errorCode);                                                                                                
}               

And this is new version of my structure:

@Structure.FieldOrder({"cbSize", "fMask", "fType", "fState", "wId", "hSubMenu", "hBmpChecked", "hBmpUnchecked", "dwItemData", "dwTypeData", "cch", "hbmpItem"})
public class MENUITEMINFOW extends Structure {                                                                                                                 
    public int cbSize;                                                                                                                                         
    public int fMask;                                                                                                                                          
    public int fType;                                                                                                                                          
    public int fState;                                                                                                                                         
    public int wId;                                                                                                                                            
    public Pointer hSubMenu;                                                                                                                                   
    public Pointer hBmpChecked;                                                                                                                                
    public Pointer hBmpUnchecked;                                                                                                                              
    public BaseTSD.LONG_PTR dwItemData;                                                                                                                        
    public Pointer dwTypeData;                                                                                                                                 
    public int cch;                                                                                                                                            
    public WinDef.HBITMAP hbmpItem;                                                                                                                            
                                                                                                                                                           
    public MENUITEMINFOW() {                                                                                                                                   
       super();                                                                                                                                               
    }                                                                                                                                                          
                                                                                                                                                           
    public MENUITEMINFOW(Pointer pointer) {                                                                                                                    
       super(pointer);                                                                                                                                        
                                                                                                                                                           
       this.read();                                                                                                                                           
    }                                                                                                                                                          
}

Thank you for your response.

java
winapi
jna
asked on Stack Overflow Aug 26, 2020 by xasd89 • edited Aug 28, 2020 by xasd89

1 Answer

0

Your structure mapping can be improved.

First, a ULONG_PTR is not the same as a WinDef.ULONGByReference. The ULONG_PTR is a pointer-sized value, but it does not actually point to anything.

JNA has a built-in BaseTSD.LONG_PTR type which you could use here.

While a Pointer technically works for hbmpItem and the other bitmap fields, the type WinDef.HBITMAP is already defined in JNA so you should use that mapping.

Finally, and the likely cause of your error, dwTypeData receives a String, but it is a buffer that you need to allocate and fill. From the MENUITEMINFO docs:

To retrieve a menu item of type MFT_STRING, first find the size of the string by setting the dwTypeData member of MENUITEMINFO to NULL and then calling GetMenuItemInfo. The value of cch+1 is the size needed. Then allocate a buffer of this size, place the pointer to the buffer in dwTypeData, increment cch, and call GetMenuItemInfo once again to fill the buffer with the string.

So in this case, you'd want to map a Pointer to dwTypeData, call the function the first time with that pointer set to Pointer.NULL, and then alloating memory for it based on incrementing cch:

menuiteminfow.cch++; 
// allocate 2 bytes per widechar
menuiteminfow.dwTypeData = new Memory(cch * 2);

You can fetch the string from there after the function call with menuiteminfow.dwTypeData.getWideString().

answered on Stack Overflow Aug 26, 2020 by Daniel Widdis • edited Aug 27, 2020 by Daniel Widdis

User contributions licensed under CC BY-SA 3.0