Jump to content


VS Project Synch macro


5 replies to this topic

#1 Guest_Wernaeh_*

  • Guests

Posted 26 September 2009 - 02:00 PM

Hello everyone :)

In the last months, I have been busy in a less-than-optimally organized C++ project.

This project involved work on a larger codebase that had absolutely no version control system available. All files were lying around in a semi-sorted directory structure on some intranet server. This, of course, made daily synching to this structure quite tedious. In particular, Visual Studio 2003 refuses to import recursive directories, and aborts importing if any files are already present within the solution.

Thus, I wrote the following macro.

It essentially discards all folders and files in selected projects, and rebuilds these from the solution directories.

During this process, new solution folders are created for each on-disk sub directory, so the source files remain nicely sorted in the IDE.

The macro has been tested in Visual Studio 2003, but should hopefully work in other VS versions with slight adaptions.

Note that the
Microsoft.VisualStudio.VCProject
and
Microsoft.VisualStudio.VCProjectEngine
references need to be added to the Visual Studio macro project for the macro to work.

Perhaps one or another might find it helpful, it's free for any use.

If anybody is interested in more integrated Visual Studio code, I also have a working COM-based C++ version of the macro available.

Cheers,
- Wernaeh



'==========================================================================
' Project generator main module.
'
' Copyright Wernaeh, Juli 2009
' Free for any use.
'
' Contact: wernaeh@gmail.com
'==========================================================================

Option Explicit On 
Option Strict Off

Imports EnvDTE
Imports System.Diagnostics
Imports Microsoft.VisualStudio.VCProject
Imports Microsoft.VisualStudio.VCProjectEngine

Public Module ProjectGen

	'==========================================================================
	' Main project generation entry point
	'==========================================================================

	Sub regenerateMain()

		'======================================================================
		' Temporaries
		'======================================================================

		' Temporary loop variables
		Dim i As Integer
		Dim j As Integer

		' Array with file extensions to be added
		Dim exts(8) As String
		exts(0) = ".c"
		exts(1) = ".cpp"
		exts(2) = ".h"
		exts(3) = ".def"
		exts(4) = ".ico"
		exts(5) = ".rc"
		exts(6) = ".flex"
		exts(7) = ".bat"


		'======================================================================
		' Determine project selection
		'======================================================================

		' Retrieve an array with selected projects
		Dim selectedprojects() As Project
		Dim selectedprojectcount As Integer
		selectedprojectcount = 0

		For i = 1 To DTE.SelectedItems.Count
			If TypeOf DTE.SelectedItems.Item(i).Project Is Project Then
				selectedprojectcount += 1
				Redim Preserve selectedprojects(selectedprojectcount - 1)			
				selectedprojects(selectedprojectcount - 1) = _
					DTE.SelectedItems.Item(i).Project
			End If
		Next


		' Notify the user if no projects have been selected
		If selectedprojectcount = 0 Then
			MsgBox("Please select some projects for processing with the" & _
                    vbNewLine & _
					"project generator within the solution view.", _
					MsgBoxStyle.OKOnly, _
					"Please select projects!")
			Return
		End If


		'======================================================================
		' Handle open documents
		'======================================================================

		' Prompt the user whether to save all open documents, if there
		' are any.
		If DTE.Documents.Count > 0 Then
			Dim usersaveall As MsgBoxResult
			usersaveall = _
				MsgBox("The project generator will close any open files." & _
                        vbNewLine & _
						"Should these be saved now?", _
						MsgBoxStyle.YesNoCancel, _
						"Save open files now?")

			If usersaveall = MsgBoxResult.Cancel
				Return
			End If

			If usersaveall = MsgBoxResult.Yes
				DTE.Documents.SaveAll()
			End If

			DTE.Documents.CloseAll(vsSaveChanges.vsSaveChangesNo)
		End If


		'======================================================================
		' Regenerate projects
		'======================================================================
		
		' Now, regenerate each project
		For i = 0 To selectedprojectcount - 1

			Dim project As Project
			project = selectedprojects(i)


			'==================================================================
			' Regeneration prompt
			'==================================================================

			' First, prompt whether regeneration is desired at all
			' for a single project.
			Dim userregenerate As MsgBoxResult
			userregenerate = _
				MsgBox("Should the project:" & vbNewLine & _
					   "        " & selectedprojects(i).Name & vbNewLine & _
					   "be regenerated?" & vbNewLine & vbNewLine & _
		"All file links and folders within the project will be removed," & _
                        vbNewLine & _
					   "and regenerated from the on-disk project structure.", _
					   MsgBoxStyle.YesNoCancel, _
					   "Really regenerate project: " & _
                        selectedprojects(i).Name & "?")

			If userregenerate = MsgBoxResult.Cancel
				Return
			End If

			If userregenerate = MsgBoxResult.No
				Goto NextProject
			End If


			'==================================================================
			' Delete old project items
			'==================================================================

			' Here, we just need to delete root project items.
			' Since we do not know how indices change when deleting
			' items, we do a copy to a save array first.
			Dim items() As ProjectItem
			Dim itemcount As Integer

			itemcount = project.ProjectItems.Count
			Redim items(itemcount - 1)

			For j = 1 To project.ProjectItems.Count
				items(j - 1) = project.ProjectItems.Item(j)				
			Next

			For j = 0 To itemcount - 1
				items(j).Remove()
			Next


			'==================================================================
			' Regenerate project items from disk
			'==================================================================

			' First, retrieve the project folder
			Dim projectfolder As String
			projectfolder = _
				Left(project.FullName, InStrRev(project.FullName, "") - 1)

			' Then, starting with the project folder,
			' add all files into our project.
			' Note there is a bug here, we can't use the common DTE interfaces
			' for managing project folders for VC projects.
			' Thus, we need to go for the C++ specific project type instead.
			Dim projectvc As VCProject
			projectvc = project.Object

			addItemsInFolder(projectfolder, "", projectvc, nothing, exts, 0)
		
			NextProject:			
		Next


		'==================================================================
		' Recursively close all treeview items
		'==================================================================

		Dim solutionexplorer As UIHierarchy
		solutionexplorer = _
			DTE.Windows.Item(Constants.vsWindowKindSolutionExplorer).Object

		For i = 1 To solutionexplorer.UIHierarchyItems.Count
			closeTreeViewHelper _
				(solutionexplorer.UIHierarchyItems.Item(i), solutionexplorer, _
				 selectedprojects, selectedprojectcount, 0)
		Next

	End Sub


	'==========================================================================
	' Directory management helper
	'==========================================================================

	Sub addItemsInFolder(basefolder As String, recursefolder As String, _
						 projectvc As VCProject, _
						 filtervc As VCFilter, exts() As String, _
						 depth As Integer)

		' Put together base directory for searching,
		' this needs to be the absolute directory within the
		' project base directory.
		Dim searchfolder As String

		If recursefolder <> "" Then
			searchfolder = basefolder & "" & recursefolder
		Else
			searchfolder = basefolder
		End If


		' First, handle files within the folder.
		Dim filename As String
		filename = Dir(searchfolder & "*")


		Do While (filename <> "")

			' Expand file to relative path within project
			If recursefolder <> "" Then
				filename = recursefolder & "" & filename
			End If

			' For each found file,
			' check if it matches the provided extensions filter.
			Dim i As Integer

			For i = 0 To UBound(exts)
				If Right(filename, Len(exts(i))) = exts(i) Then

					' File matches, so either add it to the filter
					' or the project itself
					Dim file As VCFile

					If filtervc Is Nothing Then
						file = projectvc.AddFile(filename)
					Else
						file = filtervc.AddFile(filename)
					End If

					Goto filefinished
				End If
			Next

			filefinished:

			filename = Dir()
		Loop


		' Load all folder directories into some array - 
		' otherwise recursion won't do, since Dir() does
		' not have a search handle.
		Dim dirnames() As String
		Dim dirnamecount As Integer
		dirnamecount = 0

		Dim dirname As String
		dirname = Dir(searchfolder & "*", vbDirectory)

		Do While (dirname <> "")
			If (GetAttr(searchfolder & "" & dirname) And vbDirectory) = _
                    vbDirectory Then
				dirnamecount += 1
				Redim Preserve dirnames(dirnamecount - 1)
				dirnames(dirnamecount - 1) = dirname
			End If			

			dirname = Dir()
		Loop


		' Finally, create items for each folder, and
		' recurse into subfolders.
		Dim j As Integer
	
		For j = 0 To dirnamecount - 1

			Dim newfilter As VCFilter

			If filtervc Is Nothing Then
				newfilter = projectvc.AddFilter(dirnames(j))
			Else
				newfilter = filtervc.AddFilter(dirnames(j))
			End If

			Dim newrecurse As String

			If recursefolder <> "" Then
				newrecurse = recursefolder & "" & dirnames(j)
			Else
				newrecurse = dirnames(j)
			End If

			addItemsInFolder(basefolder, newrecurse, _
							 projectvc, newfilter, exts, depth + 1)
		Next

	End Sub


	'==========================================================================
	' Tree view closing helper
	'==========================================================================

	Sub closeTreeViewHelper(hierarchyitem As UIHierarchyItem, _
							solutionexplorer As UIHierarchy, _
							selectedprojects() As Project, _
							selectedprojectcount As Integer, _
							depth As Integer)

		' Helper call for closing all but the lowest hierarchy items.
		' This is a little bit more complicated than one would assume:
		' First, we need to check if the given item is a folder.
		' This is done by checking its number of children - empty folders
		' are ignored.
		' Then, the folder is first expanded, so we may close children
		' folders - otherwise closing children doesn't work.
		' Then, the folder is closed again.
		' Folder closing is performed by selecting the folder and
		' then simulating a mouse click event.
		
		' Test if we are a valid folder - i.e. we have children.
		' For leaf entries (files), we just abort here.
		If hierarchyitem.UIHierarchyItems.Count = 0 Then
			Return
		End If


		' On level 1 (projects level), we should ignore all projects
		' that currently are not selected.
		' This keeps the update from messing with unrelated projects.
		If depth = 1 Then
			Dim j As Integer

			For j = 0 To selectedprojectcount - 1
				If selectedprojects(j) Is hierarchyitem.Object Then
					Goto projectselected
				End If			
			Next


			' Project not selected, so abort
			Return

			projectselected:
		End If


		' We are a folder, so see if we are expanded,
		' if we aren't do the expansion now.
		If Not hierarchyitem.UIHierarchyItems.Expanded Then
			hierarchyitem.Select(vsUISelectionType.vsUISelectionTypeSelect)
			solutionexplorer.DoDefaultAction()
		End If


		' Recurse on all children items
		Dim i As Integer
		For i = 1 To hierarchyitem.UIHierarchyItems.Count
			closeTreeViewHelper _
				(hierarchyitem.UIHierarchyItems.Item(i), solutionexplorer, _
				 selectedprojects, selectedprojectcount, depth + 1)
		Next


		' Finally, unexpand our object again,
		' but only if it is not on depth = 1, so root project folders and
		' files remain visible.
		' (Depth = 0 -> Solution, Depth = 1 -> Projects)
		If depth > 1 Then
			hierarchyitem.Select(vsUISelectionType.vsUISelectionTypeSelect)
			solutionexplorer.DoDefaultAction()
		End If

	End Sub

End Module


#2 Nick

    Senior Member

  • Members
  • PipPipPipPip
  • 1227 posts
  • LocationOttawa, Ontario, Canada

Posted 28 September 2009 - 07:38 AM

I don't get it. Shouldn't you instead just be using version control? TortoiseSVN and TortoiseGit are pretty awesome.

#3 rouncer

    Senior Member

  • Members
  • PipPipPipPip
  • 2725 posts

Posted 28 September 2009 - 08:22 AM

wow, havent looked at basic in a while.
you used to be able to fit a game on a disk, then you used to be able to fit a game on a cd, then you used to be able to fit a game on a dvd, now you can barely fit one on your harddrive.

#4 Wernaeh

    Senior Member

  • Members
  • PipPipPipPip
  • 368 posts

Posted 30 September 2009 - 06:07 AM

Quote

I don't get it. Shouldn't you instead just be using version control? TortoiseSVN and TortoiseGit are pretty awesome

Ya, it wasn't my decision not to use any version control - sadly, sometimes the people who decide are not those that should do so...

That's why I came up with the macro above - the only alternative was that I'd create my own local SVN repository, synch that to the coworkers network project folders on the one side, and to VS on the other.

Hope that makes it a little more clear ;)

Cheers,
- Wernaeh
Some call me mathematician, some just call me computer guy. Yet, I prefer the term professional weirdo :)

#5 Nick

    Senior Member

  • Members
  • PipPipPipPip
  • 1227 posts
  • LocationOttawa, Ontario, Canada

Posted 30 September 2009 - 09:47 AM

Ok, thanks for clarifying that. Still, it leaves a bad taste in my mouth to suggest a neat solution to a problem that actually really asks for a totally different solution: a hammer to slam on the heads of the people who decided not to use source control. :blush:

I seriously suggest to try and convince every possible coworker of using SVN, and create some momentum to demand server space for it. There's no excuse for not using version control.

#6 Wernaeh

    Senior Member

  • Members
  • PipPipPipPip
  • 368 posts

Posted 30 September 2009 - 08:36 PM

... great... now I want a hammer ;)

Cheers,
- Wernaeh
Some call me mathematician, some just call me computer guy. Yet, I prefer the term professional weirdo :)





1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users