简体   繁体   English

Excel互操作加载XLL和DLL

[英]Excel interop loading XLLs and DLLs

I have excel with the Bloomberg API ( which uses simple calls like =BDP("MS equity","ask") ). 我在Bloomberg API(使用像= BDP(“ MS equity”,“ ask”)之类的简单调用)方面表现出色。 I also have a C# application that opens an excel file (through interop) that uses the Bloomberg API. 我也有一个C#应用程序,可以打开一个使用Bloomberg API的excel文件(通过互操作)。 I have read here that addins are not loaded when you load excel through interop. 我在这里已经读到,通过互操作程序加载excel时不会加载插件。 I have even tried using the code suggested there. 我什至尝试使用建议的代码。 However, that only seems to work for XLL and XLAM files and it looks like Bloomberg also uses DLL files which doesn't get loaded. 但是,这似乎仅适用于XLL和XLAM文件,并且看起来Bloomberg还使用了不会加载的DLL文件。 Is there any way to get all of these addins to be loaded through interop? 有什么办法可以通过互操作加载所有这些插件?

I assume that, if you start excel manually the add-ins are loaded properly. 我认为,如果您手动启动excel,则加载项会正确加载。

If so, you can get an Excel Interop Object with loaded BB-Addins by calling Process p = Process.Start("excel.exe"); 如果是这样,则可以通过调用Process p = Process.Start("excel.exe");获得带有加载的BB-Addins的Excel Interop对象Process p = Process.Start("excel.exe"); and then using the AccessibleObjectFromWindow method to get the Interop Object. 然后使用AccessibleObjectFromWindow方法获取Interop对象。

An example on how to do that can be found here . 有关如何执行此操作的示例,请参见此处


Quick walkthrough 快速演练

I decided to show the steps I went through to get an Excel.Interop.Application instance with loaded bloomberg add ins. 我决定显示获得加载的 Bloomberg add插件的Excel.Interop.Application实例的步骤。 Maybe someone looks at this and finds it useful, or knows where I missed something to get a more elegant solution. 也许有人看着它,发现它有用,或者知道我在哪里错过了一些东西以获得更好的解决方案。

I assume you have the bloomberg terminal installed on your client and that you can request reference and historical data (BDP and BDH) out of excel. 我假设您的客户端上已安装Bloomberg终端,并且您可以从Excel中请求参考和历史数据(BDP和BDH)。 Otherwise this is probably not for you... 否则这可能不适合您...

The simplest way doesn't work 最简单的方法不起作用

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

Run() will throw a COMException : Run()将抛出COMException

Cannot run the macro 'RefreshAllStaticData'. 无法运行宏“ RefreshAllStaticData”。 The macro may not be available in this workbook or all macros may be disabled. 该宏可能在此工作簿中不可用,或者可能禁用了所有宏。

Loading AddIns by hand doesn't work 手动加载插件无效

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

false

But if start Excel by klicking on the Windows Taskbar, the AddIns are being loaded nicely... So I tried to start excel by starting the executable file "excel.exe". 但是,如果通过在Windows任务栏上单击来启动Excel,则会很好地加载AddIns。因此,我尝试通过启动可执行文件“ excel.exe”来启动excel。

Using Process.Start("excel.exe") works!! 使用Process.Start(“ excel.exe”)可以!!

Starting excel using the System.Diagnostics.Process class loads the bloomberg AddIns. 使用System.Diagnostics.Process类启动excel将加载Bloomberg AddIns。 (I can see the Bloomberg-RibbonTab .) (我可以看到Bloomberg-RibbonTab 。)

Now all I need is the Interop-Object of that excel process. 现在,我需要的只是该excel流程的Interop-Object。 I can get it using the above example 我可以使用上面的例子

My implementation of it 我的实现

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

Now I can use the following line of code to get an Excel.Interop.Application instance with loaded bloomberg addins! 现在,我可以使用以下代码行获取带有已加载的Bloomberg加载项的Excel.Interop.Application实例!

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

I hope this spares someone the hours I wasted with this and if someone has a more elegant solution and wants to share it, it would be much appreciated. 我希望这可以节省一些人我浪费的时间,如果有人有一个更优雅的解决方案并希望分享它,将不胜感激。

I was able to get a similar result using NetOffice . 使用NetOffice可以得到类似的结果。 This particular function will retrieve the last started instance of the excel process or create a new one if one does exist. 此特定功能将检索excel流程的上一个启动实例,如果存在,则创建一个新实例。 From there you are free to open/create a new workbook. 从那里,您可以自由打开/创建新工作簿。

The nice thing about using netoffice and this approach is that it doesn't depend on version specific interop DLLs and if you dispose of the NetOffice objects then all of the excel COM objects are cleaned up properly. 使用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);
    }
}

Usage: 用法:

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

I realize that it's been while since this question has been asked. 我意识到,问这个问题已经有一段时间了。 Nevertheless I want to share my experience as I came across the same problem. 不过,我想分享我遇到相同问题时的经验。 An easy solution is to proceed as suggested in https://blogs.msdn.microsoft.com/accelerating_things/2010/09/16/loading-excel-add-ins-at-runtime/ by first setting .Installed to false and then to true: 一个简单的解决方案是按照https://blogs.msdn.microsoft.com/accelerating_things/2010/09/16/loading-excel-add-ins-at-runtime/中的建议进行操作,方法是先设置为false,然后设置为false变为真实:

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

If you want to load a DLL into Excel addins, you must register this DLL file using RegAsm. 如果要将DLL加载到Excel加载项中,则必须使用RegAsm注册此DLL文件。 And check HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Office\\Excel\\AddIns to see whether it's registered successfully. 并检查HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Office\\Excel\\AddIns以查看其是否已成功注册。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM