I've been playing with Xtreme's excellent ownerdrawn menu example and I wanted to convert it to be able to handle resource based menus. Under 2K all works fine, the DRAWITEMSTRUCT.itemData returns a pointer to a Unicode resource string. However, the problem is with 98SE, with res menus the DRAWITEMSTRUCT.itemData just returns garbage. So I thought I'd try to find out why, I ran into an interesting problem. How do I get the string value from an owner drawn menu in 9x ?

I have tried this :

mov B[buffer],0

invoke GetMenu,[hwnd]
invoke GetSubMenu,eax,0
push eax
invoke AppendMenu,eax,MF_ENABLED + MF_OWNERDRAW ,2000,offset szMenuString

invoke SetLastError,0
mov D[mii.fMask],MIIM_STRING
mov eax,offset buffer
mov D[mii.dwTypeData],eax
mov D[mii.cch],256
pop eax
invoke GetMenuItemInfo,eax,2000,FALSE,offset mii

but all it yeilds is an empty string, no error is reported. If I set the type to MF_STRING it returns the string properly.
Posted on 2004-04-01 01:53:03 by donkey
Hello Donkey
Take a look at the source code of the XMenu object. It has the following capabilities:

Easily changing of the drawing methods to modify the ?look and feel? of menus.
Icon drawing on menu items.
Drawing of check and radio boxes.
Disabled text and icon drawing with special ROP operations
Menu language switch.
Mini text on menu separators
MDI menu

All definitions are made in the resource file (as ANSI text!).
I tested the code on Win98, NT and XP

Regards, ;)

Posted on 2004-04-01 03:36:33 by Biterider
Hi Biterider,

Thanks, I will look at it. I was more concerned with just playing around with the software and ran into the problem. It is not something that I absolutely need as I can work around it easily, the question was more a matter of interest.

The other thing that I found is that I don't know how to modify the text of an ownerdrawn menu item. Anyone know how to do that ?
Posted on 2004-04-01 09:12:24 by donkey
Hi Donkey
Thanks for your kinds words in the other post!
If you are responsible to draw the menu item you can draw what you want. You can draw the text of the menu item give by the GetMenuItemInfo API or another string stored elsewhere. Obviously, the best place to store this strings is in the menu itself using the SetMenuItemInfo API.
To use this API?s , you have to set some flags to get or set the parameters you want to handle like the state or the text itself.

Regards, :)
Posted on 2004-04-01 09:26:38 by Biterider
Hi Biterider,

The words are well deserved, you have done great work and it should be acknowledged.

That seems to work fine on my 2K box but fails on my 98SE box. For example the following code will not change the text in the menu item :

mov B[buffer],0

invoke GetMenu,[hwnd]
invoke GetSubMenu,eax,0
push eax
invoke AppendMenu,eax,MF_ENABLED + MF_STRING + MF_OWNERDRAW ,2000,offset szMnuText1

mov D[mii.fMask],MIIM_STRING
mov [mii.dwTypeData],offset szMnuText2
invoke lstrlen,offset szMnuText2
inc eax
mov D[mii.cch],eax
pop eax
invoke SetMenuItemInfo,eax,2000,FALSE,offset mii

If the item is not owner drawn it works fine, there are also no combination of flags I could find that would allow me to read the string that I just passed to the ownerdraw menu item I just appended. I tried with various flag combiations and none of them worked, I think I would have to pass the pointer in MIIM_DATA if I want to modify the string later.
Posted on 2004-04-01 09:39:32 by donkey
Hi Donkey
I remember that when I was developing the XMenu object, I had a similar problem. That is why I created the XMenuItem Object to store the text and later additional data. It seems that some OS would not take care of its menu strings if the owerdraw fags are set! :(
The only way I found is to store it in an separate structure that is under your control.


Posted on 2004-04-01 09:53:08 by Biterider
Hi Bitrider,

The answer is exceedingly dumb, SetMenuItemInfo does not seem to work but this does, welcome to the pseudo 32 bit world of Windows 9x, the schitzophrenic OS :

invoke ModifyMenu, eax, 2000, MF_BYCOMMAND+MF_OWNERDRAW, 2000, offset szMnuText2

Works for all items including resource based ones. So it would just require that I create a resource based menu without the ownerdraw and enumerate it, modifying it at startup. ie GetMenuString...ModifyMenu.
Posted on 2004-04-01 10:14:09 by donkey
Hi Donkey!
That is exactly the way I took. My first step was the creation of a ?standard menu? resource and when the XMenu object was initialized (Init method), the menu was loaded and converted to ownerdraw (Convert method), previous extraction of the crucial information from the menu item and store this info in an auxiliary object (XMenuItem). This facilitates the further insertion of new items when you are using a MDI menu.

Since I developed the XMenu stuff, I tested it on some OS, and I have clearly seen how the MS people have corrected the behavior of the menu handling, specially for ownerdraw ones over the time. In the first releases of Win98, the WM_MEASUREITEM was sent only the first time the menu was displayed. By this way, if you alter the menuitem string, there is no way to change the drawing rectangle! Fortunately, some things change to be better. I hope! :cool:


Posted on 2004-04-01 10:47:54 by Biterider
I found a way to change the drawing rectangle (I think someone on this board told me how).

All you need to do is to change the menu item type from ownerdrawn, to string and then back to ownerdrawn. I do this in my WM_INITMENUPOPUP handler, as I append accelerator keys to the menu items on the fly, and they need to have the size recalculated fairly frequently. I have used this method successfully on all the windows operating sytems.

Hope this is useful

Posted on 2004-04-01 10:57:57 by Nick
Hi Nick!
I observe that beginning with Win98SE the ?WM_MEASUREITEM bug? was corrected and NT seems never to have it. I?m not sure about Win95, but I think it has it. :rolleyes:


Posted on 2004-04-01 13:27:52 by Biterider

Did you know that there is no enumerator for menu items, I looked and looked and couldn't find one. I wrote this one and it seems to work, I have tested it to 3 nests - multiple sub menus, it will send the menu handle, id, nesting level and position to the enumeration proc, an ID of -1 indicates a submenu. The menu level is passed to indicate how far the item is nested from the main menu, for example in a menu bar the items on the bar would be 0, a popupmenu in a menu would be 1 etc... It is a recursive search and callback.

To use it :

invoke GetMenu,
Invoke EnumMenu, eax

Changed see below
Posted on 2004-04-02 02:04:30 by donkey
Win95 has the same WM_MEASUREITEM bug, but is far more stable than Win98 when it comes to changing menu parameters.

When I was developing my own owner drawn menus, the only OS I had unpredicatable problems on was Win98SE, all the others seemed to work easily - 95, NT, 2000, XP & 2003. Even running my programs through Bounds Checker didn't show up any problems, but still 98 didn't like what was happening!

Posted on 2004-04-02 02:49:00 by Nick
I've changed the enumeration to allow for a user defined callback. This will allow it to be used for multiple tasks in the same program. Everything works the same but now you have to specify a callback address:

invoke EnumMenu,,Offset EnumMenuProc

EnumMenu FRAME hMenu,pCallBack

; This is just to set the level at
; the start of the enumeration
mov D[MenuLevel], -1
invoke _EnumMenu,[hMenu],[pCallBack]
MenuLevel DD ?

_EnumMenu FRAME hMenu,pCallBack
LOCAL nItems :D
LOCAL nCount :D
LOCAL iItem :D

invoke GetMenuItemCount, [hMenu]
mov [nItems], eax
mov D[nCount], 0
jmp >L2
mov D[mii.fMask], MIIM_ID+MIIM_SUBMENU
invoke GetMenuItemInfo, [hMenu], [nCount], TRUE, OFFSET mii
cmp D[mii.hSubMenu],0
jne >
movzx eax, W[mii.wID]
push [nCount]
push [MenuLevel]
push eax
push [hMenu]
call [pCallBack]
jmp >.DONE
inc D[MenuLevel]
push [nCount]
push [MenuLevel]
push -1
push [hMenu]
call [pCallBack]
invoke _EnumMenu, [mii.hSubMenu],[pCallBack]
inc D[nCount]
mov eax, [nCount]
cmp eax, [nItems]
jb <L1

dec D[MenuLevel]

EnumMenuProc FRAME hMenu,dwID,mLevel,nPos
LOCAL buffer[256] :B

cmp D[dwID],-1
je >
; Menu item
invoke GetMenuString, [hMenu], [dwID], offset buffer, 256, MF_BYCOMMAND

; Sub menu
invoke GetMenuString, [hMenu], [nPos], offset buffer, 256, MF_BYPOSITION
Posted on 2004-04-02 13:44:24 by donkey