public static void LoadComAddIn(ExcelComAddIn addIn) { // If we are called without the addIn's DnaLibrary being set, default to the current library if (addIn.DnaLibrary == null) { addIn.DnaLibrary = DnaLibrary.CurrentLibrary; } CLSID clsId; string progId; // If we have both an explicit Guid and an explicit ProgId, then use those, else create synthetic ProgId. object[] progIdAttrs = addIn.GetType().GetCustomAttributes(typeof(ProgIdAttribute), false); object[] guidAttrs = addIn.GetType().GetCustomAttributes(typeof(GuidAttribute), false); if (progIdAttrs.Length >= 1 && guidAttrs.Length >= 1) { // Use the attributes ProgIdAttribute progIdAtt = (ProgIdAttribute)progIdAttrs[0]; progId = progIdAtt.Value; GuidAttribute guidAtt = (GuidAttribute)guidAttrs[0]; clsId = new Guid(guidAtt.Value); } else { // We pick a new Guid as ClassId for this add-in. clsId = Guid.NewGuid(); // and make the ProgId from this Guid - max 39 chars.... progId = "AddIn." + clsId.ToString("N"); } addIn.SetProgId(progId); // Put together some nicer descriptions for the Add-ins dialog. string friendlyName; if (addIn is ExcelRibbon) { friendlyName = addIn.DnaLibrary.Name; // + " (Ribbon Helper)"; (No more - it is displayed in the Ribbon tooltip!) } else if (addIn is ExcelCustomTaskPaneAddIn) { friendlyName = addIn.DnaLibrary.Name + " (Custom Task Pane Helper)"; } else { friendlyName = addIn.DnaLibrary.Name + " (COM Add-in Helper)"; } string description = string.Format("Dynamically created COM Add-in to load custom UI for the Excel Add-in {0}, located at {1}.", addIn.DnaLibrary.Name, DnaLibrary.XllPath); try { Debug.Print("Getting Application object"); object app = ExcelDnaUtil.Application; Type appType = app.GetType(); Debug.Print("Got Application object: " + app.GetType().ToString()); CultureInfo ci = new CultureInfo(1033); object excelComAddIns; object comAddIn; using (new SingletonClassFactoryRegistration(addIn, clsId)) using (new ProgIdRegistration(progId, clsId)) using (new ComAddInRegistration(progId, friendlyName, description)) { excelComAddIns = appType.InvokeMember("COMAddIns", BindingFlags.GetProperty, null, app, null, ci); // Debug.Print("Got COMAddins object: " + excelComAddIns.GetType().ToString()); appType.InvokeMember("Update", BindingFlags.InvokeMethod, null, excelComAddIns, null, ci); // Debug.Print("Updated COMAddins object with AddIn registered"); comAddIn = excelComAddIns.GetType().InvokeMember("Item", BindingFlags.InvokeMethod, null, excelComAddIns, new object[] { progId }, ci); // Debug.Print("Got the COMAddin object: " + comAddIn.GetType().ToString()); // At this point Excel knows how to load our add-in by CLSID, so we could clean up the // registry aggressively, before the actual (dangerous?) loading starts. // But this seems to lead to some distress - Excel has some assertion checked when // it updates the LoadBehavior after a successful load.... comAddIn.GetType().InvokeMember("Connect", BindingFlags.SetProperty, null, comAddIn, new object[] { true }, ci); // Debug.Print("COMAddin is loaded."); loadedComAddIns.Add(comAddIn); } } catch (Exception e) { Debug.Print("LoadComAddIn exception: " + e.ToString()); // CONSIDER: How to deal with errors here...? For now I just re-raise the exception. throw; } }
public static void LoadComAddIn(ExcelComAddIn addIn) { // If we are called without the addIn's DnaLibrary being set, default to the current library if (addIn.DnaLibrary == null) addIn.DnaLibrary = DnaLibrary.CurrentLibrary; CLSID clsId; string progId; // If we have both an explicit Guid and an explicit ProgId, then use those, else create synthetic ProgId. object[] progIdAttrs = addIn.GetType().GetCustomAttributes(typeof(ProgIdAttribute), false); object[] guidAttrs = addIn.GetType().GetCustomAttributes(typeof(GuidAttribute), false); if (progIdAttrs.Length >= 1 && guidAttrs.Length >= 1) { // Use the attributes ProgIdAttribute progIdAtt = (ProgIdAttribute)progIdAttrs[0]; progId = progIdAtt.Value; GuidAttribute guidAtt = (GuidAttribute)guidAttrs[0]; clsId = new Guid(guidAtt.Value); } else { // We pick a new Guid as ClassId for this add-in. clsId = Guid.NewGuid(); // and make the ProgId from this Guid - max 39 chars.... progId = "AddIn." + clsId.ToString("N"); } addIn.SetProgId(progId); // Put together some nicer descriptions for the Add-ins dialog. string friendlyName; if (addIn is ExcelRibbon) friendlyName = addIn.DnaLibrary.Name; // + " (Ribbon Helper)"; (No more - it is displayed in the Ribbon tooltip!) else if (addIn is ExcelCustomTaskPaneAddIn) friendlyName = addIn.DnaLibrary.Name + " (Custom Task Pane Helper)"; else friendlyName = addIn.DnaLibrary.Name + " (COM Add-in Helper)"; string description = string.Format("Dynamically created COM Add-in to load custom UI for the Excel Add-in {0}, located at {1}.", addIn.DnaLibrary.Name, DnaLibrary.XllPath); try { Debug.Print("Getting Application object"); object app = ExcelDnaUtil.Application; Type appType = app.GetType(); Debug.Print("Got Application object: " + app.GetType().ToString()); CultureInfo ci = new CultureInfo(1033); object excelComAddIns; object comAddIn; using (new SingletonClassFactoryRegistration(addIn, clsId)) using (new ProgIdRegistration(progId, clsId)) using (new ComAddInRegistration(progId, friendlyName, description)) { excelComAddIns = appType.InvokeMember("COMAddIns", BindingFlags.GetProperty, null, app, null, ci); // Debug.Print("Got COMAddins object: " + excelComAddIns.GetType().ToString()); appType.InvokeMember("Update", BindingFlags.InvokeMethod, null, excelComAddIns, null, ci); // Debug.Print("Updated COMAddins object with AddIn registered"); comAddIn = excelComAddIns.GetType().InvokeMember("Item", BindingFlags.InvokeMethod, null, excelComAddIns, new object[] { progId }, ci); // Debug.Print("Got the COMAddin object: " + comAddIn.GetType().ToString()); // At this point Excel knows how to load our add-in by CLSID, so we could clean up the // registry aggressively, before the actual (dangerous?) loading starts. // But this seems to lead to some distress - Excel has some assertion checked when // it updates the LoadBehavior after a successful load.... comAddIn.GetType().InvokeMember("Connect", BindingFlags.SetProperty, null, comAddIn, new object[] { true }, ci); // Debug.Print("COMAddin is loaded."); loadedComAddIns.Add(comAddIn); } } catch (UnauthorizedAccessException secex) { Logging.LogDisplay.WriteLine("The Ribbon helper required by add-in {0} could not be registered.\r\nThis may be due to restricted permissions on the user's HKCU\\Software\\Classes key.\r\nError message: {1}", DnaLibrary.CurrentLibrary.Name, secex.Message); return; } catch (Exception e) { Debug.Print("LoadComAddIn exception: " + e.ToString()); // CONSIDER: How to deal with errors here...? For now I just re-raise the exception. throw; } }
public static void LoadComAddIn(ExcelComAddIn addIn) { // We pick a new Guid as ClassId for this add-in. CLSID clsId = Guid.NewGuid(); // and make the ProgId from this Guid - max 39 chars.... string progId = "AddIn." + clsId.ToString("N"); addIn.SetProgId(progId); // Put together some nicer descriptions for the Add-ins dialog. string friendlyName; if (addIn is ExcelRibbon) friendlyName = addIn.DnaLibrary.Name + " (Ribbon Helper)"; else if (addIn is ExcelCustomTaskPaneAddIn) friendlyName = addIn.DnaLibrary.Name + " (Custom Task Pane Helper)"; else friendlyName = addIn.DnaLibrary.Name + " (COM Add-in Helper)"; string description = string.Format("Dynamically created COM Add-in to load custom UI for the Excel Add-in {0}, located at {1}.", addIn.DnaLibrary.Name, DnaLibrary.XllPath); try { Debug.Print("Getting Application object"); object app = ExcelDnaUtil.Application; Type appType = app.GetType(); Debug.Print("Got Application object: " + app.GetType().ToString()); CultureInfo ci = new CultureInfo(1033); object excelComAddIns; object comAddIn; using (SingletonClassFactoryRegistration regClassFactory = new SingletonClassFactoryRegistration(addIn, clsId)) using (ProgIdRegistration regProgId = new ProgIdRegistration(progId, clsId)) using (ComAddInRegistration regComAddIn = new ComAddInRegistration(progId, friendlyName, description)) { excelComAddIns = appType.InvokeMember("COMAddIns", BindingFlags.GetProperty, null, app, null, ci); // Debug.Print("Got COMAddins object: " + excelComAddIns.GetType().ToString()); appType.InvokeMember("Update", BindingFlags.InvokeMethod, null, excelComAddIns, null, ci); // Debug.Print("Updated COMAddins object with AddIn registered"); comAddIn = excelComAddIns.GetType().InvokeMember("Item", BindingFlags.InvokeMethod, null, excelComAddIns, new object[] { progId }, ci); // Debug.Print("Got the COMAddin object: " + comAddIn.GetType().ToString()); // At this point Excel knows how to load our add-in by CLSID, so we could clean up the // registry aggressively, before the actual (dangerous?) loading starts. // But this seems to lead to some distress - Excel has some assertion checked when // it updates the LoadBehavior after a successful load.... comAddIn.GetType().InvokeMember("Connect", BindingFlags.SetProperty, null, comAddIn, new object[] { true }, ci); // Debug.Print("COMAddin is loaded."); loadedComAddIns.Add(comAddIn); } } catch (Exception e) { Debug.Print("LoadComAddIn exception: " + e.ToString()); // CONSIDER: How to deal with errors here...? For now I just re-raise the exception. throw; } }