我擅长使用Bloomberg API(它使用简单的调用,如= BDP(“MS equity”,“ask”))。我还有一个C#应用程序,它打开一个使用Bloomberg API的excel文件(通过interop)。我已阅读here,当您通过互操作加载excel时,不会加载插件。我甚至尝试过使用那里建议的代码。但是,这似乎只适用于XLL和XLAM文件,看起来Bloomberg也使用了无法加载的DLL文件。有没有办法让所有这些插件通过互操作加载?
答案 0 :(得分:11)
我认为,如果您手动启动Excel,则会正确加载加载项。
如果是这样,您可以通过调用Process p = Process.Start("excel.exe");
然后使用AccessibleObjectFromWindow
方法获取Interop对象来获取带有加载的BB-Addins的Excel Interop对象。
可以找到关于如何做到这一点的示例here。
我决定展示我通过加载 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()
会抛出COMException
:
无法运行宏'RefreshAllStaticData'。宏可能在此工作簿中不可用,或者可能禁用所有宏。
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。
使用System.Diagnostics.Process
类启动excel会加载bloomberg AddIns。 (我可以看到Bloomberg-RibbonTab
。)
现在我只需要那个excel进程的Interop-Object。我可以使用above example
来获取它//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"
我希望这能让我浪费时间,如果有人有更优雅的解决方案,想要分享它,我将非常感激。
答案 1 :(得分:1)
我可以使用NetOffice获得类似的结果。此特定函数将检索excel进程的最后一个启动实例,或者如果存在,则创建一个新实例。从那里你可以自由地打开/创建一个新的工作簿。
使用netoffice和这种方法的好处是它不依赖于特定于版本的互操作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
}
答案 2 :(得分:1)
我意识到自从提出这个问题以来已经有一段时间了。然而,当我遇到同样的问题时,我想分享我的经验。 一个简单的解决方案是按照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;
}
答案 3 :(得分:0)
如果要将DLL加载到Excel插件中,则必须使用RegAsm注册此DLL文件。并检查HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office\Excel\AddIns
以查看它是否已成功注册。