perf(installer): shrink MSI size#19
Merged
Merged
Conversation
Three independent changes that together drop the self-contained publish from 310 MB to 233 MB (-25%) and the MSI from 91 MB to 68 MB (-26%): 1. Swap the NAudio metapackage for NAudio.Wasapi. The metapackage transitively pulls NAudio.WinForms, which framework-references Microsoft.WindowsDesktop.App.WindowsForms and bundled ~24 MB of WinForms binaries into the publish output. Earmark.Audio only uses NAudio.CoreAudioApi (which lives in NAudio.Wasapi), so the swap is API-compatible. 2. Prune the Microsoft.WindowsAppSDK.ML payload. WAS 1.8 transitively pulls Microsoft.WindowsAppSDK.ML 1.8.2192, which copies onnxruntime.dll (~21 MB), DirectML.dll (~19 MB), Microsoft.Windows.AI.MachineLearning.dll, and the Microsoft.ML.OnnxRuntime.dll managed wrapper into self-contained publish output. Earmark does not use Windows ML. There is no supported opt-out yet (microsoft/WindowsAppSDK#5969); this PR adds two MSBuild targets that strip the ML files from ReferenceCopyLocalPaths/None and from ResolvedFileToPublish via a filename regex. 3. Move JSON serialization to source-generated JsonSerializerContext for AppSettings (settings.json) and List<RoutingRule> (rules.json). This is trim/AOT-friendly and slightly faster, even though full trimming is currently disabled. Trimming was attempted but reverted: .NET 10's trimmer hard-strips System.StubHelpers.InterfaceMarshaler when classic-COM interop is reachable ("IL2026: Built-in COM support is not trim compatible"), even with BuiltInComInteropSupport=true and a TrimmerRootDescriptor. Earmark's Core Audio path runs through NAudio.CoreAudioApi (classic [InterfaceType(InterfaceIsIUnknown)]) and our own IPolicyConfigVista interop, so trimming would require migrating to ComWrappers source-generators. Left as future work and noted inline in the csproj.
hoobio
pushed a commit
that referenced
this pull request
Apr 27, 2026
This PR was generated automatically by [release-please](https://github.com/googleapis/release-please). --- ## [0.1.4](v0.1.3...v0.1.4) (2026-04-27) ### Bug Fixes * **installer:** wire MSI icon and add branded installer chrome ([#17](#17)) ([7881d1c](7881d1c)) ### Performance Improvements * **installer:** shrink MSI size ([#19](#19)) ([5666084](5666084)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: release-please-hoobi[bot] <279189756+release-please-hoobi[bot]@users.noreply.github.com>
This was referenced Apr 27, 2026
Open
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Drops the Earmark MSI from 91 MB to 68 MB (-26%) and the uncompressed self-contained publish output from 310 MB to 233 MB (-25%) by removing two transitively-bundled payloads that Earmark does not use.
Size before / after
Changes
NAudio.WinForms, which framework-referencesMicrosoft.WindowsDesktop.App.WindowsFormsand brought ~24 MB of WinForms binaries (System.Windows.Forms.dll,.Design,.Primitives,.Private.Windows.Core,System.Drawing.Common,System.Configuration.ConfigurationManager) into self-contained publish output. Earmark.Audio only usesNAudio.CoreAudioApi(which lives inNAudio.Wasapi), so the swap is API-compatible.Microsoft.WindowsAppSDK.ML1.8.2192, which copiesonnxruntime.dll,DirectML.dll,Microsoft.Windows.AI.MachineLearning.dll, plus theMicrosoft.ML.OnnxRuntime.dllmanaged wrapper into self-contained publish output. Earmark does not use Windows ML. There is no supported opt-out yet (microsoft/WindowsAppSDK#5969); two new MSBuild targets insrc/Earmark.App/Earmark.App.csprojstrip the ML files fromReferenceCopyLocalPaths/Noneand fromResolvedFileToPublishvia a filename regex. If WindowsAppSDK later wires a runtime probe to these files this exclusion will need to come back out, but at runtime today the WindowsAppRuntime bootstrap does not LoadLibrary onnxruntime/DirectML unless Windows ML APIs are actually invoked.JsonSerializerContextforAppSettingsandList<RoutingRule>. Trim/AOT-friendly and slightly faster, even though full trimming is currently disabled.Deliberately not done
PublishTrimmed=true. Tried it. .NET 10's trimmer hard-stripsSystem.StubHelpers.InterfaceMarshaler(the classic-COM marshalling stub) when classic COM is reachable, even withBuiltInComInteropSupport=trueand an explicitTrimmerRootDescriptor. The trimmer printsIL2026: Built-in COM support is not trim compatibleand the runtime then throwsTypeLoadExceptionon the firstMMDeviceEnumeratorconstruction. Earmark's Core Audio path goes throughNAudio.CoreAudioApi(classic[InterfaceType(InterfaceIsIUnknown)]) plus our ownIPolicyConfigVistaandIAudioPolicyConfigFactoryinterop, so trimming would require migrating to ComWrappers source-generators. Left as future work and called out inline inEarmark.App.csproj.NAudio.Core+NAudio.Wasapitogether are ~120 KB; replacing them with hand-rolled COM interop (similar to what we already do forIPolicyConfigVista) is roughly five COM interface declarations plus IID/HResult plumbing, for negligible MSI savings. Worth doing only as a precondition for the trimming work above.Testing
Validated end-to-end on this Windows 11 host:
dotnet publish src/Earmark.App/Earmark.App.csproj -c Release -p:Platform=x64 -p:RuntimeIdentifier=win-x64 -p:WindowsPackageType=None -p:WindowsAppSDKSelfContained=true -p:SelfContained=true -o publish/x64-trimmedexit 0, no new warnings beyond the pre-existing CsWinRT/.NET WinRT projection rollups.wix build ... -d PayloadDir=publish/x64-trimmed -out staging/msi-x64/Earmark-1.0.0-x64.msi installer/Earmark.wxs ...exit 0.msiexec /i staging/msi-x64/Earmark-1.0.0-x64.msi /qnexit 0; install dir%LocalAppData%\Earmark\populated;Earmark.App.exelaunches and parks in the system tray; secondary activation surfaces the main window with title "Earmark"; rule application against running processes works (latest log showsAudioPolicyService init: ... using Win11 interface,Activated factory 'Windows.Media.Internal.AudioPolicyConfig' as Win11 layout,Applied rule Media to msedge ... -> Media (Elgato Virtual Audio)).Verified no
forms,onnx,directml, ormachinelearningfiles appear in the publish output after the prune.Manual testing on a Windows 10 (19041+) or Windows 11 host
Tail of the latest log file under
%LocalAppData%\Earmark\logs\shows the expectedApplied rule .../Skip Set .../Skip re-apply ...lines after the changeUnit tests added/updated under
tests/Earmark.Core.TestsExisting tests pass (
dotnet test -p:Platform=x64) - no test changes; the existing Core.Tests scaffold has no rule-store tests todayChecklist
dotnet build src/Earmark.App/Earmark.App.csproj -c Debug -p:Platform=x64)Earmark.Core, Windows audio interop inEarmark.Audio, UI inEarmark.AppEarmark.App.csprojdocument the ML prune and trimming-disabled rationale