Sunday, March 27, 2011

Blog Post: Writing Windows Shell Extension with .NET Framework 4 (C#, VB.NET) - Part 4: Context Menu Handler with Menu-item Bitmaps

In "Writing Windows Shell Extension with .NET Framework 4 (C#, VB.NET) - Part 1: Context Menu Handler", we introduced how to write Windows shell context menu handler using .NET 4:

Lots of developers have this follow-up question: how can I add bitmap icons to those context menu items? 

Here you are the directly working code samples from Microsoft All-In-One Code Framework!

Sample download: C#, VB.NET

Implementation Details

The menu items in the context menu were added in the implementation of IContextMenu.QueryContextMenu:

        public int QueryContextMenu(
            IntPtr hMenu,
            uint iMenu,
            uint idCmdFirst,
            uint idCmdLast,
            uint uFlags)
        {
            ......
            // Use either InsertMenu or InsertMenuItem to add menu items.
            MENUITEMINFO mii = new MENUITEMINFO();
            mii.cbSize = (uint)Marshal.SizeOf(mii);
            mii.fMask = MIIM.MIIM_STRING | MIIM.MIIM_FTYPE | MIIM.MIIM_ID | MIIM.MIIM_STATE;
            mii.wID = idCmdFirst + IDM_DISPLAY;
            mii.fType = MFT.MFT_STRING;
            mii.dwTypeData = this.menuText;
            mii.fState = MFS.MFS_ENABLED;
            if (!NativeMethods.InsertMenuItem(hMenu, iMenu, true, ref mii))
            {
                return Marshal.GetHRForLastWin32Error();
            }
            ......
       }

The MENUITEMINFO structure supports displaying a bitmap next to the menu item text.  You just need to add the MIIM_BITMAP mask in MENUITEMINFO.fMask, and point MSENUITEMINFO.hbmpItem to the HBITMAP handle of a 16x16 bitmap. The modified code is as follows.

        public int QueryContextMenu(
            IntPtr hMenu,
            uint iMenu,
            uint idCmdFirst,
            uint idCmdLast,
            uint uFlags)
        {
            ......
            // Use either InsertMenu or InsertMenuItem to add menu items.
            MENUITEMINFO mii = new MENUITEMINFO();
            mii.cbSize = (uint)Marshal.SizeOf(mii);
            mii.fMask = MIIM.MIIM_BITMAP | MIIM.MIIM_STRING | MIIM.MIIM_FTYPE |
                MIIM.MIIM_ID | MIIM.MIIM_STATE;
            mii.wID = idCmdFirst + IDM_DISPLAY;
            mii.fType = MFT.MFT_STRING;
            mii.dwTypeData = this.menuText;
            mii.fState = MFS.MFS_ENABLED;
            mii.hbmpItem = this.menuBmp;
            if (!NativeMethods.InsertMenuItem(hMenu, iMenu, true, ref mii))
            {
                return Marshal.GetHRForLastWin32Error();
            }
            ......
        }

The "this.menuBmp" is initialized in the constructor of the context menu extension class:

        public FileContextMenuExt()
        {
            // Load the bitmap for the menu item.
            Bitmap bmp = Resources.OK;  // A 16x16 bmp added to the Resources of the project.
            bmp.MakeTransparent(bmp.GetPixel(0, 0));
            this.menuBmp = bmp.GetHbitmap();
        }

And the handle is released in the destructor of the class:

        ~FileContextMenuExt()
        {
            if (this.menuBmp != IntPtr.Zero)
            {
                NativeMethods.DeleteObject(this.menuBmp);
                this.menuBmp = IntPtr.Zero;
            }
        }

With these modifications, the context menu extension will display menu items with bitmap icons.

NOTE:

1. Do release the HBITMAP handle created by Bitmap.GetHbitmap. 
A common mistake is as follows.  It will leak the bitmap handle.

            // Use either InsertMenu or InsertMenuItem to add menu items.
            MENUITEMINFO mii = new MENUITEMINFO();
            mii.cbSize = (uint)Marshal.SizeOf(mii);
            mii.fMask = MIIM.MIIM_BITMAP | MIIM.MIIM_STRING | MIIM.MIIM_FTYPE |
                MIIM.MIIM_ID | MIIM.MIIM_STATE;
            mii.wID = idCmdFirst + IDM_DISPLAY;
            mii.fType = MFT.MFT_STRING;
            mii.dwTypeData = this.menuText;
            mii.fState = MFS.MFS_ENABLED;
            mii.hbmpItem = Resources.OK.GetHbitmap();  // This will leak the bitmap handle!
            if (!NativeMethods.InsertMenuItem(hMenu, iMenu, true, ref mii))
            {
                return Marshal.GetHRForLastWin32Error();
            }

 2. Do not set MFT_BITMAP in MENUITEMINFO.fType. 
The MFT_BITMAP type is for a different bitmap purpose.  It displays the menu item using a bitmap. The low-order word of the dwTypeData member is the bitmap handle. For example,

Source: http://blogs.msdn.com/b/codefx/archive/2011/03/27/writing-windows-shell-extension-with-net-framework-4-c-vb-net-part-4-context-menu-handler-with-menu-item-bitmaps.aspx

Mcafee Maximus Mantech International Manhattan Associates

No comments:

Post a Comment