From c4eb040242ea7c5d327c50e28826ae7286465cad Mon Sep 17 00:00:00 2001 From: Sergey Vlasov Date: Sun, 15 Mar 2026 13:19:46 +0500 Subject: [PATCH 1/3] Added a stable ProgId to the custom Ribbon sample. --- Source/ExcelDna.Test/RibbonController.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/ExcelDna.Test/RibbonController.cs b/Source/ExcelDna.Test/RibbonController.cs index 2898c94a..97790697 100644 --- a/Source/ExcelDna.Test/RibbonController.cs +++ b/Source/ExcelDna.Test/RibbonController.cs @@ -5,6 +5,8 @@ namespace Ribbon { [ComVisible(true)] + [ProgId("175c15e3-4dd8-49d0-94cf-f95800017594")] + [Guid("6242439e-ecc6-46eb-a666-edd3d93414a4")] public class RibbonController : ExcelRibbon { public override string GetCustomUI(string RibbonID) From 4bf825f5fd62c76dafb2ed8923c6cd0b617f44a0 Mon Sep 17 00:00:00 2001 From: Sergey Vlasov Date: Sun, 15 Mar 2026 16:59:07 +0500 Subject: [PATCH 2/3] Added stable guids for ExcelCustomTaskPaneAddIn. --- .../CustomUI/ExcelCustomTaskPane.cs | 5 ++++ Source/ExcelDna.Test/Commmands.cs | 6 +++++ Source/ExcelDna.Test/CustomPane.cs | 25 +++++++++++++++++++ 3 files changed, 36 insertions(+) create mode 100644 Source/ExcelDna.Test/CustomPane.cs diff --git a/Source/ExcelDna.Integration/CustomUI/ExcelCustomTaskPane.cs b/Source/ExcelDna.Integration/CustomUI/ExcelCustomTaskPane.cs index beab8e2f..f0b25291 100644 --- a/Source/ExcelDna.Integration/CustomUI/ExcelCustomTaskPane.cs +++ b/Source/ExcelDna.Integration/CustomUI/ExcelCustomTaskPane.cs @@ -13,6 +13,9 @@ namespace ExcelDna.Integration.CustomUI { public static class CustomTaskPaneFactory { + public const string AddInGuid = "8b752d6a-a78f-422f-907a-8905d54930be"; + public const string AddInProgId = "ExcelDna.CustomTaskPaneAddIn"; + private static ExcelCustomTaskPaneAddIn _addin; // We keep a list of CustomTaksPanes, so that we can clean up when the add-in is removed or reopened. @@ -114,6 +117,8 @@ internal static void DetachAddIn() } } + [ProgId(CustomTaskPaneFactory.AddInProgId)] + [Guid(CustomTaskPaneFactory.AddInGuid)] internal class ExcelCustomTaskPaneAddIn : ExcelComAddIn, ICustomTaskPaneConsumer { public ICTPFactory Factory; diff --git a/Source/ExcelDna.Test/Commmands.cs b/Source/ExcelDna.Test/Commmands.cs index 78b9929a..1cd4c5d3 100644 --- a/Source/ExcelDna.Test/Commmands.cs +++ b/Source/ExcelDna.Test/Commmands.cs @@ -20,5 +20,11 @@ public static void MyQueueMacro() { ExcelAsyncUtil.QueueMacro("MyTestCommand"); } + + [ExcelCommand(MenuText = "MyShowCustomPane")] + public static void MyShowCustomPane() + { + CustomPane.Show(); + } } } diff --git a/Source/ExcelDna.Test/CustomPane.cs b/Source/ExcelDna.Test/CustomPane.cs new file mode 100644 index 00000000..ad5ed481 --- /dev/null +++ b/Source/ExcelDna.Test/CustomPane.cs @@ -0,0 +1,25 @@ +using ExcelDna.Integration.CustomUI; +using System; +using System.Runtime.InteropServices; +using System.Windows.Forms; + +namespace ExcelDna.Test +{ + internal class CustomPane + { + public static void Show() + { + var myControl = new MyUserControl(); + var customPane = CustomTaskPaneFactory.CreateCustomTaskPane(myControl, nameof(myControl)); + + customPane.Visible = true; + } + } + + public interface IMyUserControl { } + + [ComVisible(true)] + [Guid("c5a18d1b-b798-49cf-9a3f-37a094905170")] + [ComDefaultInterface(typeof(IMyUserControl))] + public class MyUserControl : UserControl, IMyUserControl { } +} From 4762a0bed7966095d2925eb46be22ffad4ddf3d2 Mon Sep 17 00:00:00 2001 From: Sergey Vlasov Date: Sun, 22 Mar 2026 18:52:00 +0500 Subject: [PATCH 3/3] Added support for custom CTP add-in guids. --- .../ComInterop/ExcelComAddInHelper.cs | 14 +++- .../CustomUI/ExcelCustomTaskPane.cs | 83 ++++++++++++++----- Source/ExcelDna.Test/CustomPane.cs | 3 +- 3 files changed, 73 insertions(+), 27 deletions(-) diff --git a/Source/ExcelDna.Integration/ComInterop/ExcelComAddInHelper.cs b/Source/ExcelDna.Integration/ComInterop/ExcelComAddInHelper.cs index c0e7313e..7a447880 100644 --- a/Source/ExcelDna.Integration/ComInterop/ExcelComAddInHelper.cs +++ b/Source/ExcelDna.Integration/ComInterop/ExcelComAddInHelper.cs @@ -35,10 +35,6 @@ public static void OnUnloadComAddIn(ExcelComAddIn addIn, object addInInst) 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; - Guid clsId; string progId; @@ -62,6 +58,16 @@ public static void LoadComAddIn(ExcelComAddIn addIn) // Change from Dna.xxx.n to Dna_xxx_n to avoid McAfee bug that blocks registry writes with a "." anywhere progId = "Dna_" + clsId.ToString("N") + "_" + loadedComAddIns.Count; } + + LoadComAddIn(addIn, clsId, progId); + } + + internal static void LoadComAddIn(ExcelComAddIn addIn, Guid clsId, string progId) + { + // If we are called without the addIn's DnaLibrary being set, default to the current library + if (addIn.DnaLibrary == null) + addIn.DnaLibrary = DnaLibrary.CurrentLibrary; + addIn.SetProgId(progId); // Put together some nicer descriptions for the Add-ins dialog. diff --git a/Source/ExcelDna.Integration/CustomUI/ExcelCustomTaskPane.cs b/Source/ExcelDna.Integration/CustomUI/ExcelCustomTaskPane.cs index f0b25291..690cc2ee 100644 --- a/Source/ExcelDna.Integration/CustomUI/ExcelCustomTaskPane.cs +++ b/Source/ExcelDna.Integration/CustomUI/ExcelCustomTaskPane.cs @@ -13,9 +13,6 @@ namespace ExcelDna.Integration.CustomUI { public static class CustomTaskPaneFactory { - public const string AddInGuid = "8b752d6a-a78f-422f-907a-8905d54930be"; - public const string AddInProgId = "ExcelDna.CustomTaskPaneAddIn"; - private static ExcelCustomTaskPaneAddIn _addin; // We keep a list of CustomTaksPanes, so that we can clean up when the add-in is removed or reopened. @@ -28,18 +25,73 @@ public static CustomTaskPane CreateCustomTaskPane(Type userControlType, string t return CreateCustomTaskPane(userControlType, title, Type.Missing); } + public static CustomTaskPane CreateCustomTaskPane(Type userControlType, string title, Guid clsIdCTPAddIn, string progIdCTPAddIn) + { + return CreateCustomTaskPane(userControlType, title, Type.Missing, clsIdCTPAddIn, progIdCTPAddIn); + } + public static CustomTaskPane CreateCustomTaskPane(Type userControlType, string title, object parent) { object userControl = Activator.CreateInstance(userControlType); return CreateCustomTaskPane(userControl, title, parent); } + public static CustomTaskPane CreateCustomTaskPane(Type userControlType, string title, object parent, Guid clsIdCTPAddIn, string progIdCTPAddIn) + { + object userControl = Activator.CreateInstance(userControlType); + return CreateCustomTaskPane(userControl, title, parent, clsIdCTPAddIn, progIdCTPAddIn); + } + public static CustomTaskPane CreateCustomTaskPane(object userControl, string title) { return CreateCustomTaskPane(userControl, title, Type.Missing); } + public static CustomTaskPane CreateCustomTaskPane(object userControl, string title, Guid clsIdCTPAddIn, string progIdCTPAddIn) + { + return CreateCustomTaskPane(userControl, title, Type.Missing, clsIdCTPAddIn, progIdCTPAddIn); + } + public static CustomTaskPane CreateCustomTaskPane(object userControl, string title, object parent) + { + return CreateCustomTaskPane((progId) => CreateCustomTaskPane(progId, title, parent), userControl); + } + + public static CustomTaskPane CreateCustomTaskPane(object userControl, string title, object parent, Guid clsIdCTPAddIn, string progIdCTPAddIn) + { + return CreateCustomTaskPane((progId) => CreateCustomTaskPane(progId, title, parent, clsIdCTPAddIn, progIdCTPAddIn), userControl); + } + + // UserControl as already registered. Just create via factory and add-in. + public static CustomTaskPane CreateCustomTaskPane(string controlProgId, string title) + { + return CreateCustomTaskPane(controlProgId, title, Type.Missing); + } + + // UserControl as already registered. Just create via factory and add-in. + public static CustomTaskPane CreateCustomTaskPane(string controlProgId, string title, Guid clsIdCTPAddIn, string progIdCTPAddIn) + { + return CreateCustomTaskPane(controlProgId, title, Type.Missing, clsIdCTPAddIn, progIdCTPAddIn); + } + + public static CustomTaskPane CreateCustomTaskPane(string controlProgId, string title, object parent) + { + return CreateCustomTaskPane(GetCTPFactory(null, null), controlProgId, title, parent); + } + + public static CustomTaskPane CreateCustomTaskPane(string controlProgId, string title, object parent, Guid clsIdCTPAddIn, string progIdCTPAddIn) + { + return CreateCustomTaskPane(GetCTPFactory(clsIdCTPAddIn, progIdCTPAddIn), controlProgId, title, parent); + } + + private static CustomTaskPane CreateCustomTaskPane(ICTPFactory factory, string controlProgId, string title, object parent) + { + CustomTaskPane newCTP = factory.CreateCTP(controlProgId, title, parent); + _customTaskPanes.Add(new WeakReference(newCTP)); // TODO: Only removed when add-in is unloaded...??? + return newCTP; + } + + private static CustomTaskPane CreateCustomTaskPane(Func ctpActivator, object userControl) { // I could use the ProgId and ClsId of the UserControl type here. // But then the registration has to be persistent or coordinated, which I dislike. @@ -61,7 +113,7 @@ public static CustomTaskPane CreateCustomTaskPane(object userControl, string tit using (new ProgIdRegistration(progId, clsId)) using (new ClsIdRegistration(clsId, progId)) { - return CreateCustomTaskPane(progId, title, parent); + return ctpActivator(progId); } } catch (UnauthorizedAccessException secex) @@ -72,27 +124,16 @@ public static CustomTaskPane CreateCustomTaskPane(object userControl, string tit } } - // UserControl as already registered. Just create via factory and add-in. - public static CustomTaskPane CreateCustomTaskPane(string controlProgId, string title) - { - return CreateCustomTaskPane(controlProgId, title, Type.Missing); - } - - public static CustomTaskPane CreateCustomTaskPane(string controlProgId, string title, object parent) - { - ICTPFactory factory = GetCTPFactory(); - CustomTaskPane newCTP = factory.CreateCTP(controlProgId, title, parent); - _customTaskPanes.Add(new WeakReference(newCTP)); // TODO: Only removed when add-in is unloaded...??? - return newCTP; - } - - private static ICTPFactory GetCTPFactory() + private static ICTPFactory GetCTPFactory(Guid? clsIdCTPAddIn, string progIdCTPAddIn) { if (_addin == null) { // Register and create addin _addin = new ExcelCustomTaskPaneAddIn { DnaLibrary = DnaLibrary.CurrentLibrary }; - ExcelComAddInHelper.LoadComAddIn(_addin); + if (clsIdCTPAddIn.HasValue && !string.IsNullOrWhiteSpace(progIdCTPAddIn)) + ExcelComAddInHelper.LoadComAddIn(_addin, clsIdCTPAddIn.Value, progIdCTPAddIn); + else + ExcelComAddInHelper.LoadComAddIn(_addin); } return _addin.Factory; } @@ -117,8 +158,6 @@ internal static void DetachAddIn() } } - [ProgId(CustomTaskPaneFactory.AddInProgId)] - [Guid(CustomTaskPaneFactory.AddInGuid)] internal class ExcelCustomTaskPaneAddIn : ExcelComAddIn, ICustomTaskPaneConsumer { public ICTPFactory Factory; diff --git a/Source/ExcelDna.Test/CustomPane.cs b/Source/ExcelDna.Test/CustomPane.cs index ad5ed481..09fbadcc 100644 --- a/Source/ExcelDna.Test/CustomPane.cs +++ b/Source/ExcelDna.Test/CustomPane.cs @@ -10,7 +10,8 @@ internal class CustomPane public static void Show() { var myControl = new MyUserControl(); - var customPane = CustomTaskPaneFactory.CreateCustomTaskPane(myControl, nameof(myControl)); + var customPane = CustomTaskPaneFactory.CreateCustomTaskPane(myControl, nameof(myControl), + new Guid("dfdd066f-a8ce-4be0-ac13-20a185333473"), "1a7ad958-f8f5-43d4-9161-5bbab6ecda62"); customPane.Visible = true; }