Majorsilence.Forms.Migrator 26.0.52

dotnet tool install --global Majorsilence.Forms.Migrator --version 26.0.52
                    
This package contains a .NET tool you can call from the shell/command line.
dotnet new tool-manifest
                    
if you are setting up this repo
dotnet tool install --local Majorsilence.Forms.Migrator --version 26.0.52
                    
This package contains a .NET tool you can call from the shell/command line.
#tool dotnet:?package=Majorsilence.Forms.Migrator&version=26.0.52
                    
nuke :add-package Majorsilence.Forms.Migrator --version 26.0.52
                    

majorsilence-migrate

A .NET global tool that converts WinForms source and projects to Majorsilence.Forms.

dotnet tool install -g Majorsilence.Forms.Migrator
majorsilence-migrate <input> [options]

From a clone of the repo, without installing:

dotnet run --project tools/Majorsilence.Forms.Migrator -- <input> [options]

<input> is a .sln, a .csproj/.vbproj, a directory, or a single .cs/.vb/.resx file.

What it does

Project files (.csproj)

  • Removes UseWindowsForms / UseWPF / EnableWindowsTargeting.
  • Drops the -windows TFM suffix, keeping the .NET version (net8.0-windowsnet8.0, net10.0-windows10.0.19041.0net10.0). A multi-target <TargetFrameworks> stays plural with each entry stripped. Pass --tfm to instead force one exact framework. The same -windows strip is applied to any custom .props/.targets the project pulls in via <Import Project="…" />.
  • Drops the Microsoft.WindowsDesktop.App framework reference.
  • Removes WinForms-only NuGet packages. Vendor UI suites — Telerik.UI.for.WinForms*, DevExpress.Win*, Infragistics.Win*, C1.Win* (ComponentOne), Syncfusion.*.WinForms* — are dropped (their <PackageReference>s, and any now-empty <ItemGroup>). The Telerik/DevExpress namespaces map onto the Majorsilence.Forms.* compat layers via the source rewrites / a --map file; add more packages to drop with a map file's removePackages globs (* wildcard, case-insensitive).
  • Adds Majorsilence.Forms + a platform backend reference (package or project) — but only to projects that are actually WinForms: a project is treated as WinForms when it opts in (<UseWindowsForms>, a System.Windows.Forms assembly reference / VB project import) or any of its source uses System.Windows.Forms. Non-UI projects in a solution (data/service libraries) are left without a UI dependency.
  • Honours Central Package Management. When a Directory.Packages.props governs the project (found by walking up from the project) and central management is on, the added PackageReferences omit their Version and the versions are pinned in that Directory.Packages.props via <PackageVersion> entries instead (existing entries are left untouched). With --output the props file is mirrored into the output tree; a repo-wide props file above the migrated tree is flagged for manual editing instead.
  • Skips legacy non-SDK projects and malformed XML with a warning (never throws).

Source files (.cs / .vb)

  • System.Windows.FormsMajorsilence.Forms (using directives and fully-qualified refs).
  • System.Drawing is split three ways:
    • Primitive value types (Color, Point, Size, Rectangle, PointF, SizeF, RectangleF) are framework types Majorsilence.Forms keeps — left untouched.
    • GDI+ types (Bitmap, Brush, Pen, Font, …) → Majorsilence.Forms.Drawing.
    • WinForms-compat types Majorsilence relocates (Graphics, ContentAlignment, SystemColors, SystemBrushes, SystemPens, SystemFonts, ColorTranslator) → Majorsilence.Forms.
    • A bare using System.Drawing; is reconciled: it's kept only when a primitive (Color/Point/…) is used unqualified, otherwise removed. When GDI+ types are used unqualified, a using Majorsilence.Forms.Drawing; is added (replacing the System.Drawing import if it's no longer needed).
  • System.Drawing.Drawing2D / .Imaging / .TextMajorsilence.Forms.Drawing.*; .PrintingMajorsilence.Forms.Printing.
  • System.ComponentModel.ComponentResourceManagerMajorsilence.Forms.ComponentResourceManager (the cross-platform resource manager — see Resources below). Only that one type is redirected; the rest of System.ComponentModel is left alone.
  • ApplicationConfiguration.Initialize() is commented out (no equivalent; styles are implicit).
  • Anything it can't confidently map (e.g. Graphics, System.Drawing.Design, VisualStyles) is flagged for manual review, not silently mangled.

Solutions (.sln)

When the input is a solution and --output is set, every project it lists is converted and the .sln itself is copied into the output tree (project paths are preserved), so the result opens directly.

Visual Basic (.vbproj / .vb)

  • .vbproj projects are discovered and converted alongside .csproj.
  • Forces <MyType>Empty</MyType> — the VB "My" application framework (MyType=Windows/WindowsForms) pulls in Microsoft.VisualBasic.Devices.Computer, WinForms.Form and ApplicationSettingsBase, none of which exist cross-platform.
  • Excludes the generated My Project\*.Designer.vb files (they don't compile with MyType=Empty).
  • Rewrites project-level VB global imports (<Import Include="System.Windows.Forms" />).
  • Adds the missing form constructor automatically. MyType=Empty removes VB's implicit WinForms constructor, so a form that uses InitializeComponent but has no Public Sub New() gets one injected (after Inherits), marked with a ' [majorsilence-migrate] comment. The decision is made across the form's partial files (Form1.vb + Form1.Designer.vb): the constructor is injected only when no partial already declares one, and always into the code-behind, never a designer file. It only warns if it can't find where to insert.
  • Entry-point warning only for executables. MyType=Empty removes the auto entry point, but only Exe/WinExe projects need one — class libraries (the SDK default) are not flagged.
  • Skips the My Project\*.Designer.vb files (Resources/Settings/Application). The project converter excludes them from compilation, so they're neither converted nor flagged.
  • Remaining warnings a human still owns: real-exe entry points, and My.* usage — flagged with a member-specific hint (My.Resources → load from the .resx; My.Settings → a config/JSON-backed settings class; My.Application/My.ComputerAppContext/System.IO/RuntimeInformation).

Typed DataSets (.xsd + .Designer.vb)

These are pure ADO.NET (System.Data) — no WinForms UI — so they work cross-platform as-is. The migrator does not modify them. (Their System.ComponentModel.Design.HelpKeywordAttribute is a design-time attribute that ships in the BCL, so it is not flagged.)

Resources (.resx)

Majorsilence.Forms ships a cross-platform ComponentResourceManager that reads the .resx XML directly (no GDI+, no BinaryFormatter), so migrated designer code — resources.GetObject(...), resources.GetString(...), resources.ApplyResources(ctrl, name) — works at runtime on every OS. Because of that, the migrator only flags what genuinely can't be carried across:

  • Plain string tables, primitive designer values (Point/Size/Color/Boolean/Int32/…), and images stored as bytearray.base64 (the modern form — raw image bytes decoded by SkiaSharp) are all consumable. They're not flagged; with --output the .resx is copied into the mirrored tree.
  • BinaryFormatter/SOAP blobs are recovered from the NRBF wire format (via System.Formats.Nrbf), without running BinaryFormatter:
    • System.Drawing.Bitmap/Icon → the embedded image bytes;
    • System.Windows.Forms.ImageListStreamer → the comctl32 image-list strip (RLE + BMP + mask) decoded into per-frame images;
    • System.Data.SqlTypes scalars and DBNull (a SqlCommand's design-time parameter defaults). These are consumable, not flagged.
  • The genuinely unportable cases are flagged with case-specific guidance: ActiveX/COM control state (AxHost/OCX) has no managed equivalent — replace the control; any other serialized object must be re-created in code (BinaryFormatter is gone from modern .NET).

To let new ComponentResourceManager(typeof(Form)) find its .resx at runtime, embed the raw .resx (logical name <FullTypeName>.resx) or keep it beside the assembly. Constructing from an explicit .resx via ComponentResourceManager.FromFile/FromStream/FromXml always works.

Options

Option Description
-o, --output <dir> Write to a mirrored tree. Omit to convert in place (leaves .bak files).
-n, --dry-run Report changes without writing.
--no-backup In-place mode: don't leave a .bak beside each changed file (e.g. when under git).
--diff Print a unified diff for each changed file.
--backend <name> avalonia (default) | uno | headless.
--references <mode> package (default) | project.
--tfm <tfm> Force a target framework. Default: keep the version, just drop the -windows suffix.
--package-version <v> NuGet version for package references. Defaults to the migrator's own version — the tool and the packages ship from the same release.
--repo-root <dir> Repo root for resolving --references project paths.
--map <file> JSON file of extra namespace mappings (repeatable).
--strict Exit non-zero if any manual-review warning is produced (CI gate).
--report <file> Path for the Markdown report (default migration-report.md by the output).
--no-report Don't write the report.

Custom mappings

Bring third-party controls across with a JSON map (layered on the built-in rules):

{
  "namespaces": {
    "DevExpress.XtraEditors":  "Majorsilence.Forms.DevExpress"
  },
  "removePackages": [ "Acme.WinForms.*" ]
}

removePackages is an optional list of extra WinForms-only package-id globs to drop (on top of the built-in Telerik/DevExpress/Infragistics/ComponentOne/Syncfusion patterns).

Telerik is already built in — all of Telerik.WinControls, .UI, .UI.Docking, .UI.Data, .Data, and .Enumerations map to Majorsilence.Forms.Telerik out of the box (see Source files above), including the guards that leave unsupported namespaces (.Themes, .UI.Export, .Design, .Primitives, .Layouts) and unmapped heavyweight types (RadPdfViewer, RadRichTextEditor, …) unrewritten with a warning instead of pointing them at a namespace that doesn't exist. Remove any Telerik.* entry from your --map file — custom-map rewrites (Pass 1a) have no such guards, so a leftover Telerik entry will blindly clobber the deliberately-unrewritten heavyweight references the built-in rules are protecting.

majorsilence-migrate ./LegacyApp --map telerik.json --strict

Caveats

This is a textual transform, not a Roslyn rewrite — it's fast and tolerant of code that doesn't currently compile, but type- and member-level API differences between WinForms and Majorsilence.Forms still need a build pass and manual fixes afterward. Always build the converted project and work through the migration-report.md before committing.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

Version Downloads Last Updated
26.0.52 92 9/12/2026
26.0.51 108 8/30/2026
26.0.50 102 8/29/2026
26.0.33 114 8/25/2026
26.0.32 177 8/19/2026
26.0.31 126 8/17/2026
26.0.30 108 8/13/2026
26.0.29 106 8/8/2026
26.0.28 112 8/7/2026