C# 如何使用 Interop 加载 Excel 插件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1154558/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 09:31:45  来源:igfitidea点击:

How to load an Excel Addin using Interop

c#excelinterop

提问by user35149

I have an AddIn which I want to invoke through Excel interop from a C# winforms application.

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

I can't get the addin etc. to load unless I uninstall and resinstall it each time (this is apparantly something to do with Excel not loading addins when you use interop- btw, can't get their example to work in C#). Unfortunately this is slow and annoying to the user so I need to streamline it.

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

I want to have one instance of Excel but load an already installed addin without forcing this install/reinstall problem.

我想要一个 Excel 实例,但加载一个已安装的插件而不强制此安装/重新安装问题。

I've searched and searched but everything I find on google gives the solution to install/reinstall. Is there any other way? The add-in is installed, I just want excel to load it.

我已经搜索并搜索过,但我在谷歌上找到的所有内容都提供了安装/重新安装的解决方案。有没有其他办法?插件已安装,我只想用excel加载它。

This is what I am doing at the moment (taken from google'd advice):

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

// 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;

采纳答案by user35149

After a while I found the answer hidden in strange places in the MS help: and this blog post.

过了一会儿,我发现答案隐藏在MS 帮助中奇怪的地方:和这篇博文

That isn't all the info you need though. Things to note: you must have at least one workbook open or otherwise Excel barfs. Here's some rudementry code to get started:

但这还不是您需要的全部信息。注意事项:您必须至少打开一个工作簿,否则 Excel barfs。下面是一些入门代码:

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

If you want you can close the temporary workbook (if you've run some macros etc.) and remember to tidy everything up with plenty of calls to Marshal.ReleaseComObject!

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

回答by Louwrens Potgieter

It seems that you have to get the correct Excel process to work with. Use this class to open the Excel document:

似乎您必须获得正确的 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;
    }
}

Use the class in your application like this:

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

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