我正在处理现有的Excel VSTO插件,当用户编辑嵌入在MS Word文档中的工作簿时会导致问题。并不要求该环境中的插件功能,但它会导致嵌入故障,即使客户正在操作与插件操作无关的文件。至少,我需要让它不为该工作簿初始化自己。
我调查过的一些途径:
由于Excel的单个实例可以同时托管嵌入式和独立工作簿,因此这些信息必须位于工作簿级别。
答案 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.Path
或Workbook.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);
}