Tutorials

Coming from WiX

15 min read · For WiX v4/v5/v6 users · Migration reference

If you know WiX, you already know MSI. FalkForge targets the same Windows Installer tables — it just uses C# instead of XML. This guide translates the WiX patterns you know into FalkForge equivalents.

Concepts mapping

WiXFalkForgeNotes
.wxs source fileProgram.csC# replaces XML
<Package> elementPackageBuilder lambdaFluent API in a lambda
<Fragment>C# methods or classesJust organize your code normally
<Component>Auto-generatedFalkForge creates components automatically
<ComponentGroup>Not neededNo indirection required
<Feature> / <ComponentRef>.WithFeature("name")Chain onto any file, registry entry, or service
<Directory> treeAuto-resolved from pathsSpecify target path, directories are inferred
WixUI_Mondo etc.DialogTemplate.MondoSame 5 templates
<Property>MsiProperty classType-safe with comparison operators
<Condition>Condition class&, |, ! operators
.wxl localization filesJSON localizationCulture fallback chain
Burn bootstrapperBundle engineNativeAOT compiled, DPAPI secure properties
wix build CLIforge build or dotnet run
wix msi decompileforge decompile / forge migratedecompile = C# snippet; migrate = full buildable project
Extension NuGet packagesBuilt-in extensionsNo extra packages to install
wix.exeforge CLIbuild, validate, inspect, decompile commands

Common patterns

Each pattern shows WiX v4 XML on the left and FalkForge C# on the right.

Adding files

WiX

<Component Directory="INSTALLFOLDER">
  <File Source="app.exe" />
</Component>

FalkForge

package.AddFile("payload/app.exe")
    .To("INSTALLFOLDER")
    .WithFeature("Main");
Deep dive: Automatic components

FalkForge auto-creates the component. In WiX you must define Component + File + assign to a Feature via ComponentRef. In FalkForge, .WithFeature() handles all of that.

Registry entries

WiX

<Component Directory="INSTALLFOLDER">
  <RegistryValue Root="HKLM"
    Key="SOFTWARE\MyApp"
    Name="InstallDir"
    Type="string"
    Value="[INSTALLFOLDER]" />
</Component>

FalkForge

package.AddRegistryEntry(
    RegistryHive.LocalMachine,
    @"SOFTWARE\MyApp",
    "InstallDir",
    "[INSTALLFOLDER]")
    .WithFeature("Main");

Windows services

WiX

<Component Directory="INSTALLFOLDER">
  <File Source="myservice.exe" />
  <ServiceInstall
    Name="MyService"
    DisplayName="My Service"
    Start="auto"
    Type="ownProcess"
    Account="LocalSystem" />
  <ServiceControl
    Id="StartMyService"
    Name="MyService"
    Start="install"
    Stop="both"
    Remove="uninstall" />
</Component>

FalkForge

package.AddFile("payload/myservice.exe")
    .To("INSTALLFOLDER")
    .WithFeature("Server")
    .AddService("MyService", svc => svc
        .DisplayName("My Service")
        .StartType(ServiceStartType.Automatic)
        .Account(ServiceAccount.LocalSystem));
Deep dive: Automatic service control

FalkForge generates both ServiceInstall and ServiceControl rows automatically. In WiX you must author both explicitly.

Shortcuts

WiX

<Component Directory="ProgramMenuFolder">
  <Shortcut Id="StartMenuShortcut"
    Name="My App"
    Target="[INSTALLFOLDER]app.exe"
    WorkingDirectory="INSTALLFOLDER" />
  <RemoveFolder Id="RemovePMF"
    On="uninstall" />
  <RegistryValue Root="HKCU"
    Key="SOFTWARE\MyApp"
    Name="installed"
    Type="integer"
    Value="1"
    KeyPath="yes" />
</Component>

FalkForge

package.AddShortcut("My App",
    "[INSTALLFOLDER]app.exe")
    .ToStartMenu()
    .WithFeature("Main");
Deep dive: KeyPath and RemoveFolder

In WiX, a shortcut component needs a registry KeyPath because shortcuts can't be key paths. FalkForge handles this automatically.

Features

WiX

<Feature Id="Main"
  Title="Main Application"
  Level="1">
  <ComponentRef Id="AppComponent" />
  <Feature Id="Plugins"
    Title="Optional Plugins"
    Level="1">
    <ComponentRef Id="Plugin1" />
  </Feature>
</Feature>

FalkForge

package.AddFile("payload/app.exe")
    .WithFeature("Main");

package.AddFile("payload/plugin1.dll")
    .WithFeature("Plugins", parent: "Main");

Custom actions

WiX

<CustomAction Id="SetInstallDir"
  Property="INSTALLFOLDER"
  Value="C:\MyApp" />
<InstallExecuteSequence>
  <Custom Action="SetInstallDir"
    Before="CostFinalize" />
</InstallExecuteSequence>

FalkForge

package.AddSetPropertyAction(
    "INSTALLFOLDER", @"C:\MyApp")
    .Before(SequenceAction.CostFinalize);

Properties and conditions

WiX

<Property Id="MYPROP"
  Value="default" />
<Condition Message="Requires Windows 10">
  <![CDATA[VersionNT >= 1000]]>
</Condition>

FalkForge

package.SetProperty("MYPROP", "default");
package.AddLaunchCondition(
    Condition.IsWindows10OrLater,
    "Requires Windows 10");
Deep dive: Type-safe conditions

Condition provides pre-built checks like IsWindows10OrLater, Is64BitOS, IsAdmin. You can combine them with &, |, ! operators.

Major upgrade

WiX

<MajorUpgrade
  DowngradeErrorMessage="A newer version is installed." />

FalkForge

package.MajorUpgrade(upgrade => upgrade
    .BlockDowngrades(
        "A newer version is installed."));

Localization

WiX (.wxl file)

<WixLocalization
  Culture="en-US"
  xmlns="http://wixtoolset.org/schemas/v4/wxl">
  <String Id="WelcomeTitle">Welcome</String>
</WixLocalization>

FalkForge

package.Localization(loc => loc
    .AddBuiltInCultures()
    .DefaultCulture("en-US")
    .DetectCulture());
Deep dive: JSON localization

FalkForge uses JSON localization files instead of WiX's XML .wxl format. Built-in cultures ship with en-US and sv-SE. Custom cultures use the same JSON format.

Launch conditions

WiX

<Launch
  Condition="Privileged"
  Message="Administrator privileges required." />

FalkForge

package.AddLaunchCondition(
    Condition.IsPrivileged,
    "Administrator privileges required.");

Extension mapping

WiX ExtensionFalkForgeNotes
WixToolset.Firewall.wixextExtensions.FirewallBuilt-in, no package needed
WixToolset.Iis.wixextExtensions.IisAppPool, WebSite, Bindings, Certificates
WixToolset.Sql.wixextExtensions.SqlDB creation, script execution
WixToolset.Netfx.wixextExtensions.DotNetRegistry + filesystem probing
WixToolset.Util.wixextExtensions.UtilXmlConfig, UserMgmt, FileShare, QuietExec
WixToolset.Dependency.wixextExtensions.DependencyWiX-compatible provider/consumer model
WixToolset.Http.wixextExtensions.HttpSNI/SSL bindings, URL reservations
WixToolset.UI.wixextDialogTemplate (built-in)5 built-in templates
WixToolset.Bal.wixextInstallerApp + InstallerPage<T>Built-in WPF UI framework

Bundle migration

WiX Burn and FalkForge bundles serve the same purpose — chaining multiple packages into a single installer. Key differences:

What you gain

Migrating your existing installer

forge migrate is the recommended starting point for any WiX (or other tool) migration. It reads your compiled .msi, .msm, or WiX Burn .exe and writes a complete, buildable FalkForge C# project — not just a code snippet.

Run it

forge migrate your-product.msi -o ./migrated --falkforge-src /path/to/falkforge/src

After it finishes, ./migrated/ contains:

Build the generated project immediately:

dotnet build migrated/

If the build succeeds, the round-trip is complete. You now have an editable FalkForge project that produces an equivalent installer.

Flags

FlagDescription
<file>Installer to migrate (.msi, .msm, or .exe)
-o / --outputOutput directory (default: <filename>-migrated)
--falkforge-srcPath to the FalkForge src/ directory; sets the ProjectReference in the generated .csproj
Deep dive: migrate vs. decompile

forge decompile produces a C# code snippet you can read and copy from. It is useful for inspecting an MSI or getting a quick translation of one section.

forge migrate produces a complete project you can edit and rebuild. Use migrate when your goal is to switch your installer to FalkForge permanently. Use decompile when you just want to inspect or lift a pattern.

See demo/54-forge-migrate for a full round-trip example, including a PowerShell script that automates all three steps (build sample MSI → migrate → build generated project).

Just want to inspect? Use decompile

forge decompile your-product.msi

This generates a C# snippet from your existing MSI. Good for inspecting structure or lifting specific patterns, but it is not a buildable project. Use forge migrate (above) for a full migration.