Have you ever tried adding a new reference to lots of projects in a very large application? If you have, you know this already and if you haven’t, let me tell you: it gets old really fast… Open project, right click the project, click on “Add Reference…” and so on. As an inherently lazy demographic, this simply is not an options for us developers. So what do we do? We automate it with a VisualStudio macro.
First of all: yes, its VB – deal with it.
You can find the Macros IDE in the Tools menu of VS under Macros. There are some samples that come with VS and there is some code out there on the web, but what I want to show you is how you can automate project reference handling.
DTE.Solution.Projects
gives you a collection of project objects in your current solution. You can add projects with
DTE.Solution.AddFromFile(projectFilePath)
but to add a reference to a project, you need to get a handle on a VSProject while the projects in DTE.Solution.Projects are of type Project. To get a VSProject from a Project, simply do this
Dim vsProject = CType(DTE.Solution.Projects.Item(1).Object, VSProject)
Now you can add a reference to this project by doing this
‘ add project reference to otherProject (of type Project)
vsProject.References.AddProject(otherProject)
‘ add file reference to assemblyPath
vsProject.References.Add(assemblyPath)
To get a better picture of how you could use that. Here is some code of mine that I used to add a file reference to a bunch of projects. The macro first asks the user to select a target assembly and then a file containing a list of folder names (separated by newlines). For every entry in the folder list, the macro searches for a VB or C# project in that folder or a parent folder and if the project hasn’t already been processed, adds the project to the solution and adds the file reference.
Public Module References Public Sub CustomAddFileReference() Dim log As TextWriter = New StringWriter() If (DTE.Solution.Count <> 0) Then MsgBox("Please close solution first") Return End If DTE.Solution.Create("C:\", "automagic") Dim openFileDialog As Forms.FileDialog = New Forms.OpenFileDialog Dim winptr As WinWrapper = New WinWrapper Dim target As String Dim projects As Dictionary(Of String, Project) = New Dictionary(Of String, Project)() openFileDialog.Filter = "Assemblies (*.dll)|*.dll|All files (*.*)|*.*" openFileDialog.FilterIndex = 2 openFileDialog.Title = "Select Target Assembly" If openFileDialog.ShowDialog(winptr) = Forms.DialogResult.OK Then target = openFileDialog.FileName End If openFileDialog.Filter = "All files (*.*)|*.*" openFileDialog.FilterIndex = 1 openFileDialog.Title = "Select Project List" If openFileDialog.ShowDialog(winptr) = Forms.DialogResult.OK Then Dim reader As StreamReader = File.OpenText(openFileDialog.FileName) While (Not reader.EndOfStream) Dim line As String = reader.ReadLine().Trim() Dim projFile = FindProjectFile(line) If (Not projFile Is Nothing AndAlso Not projects.ContainsKey(projFile)) Then log.WriteLine(projFile) Try Dim source As Project = DTE.Solution.AddFromFile(projFile) projects.Add(projFile, source) Dim vsProject = CType(source.Object, VSProject) vsProject.References.Add(target) Catch ex As Exception log.WriteLine(ex.ToString()) End Try End If End While reader.Dispose() End If Dim outputPane As OutputWindowPane = GetOutputWindowPane("Macros") outputPane.OutputString(log.ToString()) End Sub Private Function FindProjectFile(ByVal directory As String) As String Dim path = directory Dim project = Nothing Dim ext = Nothing While (project Is Nothing) For Each child As String In System.IO.Directory.GetFiles(path) ext = System.IO.Path.GetExtension(child) If (ext = ".vbproj" OrElse ext = ".csproj") Then Return child End If Next path = System.IO.Path.GetFullPath(System.IO.Path.Combine(path, "..")) End While Return Nothing End Function Private Function GetOutputWindowPane(ByVal Name As String, Optional ByVal show As Boolean = True) As OutputWindowPane Dim window As Window Dim outputWindow As OutputWindow Dim outputWindowPane As OutputWindowPane window = DTE.Windows.Item(EnvDTE.Constants.vsWindowKindOutput) If show Then window.Visible = True outputWindow = window.Object Try outputWindowPane = outputWindow.OutputWindowPanes.Item(Name) Catch e As System.Exception outputWindowPane = outputWindow.OutputWindowPanes.Add(Name) End Try outputWindowPane.Activate() Return outputWindowPane End Function End Module '' This class is used to set the proper parent to any UI that you may display from within a macro. '' See the AddClassicComRef macro for an example of how this is used Public Class WinWrapper Implements System.Windows.Forms.IWin32Window Overridable ReadOnly Property Handle() As System.IntPtr Implements System.Windows.Forms.IWin32Window.Handle Get Dim iptr As New System.IntPtr(DTE.MainWindow.HWnd) Return iptr End Get End Property End Class