Click here to Skip to main content
15,896,063 members
Articles / .NET

Versioning Controlled Build

Rate me:
Please Sign up or sign in to vote.
4.90/5 (237 votes)
8 Dec 2013CPOL35 min read 2.2M   20.6K   779  
A Visual Studio add-in and command-line utility that automates versioning of .NET and VC++ projects
/*
 * Filename:    VSSolutionBrowser.cs
 * Product:     Versioning Controlled Build
 * Solution:    BuildAutoIncrement
 * Project:     AddinImplementation
 * Description: Class that collects information for all projects in Visual 
 *              Studio solution. 
 * Copyright:   Julijan �ribar, 2004-2013
 * 
 * This software is provided 'as-is', without any express or implied
 * warranty.  In no event will the author(s) be held liable for any damages
 * arising from the use of this software.
 *
 * Permission is granted to anyone to use this software for any purpose,
 * including commercial applications, and to alter it and redistribute it
 * freely, subject to the following restrictions:
 *
 * 1. The origin of this software must not be misrepresented; you must not
 *    claim that you wrote the original software. If you use this software
 *    in a product, an acknowledgment in the product documentation would be
 *    appreciated but is not required.
 * 2. Altered source versions must be plainly marked as such, and must not be
 *    misrepresented as being the original software.
 * 3. This notice may not be removed or altered from any source distribution.
 */
using EnvDTE;

#if !FX1_1
using EnvDTE80;
using DTE = EnvDTE80.DTE2;
#endif

using System;
using System.Collections;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;

using VSLangProj;
using VSLangProj2;

namespace BuildAutoIncrement {

    /// <summary>
	///   Object responsible for collecting project properties.
	/// </summary>
	public class VSSolutionBrowser : SolutionBrowser, IDisposable {
        /// <summary>
        ///   Enumeration for OnRunOrPreview setting.
        /// </summary>
        private enum OnRunOrPreviewSetting
        {
            Undefined = -1,
            SaveChanges,
            PromptToSaveChanges,
            DontSaveChanges
        };

        #region Constructor and Dispose methods

        /// <summary>
        ///   Initializes an empty <c>VSSolutionBrowser</c> object.
        /// </summary>
        /*
        private VSSolutionBrowser()
        {
            MessageFilter.Register();
        }
        */

        /// <summary>
        ///   Initializes ProjectBrowser object.
        /// </summary>
        /// <param name="devEnvApplicationObject">
        ///   Development environment from which application is started.
        /// </param>
        public VSSolutionBrowser(DTE devEnvApplicationObject, VcbConfiguration configuration)
            : base(configuration)
        {
            MessageFilter.Register();
            Debug.Assert(devEnvApplicationObject != null);
            m_devEnvApplicationObject = devEnvApplicationObject;
            PreProcess();
            m_sourceSafeCheckOut = new VSSCheckout(m_devEnvApplicationObject);
            LoadProjectsInfo();
        }

        protected override void Dispose(bool disposing)
        {
            if (!m_disposed)
            {
                try
                {
                    PostProcess();
                    MessageFilter.Revoke();
                    m_disposed = true;
                }
                finally
                {
                    base.Dispose(disposing);
                }
            }
        }

        #endregion // Constructor and Dispose method

        #region Public properties

        /// <summary>
        ///   Gets the current solution name. 
        /// </summary>
        public override string SolutionName
        {
            get { return Path.GetFileNameWithoutExtension(m_devEnvApplicationObject.Solution.FileName); }
        }

        /// <summary>
        ///   Gets the current solution filename. 
        /// </summary>
        public override string SolutionFilename
        {
            get { return m_devEnvApplicationObject.Solution.FileName; }
        }

        #endregion // Public properties

        #region Public methods

        /// <summary>
        ///   Checks out project version files for projects provided.
        /// </summary>
        /// <param name="projectsToCheckOut">
        ///   Array of <c>ProjectInfo</c> objects which must be checked out.
        /// </param>
        public override void CheckOutProjectVersionFiles(ProjectInfo[] projectsToCheckOut)
        {
            if (m_sourceSafeCheckOut != null)
                m_sourceSafeCheckOut.CheckOut(projectsToCheckOut);
        }

        #endregion // Public methods

        #region Protected methods

        /// <summary>
        ///   Basic preparation before displaying the main form.
        /// </summary>
        protected override void PreProcess()
        {
            Debug.Assert(m_devEnvApplicationObject != null);
            m_initialOnRunOrPreview = OnRunOrPreview;
            m_initialAutoloadExternalChanges = AutoloadExternalChanges;
            if (!m_initialAutoloadExternalChanges)
                AutoloadExternalChanges = true;
            if (m_numberingOptions.SaveModifiedFilesBeforeRunningAddinCommand)
                SaveAllUnsavedDocuments();
            else
                CheckForUnsavedFiles();
        }

        /// <summary>
        ///   Restores environment settings.
        /// </summary>
        protected override void PostProcess()
        {
            OnRunOrPreview = m_initialOnRunOrPreview;
            AutoloadExternalChanges = m_initialAutoloadExternalChanges;
        }

        #endregion // Protected methods

        #region Private methods

        /// <summary>
        ///   Browses the solution for all projects, creates corresponding 
        ///   <c>ProjectInfo</c> and and adds them to <c>m_projects</c> list.
        /// </summary>
        private void LoadProjectsInfo()
        {
            Debug.Assert(m_devEnvApplicationObject != null && m_devEnvApplicationObject.Solution != null);
            Debug.Assert(m_allProjects != null);
            m_allProjects.Clear();
            int level = 0;
            foreach (Project project in m_devEnvApplicationObject.Solution.Projects)
            {
                try
                {
                    ProjectTypeInfo projectTypeInfo = ProjectTypeInfo.ProjectTypeInfos[project.Kind];
                    if (projectTypeInfo != null)
                    {
                        Debug.Assert(projectTypeInfo != null);
                        // setup projects are treated differently since the version is stored inside project file!
                        if (projectTypeInfo == ProjectTypeInfo.SetupProject || projectTypeInfo == ProjectTypeInfo.InstallShieldLEProject)
                            m_allProjects.Add(GetSetupInfo(project, "", projectTypeInfo, 0));
                        else if (projectTypeInfo == ProjectTypeInfo.EnterpriseProject)
                        {
                            // it is enterprise template project with subprojects
                            ProjectInfo enterpriseTemplateProjectInfo = new ProjectInfo(project.Name, project.FullName, project.Name, projectTypeInfo, level);
                            RecurseSubProjectTree(project.ProjectItems, enterpriseTemplateProjectInfo, level);
                            m_allProjects.Add(enterpriseTemplateProjectInfo);
                        }
                        else if (projectTypeInfo == ProjectTypeInfo.SolutionFolder)
                        {
                            ProjectInfo solutionFolder = new ProjectInfo(project.Name, project.Name, project.Name, projectTypeInfo, level);
                            RecurseSubProjectTree(project.ProjectItems, solutionFolder, level);
                            m_allProjects.Add(solutionFolder);
                        }
                        else
                        {
                            m_allProjects.Add(GetProjectInfo(project, "", projectTypeInfo, level));
                        }
                    }
                    else
                        Trace.WriteLine(string.Format("Not supported project kind: {0}", project.Kind));
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.ToString());
#if DEBUG
                    ExceptionForm.Show(new WindowAdapter(m_devEnvApplicationObject.MainWindow.HWnd), e, "VCB Error");
#endif
                }
            }
        }

        /// <summary>
        ///   Recurses sub project tree (e.g. for Enterprise template projects)
        ///   searching for the largest version.
        /// </summary>
        /// <param name="projectItems">
        ///   Collection of <c>ProjectItems</c> to recurse.
        /// </param>
        private void RecurseSubProjectTree(ProjectItems parentProjectItems, ProjectInfo parentProjectInfo, int level)
        {
            level++;
            Debug.Assert(parentProjectItems != null);
            Debug.Assert(parentProjectInfo != null);
            try
            {
                foreach (ProjectItem projectItem in parentProjectItems)
                {
                    try
                    {
                        Project subProject = projectItem.SubProject;
                        if (subProject != null)
                        {
                            ProjectTypeInfo subProjectTypeInfo = ProjectTypeInfo.ProjectTypeInfos[subProject.Kind];
                            if (subProjectTypeInfo == ProjectTypeInfo.EnterpriseProject || subProjectTypeInfo == ProjectTypeInfo.SolutionFolder)
                            {
                                ProjectInfo subProjectInfo = new ProjectInfo(subProject.Name, subProject.Name, AppendBranchToPath(parentProjectInfo.UIPath, subProject.Name), subProjectTypeInfo, level);
                                RecurseSubProjectTree(subProject.ProjectItems, subProjectInfo, level);
                                parentProjectInfo.SubProjects.Add(subProjectInfo);
                            }
                            else if (subProjectTypeInfo == ProjectTypeInfo.SetupProject)
                            {
                                ProjectInfo pi = GetSetupInfo(subProject, parentProjectInfo.UIPath, subProjectTypeInfo, level);
                                Debug.Assert(pi != null);
                                Debug.Assert(m_allProjects != null);
                                parentProjectInfo.SubProjects.Add(pi);
                            }
                            else if (subProjectTypeInfo != null)
                            {
                                ProjectInfo pi = GetProjectInfo(subProject, parentProjectInfo.UIPath, subProjectTypeInfo, level);
                                Debug.Assert(pi != null);
                                Debug.Assert(m_allProjects != null);
                                parentProjectInfo.SubProjects.Add(pi);
                            }
                        }
                        else
                        {
                            // folders do not have subprojects but only project items
                            ProjectTypeInfo projectTypeInfo = ProjectTypeInfo.ProjectTypeInfos[projectItem.Kind];
                            if (projectTypeInfo == ProjectTypeInfo.VirtualFolder)
                            {
                                ProjectInfo subProjectInfo = new ProjectInfo(projectItem.Name, projectItem.Name, AppendBranchToPath(parentProjectInfo.UIPath, projectItem.Name), projectTypeInfo, level);
                                RecurseSubProjectTree(projectItem.ProjectItems, subProjectInfo, level);
                                if (subProjectInfo.SubProjects.Count > 0)
                                {
                                    parentProjectInfo.SubProjects.Add(subProjectInfo);
                                }
                            }
                        }
                    }
                    catch (NotImplementedException e)
                    {
                        Debug.WriteLine(e.ToString());
                    }
                }
            }
            catch (Exception e)
            {
                ExceptionForm.Show(new WindowAdapter(m_devEnvApplicationObject.MainWindow.HWnd), e, "Browsing Solution Error");
            }
        }

        /// <summary>
        ///   Creates <c>ProjectInfo</c> for project provided.
        /// </summary>
        /// <param name="project">
        ///   Project examined.
        /// </param>
        /// <param name="projectTypeInfo">
        ///   Project type info for the project.
        /// </param>
        /// <returns>
        ///   <c>ProjectInfo</c> for the project provided.
        /// </returns>
        private ProjectInfo GetProjectInfo(Project project, string parentUiPath, ProjectTypeInfo projectTypeInfo, int level)
        {
            if (!projectTypeInfo.IsVersionable)
            {
                return new ProjectInfo(project.Name, project.FullName, AppendBranchToPath(parentUiPath, project.Name), projectTypeInfo, level);
            }
            // array of version streams that will be provided to ProjectInfo constructor
            ArrayList versionStreams = new ArrayList();
            // AssemblyVersions used to find the largest one
            AssemblyVersions assemblyVersions = AssemblyVersions.Empty;
            // for a VC++ project search for resource file (which may contain version) if configured so
            if (projectTypeInfo == ProjectTypeInfo.VCppProject && m_numberingOptions.IncludeVCppResourceFiles)
            {
                string[] resourceFilenames = GetProjectVersionFile(project, "*.rc");
                // if VC++ project contains both AssemblyInfo file and resource 
                // file with version, compare them and get the larger value
                foreach (string resourceFilename in resourceFilenames)
                {
                    VersionStream resourceFileStream = new ResourceFileStream(resourceFilename);
                    AssemblyVersions resourceVersion = resourceFileStream.GetVersions();
                    versionStreams.Add(resourceFileStream);
                    assemblyVersions = AssemblyVersions.Max(assemblyVersions, resourceVersion);
                }
            }
            string[] assemblyInfoFilenames = GetProjectVersionFile(project, projectTypeInfo.AssemblyInfoFilename);
            Debug.Assert(assemblyInfoFilenames.Length <= 1);
            if (assemblyInfoFilenames.Length > 0)
            {
                VersionStream assemblyInfoStream = new AssemblyInfoStream(assemblyInfoFilenames[0]);
                AssemblyVersions assemblyInfoVersions = assemblyInfoStream.GetVersions();
                versionStreams.Add(assemblyInfoStream);
                if (assemblyVersions == AssemblyVersions.Empty)
                    assemblyVersions = assemblyInfoVersions;
                else
                    assemblyVersions = AssemblyVersions.Max(assemblyVersions, assemblyInfoVersions);
            }
            VersionStream[] vs = (VersionStream[])versionStreams.ToArray(typeof(VersionStream));
            bool isProjectModified = IsProjectModified(project, vs);
            return new ProjectInfo(project.Name, project.FullName, AppendBranchToPath(parentUiPath, project.Name), projectTypeInfo, isProjectModified, level, assemblyVersions, vs);
        }

        /// <summary>
        ///   Merges parent path with a new branch.
        /// </summary>
        /// <param name="parentUiPath"></param>
        /// <param name="newBranch"></param>
        /// <returns></returns>
        private string AppendBranchToPath(string parentUiPath, string newBranch)
        {
            Debug.Assert(parentUiPath != null);
            Debug.Assert(newBranch != null && newBranch.Length > 0);
            if (parentUiPath.Length == 0)
                return newBranch;
            return string.Format("{0}{1}{2}", parentUiPath, Path.DirectorySeparatorChar, newBranch);
        }

        /// <summary>
        ///   Creates <c>ProjectInfo</c> for setup project provided.
        /// </summary>
        /// <param name="project">
        ///   Project examined.
        /// </param>
        /// <param name="projectTypeInfo">
        /// </param>
        /// <returns>
        ///   <c>ProjectInfo</c> for the project provided.
        /// </returns>
        private ProjectInfo GetSetupInfo(Project project, string parentUiPath, ProjectTypeInfo projectTypeInfo, int level)
        {
            Debug.Assert(project != null);
            Debug.Assert(projectTypeInfo != null && (projectTypeInfo.ProjectType == ProjectType.SetupProject || projectTypeInfo.ProjectType == ProjectType.InstallShieldLEProject));
            string directoryName = Path.GetDirectoryName(project.FileName);
            string versionFileExtension = Path.GetExtension(projectTypeInfo.AssemblyInfoFilename);
            string setupFilename = Path.ChangeExtension(Path.GetFileName(project.UniqueName), versionFileExtension);
            string filename = Path.Combine(directoryName, setupFilename);
            Debug.Assert(File.Exists(filename));
            VersionStream versionStream = null;
            switch (projectTypeInfo.ProjectType)
            {
                case ProjectType.SetupProject:
                    versionStream = new SetupVersionStream(filename);
                    break;
                case ProjectType.InstallShieldLEProject:
                    versionStream = new InstallShieldLEVersionStream(filename);
                    break;
                default:
                    Debug.Assert(false);
                    break;
            }
            Debug.Assert(versionStream != null);
            AssemblyVersions setupVersion = versionStream.GetVersions();
            // setup projects are always at solution root
            return new ProjectInfo(project.Name, project.FullName, AppendBranchToPath(parentUiPath, project.Name), projectTypeInfo, false, level, setupVersion, new VersionStream[] { versionStream });
        }


        /// <summary>
        ///   Recurses the project to see if any file has been modified more
        ///   recently than version file and sets <c>Modified</c> flag.
        /// </summary>
        private bool IsProjectModified(Project project, VersionStream[] versionStreams)
        {
            DateTime lastWriteTime = DateTime.MinValue;
            string referenceVersionFile = null;
            foreach (VersionStream vs in versionStreams)
            {
                DateTime fileWriteTime = FileUtil.GetLastWriteTime(vs.Filename);
                if (fileWriteTime > lastWriteTime)
                {
                    lastWriteTime = fileWriteTime;
                    referenceVersionFile = vs.Filename;
                }
            }
            // check if the any file in the project has been modified later than reference file
            if (referenceVersionFile != null)
                return IsAnyFileInProjectNewer(project, lastWriteTime, referenceVersionFile);
            return false;
        }

        /// <summary>
        ///   Recurses a project and checks if there is a file that has been 
        ///   modified later than a reference time. Used to check if any file 
        ///   has been modified later than corresponding AssemblyInfo file.
        /// </summary>
        /// <param name="dateTimeToCompare">
        ///   Reference date&time.
        /// </param>
        /// <param name="referenceFile">
        ///   Reference file to which comparison is done.
        /// </param>
        /// <returns>
        ///   <c>true</c> if there is a file modified after the reference date
        ///   & time.
        /// </returns>
        private bool IsAnyFileInProjectNewer(Project project, DateTime dateTimeToCompare, string referenceFile)
        {
            Debug.Assert(project != null);
            Debug.Assert(referenceFile != null);
            if (project.ProjectItems != null)
            {
                foreach (ProjectItem projectItem in project.ProjectItems)
                {
                    if (IsProjectItemNewer(projectItem, dateTimeToCompare, referenceFile))
                    {
                        return true;
                    }
                }
            }
            return false;
        }

        /// <summary>
        ///   Checks if a project item has been modified after the reference 
        ///   date&time.
        /// </summary>
        /// <param name="projectItem">
        ///   <c>ProjectItem</c> for which check is done.
        /// </param>
        /// <param name="path">
        ///   Path to the project root.
        /// </param>
        /// <param name="dateTimeToCompare">
        ///   Reference date & time.
        /// </param>
        /// <param name="referenceFile">
        ///   Reference file to which comparison is done.
        /// </param>
        /// <returns>
        ///   <c>true</c> if the item has been modified after or at the 
        ///   same time as the reference date & time.
        /// </returns>
        private bool IsProjectItemNewer(ProjectItem projectItem, DateTime dateTimeToCompare, string referenceFile)
        {
            string pathFileName = ProjectItemInfo.GetItemFullPath(projectItem);
            if (pathFileName != null && !FileUtil.IsDirectory(pathFileName) && !FileUtil.PathsAreEqual(pathFileName, referenceFile))
            {
                int comp = dateTimeToCompare.CompareTo((object)FileUtil.GetLastWriteTime(pathFileName));
                if (comp <= 0)
                    return true;
            }
            if (projectItem != null && projectItem.ProjectItems != null)
            {
                foreach (ProjectItem projectSubItem in projectItem.ProjectItems)
                {
                    if (IsProjectItemNewer(projectSubItem, dateTimeToCompare, referenceFile))
                    {
                        return true;
                    }
                }
            }
            return false;
        }

        /// <summary>
        ///   Helper function that fetches a development environment property.
        /// </summary>
        /// <param name="category">
        ///   Category in the Tools - Options dialog.
        /// </param>
        /// <param name="page">
        ///   Page inside a category.
        /// </param>
        /// <param name="propertyName">
        ///   Name of the property to fetch.
        /// </param>
        /// <returns>
        ///   Value of the property.
        /// </returns>
        private object GetEnvProperty(string category, string page, string propertyName)
        {
            EnvDTE.Properties properties = m_devEnvApplicationObject.get_Properties(category, page);
            Property property = properties.Item(propertyName);
            Debug.Assert(property != null, "Property " + propertyName + " not found", "Category: " + category + " / Page: " + page);
            return property.Value;
        }

        /// <summary>
        ///   Helper function to set a development environment property.
        /// </summary>
        /// <param name="category">
        ///   Category in the Tools - Options dialog.
        /// </param>
        /// <param name="page">
        ///   Page inside a category.
        /// </param>
        /// <param name="propertyName">
        ///   Name of the property to set.
        /// </param>
        /// <param name="newValue">
        ///   A new value of the property.
        /// </param>
        private void SetEnvProperty(string category, string page, string propertyName, object newValue)
        {
            int retryCount = 20;
            int count = 0;
            while (count < retryCount)
            {
                try
                {
                    EnvDTE.Properties properties = m_devEnvApplicationObject.get_Properties(category, page);
                    Property property = properties.Item(propertyName);
                    Debug.Assert(property != null, "Property " + propertyName + " not found", "Category: " + category + " / Page: " + page);
                    property.Value = newValue;
                    Trace.WriteLine(string.Format("'SetEnvProperty({0}, {1}, {2}, {3})' executed in {4} attempts", category, page, propertyName, newValue, count + 1));
                    return;
                }
                catch (System.Runtime.InteropServices.COMException)
                {
                }
                catch (System.Runtime.InteropServices.InvalidComObjectException)
                {
                }
                System.Threading.Thread.Sleep(100);
                count++;
            }
            Trace.WriteLine(string.Format("Failed to execute 'SetEnvProperty({0}, {1}, {2}, {3})' in {4} attempts.", category, page, propertyName, newValue, retryCount));
        }

        /// <summary>
        ///   Gets or sets OnRunOrPreview property (Tools - Options - 
        ///   Environment category - Projects and Solutions page - Build 
        ///   and Run radiobutton group).
        /// </summary>
        private OnRunOrPreviewSetting OnRunOrPreview
        {
            get
            {
                return (OnRunOrPreviewSetting)GetEnvProperty("Environment", "ProjectsAndSolution", "OnRunOrPreview");
            }
            set
            {
                SetEnvProperty("Environment", "ProjectsAndSolution", "OnRunOrPreview", value);
                Debug.Assert(OnRunOrPreview == value);
            }
        }

        /// <summary>
        ///   Gets or sets AutoloadExternalChanges (Tools - Options -
        ///   Environment category - Documents page - Auto-load changes if 
        ///   saved checkbox).
        /// </summary>
        private bool AutoloadExternalChanges
        {
            get
            {
                return (bool)GetEnvProperty("Environment", "Documents", "AutoloadExternalChanges");
            }
            set
            {
                SetEnvProperty("Environment", "Documents", "AutoloadExternalChanges", value);
                Debug.Assert(AutoloadExternalChanges == value);
            }
        }

        /// <summary>
        ///   Checks for unsaved documents and saves documents according to 
        ///   environment settings, then turns saving option off in order to 
        ///   prevent message pop-ups during build process.
        /// </summary>
        private void CheckForUnsavedFiles()
        {
            Debug.Assert(m_initialOnRunOrPreview != OnRunOrPreviewSetting.Undefined);
            switch (m_initialOnRunOrPreview)
            {
                case OnRunOrPreviewSetting.SaveChanges:
                    SaveAllUnsavedDocuments();
                    break;
                case OnRunOrPreviewSetting.PromptToSaveChanges:
                    PromptForUnsavedDocuments();
                    // turn saving temporarily off to prevent pop-up dialogs
                    OnRunOrPreview = OnRunOrPreviewSetting.DontSaveChanges;
                    break;
                default:
                    break;
            }
        }

        /// <summary>
        ///   Saves all unsaved documents.
        /// </summary>
        private void SaveAllUnsavedDocuments()
        {
            Documents documents = m_devEnvApplicationObject.Documents;
            foreach (Document document in documents)
            {
                if (!document.Saved)
                    document.Save(document.FullName);
            }
        }

        /// <summary>
        ///   Checks for unsaved documents and if any exists, a confirmation 
        ///   dialog is shown.
        /// </summary>
        private void PromptForUnsavedDocuments()
        {
            ArrayList unsavedDocuments = new ArrayList();
            Documents documents = m_devEnvApplicationObject.Documents;
            foreach (Document document in documents)
            {
                if (!document.Saved)
                    unsavedDocuments.Add(document);
            }
            if (unsavedDocuments.Count > 0)
            {
                if (PromptUnsavedDocumentsDialog.Show(new WindowAdapter(m_devEnvApplicationObject.MainWindow.HWnd), unsavedDocuments) == System.Windows.Forms.DialogResult.Cancel)
                {
                    throw new UserCancelledException();
                }
            }
        }

        /// <summary>
        ///   Starts searching the project directories to find file with 
        ///   version information. Filename pattern argument passed contains 
        ///   pattern for the filename which is searched for. It can be a plain
        ///   filename or wildcard with a certain extension.
        /// </summary>
        /// <param name="project">
        ///   Project to recurse.
        /// </param>
        /// <param name="filenamePattern">
        ///   Name of the file to search for. May contain wildcard characters.
        /// </param>
        /// <returns>
        ///   Full path to the file with version.
        /// </returns>
        private string[] GetProjectVersionFile(Project project, string filenamePattern)
        {
            ArrayList versionFiles = new ArrayList();
            if (project.ProjectItems != null)
            {
                foreach (ProjectItem projectItem in project.ProjectItems)
                {
                    versionFiles.AddRange(RecurseProjectForVersionInfoFile(projectItem, project.Name, filenamePattern));
                }
            }
            return (string[])versionFiles.ToArray(typeof(string));
        }

        /// <summary>
        ///   Recurses project item searching for a version file with a given 
        ///   name pattern.
        /// </summary>
        /// <param name="projectItem">
        ///   <c>ProjectItem</c> to recurse.
        /// </param>
        /// <param name="path">
        ///   Path to start search from.
        /// </param>
        /// <param name="filenamePattern">
        ///   Name of the file to search for. May contain wildcard characters.
        /// </param>
        /// <returns>
        ///   Array of full filenames (including path) if version file has been 
        ///   found, otherwise, empty array.
        /// </returns>
        private string[] RecurseProjectForVersionInfoFile(ProjectItem projectItem, string path, string filenamePattern)
        {
            ArrayList versionFiles = new ArrayList();
            if (projectItem.Object != null)
            {
                string pathFileName = AppendBranchToPath(path, projectItem.Name);
                if (FileUtil.FilenameMatchesPattern(projectItem.Name, filenamePattern))
                {
                    string filename = ProjectItemInfo.GetItemFullPath(projectItem);
                    // it is not RC file (i.e. it is AssemblyInfo file), 
                    // then it is the only version file so we can return
                    if (!filenamePattern.Equals("*.rc"))
                        return new string[] { filename };
                    // there could be more .rc files, so check if this one contains version info
                    ResourceFileStream rfs = new ResourceFileStream(filename);
                    if (!rfs.GetVersions().Equals(AssemblyVersions.Empty))
                        versionFiles.Add(filename);
                }
                if (projectItem.ProjectItems != null)
                {
                    foreach (ProjectItem projectSubItem in projectItem.ProjectItems)
                    {
                        versionFiles.AddRange(RecurseProjectForVersionInfoFile(projectSubItem, pathFileName, filenamePattern));
                    }
                }
            }
            return (string[])versionFiles.ToArray(typeof(string));
        }

        #endregion // Private methods

        #region Private fields

        /// <summary>
        ///   Enclosing development environment object.
        /// </summary>
        private DTE m_devEnvApplicationObject;
        /// <summary>
        ///   Initial OnRunOrPreview setting.
        /// </summary>
        private OnRunOrPreviewSetting m_initialOnRunOrPreview = OnRunOrPreviewSetting.Undefined;

        private bool m_initialAutoloadExternalChanges;

        private bool m_disposed = false;

        #endregion // Private fields

    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Croatia Croatia
Graduated at the Faculty of Electrical Engineering and Computing, University of Zagreb (Croatia) and received M.Sc. degree in electronics. For several years he was research and lecturing assistant in the fields of solid state electronics and electronic circuits, published several scientific and professional papers, as well as a book "Physics of Semiconductor Devices - Solved Problems with Theory" (in Croatian).
During that work he gained interest in C++ programming language and have co-written "C++ Demystified" (in Croatian), 1st edition published in 1997, 2nd in 2001, 3rd in 2010, 4th in 2014.
After book publication, completely switched to software development, programming mostly in C++ and in C#.
In 2016 coauthored the book "Python for Curious" (in Croatian).

Comments and Discussions