Excel互操作加载XLL和DLL

Mar*_*B42 7 .net c# excel com-interop bloomberg

我擅长使用Bloomberg API(它使用简单的调用,如= BDP("MS equity","ask")).我还有一个C#应用程序,它打开一个使用Bloomberg API的excel文件(通过interop).我在这里读到,当您通过互操作加载excel时,不会加载插件.我甚至尝试过使用那里建议的代码.但是,这似乎只适用于XLL和XLAM文件,看起来Bloomberg也使用了无法加载的DLL文件.有没有办法让所有这些插件通过互操作加载?

Ale*_*dro 11

我假设,如果您手动启动Excel,则加载项会正确加载.

如果是这样,您可以通过调用Process p = Process.Start("excel.exe");然后使用该AccessibleObjectFromWindow方法获取Interop对象来获取带有加载的BB-Addins的Excel Interop对象.

可以在此处找到有关如何执行此操作的示例.


快速演练

我决定展示我通过加载 bloomberg add ins 获取Excel.Interop.Application实例的步骤.也许有人看着这个并发现它很有用,或者知道我错过了什么来获得更优雅的解决方案.

我假设您在客户端上安装了bloomberg终端,并且您可以从excel请求参考和历史数据(BDP和BDH).否则这可能不适合你......

最简单的方法不起作用

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

Run()会扔一个COMException:

无法运行宏'RefreshAllStaticData'.宏可能在此工作簿中不可用,或者可能禁用所有宏.

手动加载AddIns不起作用

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

但是如果通过点击Windows任务栏启动Excel,AddIns正在被很好地加载...所以我尝试通过启动可执行文件"excel.exe"来启动excel.

使用Process.Start("excel.exe")有效!!

使用System.Diagnostics.Process该类启动excel 会加载bloomberg AddIns.(我可以看到Bloomberg-RibbonTab.)

现在我只需要那个excel过程的Interop-Object.我可以使用上面的例子得到它

我实施它

//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;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我可以使用以下代码行来获取Excel.Interop.Application带有加载的bloomberg插件的实例!

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

我希望这可以让我浪费时间浪费在这个时间,如果有人有更优雅的解决方案并希望分享它,我将不胜感激.