I was writing a WinUI 3 application that uses the Entity Framework and the Microsoft Hosting Extensions for IoC.
My migrations were not working. After a lot of troubleshooting I figured out
that the EF
migrations tool has some specific assumptions on the order of
initializing the Host
, the Services
and the Application
itself. Such fine
grained control is not possible using the default generated XAML entry point.
➡ How to properly override the default generated XAML entry point?
If we check the executable for the application we can see that the Main
entry
point is actually in a class called Program
. That class is generated by the
XAML compiler along with the App
class. If we take a look in the obj
folder
of the App
project, there is a file named App.g.i.cs
which contains some
partial code generated for the App class but also code that is conditionally
included based on the presence of the DISABLE_XAML_GENERATED_MAIN
compilation
constant, defining a new class called Program
with the Main
entry point
definition.
#if !DISABLE_XAML_GENERATED_MAIN
/// <summary>
/// Program class
/// </summary>
public static class Program
{
[global::System.Runtime.InteropServices.DllImport("Microsoft.ui.xaml.dll")]
private static extern void XamlCheckProcessRequirements();
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.UI.Xaml.Markup.Compiler"," 3.0.0.2309")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.STAThreadAttribute]
static void Main(string[] args)
{
XamlCheckProcessRequirements();
global::WinRT.ComWrappersSupport.InitializeComWrappers();
global::Microsoft.UI.Xaml.Application.Start((p) => {
var context = new global::Microsoft.UI.Dispatching.DispatcherQueueSynchronizationContext(global::Microsoft.UI.Dispatching.DispatcherQueue.GetForCurrentThread());
global::System.Threading.SynchronizationContext.SetSynchronizationContext(context);
new App();
});
}
}
#endif
The easiest way to achieve our goal is obviously to define the
DISABLE_XAML_GENERATED_MAIN constant, and include your own Program.cs
.
The first change to make is to App.csproj
project definition file.
+ <!-- Use our own Main entry point so we can control the HostBuilder -->
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ <DefineConstants>DISABLE_XAML_GENERATED_MAIN</DefineConstants>
+ <StartupObject>App.Program</StartupObject>
We define the DISABLE_XAML_GENERATED_MAIN
compile time constant and provide
our own Program.cs
. The majority of the Program.cs
content is copied from the default one, which will ensure that the proper
initialization and checks are made as if we were using the default entry point.
Any additional pre- or post- initialization work needs to be placed
appropriately.
ℹ NOTE 1:
AllowUnsafeBlocks
is not really needed for the custom entry point but is required to useLibraryImport
which is preferred toDLLImport
.
ℹ NOTE 2:
In the recent versions of the
WindowsAppSDK
, bothXamlCheckProcessRequirements()
andInitializeComWrappers()
don't do anything. It is better however to keep them for maximum compatibility as they are still there in the default generated entry point.