现在的位置: 首页 > 综合 > 正文

WM5 SDK Sample InboxMenuExtensibility的一个BUG

2013年06月27日 ⁄ 综合 ⁄ 共 6469字 ⁄ 字号 评论关闭

http://www.niuray.com/develop/windows-mobile/inboxmenuextensibility-bug-fix/

 

$(Windows Mobile 5.0 Pocket PC SDK)/Samples/CPP/Win32/Inboxmenuextensibility是一个演示如何在系统收件箱中插入用户菜单和上下文菜单的程序。程序的核心是实现IContextMenu
接口,很不幸的是这个例子中QueryContextMenu的返回值是错误的。

 

咱们先来看看这个接口函数的定义:

HRESULT QueryContextMenu (
HMENU
hmenu

,
UINT
indexMenu

,
UINT
idCmdFirst

,
UINT
idCmdLast

,
UINT
uFlags


);

每个参数的具体含义可以参考MSDN,这里不再详述。例子返回的是一个真实的HRESULT,成功返回S_OK,失败返回对应的错误代码如E_INVALIDARG等。

 1:
 ///////////////////////////////////////////////////////////////////////////////

 2:
 // QueryContextMenu - IContextMenu interface Method

 3:
 //

 4:
 // Adds the extra menu items to the context menu. This method is called when

 5:
 // the menu which our menu extension is extended is being created

 6:
 //

 7:
 // Arguments:

 8:
 // [IN] HMENU hmenu - a handle the menu to insert into

 9:
 // [IN] UINT indexMenu - the position the item will be inserted after

 10:
 // [IN] UINT idCmdFirst - the lowest possible ID for a new command to use

 11:
 // [IN] UINT idCmdLast - the highest possible ID for a new command to use

 12:
 // [IN] LPCTSTR szText - the menu text of the new item

 13:
 // [IN] UINT uFlags - a set of flags indicating what type of insertion and

 14:
 // extension is allowed. Please see the QueryContextMenu topic in the

 15:
 // the SDK documentation for a complete list of these flags

 16:
 // NOTE: The inbox application will only ever send CMF_NORMAL

 17:
 //

 18:
 //

 19:
 // Return Values:

 20:
 // HRESULT - S_OK on success else E_FAIL

 21:
 //

 22:
 HRESULT STDMETHODCALLTYPE MenuExtension::QueryContextMenu(HMENU hmenu,

 23:
 UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)

 24:
 {

 25:
 HRESULT hr = S_OK;

 26:
  

 27:
 m_idc1 = idCmdFirst;

 28:
 m_idc2 = idCmdFirst + 1;

 29:
  

 30:
 CBR(m_idc2 <= idCmdLast);

 31:
  

 32:
 BOOL fEnableItem;

 33:
 TCHAR szItemName[30];

 34:
 switch
(m_ExtensionType)

 35:
 {

 36:
 case
 Context:

 37:
 // Get item text

 38:
 hr = GetCommandStringHelper(m_idc1, szItemName, ARRAYSIZE(szItemName));

 39:
 CHR(hr);

 40:
  

 41:
 // Insert the item

 42:
 hr = InsertMenuItem(hmenu, indexMenu, m_idc1, szItemName);

 43:
 CHR(hr);

 44:
  

 45:
 // Determine if we are in an appropriate folder and

 46:
 // have only a SINGLE item selected

 47:
 // if NOT - then gray out this menu option

 48:
 fEnableItem = EnableAddSenderToContacts();

 49:
 ::EnableMenuItem(hmenu, m_idc1, fEnableItem ? MF_ENABLED : MF_GRAYED);

 50:
 

 51:
 break
;

 52:
 

 53:
 case
 Softkey:

 54:
  

 55:
 // Get item text

 56:
 hr = GetCommandStringHelper(m_idc1, szItemName, ARRAYSIZE(szItemName));

 57:
 CHR(hr);

 58:
  

 59:
 // Insert the item

 60:
 hr = InsertMenuItem(hmenu, indexMenu, m_idc1, szItemName);

 61:
 CHR(hr);

 62:
  

 63:
 // Get item text

 64:
 hr = GetCommandStringHelper(m_idc2, szItemName, ARRAYSIZE(szItemName));

 65:
 CHR(hr);

 66:
  

 67:
 // Insert the item

 68:
 hr = InsertMenuItem(hmenu, indexMenu, m_idc2, szItemName);

 69:
 CHR(hr);

 70:
  

 71:
 // Determine if there are items in the ListView

 72:
 // if there are no items - then gray out these menu options

 73:
 fEnableItem = AreThereMessagesInFolder();

 74:
 ::EnableMenuItem(hmenu, m_idc1, fEnableItem ? MF_ENABLED : MF_GRAYED);

 75:
 ::EnableMenuItem(hmenu, m_idc2, fEnableItem ? MF_ENABLED : MF_GRAYED);

 76:
 break
;

 77:
 

 78:
 }

 79:
 

 80:
  

 81:
 Error:

 82:
 return
(hr);

 83:
 }

但看MSDN实际要求的返回值是这样描述的:

Return Value

QueryContextMenu
returns the number of items that were added to the context menu
. The number of items added is also the maximum menu item ID offset (USHORT
) in the code field (LOWORD
) of the scode.


Note:

If there are gaps between menu items, the number is provided as if there were no gaps.

Remarks

The QueryContextMenu
method can insert one or more new menu items into the specified menu hmenu
, at the specified location indexMenu
, with the IDs of those menu items. IDs must be in the range specified from idCmdFirst
to idCmdLast
. The QueryContextMenu
method returns the maximum menu item ID offset (USHORT
) in the code field (LOWORD
) of the scode.

The HIWORD
is reserved for context specific communications, and the LOWORD
is reserved for system use.

也就是说QueryContextMenu实际应该返回添加到context menu的个数,当然这个说法也并不准确,准确的说法应该是你添加菜单项的最大ID-idCmdFirst+1。
对于这个例子来说,应该做如下修改:

 1:
 ///////////////////////////////////////////////////////////////////////////////

 2:
 // QueryContextMenu - IContextMenu interface Method

 3:
 //

 4:
 // Adds the extra menu items to the context menu. This method is called when

 5:
 // the menu which our menu extension is extended is being created

 6:
 //

 7:
 // Arguments:

 8:
 // [IN] HMENU hmenu - a handle the menu to insert into

 9:
 // [IN] UINT indexMenu - the position the item will be inserted after

 10:
 // [IN] UINT idCmdFirst - the lowest possible ID for a new command to use

 11:
 // [IN] UINT idCmdLast - the highest possible ID for a new command to use

 12:
 // [IN] LPCTSTR szText - the menu text of the new item

 13:
 // [IN] UINT uFlags - a set of flags indicating what type of insertion and

 14:
 // extension is allowed. Please see the QueryContextMenu topic in the

 15:
 // the SDK documentation for a complete list of these flags

 16:
 // NOTE: The inbox application will only ever send CMF_NORMAL

 17:
 //

 18:
 //

 19:
 // Return Values:

 20:
 // HRESULT - S_OK on success else E_FAIL

 21:
 //

 22:
 HRESULT STDMETHODCALLTYPE MenuExtension::QueryContextMenu(HMENU hmenu,

 23:
 UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)

 24:
 {

 25:
 HRESULT hr = S_OK;

 26:
 int
 nMaxId = 0;

 27:
  

 28:
 m_idc1 = idCmdFirst;

 29:
 m_idc2 = idCmdFirst + 1;

 30:
  

 31:
 CBR(m_idc2 <= idCmdLast);

 32:
  

 33:
 BOOL fEnableItem;

 34:
 TCHAR szItemName[30];

 35:
 switch
(m_ExtensionType)

 36:
 {

 37:
 case
 Context:

 38:
 // Get item text

 39:
 hr = GetCommandStringHelper(m_idc1, szItemName, ARRAYSIZE(szItemName));

 40:
 CHR(hr);

 41:
  

 42:
 // Insert the item

 43:
 hr = InsertMenuItem(hmenu, indexMenu, m_idc1, szItemName);

 44:
 CHR(hr);

 45:
  

 46:
 // Determine if we are in an appropriate folder and

 47:
 // have only a SINGLE item selected

 48:
 // if NOT - then gray out this menu option

 49:
 fEnableItem = EnableAddSenderToContacts();

 50:
 ::EnableMenuItem(hmenu, m_idc1, fEnableItem ? MF_ENABLED : MF_GRAYED);

 51:
 nMaxId = m_idc1;

 52:
 

 53:
 break
;

 54:
 

 55:
 case
 Softkey:

 56:
  

 57:
 // Get item text

 58:
 hr = GetCommandStringHelper(m_idc1, szItemName, ARRAYSIZE(szItemName));

 59:
 CHR(hr);

 60:
  

 61:
 // Insert the item

 62:
 hr = InsertMenuItem(hmenu, indexMenu, m_idc1, szItemName);

 63:
 CHR(hr);

 64:
  

 65:
 // Get item text

 66:
 hr = GetCommandStringHelper(m_idc2, szItemName, ARRAYSIZE(szItemName));

 67:
 CHR(hr);

 68:
  

 69:
 // Insert the item

 70:
 hr = InsertMenuItem(hmenu, indexMenu, m_idc2, szItemName);

 71:
 CHR(hr);

 72:
  

 73:
 // Determine if there are items in the ListView

 74:
 // if there are no items - then gray out these menu options

 75:
 fEnableItem = AreThereMessagesInFolder();

 76:
 ::EnableMenuItem(hmenu, m_idc1, fEnableItem ? MF_ENABLED : MF_GRAYED);

 77:
 ::EnableMenuItem(hmenu, m_idc2, fEnableItem ? MF_ENABLED : MF_GRAYED);

 78:
 nMaxId = m_idc2;

 79:
 break
;

 80:
 

 81:
 }

 82:
 

 83:
  

 84:
 Error:

 85:
 //Return max item offset inserted.

 86:
 if
(nMaxId == 0)

 87:
 return
 0;

 88:
 else

 89:
 return
 (nMaxId - idCmdFirst + 1);

 90:
 // return(hr);

 91:
 }

以下是我设想的系统实现原理:

1. 系统收件箱(tmail.exe)遍历注册表中所有的插件,获得所有的插件全路径。然后按照顺序访问插件。

2. tmail.exe查询插件1的IContextMenu接口,创建IContextMenu接口实例。

3.
tmail.exe调用IContextMenu::QueryContextMenu接口,idCmdFirst和idCmdLast为预设值。设返回
值为N,tmail.exe会将idCmdFirst~idCmdFirst+N-1分配给插件1。下次调插件2时的
idCmdFirst=idCmdFirst(插件1)+N。

4. 当菜单选项被触发时,tmail.exe会根据触发的菜单ID找到对应的插件,然后调用该插件的IContextMenu::InvokeCommand接口。

几点说明:

1. 如果只有一个插件,那么不管QueryContextMenu返回任何值,只要是选择自定义的菜单,这个插件的InvokeCommand一定会被触发。

2. 如果同时挂多个插件同时其中一个插件的QueryContextMenu返回值是0,那么后面的插件会覆盖前一个的菜单项,前一个插件永远不会被调用InvokeCommand。

抱歉!评论已关闭.