Skip to content

[Vsintegration] Show project references of SDK style projects in the Dependencies node - #2048

Merged
RobertvanderHulst merged 1 commit into
X-Sharp:devfrom
hpetriffer:fix/sdk-project-reference-nodes
Aug 12, 2026
Merged

[Vsintegration] Show project references of SDK style projects in the Dependencies node#2048
RobertvanderHulst merged 1 commit into
X-Sharp:devfrom
hpetriffer:fix/sdk-project-reference-nodes

Conversation

@hpetriffer

Copy link
Copy Markdown
Contributor

The Projects node under Dependencies stayed empty when a solution with SDK style X# projects was opened. It only showed up after adding a project reference through the dialog, and was gone again after the next reload.

Root cause

No ProjectReferenceNode was created at all:

  1. XSharpSdkProjectNode.Clean() strips the Project (guid) and Name metadata from every ProjectReference item before saving, so a saved .xsproj correctly only contains <ProjectReference Include="..\Foo\Foo.xsproj" />.
  2. XSharpReferenceContainerNode.CreateProjectReferenceNode() resolved guid and name for the SDK case — and then dropped both on the floor. Nothing was written to the project element.
  3. The ProjectReferenceNode(ProjectNode, ProjectElement) constructor deliberately skips the "invent a random guid" fallback for SDK projects, and then called new Guid(guidString) on an empty string inside a try/finally with no catchFormatException.
  4. ReferenceContainerNode.CreateReferenceNode() swallows that exception (a Debug.WriteLine only when a debugger is attached) and returns null.
  5. The Projects folder is created lazily, on the first XSharpProjectReferenceNode that is added — so the whole folder was missing, not just the reference.

Adding a reference interactively goes through the other constructor, which parses the guid out of the bstrProjRef string, which is why that path worked.

FixReferences(), the mechanism meant to complete these references once the solution has finished loading, had nothing left to iterate over. It was broken on its own as well: when the referenced project was found (refnode != null) it never assigned refnodeGuid, so it always reported failure.

Changes

  • XSharpReferenceContainerNode — the SDK branch now stores the resolved guid and name on the in-memory project element, and resolves foreign / not-yet-loaded projects via ProjectInfo / GetProjectGuid, the same way FixReferences() does. Clean() still keeps them out of the saved project file. Node creation is wrapped in try/catch + Logger.Exception, consistent with the sibling Create*Node methods, so a failure can never silently drop a reference again.
  • ProjectReferenceNode — tolerate a missing or malformed guid: the node is created with Guid.Empty and without a build dependency instead of throwing, so it always appears in the hierarchy. Added UpdateReferencedProjectGuid() to attach the guid and the build dependency later and redraw the icon.
  • XSharpProjectNode.FixReferences() — use refnode.ProjectIDGuid when the referenced project is one of ours; resolve per node instead of skipping the remaining nodes after the first failure; push the resolved guid into the node; log what stayed unresolved.
  • ProjectNode.BuildDependencies — do not register a ProjectInfo with an empty guid. It is cached by url and would block the later resolution of the real guid.
  • XSharpShellEvents — also complete incomplete references on OnAfterBackgroundSolutionLoadComplete. With deferred solution load not all projects exist yet when OnAfterOpenSolution fires.

Notes for reviewers

  • Behaviour for legacy (non-SDK) projects is unchanged: an empty guid still gets the random-guid fallback. The only difference there is that a malformed guid no longer throws away the node.
  • The in-memory Project/Name metadata does not reach the project file: ProjectNode.Save() calls BeforeSave()XSharpSdkProjectNode.Clean(), which removes it, and Save() restores it afterwards via RestoreProperties(). Reload() clears the dirty flag in its finally, so writing the metadata during ProcessReferences() does not leave the project dirty.
  • ProjectPackage2022 builds clean. The VS2019 variant (ProjectPackage.csproj) does not build in my working tree, but that is pre-existing and unrelated — NuGet restore for net472 plus missing Community.VisualStudio.Toolkit types in Support.csproj/Logger.cs, files this PR does not touch.
  • Not verified in the running IDE yet: worth a check that Dependencies → Projects is populated on load and that project build order is still correct.

🤖 Generated with Claude Code

…Dependencies node

The "Projects" node under "Dependencies" stayed empty when a solution with SDK
style X# projects was opened. It only appeared after adding a project reference
interactively, and was gone again after the next reload.

No ProjectReferenceNode was created at all:

- XSharpSdkProjectNode.Clean() strips the Project (guid) and Name metadata from
  every ProjectReference item before saving, so a saved .xsproj only contains
  <ProjectReference Include="..\Foo\Foo.xsproj" />.
- XSharpReferenceContainerNode.CreateProjectReferenceNode() resolved guid and
  name for the SDK case but never wrote them to the project element.
- The ProjectReferenceNode constructor deliberately skips the "invent a guid"
  fallback for SDK projects and then called new Guid(guidString) on an empty
  string inside a try/finally without a catch, so it threw FormatException.
- ReferenceContainerNode.CreateReferenceNode() swallows that exception without
  logging and returns null, so the node silently disappeared. Because the
  "Projects" folder is only created when the first XSharpProjectReferenceNode is
  added, the whole folder was missing.
- Adding a reference through the dialog uses the other constructor, which parses
  the guid from the bstrProjRef string, which is why that path worked.

FixReferences(), which is supposed to complete these references once the
solution has finished loading, had nothing left to iterate over. It was broken
on its own as well: when the referenced project was found it never assigned
refnodeGuid, so it always reported failure.

- XSharpReferenceContainerNode: store the resolved guid and name on the in
  memory project element and resolve foreign projects via ProjectInfo /
  GetProjectGuid, the same way FixReferences() does. Clean() still keeps them
  out of the saved project file. Node creation is wrapped in try/catch with
  Logger.Exception so a failure can no longer drop a reference silently.
- ProjectReferenceNode: tolerate a missing or malformed guid. The node is
  created with Guid.Empty and without a build dependency instead of throwing.
  Added UpdateReferencedProjectGuid() to attach the guid and the build
  dependency later and redraw the icon.
- XSharpProjectNode.FixReferences(): use refnode.ProjectIDGuid when the
  referenced project is one of ours, resolve per node instead of skipping the
  remaining nodes after the first failure, push the guid into the node and log
  what stayed unresolved.
- ProjectNode.BuildDependencies: do not register a ProjectInfo with an empty
  guid. It would be cached by url and would block the later resolution.
- XSharpShellEvents: also complete incomplete references on
  OnAfterBackgroundSolutionLoadComplete. With deferred solution load not all
  projects exist yet when OnAfterOpenSolution fires.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@RobertvanderHulst
RobertvanderHulst merged commit 9e0db09 into X-Sharp:dev Aug 12, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants