How to load an Excel Addin using Interop
I have an AddIn which I want to invoke through Excel interop from a C# winforms application.
I can't get the addin etc. to load unless I uninstall and resinstall it each time (this is apparantly something to do with Excel not loading addins when you use interop - btw, can't get their example to work in C#). Unfortunately this is slow and annoying to the user so I need to streamline it.
I want to have one instance of Excel but load an already installed addin without forcing this install/r开发者_StackOverfloweinstall problem.
I've searched and searched but everything I find on google gives the solution to install/reinstall. Is there any other way? The add-in is installed, I just want excel to load it.
This is what I am doing at the moment (taken from google'd advice):
// loop over the add-ins and if you find it uninstall it.
foreach (AddIn addIn in excel.AddIns)
if (addIn.Name.Contains("My Addin"))
addin.Installed = false;
// install the addin
var addin = excel.AddIns.Add("my_addin.xll", false);
addin.Installed = true;
After a while I found the answer hidden in strange places in the MS help: and this blog post.
That isn't all the info you need though. Things to note: you must have at least one workbook open or otherwise Excel barfs. Here's some rudementry code to get started:
var excel = new Application();
var workbook = excel.workbooks.Add(Type.Missing);
excel.RegisterXLL(pathToXll);
excel.ShowExcel();
If you want you can close the temporary workbook (if you've run some macros etc.) and remember to tidy everything up with plenty of calls to Marshal.ReleaseComObject!
It seems that you have to get the correct Excel process to work with. Use this class to open the Excel document:
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 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 Application GetExcelInterop(int? processId = null)
{
var p = processId.HasValue ? Process.GetProcessById(processId.Value) : Process.Start("excel.exe");
try
{
Thread.Sleep(5000);
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 Application SearchExcelInterop(Process p)
{
Window ptr = null;
int hwnd = 0;
int hWndParent = (int)p.MainWindowHandle;
if (hWndParent == 0) throw new Exception();
EnumChildWindows(hWndParent, EnumChildFunc, ref hwnd);
if (hwnd == 0) throw new Exception();
int hr = AccessibleObjectFromWindow(hwnd, DW_OBJECTID, rrid.ToByteArray(), ref ptr);
if (hr < 0) throw new Exception();
return ptr.Application;
}
}
Use the class in your application like this:
static void Main(string[] args)
{
Microsoft.Office.Interop.Excel.Application oExcel = ExcelInteropService.GetExcelInterop();
foreach (AddIn addIn in oExcel.AddIns)
{
addIn.Installed = true;
}
}
I had a similar problem and none of the above solutions seemed to work for me. So, I finally came up with the idea of simply opening the ".xla" file as a workbook :
//I first get the path to file, in my case it was in the same dir as the .exe
var XLA_NAME = Path.Combine(Directory.GetCurrentDirectory(), ".xla");
Excel.Application xlRep = new Excel.Application();
xlRep.visible = true;
xlRep.Workbooks.Open("XLA_NAME");
精彩评论