AutoHotkey Homepage AutoHotkey Community
Let's help each other out
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Menu Icons v2 (stdlib)
Goto page Previous  1, 2, 3, 4, 5, 6  Next
 
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions
View previous topic :: View next topic  
Author Message
animeaime



Joined: 04 Nov 2008
Posts: 1046

PostPosted: Tue Jan 27, 2009 6:16 pm    Post subject: Reply with quote

Ok. Now that I have the OOP i need, I'm starting to pier into your Menu Icons code. From what I can see, The only time you use dwItemData is in MI_SetMenuItemIcon. Also, you don't use dwMenuData (in a MenuInfo structure) at all. Also, you don't make use of MIIM_TYPE. Am I correct in saying this?

Now, for cleanup. You say to call MI_RemoveIcons whenever a Menu is destroyed, should the wrapper also call MI_SetMenuItemIcon(MenuNameOrHandle, ItemPos, 0) whenever a menu item is deleted? Also, is there any other cleanup that the wrapper needs to provide? Like you said, no one wants to deal with worrying about having to free values, so I'm just trying to understand what values need to be freed, and when.

Thank for again for your assistance, and of coure a BIG thanks for the template you gave me, without it I couldn't have made OOP or a menu wrapper a possibility.
_________________
As always, if you have any further questions, don't hesitate to ask.

Add OOP to your scripts via the Class Library. Check out my scripts.
Back to top
View user's profile Send private message Send e-mail
Lexikos



Joined: 17 Oct 2006
Posts: 4468
Location: Qld, Australia

PostPosted: Wed Jan 28, 2009 7:12 am    Post subject: Reply with quote

animeaime wrote:
From what I can see, The only time you use dwItemData is in MI_SetMenuItemIcon.
It is retrieved indirectly by MI_OwnerDrawnMenuItemWndProc when WM_DRAWITEM is received, via the DRAWITEMSTRUCT structure (lParam).
Quote:
Also, you don't use dwMenuData (in a MenuInfo structure) at all.
No need. Icon handles are associated with menu items, not menus.
Quote:
Also, you don't make use of MIIM_TYPE. Am I correct in saying this?
Yes.
Quote:
Now, for cleanup. You say to call MI_RemoveIcons whenever a Menu is destroyed, should the wrapper also call MI_SetMenuItemIcon(MenuNameOrHandle, ItemPos, 0) whenever a menu item is deleted?
Yes, because MI_SetMenuItemIcon explicitly deletes the previous icon or bitmap. It must be called before the item is removed. MI_RemoveIcons merely calls MI_SetMenuItemIcon for each item.
Quote:
Also, is there any other cleanup that the wrapper needs to provide?
No, other than resources used by the wrapper itself...

Keep in mind that dwItemData is only used if owner-drawing is necessary. Windows Vista and later support 32-bit bitmaps in menus, so owner-drawing is not used. MI_SetMenuItemIcon frees the previous icon or bitmap as appropriate.
Back to top
View user's profile Send private message Visit poster's website
animeaime



Joined: 04 Nov 2008
Posts: 1046

PostPosted: Wed Jan 28, 2009 7:57 am    Post subject: Reply with quote

Lexikos wrote:
[dwItemData] is retrieved indirectly by MI_OwnerDrawnMenuItemWndProc when WM_DRAWITEM is received, via the DRAWITEMSTRUCT structure (lParam).

So, in MI_OwnerDrawnMenuItemWndProc, each of those h_icon values are dwItemData (same value as if retrieved using getMenuItemInfo)?
_________________
As always, if you have any further questions, don't hesitate to ask.

Add OOP to your scripts via the Class Library. Check out my scripts.
Back to top
View user's profile Send private message Send e-mail
Lexikos



Joined: 17 Oct 2006
Posts: 4468
Location: Qld, Australia

PostPosted: Wed Jan 28, 2009 8:12 am    Post subject: Reply with quote

Yes.
Back to top
View user's profile Send private message Visit poster's website
animeaime



Joined: 04 Nov 2008
Posts: 1046

PostPosted: Wed Jan 28, 2009 8:32 am    Post subject: Reply with quote

k, thank you for that. I'll include it in the wrapper.
_________________
As always, if you have any further questions, don't hesitate to ask.

Add OOP to your scripts via the Class Library. Check out my scripts.
Back to top
View user's profile Send private message Send e-mail
animeaime



Joined: 04 Nov 2008
Posts: 1046

PostPosted: Wed Jan 28, 2009 11:19 am    Post subject: Reply with quote

I just thought of this...do I need to call MI_RemoveIcons for the left over menus when the program closes, or does the OS free that memory automatically? And if so, is this true for any allocated memory? Meaning, I only need to free the memory while running to prevent memory leaks, but on close it will free the memory for me. Or do I need to free all the memory I allocated?
_________________
As always, if you have any further questions, don't hesitate to ask.

Add OOP to your scripts via the Class Library. Check out my scripts.
Back to top
View user's profile Send private message Send e-mail
Lexikos



Joined: 17 Oct 2006
Posts: 4468
Location: Qld, Australia

PostPosted: Wed Jan 28, 2009 11:22 am    Post subject: Reply with quote

Memory and GDI resources allocated to a process are released automatically when the process terminates.
Back to top
View user's profile Send private message Visit poster's website
animeaime



Joined: 04 Nov 2008
Posts: 1046

PostPosted: Wed Jan 28, 2009 11:29 am    Post subject: Reply with quote

So the only reason we need to call removeIcons is to regain the memory while the programming is running?
_________________
As always, if you have any further questions, don't hesitate to ask.

Add OOP to your scripts via the Class Library. Check out my scripts.
Back to top
View user's profile Send private message Send e-mail
Lexikos



Joined: 17 Oct 2006
Posts: 4468
Location: Qld, Australia

PostPosted: Wed Jan 28, 2009 11:41 am    Post subject: Reply with quote

Yes.
Back to top
View user's profile Send private message Visit poster's website
animeaime



Joined: 04 Nov 2008
Posts: 1046

PostPosted: Mon Feb 02, 2009 12:29 am    Post subject: Reply with quote

Ummm... I need some help. I'm checking out the code trying to find a fix, but could use a second pair of eyes, if you don't mind that is.

The code below should output the menu handle both times. However, if the line in red's menu name is "tray", then the second msgbox shows a 0. If the menu name isn't "tray" (e.g. menu1 or menu2), then it's fine.

update:
it seems that if the tray menu's handle is retrieved at all, then there is a problem. Also, the problem only occurs when the tray menu is retrieved and later a menu item (with a submenu) is deleted from the tray menu.

update2:
OK, I can describe the problem. If MenuA's menu handle is retrieved (using MI_getMenuHande), and then later on a menu item in MenuA (with submenu MenuB) is deleted, MenuB returns 0 for it's menu handle. Now, this might be a AHK bug - based on how "specific" the problem is, I wouldn't doubt it.

update3:
I found a "fix" which will be added to my menu wrapper. I'm testing it extensively now... sorry for troubling you Very Happy

Code:
;MenuA - menu2
;MenuB - menu1
Menu, menu1, add
Menu, menu2, add, Menu1, :Menu1

;retrieve MenuA's menu handle
hMenu := MI_getMenuHandle("menu2")

;just a check of MenuB's menu handle
MsgBox, % MI_getMenuHandle("menu1")

;delete a menu item in MenuA that has MenuB as a submenu
;hMenu is the menu handle for the item about to be deleted
;MenuItemIndex is the index for soon to be removed item
DllCall("RemoveMenu", "uint", hMenu, "uint", MenuItemIndex, "uint", 0x400)
Menu, menu2, delete, Menu1

;menuB`s handle returns 0
MsgBox, % MI_getMenuHandle("menu1")

return

_________________
As always, if you have any further questions, don't hesitate to ask.

Add OOP to your scripts via the Class Library. Check out my scripts.
Back to top
View user's profile Send private message Send e-mail
Lexikos



Joined: 17 Oct 2006
Posts: 4468
Location: Qld, Australia

PostPosted: Mon Feb 02, 2009 9:58 am    Post subject: Reply with quote

It looks like you've found a bug, possibly caused by AutoHotkey's use of DeleteMenu to remove the menu item.
Quote:
Source: MSDN: DeleteMenu Function ()
The DeleteMenu function deletes an item from the specified menu. If the menu item opens a menu or submenu, this function destroys the handle to the menu or submenu and frees the memory used by the menu or submenu.
I'll look into it further.

Edit: I've posted a bug report, workaround (without using DllCall) and solution: Deleting a menu item erroneously deletes its sub-menu.
Back to top
View user's profile Send private message Visit poster's website
animeaime



Joined: 04 Nov 2008
Posts: 1046

PostPosted: Mon Feb 02, 2009 12:42 pm    Post subject: Reply with quote

But it only happens if you check the handle... if you don't check the handle, there is no bug. Removing the line in red "fixes" the problem.

Causes the bug
Code:
#SingleInstance Force
#NoEnv

; Menu, tray, NoStandard

;MenuA - tray
;MenuB - menu1
Menu, menu1, add, Item1, OutputSelection
Menu, tray, add, Menu2, :Menu1

;retrieve MenuA's menu handle
;remove this and no bug
hMenu := MI_getMenuHandle("tray")

;just a check of MenuB's menu handle
MsgBox, % MI_getMenuHandle("menu1")

MenuItemIndex := DllCall("GetMenuItemCount", "uint", hMenu) - 1

;delete a menu item in MenuA that has MenuB as a submenu
; DllCall("RemoveMenu", "uint", hMenu, "uint", MenuItemIndex, "uint", 0x400)
Menu, tray, delete, Menu2

;menuB`s handle returns 0
MsgBox, % MI_getMenuHandle("menu1")

return


No bug
Code:
#SingleInstance Force
#NoEnv

; Menu, tray, NoStandard

;MenuA - tray
;MenuB - menu1
Menu, menu1, add, Item1, OutputSelection
Menu, tray, add, Menu2, :Menu1

;retrieve MenuA's menu handle
;remove the check of MenuA's menu handle (and no bug)

;just a check of MenuB's menu handle
MsgBox, % MI_getMenuHandle("menu1")

MenuItemIndex := DllCall("GetMenuItemCount", "uint", hMenu) - 1

;delete a menu item in MenuA that has MenuB as a submenu
; DllCall("RemoveMenu", "uint", hMenu, "uint", MenuItemIndex, "uint", 0x400)
Menu, tray, delete, Menu2

;menuB`s handle returns 0
MsgBox, % MI_getMenuHandle("menu1")

return


Also, I applied your fix, but the menu handle changes values... (which implies that AHK is recreating the menu - which is problematic because it doesn't restore icons, or the dwItemData so that I could rebuild the menu icons/data structure). Haven't checked under this new structure, but when using Menu, NoStandard it recreates the menu and all the icons and my menu structures stored in the dwMenuData and dwItemData is lost.

Code:
Menu, Sub, Add, Item 1, _
Menu, Main, Add, Sub-Menu, :Sub

hMenu := MI_GetMenuHandle("main")
MsgBox, % "sub`s handle: " . MI_GetMenuHandle("sub")

; Menu, Main, Add, Plain old item, _
; Menu, Main, Show
; fixes problem, but changes the menu handle
Menu, Main, Add, Sub-Menu, _

; does fix problem
; DllCall("RemoveMenu", "uint", hMenu, "uint",0, "uint", 0x400)
Menu, Main, Delete, Sub-Menu

MsgBox, % "sub's handle: " . MI_GetMenuHandle("sub")

; Menu, Main, Show
; Menu, Sub, Show
ExitApp
_:
return

MI_GetMenuHandle(menu_name)
{
    static   h_menuDummy
    ; v2.2: Check for !h_menuDummy instead of h_menuDummy="" in case init failed last time.
    If !h_menuDummy
    {
        Menu, menuDummy, Add
        Menu, menuDummy, DeleteAll

        Gui, 99:Menu, menuDummy
        ; v2.2: Use LastFound method instead of window title. [Thanks animeaime.]
        Gui, 99:+LastFound

        h_menuDummy := DllCall("GetMenu", "uint", WinExist())

        Gui, 99:Menu
        Gui, 99:Destroy

        ; v2.2: Return only after cleaning up. [Thanks animeaime.]
        if !h_menuDummy
            return 0
    }

    Menu, menuDummy, Add, :%menu_name%

    h_menu := DllCall( "GetSubMenu", "uint", h_menuDummy, "int", 0 )

    DllCall( "RemoveMenu", "uint", h_menuDummy, "uint", 0, "uint", 0x400)
    Menu, menuDummy, Delete, :%menu_name%

    return h_menu
}

_________________
As always, if you have any further questions, don't hesitate to ask.

Add OOP to your scripts via the Class Library. Check out my scripts.
Back to top
View user's profile Send private message Send e-mail
Lexikos



Joined: 17 Oct 2006
Posts: 4468
Location: Qld, Australia

PostPosted: Mon Feb 02, 2009 1:50 pm    Post subject: Reply with quote

animeaime wrote:
But it only happens if you check the handle...
Did you try the sample code I posted in Bug Reports? It also happens if you Show the menu rather than calling MI_GetMenuHandle. I believe this is because AutoHotkey does not create the menu handle until it is needed. Such situations include when the menu is shown for the first time or added as a sub-menu of some other menu. In some cases the menu handle is deleted either by AutoHotkey or as a side-effect of a Windows API, and is recreated by AutoHotkey the next time a handle is needed.

On the other hand, this bug is evident because AutoHotkey only tries to recreate the menu if it knows or "suspects" the menu handle was destroyed. If AutoHotkey checked !IsMenu(mMenu) rather than !mMenu, the sub-menu would still be deleted when the item it is associated with is deleted, but it would be less apparent since AutoHotkey would automatically recreate the menu. It would still be apparent if the script checks the menu handle, or if the menu is also a sub-menu in some other menu

(mMenu is a field of AutoHotkey's UserMenu structure, used to store the menu handle.)

Quote:
the menu handle changes values... (which implies that AHK is recreating the menu
It is.
Quote:
which is problematic
I agree. When I get more time, I will compile a list of situations which may cause this. I haven't researched it in much depth, so I only know that it does happen.
Quote:
when using Menu, NoStandard it recreates the menu
I was vaguely aware of this. NoStandard is implemented as follows:
Code:
ResultType UserMenu::ExcludeStandardItems()
{
   if (!mIncludeStandardItems)
      return OK;
   mIncludeStandardItems = false;
   return Destroy(); // It will be recreated automatically the next time the user displays it.
}

One solution would be to not store data via the Win32 Menu API. Either way, problems will be encountered if the script doesn't go through the proper "channels" (such as removing icons before deleting a menu).

Another solution might be to implement more built-in features:
  • For menu icons, it is not an issue if the menu handle is destroyed, since the bitmaps and icons will remain valid. AutoHotkey would automatically reapply them when it recreates the menu.
  • Allowing user data to be associated with menu items would be a step forward, but might not be a complete solution without a way to detect when AutoHotkey recreates the menu. It depends on what kind of information would need to be associated with a menu item, and whether the data is relevant to the Win32 side of things. This may become clearer once I've done more research.
Built-in menu icons have recently risen in my list of priorities. You are partly to thank for that.
Back to top
View user's profile Send private message Visit poster's website
animeaime



Joined: 04 Nov 2008
Posts: 1046

PostPosted: Mon Feb 02, 2009 2:35 pm    Post subject: Reply with quote

Lexikos wrote:
Built-in menu icons have recently risen in my list of priorities. You are partly to thank for that.

What can I say, your menu icons suite is something I personally could not live without (well, you know, code wise). Whenever I get back to it...my bookmarks manager will be able to show favicons on the menu thanks to your suite (small, maybe, but I think it helps IMENSELY when looking through bookmarks - I love seeing favicons in my browser bookmarks).

Some requests. If you can, keep the dwItem/MenuData free AND COPY IT that would be great. When AHK recreates the menu, it doesn't save ALL the info (which is part of the problem). Thus, if you could add the ability to retain the dwItem/MenuData when recreating the menu, that would be AWE-SOME. Since my class library can be used to store any kind of data all linked to a single address (which is then used to "extract" the class' fields using getters/setters), if you could add the ability to retain the dwItem/MenuData, then my class library can be used to allow users to associate values (of any sorts, even other classes) with a menu/menu item - that's what the wrapper does now. Also, whenever an item is added, it stores the Label-Or-SubLabel (to allow moving a menu item from one menu to another menu - movement in the same menu doesn't require the menu label to be used, since no item is added). Also, it stores the MenuName (as seen by AHK) with the menu (just in case... and for any uses it may have). The only problem is that AHK "randomly" deletes the menu icons/dwMenuItem data. The wrapper removes the need to rebuild the menu when adding items at points other than the end, removing separators, and moving items around, but it can't, yet, compensate for the "random recreating", so if you could fix that end, that would be GREAT!

I don't want you to feel like you have to add in tons of stuff that AHK doesn't have, because what I have in the works does that. The class library (thanks again for the template for that), allows storing values (of all kinds) all referenced from a single address (extract the class data via getters/setters), so you don't need to worry about the ability to associate data with a menu, because I have that covered. Also, the ability to add items to the middle (or front) of a menu, move items within a menu, move items to another menu (while retaining the "goto label", menu data, icons, etc.) is provided (and works) with the menu wrapper I have.

Just some side notes.

A way to tell if using the Menu, add command added the menu item or only updated it would be great. Right now, I have a "hack" of adding a menu separator, storing the old size, deleting the "temp" separator (and decreasing the old size by one), adding the item, comparing the old/new size, and then, from that, knowing if the menu item was added or only updated. The only problem is that when AHK recreates the menus, since the separator was removed from a DllCall, not an AHK command, AHK doesn't realize that the menu separator doesn't exist and adds it back - which is fine if AHK didn't recreate the menu randomly, since I can control that.

Right now, the menu wrapper I have set up retains all the menu data, and all the menu item data except MIIM_ID (do you know what this is for?). I did some testing before and it caused problems, but I'm checking it again.

Here is the code my menu wrapper uses to restore menu items (to allow moving them, or inserting besides the end).

Code:
Menu_getMenuItemInfo(hMenu, Position, ByRef mii)
{
    /*
    WINVER >= 0x400
    0x1 - MIIM_STATE (included)
    0x2 - MIIM_ID (excluded, not needed, and causes problems if used)
    0x4 - MIIM_SUBMENU (included)
    0x8 - MIIM_CHECKMARKS (included)
    0x10 - MIIM_TYPE (not included; only for pre Win98)
    0x20 - MIIM_DATA (included)

    WINVER >= 0x500 (Win98/ME/2000/XP/Vista)
    these replace MIIM_TYPE
    0x40 - MIIM_STRING (excluded, but done using InsertMenu_private)
    0x80 - MIIM_BITMAP (included)
    0x100 - MIIM_FTYPE (included)
    */

    ;Creates a MenuItemInfo structure
    VarSetCapacity(mii, 48, 0), NumPut(48, mii), NumPut(0x1AD, mii, 4)

    return DllCall("GetMenuItemInfo"
        , "uInt", hMenu
        , "uInt", Position - 1
        , "int", true       ;use menu position
        , "uInt", &mii)
}


As noted, because how AHK does the "goto label" for menus, I had to "wiggle" a bit to restore the data without losing that, but it works fine...just need to find a way to remove a submenu without AHK destroying it on me Wink
_________________
As always, if you have any further questions, don't hesitate to ask.

Add OOP to your scripts via the Class Library. Check out my scripts.
Back to top
View user's profile Send private message Send e-mail
animeaime



Joined: 04 Nov 2008
Posts: 1046

PostPosted: Mon Feb 02, 2009 3:04 pm    Post subject: Reply with quote

Got it. Yet another hack, but it works. I got the idea from you. What the code does is when adding a menu item, it uses a "fake label" (Label) instead of a submenu. Then, using a DllCall, it sets the submenu. This way, AHK doesn't know about the menu (and thus, can't destroy it). If you can see any potential problems with this design, please inform me - I'm not trying to create memory leaks because of this. It still uses a DllCall to RemoveMenu on delete. Not sure why that's needed, but I'm looking into it. Once again, thanks for the idea.
_________________
As always, if you have any further questions, don't hesitate to ask.

Add OOP to your scripts via the Class Library. Check out my scripts.
Back to top
View user's profile Send private message Send e-mail
Display posts from previous:   
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Scripts & Functions All times are GMT
Goto page Previous  1, 2, 3, 4, 5, 6  Next
Page 5 of 6

 
Jump to:  
You can post new topics in this forum
You can reply to topics in this forum


Powered by phpBB © 2001, 2005 phpBB Group