Hybrid Blazor + NativeAOT = ❤️

Andrii Kurdiumov
3 min readDec 23, 2022

--

Boring picture, as mostly this article.

It’s been a while when I post something new and interesting. After my Winforms post appears on .NET 7 WinForms announcement, some people express interest in having WebView2 to support Blazor. I have this request for about ~2 years, so I decide to poke this beast. Blazor is much more modern the WinForms, so it’s better suited for desktop nowadays if you have web experience.

Mostly hybrid blazor rely on the WebView2 to draw UI, and because WebView2 is COM based, I need to add bunch of RCW and CCW which is specific to WebView2.

To add support for WebView2, I create project for facade assembly which will contain same interfaces and private classes as defined in Microsoft.Web.WebView2.Core . Now I have WebView2 interfaces inside my solution, which mimic real one. After I reference these project setting PrivateAssets=All

Then I add IgnoreAccessChecksToAttribute to my assembly, so runtime did not complain that I access internal classes from other assembly.

Now I can reference classes and interfaces from my fake project at build time. At runtime obviously I will use normal assembly, but runtime will ignore protections, because I politely ask so.

After that, I do a lot of grunt work. I do not want to go into details, because it’s boring, and better look at content of WinFormsComInterop/WinFormsComInterop/WebView2 at main · kant2002/WinFormsComInterop (github.com)

That bring almost all what’s needed for WebView2. That was relatively fast, because I use source generator which allow me faster create RCW and CCW implementations. But we target for Blazor, and that’s slightly different beast. Once I plug updated library to relatively vanilla WinForms Blazor sample, dynamic generics start crashing at runtime. It was actually from 2 offenders indirectly related to Blazor — System.Text.Json and Microsoft.Interop or probably how Microsoft.AspNetCore.Components.Web encodes communications with JS runtimes. I think this snipped can be valuable, so I add it to my RdXmlLibrary.

Blazor itself give me just one reason to touch rd.xml file

Essentially, I have to root only my RootComponent (apologize for tautology).

After that, I have nice 46 Mb executable which is loading fast, but slow to initialize WebView2. Probably that’s can be hidded using splash screen or something. Otherwise all app is self-contained and working fine.

Our nice Blazor counter

Sample source code: WinFormsComInterop/samples/BlazorHybrid at main · kant2002/WinFormsComInterop (github.com)

To reproduce on your project you should do followinng steps

  1. Add WinFormsComInterop package. For example using: dotnet add package WinFormsComInterop
  2. Add following line to your Program.cs
    ComWrappers.RegisterForMarshalling(WinFormsComInterop.WebView2.WebView2ComWrapper.Instance);
  3. Add <PackageReference Include=”Microsoft.Web.WebView2" Version=”1.0.1462.37" /> as depedency.
  4. Add RD.xml file from https://github.com/kant2002/RdXmlLibrary/blob/main/Microsoft.AspNetCore.Components.Web.rd.xml to project
  5. Add <RdXmlFile Include=”Microsoft.AspNetCore.Components.Web.rd.xml” /> to project file
  6. Make sure that RootComponents are added to you app-specific rd.xml

One more UI tech is now supported by NativeAOT. And as usual if you encounter specific issue, let me know.

--

--