Skip to content

Commit 8482a88

Browse files
committed
fix: handle unloaded projects using IVsHierarchy
Unloaded projects don't expose a Project object via the DTE automation model. Use IVsMonitorSelection.GetCurrentSelection() to get the IVsHierarchy and retrieve the project path via GetCanonicalName().
1 parent ad0489b commit 8482a88

File tree

1 file changed

+54
-0
lines changed

1 file changed

+54
-0
lines changed

src/CodingWithCalvin.OpenInNotepadPlusPlus/Helpers/ProjectHelpers.cs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
using System;
22
using System.IO;
3+
using System.Runtime.InteropServices;
34
using EnvDTE;
45
using EnvDTE80;
6+
using Microsoft.VisualStudio;
57
using Microsoft.VisualStudio.Shell;
8+
using Microsoft.VisualStudio.Shell.Interop;
69

710
namespace CodingWithCalvin.OpenInNotepadPlusPlus.Helpers
811
{
@@ -25,11 +28,62 @@ UIHierarchyItem selectedItem in (Array)
2528
return GetProjectPath(project, dte.Solution);
2629
case Solution solution:
2730
return solution.FullName;
31+
default:
32+
// Handle unloaded projects - they don't expose a Project object
33+
// but we can get the path via IVsMonitorSelection and IVsHierarchy
34+
var path = GetPathFromHierarchy();
35+
if (!string.IsNullOrEmpty(path))
36+
{
37+
return path;
38+
}
39+
break;
2840
}
2941
}
3042
return null;
3143
}
3244

45+
private static string GetPathFromHierarchy()
46+
{
47+
ThreadHelper.ThrowIfNotOnUIThread();
48+
49+
// Use IVsMonitorSelection to get the current selection's IVsHierarchy
50+
// This works for unloaded projects where selectedItem.Object is not a Project
51+
var monitorSelection = Package.GetGlobalService(typeof(SVsShellMonitorSelection)) as IVsMonitorSelection;
52+
if (monitorSelection == null)
53+
{
54+
return null;
55+
}
56+
57+
var hr = monitorSelection.GetCurrentSelection(out var hierarchyPtr, out var itemId, out _, out _);
58+
if (hr != VSConstants.S_OK || hierarchyPtr == IntPtr.Zero)
59+
{
60+
return null;
61+
}
62+
63+
try
64+
{
65+
var hierarchy = Marshal.GetObjectForIUnknown(hierarchyPtr) as IVsHierarchy;
66+
if (hierarchy == null)
67+
{
68+
return null;
69+
}
70+
71+
// GetCanonicalName returns the full path for project files
72+
if (hierarchy.GetCanonicalName(itemId, out var canonicalName) == VSConstants.S_OK
73+
&& !string.IsNullOrEmpty(canonicalName)
74+
&& File.Exists(canonicalName))
75+
{
76+
return canonicalName;
77+
}
78+
}
79+
finally
80+
{
81+
Marshal.Release(hierarchyPtr);
82+
}
83+
84+
return null;
85+
}
86+
3387
private static string GetProjectPath(Project project, Solution solution)
3488
{
3589
ThreadHelper.ThrowIfNotOnUIThread();

0 commit comments

Comments
 (0)