Excel互操作加载XLL和DLL
我使用了Bloomberg API(使用简单的调用如= BDP(“MS股权”,“请求”))。 我也有一个C#应用程序,用于打开使用Bloomberg API的excel文件(通过interop)。 我读过这里 ,当你通过interop加载excel的时候没有加载插件。 我甚至尝试使用在那里build议的代码。 但是,这似乎只适用于XLL和XLAM文件,它看起来像彭博社也使用DLL文件没有得到加载。 有什么办法让所有这些插件通过互操作加载?
我假设,如果你手动启动excel,加载项被正确加载。
如果是这样,你可以通过调用Process p = Process.Start("excel.exe");
来获得一个带有加载的BB-Addins的Excel互操作对象Process p = Process.Start("excel.exe");
然后使用AccessibleObjectFromWindow
方法获取互操作对象。
如何做到这一点的例子可以在这里find。
快速演练
我决定展示我所经历的步骤,以获得一个Excel.Interop.Application
实例与加载 bloomberg添加。 也许有人看着这个,发现它有用,或知道我错过了什么东西,以获得更优雅的解决scheme。
我假设你的客户端上已经安装了bloombergterminal,并且可以请求excel中的参考和历史数据(BDP和BDH)。 否则这可能不适合你…
最简单的方法是行不通的
var excel = new Microsoft.Office.Interop.Excel.Application(); excel.Workbooks.Open(filePath); excel.Run("RefreshAllStaticData");
Run()
会抛出一个COMException
:
无法运行macros'RefreshAllStaticData'。 macros可能不在此工作簿中可用或所有macros可能被禁用。
手动加载AddIns不起作用
var excel = new Microsoft.Office.Interop.Excel.Application(); var addIn = ex.AddIns.Add(filename); addIn.Installed = true; Console.WriteLine(addIn.IsOpen);
假
但是,如果通过在Windows任务栏上单击来启动Excel,AddIns正在被很好地加载…所以我试图通过启动可执行文件“excel.exe”来启动excel。
使用Process.Start(“excel.exe”)的作品!
使用System.Diagnostics.Process
类启动excel加载bloomberg AddIns。 (我可以看到Bloomberg-RibbonTab
。)
现在我需要的就是那个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; } }
现在我可以使用下面这行代码来获取一个带有加载的bloomberg Excel.Interop.Application
实例!
var excel = ExcelInteropService.GetExcelInterop(); excel.Workbooks.Open(filename); excel.Run("RefreshAllStaticData"); // works "like a charm"
我希望这可以让我省下那些浪费的时间,如果有人有一个更优雅的解决scheme,并希望分享它,将不胜感激。
我能够使用NetOffice获得类似的结果。 这个特定的函数将检索最后启动的excel进程的实例,或者如果存在的话创build一个新的实例。 从那里你可以自由地打开/创build一个新的工作簿。
使用netoffice和这种方法的好处是,它不依赖于特定于版本的interop DLL,并且如果您丢弃了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加载项中,则必须使用RegAsm注册此DLL文件。 并检查HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office\Excel\AddIns
以查看它是否已成功注册。