├── .gitattributes ├── .gitignore ├── LICENSE.md ├── README.md ├── Source ├── ExcelDnaHost │ ├── AddInDna.master │ ├── AddInLoader.cs │ ├── AddInXll.master │ ├── DteHelper.cs │ ├── ExcelDnaHost-AddIn.dna │ ├── ExcelDnaHost.csproj │ ├── MsdevManager.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Ribbon.cs │ ├── VsConnection.cs │ ├── VsIde.cs │ ├── VsLink.cs │ ├── VsLinkMessage.cs │ ├── VsLinkServer.cs │ └── packages.config ├── ExcelDnaTools.sln └── ExcelDnaTools │ ├── DebugManager.cs │ ├── ExcelConnection.cs │ ├── ExcelDnaTools.csproj │ ├── ExcelDnaTools.vsct │ ├── ExcelDnaToolsPackage.cs │ ├── GlobalSuppressions.cs │ ├── Guids.cs │ ├── Key.snk │ ├── MyControl.xaml │ ├── MyControl.xaml.cs │ ├── MyToolWindow.cs │ ├── Notes.txt │ ├── PkgCmdID.cs │ ├── Properties │ └── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Resources │ ├── Images.png │ └── Package.ico │ ├── SolutionHelper.cs │ ├── VSPackage.resx │ ├── VsLinkClient.cs │ └── source.extension.vsixmanifest └── XmlSchemas ├── ExcelDna.DnaLibrary.xsd ├── ExcelDnaCatalog.xml ├── ExcelDnaSmall.ico ├── Properties └── AssemblyInfo.cs ├── XmlSchemas.csproj ├── XmlSchemas.sln ├── customUI.xsd ├── customui14.xsd └── source.extension.vsixmanifest /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore temporary files created to run ExcelDna integration tests 2 | Source/Tests/.exceldna.addin/ 3 | Source/Tests/ExcelDna.AddIn.Tasks.IntegrationTests.TestTarget/out/ 4 | 5 | ## Ignore Visual Studio temporary files, build results, and 6 | ## files generated by popular Visual Studio add-ons. 7 | 8 | # User-specific files 9 | .vs/ 10 | *.suo 11 | *.user 12 | *.sln.docstates 13 | *.xll 14 | 15 | # Build results 16 | 17 | [Dd]ebug/ 18 | [Rr]elease/ 19 | [Dd]ebug64/ 20 | [Rr]elease64/ 21 | x64/ 22 | build/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | Package/nupkg/ 26 | 27 | # Ignore NuGet Packages 28 | *.nupkg 29 | **/packages/* 30 | 31 | # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets 32 | !packages/*/build/ 33 | !Package/*/build/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | *_i.c 40 | *_p.c 41 | *.ilk 42 | *.meta 43 | *.obj 44 | *.pch 45 | *.pgc 46 | *.pgd 47 | *.rsp 48 | *.sbr 49 | *.tlb 50 | *.tli 51 | *.tlh 52 | *.tmp 53 | *.tmp_proj 54 | *.log 55 | *.vspscc 56 | *.vssscc 57 | .builds 58 | *.pidb 59 | *.log 60 | *.scc 61 | 62 | # Visual C++ cache files 63 | ipch/ 64 | *.aps 65 | *.ncb 66 | *.opensdf 67 | *.sdf 68 | *.cachefile 69 | *.VC.opendb 70 | *.VC.db 71 | 72 | # Visual Studio profiler 73 | *.psess 74 | *.vsp 75 | *.vspx 76 | 77 | # Guidance Automation Toolkit 78 | *.gpState 79 | 80 | # ReSharper is a .NET coding add-in 81 | _ReSharper*/ 82 | *.[Rr]e[Ss]harper 83 | 84 | # TeamCity is a build add-in 85 | _TeamCity* 86 | 87 | # DotCover is a Code Coverage Tool 88 | *.dotCover 89 | 90 | # NCrunch 91 | *.ncrunch* 92 | .*crunch*.local.xml 93 | 94 | # Installshield output folder 95 | [Ee]xpress/ 96 | 97 | # DocProject is a documentation generator add-in 98 | DocProject/buildhelp/ 99 | DocProject/Help/*.HxT 100 | DocProject/Help/*.HxC 101 | DocProject/Help/*.hhc 102 | DocProject/Help/*.hhk 103 | DocProject/Help/*.hhp 104 | DocProject/Help/Html2 105 | DocProject/Help/html 106 | 107 | # Click-Once directory 108 | publish/ 109 | 110 | # Publish Web Output 111 | *.Publish.xml 112 | 113 | # NuGet Packages Directory 114 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 115 | #packages/ 116 | 117 | # Windows Azure Build Output 118 | csx 119 | *.build.csdef 120 | 121 | # Windows Store app package directory 122 | AppPackages/ 123 | 124 | # Others 125 | sql/ 126 | *.Cache 127 | ClientBin/ 128 | [Ss]tyle[Cc]op.* 129 | ~$* 130 | *~ 131 | *.dbmdl 132 | *.[Pp]ublish.xml 133 | *.pfx 134 | *.publishsettings 135 | 136 | # RIA/Silverlight projects 137 | Generated_Code/ 138 | 139 | # Backup & report files from converting an old project file to a newer 140 | # Visual Studio version. Backup files are not needed, because we have git ;-) 141 | _UpgradeReport_Files/ 142 | Backup*/ 143 | UpgradeLog*.XML 144 | UpgradeLog*.htm 145 | 146 | # SQL Server files 147 | App_Data/*.mdf 148 | App_Data/*.ldf 149 | 150 | 151 | #LightSwitch generated files 152 | GeneratedArtifacts/ 153 | _Pvt_Extensions/ 154 | ModelManifest.xml 155 | 156 | # ========================= 157 | # Windows detritus 158 | # ========================= 159 | 160 | # Windows image file caches 161 | Thumbs.db 162 | ehthumbs.db 163 | 164 | # Folder config file 165 | Desktop.ini 166 | 167 | # Recycle Bin used on file shares 168 | $RECYCLE.BIN/ 169 | 170 | # Mac desktop service store files 171 | .DS_Store 172 | /Source/Tests/ExcelDna.AddIn.Tasks.IntegrationTests.TestTarget/MultipleAddInBuild/MultipleAddInProjectOne.dll 173 | /Source/Tests/ExcelDna.AddIn.Tasks.IntegrationTests.TestTarget/MultipleAddInBuild/MultipleAddInProjectOne.pdb 174 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Govert van Drimmelen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Visual Studio Tools for Excel-DNA 2 | ================================= 3 | 4 | Visual Studio Tools for Excel-DNA is a Visual Studio extension (targeting VS 2012 Professional and better) to assist in the development of Excel-DNA add-ins for Excel. 5 | 6 | The aims is to enable VBA users to move to .NET (with Excel-DNA) more easily, without reinventing the IDE. 7 | 8 | Current Status 9 | -------------- 10 | 11 | We're exploring some ideas... 12 | 13 | License 14 | ------- 15 | 16 | Visual Studio Tools for Excel-DNA is licensed under the MIT license. 17 | -------------------------------------------------------------------------------- /Source/ExcelDnaHost/AddInDna.master: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Source/ExcelDnaHost/AddInLoader.cs: -------------------------------------------------------------------------------- 1 | using ExcelDna.Integration; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace ExcelDna.ExcelDnaTools 10 | { 11 | class AddInLoader 12 | { 13 | // Puts an .xll next to the .dll, together with a trivial .dna file, and loads as an add-in into Excel 14 | // CONSIDER: Keep track of what we change in the directory, so we can clean up when unloading. 15 | // CONSIDER: Should Excel-DNA support setting the BaseDirectory explicitly, so that we would not need to copy the .xll into the project output directory. 16 | // TODO: Put the templates into a resource, or build from scratch using the executing .xll, or something. 17 | // TODO: 64-bit support 18 | // TODO: Only do this the first time...? 19 | public static void RegisterDll(string addInPath) 20 | { 21 | var xllDirectory = Path.GetDirectoryName(ExcelDnaUtil.XllPath); 22 | var masterXllPath = Path.Combine(xllDirectory, "AddInXll.master"); 23 | var masterDnaPath = Path.Combine(xllDirectory, "AddInDna.master"); 24 | 25 | var addInDirectory = Path.GetDirectoryName(addInPath); 26 | var externalLibraryPath = Path.GetFileName(addInPath); 27 | var addInXllPath = Path.Combine(addInDirectory, Path.ChangeExtension(externalLibraryPath, "xll")); 28 | var addInDnaPath = Path.ChangeExtension(addInXllPath, "dna"); 29 | 30 | if (!File.Exists(addInXllPath)) 31 | { 32 | File.Copy(masterXllPath, addInXllPath, false); 33 | } 34 | if (!File.Exists(addInDnaPath)) 35 | { 36 | File.Copy(masterDnaPath, addInDnaPath, false); 37 | var dnaContent = File.ReadAllText(addInDnaPath); 38 | dnaContent = dnaContent.Replace("%AddIn_Path%", externalLibraryPath); 39 | File.WriteAllText(addInDnaPath, dnaContent); 40 | } 41 | 42 | ExcelIntegration.RegisterXLL(addInXllPath); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Source/ExcelDnaHost/AddInXll.master: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Excel-DNA/VSExcel/aa85e670e9a063c9167b5fbf99660d6759ce5763/Source/ExcelDnaHost/AddInXll.master -------------------------------------------------------------------------------- /Source/ExcelDnaHost/DteHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.InteropServices; 5 | using System.Text; 6 | 7 | namespace AppToolsHost 8 | { 9 | // This code from: http://www.codeproject.com/Articles/7984/Automating-a-specific-instance-of-Visual-Studio-NE 10 | // By Mohamed Hendawi 11 | class DteHelper 12 | { 13 | [DllImport("user32.dll")] 14 | private static extern bool SetForegroundWindow(IntPtr hWnd); 15 | 16 | private const int SW_RESTORE = 9; 17 | [DllImport("user32.dll")] 18 | private static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow); 19 | 20 | [DllImport("user32.dll")] 21 | private static extern bool IsIconic(IntPtr hWnd); 22 | 23 | /// 24 | /// Raises an instance of the Visual Studio IDE to the foreground. 25 | /// 26 | /// The DTE object for the IDE you 27 | /// would like to raise to the foreground 28 | 29 | public static void ShowIDE(EnvDTE.DTE ide) 30 | { 31 | // To show an existing IDE, we get the HWND for the MainWindow 32 | // and do a little interop to bring the desired IDE to the 33 | // foreground. I tried some of the following other potentially 34 | // promising approaches but could only succeed in getting the 35 | // IDE's taskbar button to flash (this is as designed). Ex: 36 | // 37 | // ide.MainWindow.Activate(); 38 | // ide.MainWindow.SetFocus(); 39 | // ide.MainWindow.Visible = true; 40 | // ide.MainWindow.WindowState = EnvDTE.vsWindowState.vsWindowStateMinimize; 41 | // ide.MainWindow.WindowState = EnvDTE.vsWindowState.vsWindowStateMaximize; 42 | 43 | System.IntPtr hWnd = (System.IntPtr)ide.MainWindow.HWnd; 44 | if (IsIconic(hWnd)) 45 | { 46 | ShowWindowAsync(hWnd, SW_RESTORE); 47 | } 48 | SetForegroundWindow(hWnd); 49 | ide.MainWindow.Visible = true; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Source/ExcelDnaHost/ExcelDnaHost-AddIn.dna: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Source/ExcelDnaHost/ExcelDnaHost.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {664F7E0C-6316-4E35-86FC-2D6E28F583B8} 8 | Library 9 | Properties 10 | ExcelDna.ExcelDnaTools 11 | ExcelDnaHost 12 | v4.5 13 | 512 14 | 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | false 25 | 26 | 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | false 34 | 35 | 36 | 37 | True 38 | 39 | 40 | ..\packages\Excel-DNA.Lib.0.32.0\lib\ExcelDna.Integration.dll 41 | False 42 | 43 | 44 | True 45 | True 46 | ..\packages\Excel-DNA.Interop.14.0.1\lib\Microsoft.Office.Interop.Excel.dll 47 | 48 | 49 | True 50 | True 51 | ..\packages\Excel-DNA.Interop.14.0.1\lib\Microsoft.Vbe.Interop.dll 52 | 53 | 54 | True 55 | True 56 | ..\packages\Excel-DNA.Interop.14.0.1\lib\Office.dll 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | PreserveNewest 81 | 82 | 83 | PreserveNewest 84 | ASPXCodeBehind 85 | 86 | 87 | PreserveNewest 88 | 89 | 90 | 91 | 92 | 93 | xcopy "C:\Work\ExcelDna\ExcelDnaTools\Source\packages\Excel-DNA.0.32.0\tools\ExcelDna.xll" "$(TargetDir)ExcelDnaHost-AddIn.xll*" /C /Y 94 | xcopy "$(TargetDir)ExcelDnaHost-AddIn.dna*" "$(TargetDir)ExcelDnaHost-AddIn64.dna*" /C /Y 95 | xcopy "C:\Work\ExcelDna\ExcelDnaTools\Source\packages\Excel-DNA.0.32.0\tools\ExcelDna64.xll" "$(TargetDir)ExcelDnaHost-AddIn64.xll*" /C /Y 96 | "C:\Work\ExcelDna\ExcelDnaTools\Source\packages\Excel-DNA.0.32.0\tools\ExcelDnaPack.exe" "$(TargetDir)ExcelDnaHost-AddIn.dna" /Y 97 | "C:\Work\ExcelDna\ExcelDnaTools\Source\packages\Excel-DNA.0.32.0\tools\ExcelDnaPack.exe" "$(TargetDir)ExcelDnaHost-AddIn64.dna" /Y 98 | 99 | 106 | -------------------------------------------------------------------------------- /Source/ExcelDnaHost/MsdevManager.cs: -------------------------------------------------------------------------------- 1 | // This code is from the CodeProject article: http://www.codeproject.com/Articles/7984/Automating-a-specific-instance-of-Visual-Studio-NE 2 | // by Mohamed Hendawi. (Thank you!) 3 | 4 | using System; 5 | using System.Collections; 6 | using System.ComponentModel; 7 | using System.Runtime.InteropServices; 8 | using System.Threading; 9 | using EnvDTE; 10 | using Microsoft.Win32; 11 | 12 | namespace MsdevManager 13 | { 14 | /// 15 | /// Utility class to get you a list of the running instances of the Microsoft Visual 16 | /// Studio IDE. The list is obtained by looking at the system's Running Object Table (ROT) 17 | /// 18 | /// 19 | /// Other ways to get a pointer to a VisualStudio instance: 20 | /// 21 | /// EnvDTE.DTE dte = (EnvDTE.DTE) System.Runtime.InteropServices.Marshal.GetActiveObject("VisualStudio.DTE.7.1"); 22 | 23 | public class Msdev 24 | { 25 | #region Interop imports 26 | 27 | [DllImport("ole32.dll")] 28 | public static extern int GetRunningObjectTable(int reserved, out UCOMIRunningObjectTable prot); 29 | 30 | [DllImport("ole32.dll")] 31 | public static extern int CreateBindCtx(int reserved, out UCOMIBindCtx ppbc); 32 | 33 | [DllImport("user32.dll")] 34 | private static extern bool SetForegroundWindow(IntPtr hWnd); 35 | 36 | private const int SW_RESTORE = 9; 37 | [DllImport("user32.dll")] 38 | private static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow); 39 | 40 | [DllImport("user32.dll")] 41 | private static extern bool IsIconic(IntPtr hWnd); 42 | 43 | #endregion 44 | 45 | /// 46 | /// Get the DTE object for the instance of Visual Studio IDE that has 47 | /// the specified solution open. 48 | /// 49 | /// The absolute filename of the solution 50 | /// Corresponding DTE object or null if no such IDE is running 51 | public static EnvDTE.DTE GetIDEInstance(string solutionFile) 52 | { 53 | Hashtable runningInstances = GetIDEInstances(true); 54 | IDictionaryEnumerator enumerator = runningInstances.GetEnumerator(); 55 | 56 | while (enumerator.MoveNext()) 57 | { 58 | try 59 | { 60 | _DTE ide = (_DTE)enumerator.Value; 61 | if (ide != null) 62 | { 63 | if (ide.Solution.FullName == solutionFile) 64 | { 65 | return (EnvDTE.DTE)ide; 66 | } 67 | } 68 | } 69 | catch { } 70 | } 71 | 72 | return null; 73 | } 74 | 75 | /// 76 | /// Raises an instance of the Visual Studio IDE to the foreground. 77 | /// 78 | /// The DTE object for the IDE you would like to raise to the foreground 79 | public static void ShowIDE(EnvDTE.DTE ide) 80 | { 81 | // To show an existing IDE, we get the HWND for the MainWindow 82 | // and do a little interop to bring the desired IDE to the 83 | // foreground. I tried some of the following other potentially 84 | // promising approaches but could only succeed in getting the 85 | // IDE's taskbar button to flash. Ex: 86 | // 87 | // ide.MainWindow.Activate(); 88 | // ide.MainWindow.SetFocus(); 89 | // ide.MainWindow.Visible = true; 90 | // ide.MainWindow.WindowState = EnvDTE.vsWindowState.vsWindowStateMinimize; 91 | // ide.MainWindow.WindowState = EnvDTE.vsWindowState.vsWindowStateMaximize; 92 | 93 | System.IntPtr hWnd = (System.IntPtr)ide.MainWindow.HWnd; 94 | if (IsIconic(hWnd)) 95 | { 96 | ShowWindowAsync(hWnd, SW_RESTORE); 97 | } 98 | SetForegroundWindow(hWnd); 99 | ide.MainWindow.Visible = true; 100 | } 101 | 102 | public static void ShowIDE(string solutionFile) 103 | { 104 | EnvDTE.DTE ide = Msdev.GetIDEInstance(solutionFile); 105 | if (ide != null) 106 | { 107 | ShowIDE(ide); 108 | } 109 | else 110 | { 111 | // To create a new instance of the IDE, opened to the selected solution we 112 | // could try: 113 | // 114 | // Type dteType = Type.GetTypeFromProgID("VisualStudio.DTE.7.1"); 115 | // EnvDTE.DTE dte = Activator.CreateInstance(dteType) as EnvDTE.DTE; 116 | // dte.MainWindow.WindowState = EnvDTE.vsWindowState.vsWindowStateMaximize; 117 | // dte.MainWindow.Visible = true; 118 | // dte.Solution.Open( solutionFile.Filename ); 119 | // 120 | // This works but the new devenv.exe process does not exit when you close the 121 | // IDE. You could then just reattach as described and the closed IDE would 122 | // quickly redisplay (possibly useful as a feature). 123 | // 124 | // Instead we lookup the path to the IDE executable in the registry and 125 | // just start another process. 126 | 127 | RegistryKey devKey = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\VisualStudio\\7.1\\Setup\\VS"); 128 | string idePath = (string)devKey.GetValue("EnvironmentPath"); 129 | 130 | System.Diagnostics.Process p = new System.Diagnostics.Process(); 131 | p.StartInfo.RedirectStandardOutput = false; 132 | p.StartInfo.Arguments = solutionFile; 133 | p.StartInfo.FileName = idePath; 134 | p.StartInfo.UseShellExecute = true; 135 | p.Start(); 136 | } 137 | } 138 | 139 | /// 140 | /// Get a table of the currently running instances of the Visual Studio .NET IDE. 141 | /// 142 | /// Only return instances that have opened a solution 143 | /// A hashtable mapping the name of the IDE in the running object table to the corresponding DTE object 144 | public static Hashtable GetIDEInstances(bool openSolutionsOnly) 145 | { 146 | Hashtable runningIDEInstances = new Hashtable(); 147 | Hashtable runningObjects = GetRunningObjectTable(); 148 | 149 | IDictionaryEnumerator rotEnumerator = runningObjects.GetEnumerator(); 150 | while (rotEnumerator.MoveNext()) 151 | { 152 | string candidateName = (string)rotEnumerator.Key; 153 | if (!candidateName.StartsWith("!VisualStudio.DTE")) 154 | continue; 155 | 156 | _DTE ide = rotEnumerator.Value as _DTE; 157 | if (ide == null) 158 | continue; 159 | 160 | if (openSolutionsOnly) 161 | { 162 | try 163 | { 164 | string solutionFile = ide.Solution.FullName; 165 | if (solutionFile != String.Empty) 166 | { 167 | runningIDEInstances[candidateName] = ide; 168 | } 169 | } 170 | catch { } 171 | } 172 | else 173 | { 174 | runningIDEInstances[candidateName] = ide; 175 | } 176 | } 177 | return runningIDEInstances; 178 | } 179 | 180 | /// 181 | /// Get a snapshot of the running object table (ROT). 182 | /// 183 | /// A hashtable mapping the name of the object in the ROT to the corresponding object 184 | [STAThread] 185 | public static Hashtable GetRunningObjectTable() 186 | { 187 | Hashtable result = new Hashtable(); 188 | 189 | int numFetched; 190 | UCOMIRunningObjectTable runningObjectTable; 191 | UCOMIEnumMoniker monikerEnumerator; 192 | UCOMIMoniker[] monikers = new UCOMIMoniker[1]; 193 | 194 | GetRunningObjectTable(0, out runningObjectTable); 195 | runningObjectTable.EnumRunning(out monikerEnumerator); 196 | monikerEnumerator.Reset(); 197 | 198 | while (monikerEnumerator.Next(1, monikers, out numFetched) == 0) 199 | { 200 | UCOMIBindCtx ctx; 201 | CreateBindCtx(0, out ctx); 202 | 203 | string runningObjectName; 204 | monikers[0].GetDisplayName(ctx, null, out runningObjectName); 205 | 206 | object runningObjectVal; 207 | runningObjectTable.GetObject(monikers[0], out runningObjectVal); 208 | 209 | result[runningObjectName] = runningObjectVal; 210 | } 211 | 212 | return result; 213 | } 214 | 215 | public static bool CompareInstances(Hashtable instances1, Hashtable instances2) 216 | { 217 | bool changed = false; 218 | foreach (string instances1Key in instances1.Keys) 219 | { 220 | if (!instances2.ContainsKey(instances1Key)) 221 | { 222 | changed = true; 223 | break; 224 | } 225 | } 226 | 227 | if (!changed) 228 | { 229 | foreach (string instances2Key in instances2.Keys) 230 | { 231 | if (!instances1.ContainsKey(instances2Key)) 232 | { 233 | changed = true; 234 | break; 235 | } 236 | } 237 | } 238 | 239 | return changed; 240 | } 241 | } 242 | 243 | public class MsdevMonitorThread 244 | { 245 | public delegate void MonitorMsdevHandler(); 246 | public event MonitorMsdevHandler Changed; 247 | 248 | private System.Threading.Thread m_thread = null; 249 | private ISynchronizeInvoke m_invokeObject = null; 250 | private int m_period = 2000; 251 | private bool m_isRunning = false; 252 | private bool m_openSolutionsOnly = false; 253 | 254 | public MsdevMonitorThread(ISynchronizeInvoke invokeObject, bool openSolutionsOnly) 255 | { 256 | m_invokeObject = invokeObject; 257 | m_openSolutionsOnly = openSolutionsOnly; 258 | } 259 | 260 | ~MsdevMonitorThread() 261 | { 262 | Stop(); 263 | } 264 | 265 | public void Start() 266 | { 267 | m_isRunning = true; 268 | if (m_thread == null) 269 | m_thread = new System.Threading.Thread(new ThreadStart(ThreadMain)); 270 | m_thread.Start(); 271 | } 272 | 273 | public void Stop() 274 | { 275 | m_isRunning = false; 276 | m_thread = null; 277 | } 278 | 279 | private void ThreadMain() 280 | { 281 | // Take a snapshot of the currently running instances of Visual Studio 282 | // We'll also separately keep track of the solution files that each 283 | // instance has open at this time. We'll use it to detect when an 284 | // IDE has loaded or unloaded a solution. 285 | 286 | Hashtable snapshotInstances = Msdev.GetIDEInstances(m_openSolutionsOnly); 287 | Hashtable snapshotSolutions = new Hashtable(); 288 | foreach (string snapshotKey in snapshotInstances.Keys) 289 | { 290 | string solutionFile = String.Empty; 291 | try 292 | { 293 | EnvDTE.DTE ide = (EnvDTE.DTE)snapshotInstances[snapshotKey]; 294 | solutionFile = ide.Solution.FullName; 295 | } 296 | catch { } 297 | 298 | snapshotSolutions[snapshotKey] = solutionFile; 299 | } 300 | 301 | // We'll just keep looping in this thread, periodically checking the 302 | // currently running list of IDE's. If there is any change we'll 303 | // raise a Changed event. 304 | 305 | while (m_isRunning) 306 | { 307 | System.Threading.Thread.Sleep(m_period); 308 | if (Changed != null) 309 | { 310 | Hashtable currentInstances = Msdev.GetIDEInstances(m_openSolutionsOnly); 311 | bool changed = Msdev.CompareInstances(snapshotInstances, currentInstances); 312 | if (changed) 313 | { 314 | m_invokeObject.BeginInvoke(Changed, null); 315 | snapshotInstances = currentInstances; 316 | } 317 | else 318 | { 319 | foreach (string currentKey in currentInstances.Keys) 320 | { 321 | string prevSolutionFile = (string)snapshotSolutions[currentKey]; 322 | string currentSolutionFile = String.Empty; 323 | try 324 | { 325 | EnvDTE.DTE ide = (EnvDTE.DTE)currentInstances[currentKey]; 326 | currentSolutionFile = ide.Solution.FullName; 327 | } 328 | catch { } 329 | if (prevSolutionFile != currentSolutionFile) 330 | { 331 | m_invokeObject.BeginInvoke(Changed, null); 332 | snapshotInstances = currentInstances; 333 | snapshotSolutions[currentKey] = currentSolutionFile; 334 | break; 335 | } 336 | } 337 | } 338 | } 339 | } 340 | } 341 | } 342 | } 343 | -------------------------------------------------------------------------------- /Source/ExcelDnaHost/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("ExcelDnaHost")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("ExcelDnaHost")] 13 | [assembly: AssemblyCopyright("Copyright © 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("c0c944c1-e478-460a-ac79-9ee89f96e821")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /Source/ExcelDnaHost/Ribbon.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.InteropServices; 4 | using System.Linq; 5 | using System.Text; 6 | using ExcelDna.Integration; 7 | using ExcelDna.Integration.CustomUI; 8 | 9 | namespace ExcelDna.ExcelDnaTools 10 | { 11 | [ComVisible(true)] 12 | public class Ribbon : ExcelRibbon 13 | { 14 | public override string GetCustomUI(string RibbonID) 15 | { 16 | // string tabLabel = ExcelDnaUtil.ExcelVersion >= 15.0 ? "EXCEL-DNA" : "Excel-DNA"; 17 | return 18 | @" 19 | 20 | 21 | 22 | 23 | 83 | 84 | 85 | 92 | 93 | 100 | 101 | 109 | 110 | 111 | 112 | 113 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | -------------------------------------------------------------------------------- /Source/ExcelDnaTools/ExcelDnaToolsPackage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Globalization; 4 | using System.IO; 5 | using System.Runtime.InteropServices; 6 | using System.ComponentModel.Design; 7 | using Microsoft.Win32; 8 | using Microsoft.VisualStudio; 9 | using Microsoft.VisualStudio.Shell.Interop; 10 | using Microsoft.VisualStudio.OLE.Interop; 11 | using Microsoft.VisualStudio.Shell; 12 | 13 | namespace ExcelDna.ExcelDnaTools 14 | { 15 | /// 16 | /// This is the class that implements the package exposed by this assembly. 17 | /// 18 | /// The minimum requirement for a class to be considered a valid package for Visual Studio 19 | /// is to implement the IVsPackage interface and register itself with the shell. 20 | /// This package uses the helper classes defined inside the Managed Package Framework (MPF) 21 | /// to do it: it derives from the Package class that provides the implementation of the 22 | /// IVsPackage interface and uses the registration attributes defined in the framework to 23 | /// register itself and its components with the shell. 24 | /// 25 | // This attribute tells the PkgDef creation utility (CreatePkgDef.exe) that this class is 26 | // a package. 27 | [PackageRegistration(UseManagedResourcesOnly = true)] 28 | // This attribute is used to register the information needed to show this package 29 | // in the Help/About dialog of Visual Studio. 30 | [InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)] 31 | // This attribute is needed to let the shell know that this package exposes some menus. 32 | [ProvideMenuResource("Menus.ctmenu", 1)] 33 | 34 | // [ProvideAutoLoad(VSConstants.UICONTEXT.NoSolution_string)] // If we always want to be loaded...? 35 | 36 | // This attribute registers a tool window exposed by this package. 37 | [ProvideToolWindow(typeof(MyToolWindow))] 38 | [Guid(GuidList.guidExcelDnaToolsPkgString)] 39 | public sealed class ExcelDnaToolsPackage : Package, System.IServiceProvider 40 | { 41 | ExcelConnection _connection; 42 | DebugManager _debugManager; 43 | SolutionHelper _solutionHelper; 44 | 45 | /// 46 | /// Default constructor of the package. 47 | /// Inside this method you can place any initialization code that does not require 48 | /// any Visual Studio service because at this point the package object is created but 49 | /// not sited yet inside Visual Studio environment. The place to do all the other 50 | /// initialization is the Initialize method. 51 | /// 52 | public ExcelDnaToolsPackage() 53 | { 54 | Debug.WriteLine(string.Format(CultureInfo.CurrentCulture, "Entering constructor for: {0}", this.ToString())); 55 | } 56 | 57 | /// 58 | /// This function is called when the user clicks the menu item that shows the 59 | /// tool window. See the Initialize method to see how the menu item is associated to 60 | /// this function using the OleMenuCommandService service and the MenuCommand class. 61 | /// 62 | private void ShowToolWindow(object sender, EventArgs e) 63 | { 64 | // Get the instance number 0 of this tool window. This window is single instance so this instance 65 | // is actually the only one. 66 | // The last flag is set to true so that if the tool window does not exists it will be created. 67 | ToolWindowPane window = this.FindToolWindow(typeof(MyToolWindow), 0, true); 68 | if ((null == window) || (null == window.Frame)) 69 | { 70 | throw new NotSupportedException(Resources.CanNotCreateWindow); 71 | } 72 | IVsWindowFrame windowFrame = (IVsWindowFrame)window.Frame; 73 | Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(windowFrame.Show()); 74 | } 75 | 76 | 77 | ///////////////////////////////////////////////////////////////////////////// 78 | // Overridden Package Implementation 79 | #region Package Members 80 | 81 | /// 82 | /// Initialization of the package; this method is called right after the package is sited, so this is the place 83 | /// where you can put all the initialization code that rely on services provided by VisualStudio. 84 | /// 85 | protected override void Initialize() 86 | { 87 | IVsUIShell uiShell = (IVsUIShell)GetService(typeof(SVsUIShell)); 88 | Guid clsid = Guid.Empty; 89 | int result; 90 | uiShell.ShowMessageBox(0, 91 | ref clsid, 92 | "ExcelDnaTools", 93 | "Hello There!!!", 94 | string.Empty, 95 | 0, 96 | OLEMSGBUTTON.OLEMSGBUTTON_OK, 97 | OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST, 98 | OLEMSGICON.OLEMSGICON_INFO, 99 | 0, // false 100 | out result); 101 | Debug.WriteLine (string.Format(CultureInfo.CurrentCulture, "Entering Initialize() of: {0}", this.ToString())); 102 | base.Initialize(); 103 | 104 | // Initialize our internal helpers 105 | _connection = new ExcelConnection(); // Not connected at this point 106 | _debugManager = new DebugManager(this, _connection); 107 | _solutionHelper = new SolutionHelper(this); 108 | 109 | // Add our command handlers for menu (commands must exist in the .vsct file) 110 | OleMenuCommandService mcs = GetService(typeof(IMenuCommandService)) as OleMenuCommandService; 111 | if (mcs == null) 112 | { 113 | Debug.Fail("MenuCommandService not found!?"); 114 | return; 115 | } 116 | 117 | // Create the command for the menu item. 118 | CommandID menuCommandID = new CommandID(GuidList.guidExcelDnaToolsCmdSet, (int)PkgCmdIDList.cmdidExcelDna); 119 | MenuCommand menuItem = new MenuCommand(MenuItemCallback, menuCommandID ); 120 | mcs.AddCommand( menuItem ); 121 | 122 | // Create the command for the tool window 123 | CommandID toolwndCommandID = new CommandID(GuidList.guidExcelDnaToolsCmdSet, (int)PkgCmdIDList.cmdidExcelDnaExplorer); 124 | MenuCommand menuToolWin = new MenuCommand(ShowToolWindow, toolwndCommandID); 125 | mcs.AddCommand( menuToolWin ); 126 | 127 | CommandID attachExcelCommandID = new CommandID(GuidList.guidExcelDnaToolsCmdSet, (int)PkgCmdIDList.cmdidExcelDnaAttachExcel); 128 | OleMenuCommand menuAttachExcel = new OleMenuCommand(AttachExcelCallback, attachExcelCommandID); 129 | menuAttachExcel.ParametersDescription = "$"; // Documented http://www.getcodesamples.com/src/7D389846/8A0A4E48 130 | mcs.AddCommand(menuAttachExcel); 131 | 132 | CommandID attachDebuggerCommandID = new CommandID(GuidList.guidExcelDnaToolsCmdSet, (int)PkgCmdIDList.cmdidExcelDnaAttachDebugger); 133 | OleMenuCommand menuAttachDebugger = new OleMenuCommand(AttachDebuggerCallback, attachDebuggerCommandID); 134 | mcs.AddCommand(menuAttachDebugger); 135 | } 136 | #endregion 137 | 138 | /// 139 | /// This function is the callback used to execute a command when the a menu item is clicked. 140 | /// See the Initialize method to see how the menu item is associated to this function using 141 | /// the OleMenuCommandService service and the MenuCommand class. 142 | /// 143 | private void MenuItemCallback(object sender, EventArgs e) 144 | { 145 | // Show a Message Box to prove we were here 146 | IVsUIShell uiShell = (IVsUIShell)GetService(typeof(SVsUIShell)); 147 | Guid clsid = Guid.Empty; 148 | int result; 149 | //Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(uiShell.ShowMessageBox( 150 | // 0, 151 | // ref clsid, 152 | // "ExcelDnaTools", 153 | // string.Format(CultureInfo.CurrentCulture, "Inside {0}.MenuItemCallback()", this.ToString()), 154 | // string.Empty, 155 | // 0, 156 | // OLEMSGBUTTON.OLEMSGBUTTON_OK, 157 | // OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST, 158 | // OLEMSGICON.OLEMSGICON_INFO, 159 | // 0, // false 160 | // out result)); 161 | // uiShell.GetToolWindowEnum 162 | 163 | Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(uiShell.ShowMessageBox( 164 | 0, 165 | ref clsid, 166 | "ExcelDnaTools", 167 | "Wrote Register message", 168 | string.Empty, 169 | 0, 170 | OLEMSGBUTTON.OLEMSGBUTTON_OK, 171 | OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST, 172 | OLEMSGICON.OLEMSGICON_INFO, 173 | 0, // false 174 | out result)); 175 | } 176 | 177 | // TODO: Rename this to ConnectExcel 178 | void AttachExcelCallback(object sender, EventArgs e) 179 | { 180 | var omce = e as OleMenuCmdEventArgs; 181 | if (omce == null) 182 | return; 183 | 184 | // Show a Message Box to prove we were here 185 | IVsUIShell uiShell = (IVsUIShell)GetService(typeof(SVsUIShell)); 186 | Guid clsid = Guid.Empty; 187 | int result; 188 | Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(uiShell.ShowMessageBox( 189 | 0, 190 | ref clsid, 191 | "ExcelDnaTools", 192 | "Inside AttachExcel: " + omce.InValue + " !!!", 193 | string.Empty, 194 | 0, 195 | OLEMSGBUTTON.OLEMSGBUTTON_OK, 196 | OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST, 197 | OLEMSGICON.OLEMSGICON_INFO, 198 | 0, // false 199 | out result)); 200 | 201 | _connection.AttachExcel((string)omce.InValue); 202 | 203 | } 204 | 205 | void AttachDebuggerCallback(object sender, EventArgs e) 206 | { 207 | // CONSIDER: Different approaches here: 208 | // 1. Get current project through Shell, figure out path and reload, the attach debugger 209 | // 2. Use debugger events to know when to reload 210 | 211 | // This is suitable for NuGet Excel-DNA package. 212 | var startArguments = _solutionHelper.GetActiveStartArguments(); 213 | if (!string.IsNullOrEmpty(startArguments) && startArguments.EndsWith(".xll")) 214 | { 215 | // TODO: UI indicator for slow call... 216 | _connection.RegisterAddIn(startArguments).Wait(); 217 | } 218 | else 219 | { 220 | // Maybe this is just a Class Library project, without the full Excel-DNA package. 221 | var pathName = _solutionHelper.GetActiveTargetName(); 222 | _connection.RegisterAddIn(pathName).Wait(); 223 | } 224 | _debugManager.AttachDebuggerToExcel(); 225 | } 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /Source/ExcelDnaTools/GlobalSuppressions.cs: -------------------------------------------------------------------------------- 1 | // This file is used by Code Analysis to maintain SuppressMessage 2 | // attributes that are applied to this project. Project-level 3 | // suppressions either have no target or are given a specific target 4 | // and scoped to a namespace, type, member, etc. 5 | // 6 | // To add a suppression to this file, right-click the message in the 7 | // Error List, point to "Suppress Message(s)", and click "In Project 8 | // Suppression File". You do not need to add suppressions to this 9 | // file manually. 10 | 11 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1017:MarkAssembliesWithComVisible")] 12 | -------------------------------------------------------------------------------- /Source/ExcelDnaTools/Guids.cs: -------------------------------------------------------------------------------- 1 | // Guids.cs 2 | // MUST match guids.h 3 | using System; 4 | 5 | namespace ExcelDna.ExcelDnaTools 6 | { 7 | static class GuidList 8 | { 9 | public const string guidExcelDnaToolsPkgString = "10e82a35-4493-43be-b6d3-228399509924"; 10 | public const string guidExcelDnaToolsCmdSetString = "ed07e70d-6403-4bc3-aa0a-50c726b6442b"; 11 | public const string guidToolWindowPersistanceString = "f4ef482c-71f3-4638-88bd-65eedaa4665e"; 12 | 13 | public static readonly Guid guidExcelDnaToolsCmdSet = new Guid(guidExcelDnaToolsCmdSetString); 14 | }; 15 | } -------------------------------------------------------------------------------- /Source/ExcelDnaTools/Key.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Excel-DNA/VSExcel/aa85e670e9a063c9167b5fbf99660d6759ce5763/Source/ExcelDnaTools/Key.snk -------------------------------------------------------------------------------- /Source/ExcelDnaTools/MyControl.xaml: -------------------------------------------------------------------------------- 1 |  11 | 12 | 13 | Excel-DNA Explorer 14 |