我的VSTO Excel插件如何判断工作簿是否嵌入到word文档中?

时间:2016-08-24 23:10:13

标签: c# excel ms-word vsto ole

我正在处理现有的Excel VSTO插件,当用户编辑嵌入在MS Word文档中的工作簿时会导致问题。并不要求该环境中的插件功能,但它会导致嵌入故障,即使客户正在操作与插件操作无关的文件。至少,我需要让它不为该工作簿初始化自己。

我调查过的一些途径:

  1. Microsoft.Office.Interop.Excel.Workbook.Application 的文档为:“ 对象限定符,此属性返回Application对象 代表Microsoft Excel应用程序。与...一起使用时 对象限定符,此属性返回一个Application对象 表示指定对象的创建者(您可以使用它 具有OLE自动化对象的属性,用于返回应用程序 那个对象)。“这听起来很有希望,但是,我不明白 “使用对象限定符”是指在C#的上下文中。
  2. This link建议检查命令行参数。但是,如果我打开Excel独立,然后打开包含Excel对象的Word文档,Word将使用相同的实例进行嵌入,命令行参数将不包含“-embedded”标志。
  3. 我想强制OLE使用新的Excel实例(而不是重用现有的独立实例),但我也无法弄清楚如何做到这一点。
  4. 由于Excel的单个实例可以同时托管嵌入式和独立工作簿,因此这些信息必须位于工作簿级别。

3 个答案:

答案 0 :(得分:0)

您可以通过检查Workbook.Container属性来确定工作簿是否嵌入到另一个应用程序中。它将包含Word Document对象,如果嵌入了工作簿,则该对象包含嵌入的工作簿。否则调用该属性将抛出异常,因此请确保将检查包装到try-catch块中:

public bool IsEmbedded(Workbook workbook)
{
    try
    {
        // via the container you get a handle to the parent Word document object
        var container = workbook.Container;
        return true;
    }
    catch (COMException ex)
    {
        if (ex.ErrorCode == -2146822566)
        {
            // Exception message is: 
            // "This property is only available if the document is an OLE object."   
            return false;
        }

        throw;
    }
}

可以依赖Workbook.PathWorkbook.Name属性。

对于我,当我检查嵌入式Excel工作簿的这些属性时,我得到以下结果:

// this is empty for an embedded workbook
Application.Workbooks[1].Path; 

// this contains the string "Worksheet in <document name>.docx"
Application.Workbooks[1].Name;

答案 1 :(得分:0)

这可以告诉您工作簿是否是嵌入的OLE对象(检查工作簿的另一个答案.Container在Office 2016上对我不起作用): https://theofficecontext.com/2013/04/10/how-to-determine-if-an-excel-workbook-is-embedded-and-more/

public static class ExcelExtensionMethods
{

[DllImport("ole32.dll")]
static extern int CreateBindCtx(uint reserved, out IBindCtx ppbc);

/// <summary>
/// WORKBOOK EXTENSION METHOD
/// Checks to see if the Workbook is embeeded inside of 
/// another ActiveX Document type, sy=uch as Word or Excel.
/// </summary>
/// <param name="PobjWb"></param>
/// <returns></returns>
public static bool IsEmbedded(this Excel.Workbook PobjWb)
{
    if (PobjWb.Path == null || PobjWb.Path.Length == 0)
    {
        try
        {
            // requires using Microsoft.VisualStudio.OLE.Interop;
            // and you have to manually add this to reference from here:
            // C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\PrivateAssemblies\Microsoft.VisualStudio.OLE.Interop.dll
            IOleObject LobjOleObject = ((object)PobjWb) as IOleObject;
            IOleClientSite LobjPpClientSite;
            // get the client site
            LobjOleObject.GetClientSite(out LobjPpClientSite);
            // if there is one - we are embedded
            if (LobjPpClientSite != null)
            {
                return true;
            }
            else
            {
                // not embedded
                return false;
            }
        }
        catch (Exception ex)
        {
            // exception
            Debug.Print(ex.ToString());
            return false;
        }
        finally { }
    }
    else
    {
        // not embedded
        return false;
    }
}

/// <summary>
/// WORKBOOK EXTENSION METHOD
/// This method return the name of the class that we
/// are embedded inside of.
/// If we are not embedded it return null.
/// If there is any exception it return null.
/// If the container cannot be accessed it returns UNKNOWN.
/// </summary>
/// <param name="PobjWb"></param>
/// <returns></returns>
public static string EmbedClassName(this Excel.Workbook PobjWb)
{
    try
    {
        IOleObject LobjOleObject = ((object)PobjWb) as IOleObject;
        IOleClientSite LobjPpClientSite;
        // get the client site
        LobjOleObject.GetClientSite(out LobjPpClientSite);
        if (LobjPpClientSite != null)
        {
            IOleContainer LobjPpContainer;
            LobjPpClientSite.GetContainer(out LobjPpContainer);
            if (LobjPpContainer != null)
            {
                return LobjPpContainer.GetType().Name;
            }
            else
            {
                // something wrong - container is not valid
                return "UNKNOWN";
            }
        }
        else
        {
            // not embedded
            return null;
        }
    }
    catch (Exception ex)
    {
        Debug.Print(ex.ToString());
        return null; // failed
    }
}

/// <summary>
/// WORKBOOK EXTENSION METHOD
/// Get the full path to the file that the workbook is embedded 
/// inside of. 
/// If we are not embeeded then this will return null.
/// If we are embedded but there are issues with the container
/// or an exception occurs, it will return null.
/// Otherwise we get the full path and filename.
/// </summary>
/// <param name="PobjWb"></param>
/// <returns></returns>
public static string EmbedMoniker(this Excel.Workbook PobjWb)
{
    try
    {
        IOleObject LobjOleObject = ((object)PobjWb) as IOleObject;
        IOleClientSite LobjPpClientSite;
        // get the client site
        LobjOleObject.GetClientSite(out LobjPpClientSite);
        if (LobjPpClientSite != null)
        {
            IOleContainer LobjPpContainer;
            LobjPpClientSite.GetContainer(out LobjPpContainer);
            if (LobjPpContainer != null)
            {
                // get the moniker
                IMoniker LobjMoniker;
                LobjPpClientSite.GetMoniker((uint)OLEGETMONIKER.OLEGETMONIKER_FORCEASSIGN,
                                            (uint)OLEWHICHMK.OLEWHICHMK_OBJFULL,
                                            out LobjMoniker);
                if (LobjMoniker != null)
                {
                    // now pull the moniker display name
                    // this will be in the form of PATH!Context
                    string LstrDisplayName;
                    IBindCtx LobjCtx = null;
                    CreateBindCtx(0, out LobjCtx); // required (imported function)
                    LobjMoniker.GetDisplayName(LobjCtx, null, out LstrDisplayName);
                    // remove context is exists
                    if (LstrDisplayName.Contains("!"))
                    {
                        string[] LobjMonikerArray = LstrDisplayName.Split('!');
                        // return the first part - which should be the path
                        return LobjMonikerArray[0];
                    }
                    else
                    {
                        // return full display name
                        return LstrDisplayName;
                    }
                }
                else
                {
                    // no moniker value
                    return null;
                }
            }
            else
            {
                // something wrong - container is not valid
                return null;
            }
        }
        else
        {
            // not embedded
            return null;
        }
    }
    catch (Exception ex)
    {
        Debug.Print(ex.ToString());
        return null; // failed
    }
}
}

答案 2 :(得分:0)

我在Word中嵌入Excel时遇到了同样的问题。解决方案是Marshal.ReleaseComObject在每个事件函数末尾的所有对象。

示例:

private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
   // code
   Marshal.ReleaseComObject(sender);
   Marshal.ReleaseComObject(e);
}