Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using CodingWithCalvin.Otel4Vsix;
using EnvDTE;
using EnvDTE80;
using Microsoft.VisualStudio.Shell.Interop;

namespace CodingWithCalvin.ProjectRenamifier
{
Expand Down Expand Up @@ -172,9 +173,11 @@ private void RenameProject(Project project, DTE2 dte)
var stepIndex = 0;
var projectRemovedFromSolution = false;
var projectReaddedToSolution = false;
List<string> referencingProjects = null;
List<ProjectReferenceService.ReferencingProjectInfo> referencingProjects = null;
Project parentSolutionFolder = null;

var vsSolution = ServiceProvider.GetService(typeof(SVsSolution)) as IVsSolution;

try
{
// Step 1: Collect projects that reference this project before removal
Expand Down Expand Up @@ -230,7 +233,7 @@ private void RenameProject(Project project, DTE2 dte)
// Step 9: Update references in projects that referenced this project
ExecuteStep(progressDialog, stepIndex++, () =>
{
ProjectReferenceService.UpdateProjectReferences(referencingProjects, oldProjectFilePath, projectFilePath);
ProjectReferenceService.UpdateProjectReferences(vsSolution, referencingProjects, oldProjectFilePath, projectFilePath);
});

// Step 10: Re-add project to solution, preserving solution folder location
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
using System.IO;
using System.Xml;
using EnvDTE;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.Shell.Interop;

namespace CodingWithCalvin.ProjectRenamifier.Services
{
Expand All @@ -10,17 +12,26 @@ namespace CodingWithCalvin.ProjectRenamifier.Services
/// </summary>
internal static class ProjectReferenceService
{
/// <summary>
/// Metadata for a project that references the target project being renamed.
/// </summary>
public sealed class ReferencingProjectInfo
{
public string FullPath { get; set; }
public string UniqueName { get; set; }
}

/// <summary>
/// Finds all projects in the solution that reference the specified project.
/// </summary>
/// <param name="solution">The solution to search.</param>
/// <param name="targetProjectPath">The full path to the project being renamed.</param>
/// <returns>A list of project paths that reference the target project.</returns>
public static List<string> FindProjectsReferencingTarget(Solution solution, string targetProjectPath)
/// <returns>A list of referencing project descriptors (full path + unique name).</returns>
public static List<ReferencingProjectInfo> FindProjectsReferencingTarget(Solution solution, string targetProjectPath)
{
ThreadHelper.ThrowIfNotOnUIThread();

var referencingProjects = new List<string>();
var referencingProjects = new List<ReferencingProjectInfo>();
var targetFileName = Path.GetFileName(targetProjectPath);

foreach (Project project in solution.Projects)
Expand All @@ -34,7 +45,7 @@ public static List<string> FindProjectsReferencingTarget(Solution solution, stri
/// <summary>
/// Recursively searches a project (and solution folders) for references to the target.
/// </summary>
private static void FindReferencesInProject(Project project, string targetProjectPath, string targetFileName, List<string> referencingProjects)
private static void FindReferencesInProject(Project project, string targetProjectPath, string targetFileName, List<ReferencingProjectInfo> referencingProjects)
{
ThreadHelper.ThrowIfNotOnUIThread();

Expand Down Expand Up @@ -67,7 +78,11 @@ private static void FindReferencesInProject(Project project, string targetProjec
{
if (ProjectReferencesTarget(project.FullName, targetFileName))
{
referencingProjects.Add(project.FullName);
referencingProjects.Add(new ReferencingProjectInfo
{
FullPath = project.FullName,
UniqueName = project.UniqueName,
});
}
}
}
Expand Down Expand Up @@ -118,18 +133,77 @@ private static bool ProjectReferencesTarget(string projectFilePath, string targe

/// <summary>
/// Updates project references in all projects that referenced the old project path.
/// Each referencing project is temporarily unloaded via <see cref="IVsSolution4"/> so Visual Studio
/// releases its file handle before we rewrite the .csproj on disk, then reloaded afterwards.
/// </summary>
/// <param name="referencingProjectPaths">Projects that need their references updated.</param>
/// <param name="vsSolution">The Visual Studio solution service used to unload and reload projects.</param>
/// <param name="referencingProjects">Projects that need their references updated.</param>
/// <param name="oldProjectPath">The old path to the renamed project.</param>
/// <param name="newProjectPath">The new path to the renamed project.</param>
public static void UpdateProjectReferences(List<string> referencingProjectPaths, string oldProjectPath, string newProjectPath)
public static void UpdateProjectReferences(IVsSolution vsSolution, List<ReferencingProjectInfo> referencingProjects, string oldProjectPath, string newProjectPath)
{
ThreadHelper.ThrowIfNotOnUIThread();

var oldFileName = Path.GetFileName(oldProjectPath);
var solution4 = vsSolution as IVsSolution4;

foreach (var info in referencingProjects)
{
UpdateSingleProjectReference(vsSolution, solution4, info, oldFileName, oldProjectPath, newProjectPath);
}
}

private static void UpdateSingleProjectReference(
IVsSolution vsSolution,
IVsSolution4 solution4,
ReferencingProjectInfo info,
string oldFileName,
string oldProjectPath,
string newProjectPath)
{
ThreadHelper.ThrowIfNotOnUIThread();

var projectGuid = System.Guid.Empty;
var unloaded = false;

if (solution4 != null && TryGetProjectGuid(vsSolution, info.UniqueName, out projectGuid))
{
var hr = solution4.UnloadProject(ref projectGuid, (uint)_VSProjectUnloadStatus.UNLOADSTATUS_UnloadedByUser);
unloaded = ErrorHandler.Succeeded(hr);
}

foreach (var projectPath in referencingProjectPaths)
try
{
UpdateReferencesInProject(projectPath, oldFileName, oldProjectPath, newProjectPath);
UpdateReferencesInProject(info.FullPath, oldFileName, oldProjectPath, newProjectPath);
}
finally
{
if (unloaded)
{
solution4.ReloadProject(ref projectGuid);
}
}
}

private static bool TryGetProjectGuid(IVsSolution vsSolution, string uniqueName, out System.Guid projectGuid)
{
ThreadHelper.ThrowIfNotOnUIThread();

projectGuid = System.Guid.Empty;

if (string.IsNullOrEmpty(uniqueName))
{
return false;
}

var hr = vsSolution.GetProjectOfUniqueName(uniqueName, out var hierarchy);
if (!ErrorHandler.Succeeded(hr) || hierarchy == null)
{
return false;
}

hr = vsSolution.GetGuidOfProject(hierarchy, out projectGuid);
return ErrorHandler.Succeeded(hr) && projectGuid != System.Guid.Empty;
}

/// <summary>
Expand Down
Loading