question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Proposal: Enable DynamicDependencies

See original GitHub issue

Proposal: 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 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:closed
  • Created 3 years ago
  • Comments:29 (26 by maintainers)

github_iconTop GitHub Comments

3reactions
mqudsicommented, Aug 26, 2021

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 triggering RPC_E_WRONG_THREAD exceptions, I too needed to inject some code into Main() 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 a Program.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:

namespace Foo
{
#if DISABLE_XAML_GENERATED_MAIN
    public static class Program
    {
        [STAThread]
        static void Main(string[] args)
        {
            global::WinRT.ComWrappersSupport.InitializeComWrappers();
            global::Microsoft.UI.Xaml.Application.Start((p) => {
                var context = new global::Microsoft.System.DispatcherQueueSynchronizationContext(global::Microsoft.System.DispatcherQueue.GetForCurrentThread());
                global::System.Threading.SynchronizationContext.SetSynchronizationContext(context);
                new App();
            });
        }
    }
#endif
}

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 after InitializeComWrappers() - I’ll have no clue and my alternate Main() 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, generated Program.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 a Program.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), the Program.cs file with its Main() 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 the Main() 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 the Program.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 a Program.cs in the template and in the Main() just call into a generated Program.generated.cs’s partial 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-generated Main() in the non-generated Program.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.

2reactions
sylveoncommented, Feb 9, 2021

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.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Proposal: Allow dynamic dependency override · Issue #2205
Proposal : Enable overriding one or more 'umbrella' chart dependencies with specific sub-chart version(s) and/or sub-chart repo(s) at chart ...
Read more >
Dynamic Dependencies - Packaging - Python discussion
I proposed selector packages to isolate these kinds of decisions from the package's requires list, but it seems more likely we'll just continue ......
Read more >
Use the dynamic dependency API to reference MSIX ...
The dynamic dependency API enables unpackaged apps to reference and to use framework packages such as WinUI 2 and the DirectX Runtime.
Read more >
DynamicDependencies.md
DynamicDependencies enable access to packaged content via APIs at runtime. ... This is the spec for proposal MSIX Dynamic Dependencies - allow any...
Read more >
Dynamic Module Reform details
Enabling Circular Dependencies with Dynamic Module Records ... This change also enable us to match the semantics of NCJS when it comes to...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found