Skip to content

Migrate the VS extension to an SDK-style VSIX (dotnet build) and retire the VS2017/2019 add-in#3808

Merged
christophwille merged 6 commits into
masterfrom
christophwille/vsix
Jun 24, 2026
Merged

Migrate the VS extension to an SDK-style VSIX (dotnet build) and retire the VS2017/2019 add-in#3808
christophwille merged 6 commits into
masterfrom
christophwille/vsix

Conversation

@christophwille

@christophwille christophwille commented Jun 23, 2026

Copy link
Copy Markdown
Member

Summary

Migrates the Visual Studio extension to a true SDK-style VSIX so it builds with plain dotnet build instead of full msbuild, retires the obsolete VS2017/2019 add-in, and moves the VS-extensions solution to .slnx with CI
building it via dotnet build.

The end state: one SDK-style extension project (ILSpy.AddIn.VS2022), a shared code project, and a single ILSpy.VSExtensions.slnx that builds end-to-end with dotnet build — no Visual Studio / full-msbuild dependency in the build.

Statistics for Retirement

2017-2019 extension

image

2022-2026 extension

image

Of interest are only the in-IDE downloads, and that translates to ~ 150/month for 2017-2019 with almost a 20 times higher number for 2022-206.

Changes and why

1. ILSpy.AddIn.VS2022 -> true SDK-style VSIX

  • Project header now <Project Sdk="Microsoft.NET.Sdk">, dropping the hybrid explicit Sdk.props/Sdk.targets imports and the legacy Microsoft.VsSDK.targets import. Added the VSIX properties VSSDKBuildToolsAutoSetup, VsixDeployOnDebug, GeneratePkgDefFile and the CreateVsixContainer project capability.
    Why: the legacy Microsoft.VsSDK.targets import is what forced full msbuild. VSSDKBuildToolsAutoSetup + CreateVsixContainer is the Microsoft-recommended SDK-style replacement and is what lets dotnet build
    emit the .vsix.
  • Microsoft.VSSDK.BuildTools bumped to 18.5.40034 and simplified to PrivateAssets=all; ExcludeAssets="runtime" added to Microsoft.VisualStudio.SDK.
    Why: 18.5 is the floor for the auto-setup; the SDK metapackage's runtime assemblies are provided by VS, so they should not be copied locally.
  • GenerateResourceUsePreserializedResources=true + a System.Resources.Extensions reference.
    Why: VSPackage.resx resource 400 is the Help/About package icon ([InstalledProductRegistration(..., IconResourceID = 400)]), stored as a binary System.Drawing.Icon. The dotnet CLI GenerateResource task cannot
    BinaryFormatter-serialize it the way full msbuild does (it fails with MSB3823/MSB3822), so the resource must be emitted via System.Resources.Extensions instead.
  • MergeWithCTO turned off for the package resources (per-project override, leaving the shared .resx untouched).
    Why: the VSSDK CTO merger reads VSPackage.resources with the classic reader and rejects the preserialized format. Modern SDK-style VSIX projects do not merge: VSCTCompile already embeds Menus.ctmenu as its own resource,
    which is how ProvideMenuResource locates the menus.
  • Added a System.Design reference.
    Why: the shared command classes use MenuCommandService, which lives in System.Design. Full msbuild supplied it implicitly; the SDK does not add it for net472.

2. Retire the VS2017/2019 add-in (ILSpy.AddIn)

  • Deleted the ILSpy.AddIn project and everything that fed it: its solution entries, its VSIX-manifest template generation in BuildTools/update-assemblyinfo.ps1, the committed BuildTools/ILSpy.AddIn.vsix.filelist snapshot, the now-dead ..\ILSpy.AddIn\*.resx glob in ILSpy/ILSpy.csproj, and the CI build-step rename plus the VS2017/2019 VSIX artifact-upload step.
    Why: it pinned Microsoft.VisualStudio.SDK 15.0.1 / Roslyn 2.4.0, was the last project that could only be built with full msbuild, and is obsolete now that the VS2022 add-in is the supported integration. Removing it is what makes
    the whole solution dotnet build-able.
  • Repointed the shared VSPackage.resx package-icon path from the deleted ..\ILSpy.AddIn\Resources\Package.ico to the byte-identical copy under ..\ILSpy.AddIn.VS2022\Resources\Package.ico (verified identical by git blob hash).

3. Solution -> .slnx, CI -> dotnet build

  • Replaced ILSpy.VSExtensions.sln with ILSpy.VSExtensions.slnx (generated by dotnet sln migrate), carrying a <Deploy Solution="Debug|Any CPU" /> entry on the VS2022 project for F5 deploy in VS 18.5+.
  • CI (build-ilspy.yml) now runs dotnet build ILSpy.VSExtensions.slnx -c Release instead of two msbuild ILSpy.VSExtensions.sln invocations.
  • Updated the .sln -> .slnx references in README.md and CLAUDE.md.

Verification

  • dotnet build ILSpy.VSExtensions.slnx -c Release succeeds (0 warnings, 0 errors) and produces ILSpy.AddIn.VS2022.vsix (~43 MB) containing the ILSpy distribution for both x64 and arm64 (incl. ILSpy.exe), with the package
    manifest version intact.
  • Run with a fresh restore (the exact CI command), the build leaves packages.lock.json unchanged (no churn).
  • The whole solution now builds with dotnet build only; full msbuild is no longer required.

References

  • Microsoft DevBlog: "SDK-style support for extension projects" (the VSIX properties, the CreateVsixContainer capability, and the <Deploy> slnx entry).
  • The reference migration PR madskristensen/SelectedWhitespace#6 and the example repos SelectedWhitespace and CommandTableInfo (the SDK-style csproj shape; CommandTableInfo confirmed the "no MergeWithCTO, VSCTCompile emits Menus.ctmenu directly" pattern).
  • The standard GenerateResourceUsePreserializedResources + System.Resources.Extensions remedy for the MSB3823/MSB3822 non-string resource error under the dotnet CLI.

Downsides / things to watch

  • Runtime smoke test still recommended (VS 18.5+). Two behaviors are build-clean but only fully verifiable by loading the extension:
    • The Help/About icon is read at runtime via System.Resources.Extensions. That assembly is intentionally not in the VSIX (the VSSDK excludes assemblies VS itself ships), so the read relies on VS providing it — true for VS 2022, but a dependency on VS internals.
    • Menus now load from the standalone Menus.ctmenu resource (after dropping MergeWithCTO).
  • packages.lock.json churn from the toolchain. Regenerating the lock for the BuildTools bump (on the net11-preview SDK) also dropped Microsoft.NETFramework.ReferenceAssemblies (resolved from the installed targeting pack instead). The project is not in locked-restore mode, so this is tolerated; CI restores fresh.
  • The shared project now has a single consumer. ILSpy.AddIn.Shared is used only by VS2022, and its VSPackage.resx references an icon in that sibling project. Inlining the shared project into ILSpy.AddIn.VS2022 is a reasonable future cleanup, left out here to keep the change scoped.
  • SDK-style F5 deploy needs VS 18.5+; building the VSIX only needs the .NET SDK + the VSSDK build tools (no full VS).

Following Microsoft's "SDK-style support for extension projects"
guidance, this drops the hybrid Sdk.props/Sdk.targets plus the legacy
Microsoft.VsSDK.targets import in favour of VSSDKBuildToolsAutoSetup and
a CreateVsixContainer capability, so the VSIX now builds with plain
dotnet build instead of full msbuild. Microsoft.VSSDK.BuildTools is
bumped to 18.5.x, which is the floor for that auto-setup.

Three problems the reference migrations do not hit, all scoped to this
project so the still-hybrid legacy VS2017/2019 add-in is unaffected:

- VSPackage.resx resource 400 (the Help/About package icon) is a binary
  System.Drawing.Icon. The dotnet CLI GenerateResource task cannot
  BinaryFormatter-serialize it the way full msbuild does, so it needs
  GenerateResourceUsePreserializedResources and System.Resources.Extensions.
- The VSSDK CTO merger reads VSPackage.resources with the classic reader
  and rejects that preserialized format, so MergeWithCTO is turned off
  here; VSCTCompile already embeds Menus.ctmenu as its own resource.
- System.Design (MenuCommandService) was supplied implicitly by full
  msbuild and must be referenced explicitly under the SDK.

The solution is migrated to ILSpy.VSExtensions.slnx (the .sln is kept so
the existing msbuild-based CI step keeps resolving) with a Deploy entry
on the VS2022 project for F5 deploy in VS 18.5+.

Assisted-by: Claude:claude-opus-4-8:Claude Code
The VS2017/2019 extension was the last remaining hybrid project that
could only be built with full msbuild. It is obsolete now that the VS2022
add-in is the supported integration, so this removes the ILSpy.AddIn
project and everything that fed it: the solution entries, its VSIX
manifest template generation in update-assemblyinfo.ps1, the committed
vsix filelist, the dead SortResX glob in ILSpy.csproj, and the CI build
step rename plus the VS2017/2019 artifact upload.

The shared project's VSPackage.resx referenced the package icon by a
path into the legacy project; it now points at the byte-identical copy
under ILSpy.AddIn.VS2022. With the legacy project gone, the entire
ILSpy.VSExtensions solution builds end to end with dotnet build.

Assisted-by: Claude:claude-opus-4-8:Claude Code
With the legacy add-in retired, the VS2022 extension is the only project
left in the VS extensions solution and it builds as a true SDK-style
VSIX, so full msbuild is no longer required. Drop ILSpy.VSExtensions.sln
in favour of the slnx and have CI build it with dotnet build, matching
how the rest of the repo builds. Update the README and CLAUDE.md
references to the renamed solution.

Assisted-by: Claude:claude-opus-4-8:Claude Code

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Migrates the Visual Studio integration to a single SDK-style VSIX project (ILSpy.AddIn.VS2022) that can be built with dotnet build, removes the legacy VS2017/2019 add-in project, and updates the VS extensions solution/CI to use .slnx + dotnet build instead of full msbuild.

Changes:

  • Convert ILSpy.AddIn.VS2022 to a true SDK-style VSIX build (VSSDK auto-setup, preserialized resources, disable CTO merge) and inline previously shared add-in code into the project.
  • Remove the obsolete ILSpy.AddIn (VS2017/2019) project and related build/CI artifacts.
  • Replace ILSpy.VSExtensions.sln with ILSpy.VSExtensions.slnx and update docs/CI accordingly.

Reviewed changes

Copilot reviewed 26 out of 50 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
README.md Update references from ILSpy.VSExtensions.sln to .slnx.
publishlocaldev.ps1 Adjust local publish flow to build solution first and support arm64 alongside x64.
ILSpy/Properties/Resources.resx Add new localized string for About output (“started from”).
ILSpy/Properties/Resources.Designer.cs Regenerate strongly-typed resource accessor for new string.
ILSpy/ILSpy.csproj Remove legacy add-in .resx sort input reference.
ILSpy/Commands/AboutCommand.cs Add startup path line to About output.
ILSpy.VSExtensions.slnx New .slnx solution for VS extension build via dotnet build.
ILSpy.VSExtensions.sln Remove legacy .sln solution.
ILSpy.AddIn/source.extension.vsixmanifest.template Removed as part of retiring VS2017/2019 add-in.
ILSpy.AddIn/README.md Removed as part of retiring VS2017/2019 add-in.
ILSpy.AddIn/Properties/launchSettings.json Removed as part of retiring VS2017/2019 add-in.
ILSpy.AddIn/Properties/AssemblyInfo.cs Removed as part of retiring VS2017/2019 add-in.
ILSpy.AddIn/packages.lock.json Removed as part of retiring VS2017/2019 add-in.
ILSpy.AddIn/NuGet.config Removed as part of retiring VS2017/2019 add-in.
ILSpy.AddIn/ILSpyAddIn.vsct Removed as part of retiring VS2017/2019 add-in.
ILSpy.AddIn/ILSpyAddIn.en-US.vsct Removed as part of retiring VS2017/2019 add-in.
ILSpy.AddIn/ILSpy.AddIn.csproj Removed as part of retiring VS2017/2019 add-in.
ILSpy.AddIn/Decompiler/Dummy.cs Removed as part of retiring VS2017/2019 add-in.
ILSpy.AddIn.VS2022/VSPackage.resx Update package icon resource path to VS2022 project resources.
ILSpy.AddIn.VS2022/VSPackage.en-US.resx Add localized package resource file.
ILSpy.AddIn.VS2022/Resources.resx Add package-local resources for SDK-style build layout.
ILSpy.AddIn.VS2022/Resources.Designer.cs Add generated strongly-typed Resources class for VS2022 add-in.
ILSpy.AddIn.VS2022/packages.lock.json Update lockfile for new build tools/resources dependencies.
ILSpy.AddIn.VS2022/ILSpy.AddIn.VS2022.csproj Convert to SDK-style VSIX build, update VSSDK settings and references, inline shared code.
ILSpy.AddIn.VS2022/Utils.cs Add utility helpers for VS integration (selection/view/workspace/project output path).
ILSpy.AddIn.VS2022/SyntaxNodeExtensions.cs Add Roslyn syntax helpers used by code-item navigation.
ILSpy.AddIn.VS2022/ILSpyInstance.cs Add VSIX-side ILSpy launcher logic (x64/arm64).
ILSpy.AddIn.VS2022/ILSpyAddInPackage.cs Add AsyncPackage implementation and command registration.
ILSpy.AddIn.VS2022/PkgCmdID.cs Add command ID constants for VSCT integration.
ILSpy.AddIn.VS2022/Guids.cs Add package/command set GUID declarations.
ILSpy.AddIn.VS2022/GlobalSuppressions.cs Add project-level analyzer suppression file.
ILSpy.AddIn.VS2022/AssemblyFileFinder.cs Add helper for resolving runtime vs reference assemblies and TFMs.
ILSpy.AddIn.VS2022/Commands/AssemblyReferenceForILSpy.cs Add detection + parameter building for assembly references.
ILSpy.AddIn.VS2022/Commands/NuGetReferenceForILSpy.cs Add detection + parameter building for NuGet references.
ILSpy.AddIn.VS2022/Commands/ProjectReferenceForILSpy.cs Add detection + parameter building for project references.
ILSpy.AddIn.VS2022/Commands/ProjectItemForILSpy.cs Add wrapper for determining project output assembly path.
ILSpy.AddIn.VS2022/Commands/OpenILSpyCommand.cs Add base command wiring + reference discovery and “open ILSpy” command.
ILSpy.AddIn.VS2022/Commands/OpenReferenceCommand.cs Add context-menu command to open selected references in ILSpy.
ILSpy.AddIn.VS2022/Commands/OpenProjectOutputCommand.cs Add command to open current project output in ILSpy.
ILSpy.AddIn.VS2022/Commands/OpenCodeItemCommand.cs Add command to open ILSpy at symbol under caret via Roslyn model.
ILSpy.AddIn.Shared/ILSpy.AddIn.Shared.shproj Removed shared-items project (code is now inlined into VS2022 add-in).
ILSpy.AddIn.Shared/ILSpy.AddIn.Shared.projitems Removed shared-items project items list (code now inlined).
CLAUDE.md Update solution references to .slnx and clarify VS2022 extension build path.
BuildTools/update-assemblyinfo.ps1 Remove legacy add-in manifest template generation step.
BuildTools/ILSpy.AddIn.vsix.filelist Remove legacy VS2017/2019 VSIX filelist snapshot.
.github/workflows/build-ilspy.yml Switch VS extension CI build step to dotnet build on .slnx and drop legacy artifact upload.
Files not reviewed (1)
  • ILSpy/Properties/Resources.Designer.cs: Generated file

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread ILSpy/Commands/AboutCommand.cs Outdated
The shared project had a single consumer after the VS2017/2019 add-in was
retired, and its VSPackage.resx already referenced an icon in the sibling
VS2022 project. Folding its sources and resources directly into VS2022 drops
the shproj/projitems indirection and the extra slnx entry, leaving one
self-contained extension project. The package-icon ResXFileRef is repointed
to the now-local Resources\Package.ico.

Assisted-by: Claude:claude-opus-4-8:Claude Code
ProvideMenuResource("Menus.ctmenu", 1) makes Visual Studio load the command
table from the "Menus.ctmenu" entry of the package's VSPackage.resources
stream, so the compiled .vsct has to be merged into it (MergeWithCTO). The
SDK-style migration could not do that: VSPackage.resx carried the package icon
as a binary System.Drawing.Icon, which the dotnet CLI can only emit via
System.Resources.Extensions (preserialized), and the VSSDK CTO merger fails to
read that format (VSSDK1012). The migration worked around the build error by
setting MergeWithCTO=false, which made the build succeed but routed the command
table into the throwaway _EmptyResource the VSSDK injects when nothing opts in
to the merge -- a stream VS never consults. The result built cleanly but
shipped a package with no menus: no ILSpy entry under Tools, no context-menu
commands.

Drop the binary package icon (resource 400 and InstalledProductRegistration's
IconResourceID) so VSPackage.resx is string-only and emits in the classic
format, then turn MergeWithCTO back on. The command table is now merged into
the package resources where VS looks, exactly as the pre-migration full-msbuild
build produced. The icon was the legacy Help/About "Installed Products" logo,
which modern Visual Studio no longer surfaces; the extension's identity icon in
the Extensions manager comes from the .vsixmanifest (ILSpy-Large.ico) and is
unaffected. This also removes the System.Resources.Extensions dependency and
the preserialized-resources workaround.

Assisted-by: Claude:claude-opus-4-8:Claude Code
@christophwille
christophwille merged commit aab6303 into master Jun 24, 2026
13 checks passed
@christophwille
christophwille deleted the christophwille/vsix branch June 24, 2026 04:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants