Groups | Blog | Home
all groups > dotnet interop > march 2005 >

dotnet interop : IContextMenu and IContextMenu question


RonNanko NO[at]SPAM gm-squared.de
3/11/2005 12:05:20 PM
Hi,

I am working on a contextmenu shell extension, which works quite fine
as long as I implement the IContextMenu interface.

However, as I need to include an owner-drawn menu item, I would like
to switch to IContextMenu3 (as I read that IContextMenu2 is not called
by the shell). When I do this, interestingly
IContextMenu2::HandleMenuMsg is called instead of
IContextMenu3::HandleMenuMsg2 and the umsg parameter contains some
strange (seemingly random) number. HandleMenuMsg gets called three
times.

Furthermore, as I return a value of 0 (S_OK) after (not) handling
HandleMenuMsg (as I do not know what the umsg means), I get a null
pointer exception error (reference to memory block 0x...000c).

Below you will find an extract of the sources. I have includes the
QueryContextMenu method so that you can take a look at the
initialization of the menu. Of course I have only abbreviated
HandleMenuMsg and HandleMenuMsg2 to keep the post small. ;)

Any "Helpers"-functions you might find are only DLLImports of
Win32-functions of the same name.

Hope someone can help,
Ron

namespace ShellExt
{
[ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
GuidAttribute("000214e4-0000-0000-c000-000000000046")]
public interface IContextMenu
{
[PreserveSig()]
int QueryContextMenu(uint hmenu, uint iMenu, int idCmdFirst, int
idCmdLast, uint uFlags);
[PreserveSig()]
void InvokeCommand (IntPtr pici);
[PreserveSig()]
void GetCommandString(int idcmd, uint uflags, int reserved,
StringBuilder commandstring, int cch);
}

[ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
GuidAttribute("000214e4-0000-0000-c000-000000000046")]
public interface IContextMenu2 : IContextMenu
{
[PreserveSig]
int HandleMenuMsg( uint uMsg, IntPtr wParam, IntPtr lParam );
}

[ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
GuidAttribute("BCFCE0A0-EC17-11D0-8D10-00A0C90F2719")]
public interface IContextMenu3 : IContextMenu2
{
[PreserveSig()]
int HandleMenuMsg2( uint uMsg, IntPtr wParam, IntPtr lParam, IntPtr
plResult );
}

public class AABBContextMenuExtension : IShellExtInit, IContextMenu3,
IPersistFile, IQueryInfo
{

....

int IContextMenu.QueryContextMenu(uint hMenu, uint iMenu, int
idCmdFirst, int idCmdLast, uint uFlags)
{
int nID = 0;
if ( (uFlags & 0xf) == 0 || (uFlags & (uint)CMF.CMF_EXPLORE) != 0)
{
MENUITEMINFO mii = new MENUITEMINFO();
mii.cbSize = 48;
mii.fMask = (uint) MIIM.TYPE | (uint)MIIM.STATE;
mii.fType = (uint) MF.STRING;
mii.dwTypeData = "myMenu";
mii.fState = (uint) MF.ENABLED;
Helpers.InsertMenuItem(hMenu, (uint)(iMenu + ++nID), 1, ref mii);
}

return nID;
}

int IContextMenu3.HandleMenuMsg2(uint uMsg, IntPtr wParam, IntPtr
lParam, IntPtr plResult )
{
return (0);
}

int IContextMenu2.HandleMenuMsg(uint uMsg, IntPtr wParam, IntPtr
lParam)
{
return (0);
}

Mattias Sjögren
3/11/2005 10:00:55 PM
Ron,

[quoted text, click to view]

Interface inheritance doesn't work the way you expect with COM
interop. You have to include all methods of the base interface
(IContextMenu here) in the derived interface (IContextMenu2).



Mattias

--
Mattias Sjögren [MVP] mattias @ mvps.org
http://www.msjogren.net/dotnet/ | http://www.dotnetinterop.com
RonNanko NO[at]SPAM gm-squared.de
3/12/2005 2:03:55 AM
Mattias, thanks for the quick reply. I now did it like this:

[ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
GuidAttribute("000214e4-0000-0000-c000-000000000046")]
public interface IContextMenu2 : IContextMenu
{
[PreserveSig()]
new int QueryContextMenu(uint hmenu, uint iMenu, int idCmdFirst, int
idCmdLast, uint uFlags);
[PreserveSig()]
new void InvokeCommand (IntPtr pici);
[PreserveSig()]
new void GetCommandString(int idcmd, uint uflags, int reserved,
StringBuilder commandstring, int cch);

[PreserveSig]
int HandleMenuMsg( uint uMsg, IntPtr wParam, IntPtr lParam );
}

[ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
GuidAttribute("BCFCE0A0-EC17-11D0-8D10-00A0C90F2719")]
public interface IContextMenu3 : IContextMenu2
{
[PreserveSig()]
new int QueryContextMenu(uint hmenu, uint iMenu, int idCmdFirst, int
idCmdLast, uint uFlags);
[PreserveSig()]
new void InvokeCommand (IntPtr pici);
[PreserveSig()]
new void GetCommandString(int idcmd, uint uflags, int reserved,
StringBuilder commandstring, int cch);
[PreserveSig]
new int HandleMenuMsg( uint uMsg, IntPtr wParam, IntPtr lParam );

[PreserveSig()]
int HandleMenuMsg2( uint uMsg, IntPtr wParam, IntPtr lParam, IntPtr
plResult );
}

Did I get the interfaces right?

Then, in QueryContextMenu, I create a menu item like this (nID is
initialized with 0):

MENUITEMINFO oOwnerDrawn = new MENUITEMINFO();
oOwnerDrawn.cbSize = 48;
oOwnerDrawn.fMask = (uint) MIIM.TYPE | (uint) MIIM.ID;
oOwnerDrawn.wID = ++nID;
oOwnerDrawn.fType = (uint) MF.OWNERDRAW;
oOwnerDrawn.dwTypeData = "applicationDefinedData";
Helpers.InsertMenuItem(hMenu, (uint)(iMenu + nID), 1, ref
oOwnerDrawn);

(where Helpers.InsertMenuItem actually is a DLLImport of
user32.InsertMenuItem)

This results in a blank menu item shown in the context menu, neither
HandleMenuMsg nor HandleMenuMsg2 are called. Here are their prototypes:

public int HandleMenuMsg2(uint uMsg, IntPtr wParam, IntPtr lParam,
IntPtr plResult );
public int HandleMenuMsg(uint uMsg, IntPtr wParam, IntPtr lParam);

What am I missing?

-- Ron
Mattias Sjögren
3/14/2005 11:54:23 PM

[quoted text, click to view]

Looks good enough that they should work (but not very Win64 friendly).


[quoted text, click to view]

By passing 1 (true) as the third argument you're inserting by
position. You should probably pass 0 (false) to insert by ID.



Mattias

--
Mattias Sjögren [MVP] mattias @ mvps.org
http://www.msjogren.net/dotnet/ | http://www.dotnetinterop.com
RonNanko NO[at]SPAM gm-squared.de
3/14/2005 11:55:12 PM
[quoted text, click to view]
friendly).

I am open for suggestions ;)

[quoted text, click to view]

Changing this causes the menu item to appear at the very bottom of the
context menu, as compared to the "1"-version, where it appeared at the
very top.

The problem still persists though, neither HandleMenuMsg2 nor
HandleMenuMsg get called. Getting paranoid, I inserted a MessageBox
into those methods, just to make sure that it was not a debugging
issue, where the breakpoint is ignored. But no MessageBox either.

Seems to me like the message is intercepted elsewhere - whereever that
might be. Any ideas?

-- Ron
AddThis Social Bookmark Button