Proposal: Enable DynamicDependencies
See original GitHub issueProposal: Extensibility hooks in generated main
Summary
Optionally add hooks in the generated main code for developers to provide code to execute before any WinUI code. Bonus points for hooks at the end of the generated main and other useful points for developers to add functionality without manually editing or otherwise hacking generated files in fragile ways.
Rationale
- MSIX Dynamic Dependencies requires API calls before using WinUI in a Framework package
MSIX Dynamic Dependencies supports non-packaged processes using Framework package goods by calling APIs at runtime, accomplishing what packaged apps do via <PackageDependency>
in their appxmanifest.xml
. For non-packaged apps to use XAML via a Framework package the app needs to call APIs before accessing WinUI.
This is currently impossible with XAML’s generated main method. There’s no opportunity for the developer to provide code to execute before the generated main uses XAML (not without fragile manual editing of the generated main). Per https://github.com/microsoft/ProjectReunion/issues/89#issuecomment-701455098
Developers shouldn’t have to write code in their main method. Maybe this is fine for frameworks where the app author owns the main method. But in WPF and WinUI, the main method is generated by tooling.
The generated main should have extensibility hooks, e.g. the generated main has pre() and post() calls to InsertYourCodeHere? Maybe a new main-extensions.cpp is created with a new XAML project with void XamlMainPre() { /insert code here/ } and XamlMainPost() for devs to inject logic into their app’s startup flow?
There may be other reasons (e.g. a crash handler).
See Proposal: Extensibility hooks in generated main #3632 for the equivalent ask of WPF.
Scope
Capability | Priority |
---|---|
Optionally support a ‘pre-Main’ callout for developers to provide code before the generated main logic w/o editing generated files | Must |
Optionally support a ‘post-Main’ callout for developers to provide code after the generated main logic w/o editing generated files | Should |
Open Questions
Are there other desirable extension points in generated files/code?
Issue Analytics
- State:
- Created 3 years ago
- Comments:29 (26 by maintainers)
Top GitHub Comments
FWIW, not mentioned in this issue is that the generated
Main()
entrypoint for WinUI projects is gated behind an#if !DISABLE_XAML_GENERATED_MAIN
preprocessor check. To work around some Windows App SDK and WinUI threading model bugs pertaining to background activation triggeringRPC_E_WRONG_THREAD
exceptions, I too needed to inject some code intoMain()
prior to anything touching the WinUI or WindowsAppSDK libraries, and I was able to do so without too much difficulty thanks to that preprocessor check.If you just define
DISABLE_XAML_GENERATED_MAIN
in your project and then add aProgram.cs
that includes the following, you get a non-generated equivalent to the default startup code that you can then modify to inject any init calls prior to WinUI/WindowsAppSDK calls:In terms of directing developers developing unpackaged apps to do this themselves (vs some sort of pre/post hooks), it’s not too bad (the ugliest part of it is defining the constant in the csproj file), but my concern right out of the gate was that there’s no guarantee that any of the code I copy-and-pasted out of the generated
Program.cs
won’t change at any time; say with WinUI 3.1 the devs decide to add another call afterInitializeComWrappers()
- I’ll have no clue and my alternateMain()
will continue to be called and run as-is, but might run into undefined behavior at runtime because it skipped doing something integral that would have been included in an automatically updated, generatedProgram.cs
.Can I ask why
Program.cs
is generated rather than just being part of the WinUI 3 project template? Back in the days of C# 1.0, we had aProgram.cs
included as part of the “C# Windows Forms” template, and it really didn’t cause any grief. Developers just getting started simply ignored it and focused on the main window SWF C# file, but when you needed to dig into the application startup code (say to add single-instance detection and redirection), theProgram.cs
file with itsMain()
routine was right there for you to hack away at.To give a more modern and perhaps more relevant example, ASP.NET Core projects again include a
Program.cs
file with theMain()
startup routine as part of the template rather than generated as part of the build process. In the upgrade from ASP.NET Core 2.2 to ASP.NET Core 3.1, breaking changes were made to the ASP.NET Core entrypoint that required users to manually open and patch theProgram.cs
file to start up the ASP.NET Core runtime in the new fashion and it was simply documented in the release notes.** To be fair, the old startup code wouldn’t compile - which is a good thing. I’d go so far as to include a hard-coded version number of version enum as a trailing parameter e.g.
Microsoft.UI.Xaml.Application.Start((ApplicationInitializationCallback) ..., ApplicationStartupVersion.Version3)
and throw an exception at startup in a subsequent release that requires a different initialization procedure with a helpful error message and maybe a link to the release notes/upgrade docs so that no one calls into a newer version of the Application startup with an older version of the startup code.Or you could include a non-generated
partial static class Program
in aProgram.cs
in the template and in theMain()
just call into a generatedProgram.generated.cs
’spartial static class Program { void StartApp() { ... } }
and move all init code into there; it would be always regenerated so always up to date, but developers would have access to the non-generatedMain()
in the non-generatedProgram.cs
to do whatever they want with.Personally, I’d go with option a over option b because I prefer less voodoo and there’s already way too much of that when dealing with packaged apps and WinRT.
Even if vcpkg isn’t meant to be supported for the first release, I think it would be wise to design a solution that is not entirely coupled to NuGet to ensure that when vcpkg gets support it doesn’t break existing consumers using NuGet.