如何使用Interop加载Excel插件

6

我有一个AddIn,想要通过C# WinForms应用程序从Excel互操作调用它。

除非我每次卸载并重新安装它,否则我无法加载插件等(这显然与使用互操作时Excel不加载插件有关 - 顺便说一句,我无法让他们的示例在C#中工作)。不幸的是,这对用户来说很慢且麻烦,因此我需要简化它。

我想要有一个Excel实例,但加载一个已经安装的插件而不强制进行安装/重新安装。

我已经搜索了很多,但是在Google上找到的所有内容都解决了安装/重新安装的问题。还有其他方法吗?插件已安装,我只想让Excel加载它。

这是我目前正在做的事情(取自谷歌建议):

// loop over the add-ins and if you find it uninstall it.
foreach (AddIn addIn in excel.AddIns)
    if (addIn.Name.Contains("My Addin"))
        addin.Installed = false;

    // install the addin
    var addin = excel.AddIns.Add("my_addin.xll", false);
        addin.Installed = true;

你能解释一下为什么需要进行安装/卸载吗?我在试图理解Excel加载插件的方式,一旦设置每次加载时,为什么它需要安装/卸载呢? - shahkalpesh
这段代码是从哪里调用的?我猜是另一个Excel AddIn?还是一个Winform C#应用程序? - Stan R.
我已经根据您提出的要点更新了问题,但简单来说: Excel无法通过Interop加载插件,我在网上找到的所有信息都表明卸载/安装是解决方案。代码是从C# WinForms应用程序中调用的。 - user35149
1
请注意,Installed 的意思更像是“启用”。将 Installed 属性设置为 false 然后再设置为 true 并不是“安装”和“卸载”,而只是打开和关闭插件。 - jwg
3个回答

8

过了一会儿,我在MS帮助这篇博客文章中发现了隐藏的答案。

但这并不是你所需要的全部信息。请注意:您必须至少打开一个工作簿,否则Excel将无法正常运行。以下是一些基础代码供您参考:

var excel = new Application();
var workbook = excel.workbooks.Add(Type.Missing);
excel.RegisterXLL(pathToXll);
excel.ShowExcel();

如果您愿意,可以关闭临时工作簿(如果您运行了一些宏等),并记得使用大量的调用Marshal.ReleaseComObject来清理所有内容!


1

看起来你需要使用正确的Excel进程。使用这个类打开Excel文档:

 class ExcelInteropService
{
    private const string EXCEL_CLASS_NAME = "EXCEL7";

    private const uint DW_OBJECTID = 0xFFFFFFF0;

    private static Guid rrid = new Guid("{00020400-0000-0000-C000-000000000046}");

    public delegate bool EnumChildCallback(int hwnd, ref int lParam);

    [DllImport("Oleacc.dll")]
    public static extern int AccessibleObjectFromWindow(int hwnd, uint dwObjectID, byte[] riid, ref Window ptr);

    [DllImport("User32.dll")]
    public static extern bool EnumChildWindows(int hWndParent, EnumChildCallback lpEnumFunc, ref int lParam);

    [DllImport("User32.dll")]
    public static extern int GetClassName(int hWnd, StringBuilder lpClassName, int nMaxCount);

    public static Application GetExcelInterop(int? processId = null)
    {
        var p = processId.HasValue ? Process.GetProcessById(processId.Value) : Process.Start("excel.exe");
        try
        {
            Thread.Sleep(5000);
            return new ExcelInteropService().SearchExcelInterop(p);
        }
        catch (Exception)
        {
            Debug.Assert(p != null, "p != null");
            return GetExcelInterop(p.Id);
        }
    }

    private bool EnumChildFunc(int hwndChild, ref int lParam)
    {
        var buf = new StringBuilder(128);
        GetClassName(hwndChild, buf, 128);
        if (buf.ToString() == EXCEL_CLASS_NAME) { lParam = hwndChild; return false; }
        return true;
    }

    private Application SearchExcelInterop(Process p)
    {
        Window ptr = null;
        int hwnd = 0;

        int hWndParent = (int)p.MainWindowHandle;
        if (hWndParent == 0) throw new Exception();

        EnumChildWindows(hWndParent, EnumChildFunc, ref hwnd);
        if (hwnd == 0) throw new Exception();

        int hr = AccessibleObjectFromWindow(hwnd, DW_OBJECTID, rrid.ToByteArray(), ref ptr);
        if (hr < 0) throw new Exception();

        return ptr.Application;
    }
}

在您的应用程序中使用该类,如下所示:

static void Main(string[] args)
{
    Microsoft.Office.Interop.Excel.Application oExcel = ExcelInteropService.GetExcelInterop();
    foreach (AddIn addIn in oExcel.AddIns)
        {
            addIn.Installed = true;
        }
}

0

我曾经遇到过类似的问题,但以上解决方案都无法解决。最终我想到了一个简单的方法:将“.xla”文件作为工作簿打开:

//I first get the path to file, in my case it was in the same dir as the .exe
var XLA_NAME = Path.Combine(Directory.GetCurrentDirectory(), ".xla");

Excel.Application xlRep = new Excel.Application();
xlRep.visible = true;
xlRep.Workbooks.Open("XLA_NAME");

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接