Взаимодействие Excel загрузка XLLs и DLL

У меня есть excel с API Bloomberg (который использует простые вызовы, такие как =BDP("MS equity","ask") ). У меня также есть приложение c#, которое открывает файл excel (через взаимодействие), который использует API Bloomberg. Я читал здесь что добавления не загружаются при загрузке excel через взаимодействие. Я даже попытался использовать предложенный там код. Однако это работает только для XLL и XLAM-файлов, и похоже, что Bloomberg также использует DLL-файлы, которые не загружаются. Есть ли способ загрузить все эти дополнения через interop?

4 ответов


я предполагаю, что при запуске excel вручную надстройки загружаются правильно.

если это так, вы можете получить объект взаимодействия Excel с загруженными BB-Addins, вызвав Process p = Process.Start("excel.exe");, а затем с помощью AccessibleObjectFromWindow метод для получения объекта взаимодействия.

пример того, как это сделать, можно найти здесь.


краткое описание

я решил показать шаги, которые я прошел, чтобы получить Excel.Interop.Application экземпляр загружается bloomberg добавить ins. Может быть, кто-то смотрит на это и находит это полезным, или знает, где я пропустил что-то, чтобы получить более элегантное решение.

я предполагаю, что у вас установлен терминал bloomberg на вашем клиенте и что вы можете запросить справочные и исторические данные (BDP и BDH) из excel. В противном случае это, вероятно, не для вас...

самый простой способ не работает

var excel = new Microsoft.Office.Interop.Excel.Application();
excel.Workbooks.Open(filePath);
excel.Run("RefreshAllStaticData");

Run() бросит COMException:

не удается запустить макрос 'RefreshAllStaticData'. Макрос может быть недоступен в этой книге или все макросы могут быть отключены.

загрузка дополнений вручную не работает

var excel = new Microsoft.Office.Interop.Excel.Application();
var addIn = ex.AddIns.Add(filename);
addIn.Installed = true;
Console.WriteLine(addIn.IsOpen);

false

но если запустить Excel, кликнув на панели задач Windows, дополнения загружаются красиво... Поэтому я попытался запустить excel, запустив исполняемый файл " excel.исполняемый."

используя Процесс.Начало ("excel.исполняемые") работает!!

запуск excel с помощью System.Diagnostics.Processкласс загружает дополнения bloomberg. (Я вижу Bloomberg-RibbonTab.)

теперь все, что мне нужно, это Interop-объект этого процесса excel. Я могу получить его, используя выше пример

моя реализация это

//From http://blogs.officezealot.com/whitechapel/archive/2005/04/10/4514.aspx
public 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 Microsoft.Office.Interop.Excel.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 Microsoft.Office.Interop.Excel.Application GetExcelInterop(int? processId = null)
    {
        var p = processId.HasValue ? Process.GetProcessById(processId.Value) : Process.Start("excel.exe");
        try
        {
            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 Microsoft.Office.Interop.Excel.Application SearchExcelInterop(Process p)
    {
        Microsoft.Office.Interop.Excel.Window ptr = null;
        int hwnd = 0;

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

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

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

        return ptr.Application;
    }
}

теперь я могу использовать следующую строку кода, чтобы получить Excel.Interop.Application экземпляр с загруженными Bloomberg addins!

var excel = ExcelInteropService.GetExcelInterop();
excel.Workbooks.Open(filename);
excel.Run("RefreshAllStaticData");
// works "like a charm"

я надеюсь, что это избавляет кого-то от часов, которые я потратил на это, и если у кого-то есть более элегантное решение и хочет поделиться им, это было бы очень ценно.


я смог получить аналогичный результат, используя NetOffice. Эта конкретная функция извлекает последний запущенный экземпляр процесса excel или создает новый, если он существует. Оттуда вы можете открыть/создать новую книгу.

хорошая вещь об использовании netoffice и этот подход заключается в том, что он не зависит от конкретной версии библиотек взаимодействия, и если вы утилизируете объекты NetOffice, то все объекты excel COM очищаются должным образом.

using Xl = NetOffice.ExcelApi;    
private static Xl.Application GetOrStartExcel(bool started = false)
{
    Xl.Application application = null;

    try
    {
        object nativeProxy = System.Runtime.InteropServices.Marshal.GetActiveObject("Excel.Application");
        application = new Xl.Application(null, nativeProxy);
        return application;
    }
    catch (Exception ex)
    {
        if (!started) Process.Start("excel.exe");
        Thread.Sleep((int) TimeSpan.FromSeconds(1).TotalMilliseconds);
        return GetOrStartExcel(true);
    }
}

использование:

using (var excel = GetOrStartExcel())
{
    //Do work with excel object
}

Если вы хотите загрузить DLL в Excel addins, вы должны зарегистрировать этот DLL-файл с помощью RegAsm. И проверьте HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office\Excel\AddIns чтобы увидеть, успешно ли он зарегистрирован.


Я понимаю, что прошло некоторое время с тех пор, как этот вопрос был задан. Тем не менее я хочу поделиться своим опытом, как я столкнулся с той же проблемой. Простое решение заключается в следующем:https://blogs.msdn.microsoft.com/accelerating_things/2010/09/16/loading-excel-add-ins-at-runtime/ по первой настройке .Установлено значение false, а затем true:

this._application = new Application
                {
                    EnableEvents = true,
                    Visible = true
                };

                //Load Bloomberg Add-In
                for (var i = 1; i <= this._application.AddIns.Count; i++)
                {
                    var currentAddIn = this._application.AddIns.Item[i];

                    if (currentAddIn.Name != "BloombergUI.xla") continue;

                    currentAddIn.Installed = false;
                    currentAddIn.Installed = true;
                }