如何可靠地获取 Outlook 2013 插件中联系人上下文菜单的对象?

How can I reliably get the object of a contact context menu in an Outlook 2013 addin?

我在 the example in this article 之后的 Outlook 2013 中为 联系人 添加了一个上下文菜单条目。这是 XML:

<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui" onLoad="Ribbon_Load">
  <contextMenus>
    <contextMenu idMso="ContextMenuContactItem">
      <button id="MyContextMenuContactItem" label="Do something..." onAction="OnDoSomething"  insertAfterMso="DialMenu"/>
    </contextMenu>
  </contextMenus>
</customUI>

条目正确显示在菜单中,当我单击它时,我的事件处理程序被执行:

public void OnDoSomething(IRibbonControl control)
{
    object context = control.Context;
    System.Diagnostics.Debug.WriteLine(context.GetType());
    if ((context as IMsoContactCard) != null) System.Diagnostics.Debug.WriteLine("IMsoContactCard");
    if ((context as ContactItem) != null) System.Diagnostics.Debug.WriteLine("ContactItem");
    if ((context as ContactCard) != null) System.Diagnostics.Debug.WriteLine("ContactCard");
    if ((context as _ContactItem) != null) System.Diagnostics.Debug.WriteLine("_ContactItem");
}

referenced article 似乎表明上下文应该是 IMsoContactCard,但这不是我得到的。打印出 context.GetType() 的行正在显示 System.__ComObject.

This article here 似乎表明我应该能够将该对象转换成有用的东西,但是所有将其转换成合乎逻辑的东西的尝试(IMsoContactCardContactItemContactCard_ContactItem) 都失败了。

为了回避这个问题,我按照 the instructions in this article 尝试跟踪当前选择的项目。这实际上工作得很好,需要注意的是当前选择的项目并不总是上下文菜单适用的项目。

具体来说,我可以左键单击一个联系人,它会突出显示并且我的选择事件会触发。如果我然后右键单击 不同的联系人 以调出上下文菜单 而不是先左键单击它 ,那么该联系人将被勾勒出来但未突出显示,并且未触发我的选择事件。发生这种情况时,我最终将上下文菜单单击应用到错误的联系人。

如有任何建议或指导,我们将不胜感激。谢谢。


根据 Eugene Astafiev 提供的信息更新解决方案

根据下面提供的信息,我能够确定此特定回调的 ContextMicrosoft.Office.Interop.Outlook.Selection 类型。然后我可以使用它来获取 ContactItem 如下:

private ContactItem GetContactItemFromControlContext(IRibbonControl control)
{
    var selection = control.Context as Selection;
    if (selection != null && selection.Count == 1) 
        return selection[1] as ContactItem;
    else
        return null;
}

上下文对象的类型取决于您单击的位置和上下文菜单类型。要获取基础类型,您需要执行以下操作:

  1. 将对象转换为 IDispatch 接口。
  2. 使用 IDispatch 接口的 GetTypeInfo 方法获取 ITypeInfo 接口。
  3. 使用 ITypeInfo 接口的 GetDocumentation 方法获取类型名称。

    public static string GetTypeName(object comObj)
    {
    
        if (comObj == null)
            return String.Empty;
    
        if (!Marshal.IsComObject(comObj))
            //The specified object is not a COM object
            return String.Empty;
    
        IDispatch dispatch = comObj as IDispatch;
        if (dispatch == null)
            //The specified COM object doesn't support getting type information
            return String.Empty;
    
        ComTypes.ITypeInfo typeInfo = null;
        try
        {
            try
            {
                // obtain the ITypeInfo interface from the object
                dispatch.GetTypeInfo(0, 0, out typeInfo);
            }
            catch (Exception ex)
            {
                //Cannot get the ITypeInfo interface for the specified COM object
                return String.Empty;
            }
    
            string typeName = "";
            string documentation, helpFile;
            int helpContext = -1;
    
            try
            {
                //retrieves the documentation string for the specified type description 
                typeInfo.GetDocumentation(-1, out typeName, out documentation,
                    out helpContext, out helpFile);
            }
            catch (Exception ex)
            {
                // Cannot extract ITypeInfo information
                return String.Empty;
            }
            return typeName;
        }
        catch (Exception ex)
        {
            // Unexpected error
            return String.Empty;
        }
        finally
        {
            if (typeInfo != null) Marshal.ReleaseComObject(typeInfo);
        }
    }
    

    }

有关详细信息,请参阅 HowTo: get the actual type name behind System.__ComObject with Visual C# or VB.NET