Use WebView2 In Console Application

What is WebView2

Check out the introduction of WebView2

Supported Programming Environments

According to the introduction, WebView2 is supported in the following programming environments:

  • Win32 C/C++
  • .NET Framework 4.5 or later
  • .NET Core 3.1 or later
  • .NET 5
  • .NET 6
  • WinUI 2.0
  • WinUI 3.0

Application in Console Application

WebView2 can be applied in WinForm, WPF, MAUI, etc., which have a UI.

Referring to this issue. I found a way to approach it using message-only window.

There is an article discussing the message-only window approach.

The strategy involves having a UI thread to trigger the WebView2 controller.

Code snippets

.csproj

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup Label="Globals">
<WebView2UseWinRT>False</WebView2UseWinRT>
</PropertyGroup>

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0-windows</TargetFramework>
<UseWPF>true</UseWPF>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<InvariantGlobalization>true</InvariantGlobalization>
<SupportedOSPlatformVersion>7.0</SupportedOSPlatformVersion>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.2210.55" />
<PackageReference Include="WebView2.Runtime.X64" Version="120.0.2210.91" />
</ItemGroup>

</Project>

program.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
using Microsoft.Web.WebView2.Core;
using System.IO;
using System.Windows.Threading;

namespace Demo;

internal class Program
{
static readonly IntPtr HWND_MESSAGE = new IntPtr(-3);

static void Main(string[] args)
{
var htmlContent = @"
<!doctype html>
<html>
<head>
<title>This is the title!</title>
</head>
<body>
<p>Hello World</p>
</body>
</html>
";
// create a new thread
var uiThread = new Thread((htmlContent) =>
{
Dispatcher.CurrentDispatcher.Invoke(async (string htmlContent) =>
{
// setup webview runtime
var runtimePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "WebView2");
var environment = await CoreWebView2Environment.CreateAsync(runtimePath, null, null);

// create controller
var browserController = await environment.CreateCoreWebView2ControllerAsync(HWND_MESSAGE);

// create view
var view = browserController.CoreWebView2;

// load html content, could also use other methods to fetch request
// html content is limited to 2 MB
view.NavigateToString(htmlContent);

// get html content body
var response = await view.ExecuteScriptAsync("document.body.outerHTML");

// print html content to pdf files, the file path should be full path, cannot use relative path
await view.PrintToPdfAsync(@"D:\UseWebView2InConsoleApp\Demo\bin\Debug\net8.0-windows\hello.pdf");

Console.WriteLine("Success!");
},
new object[] { htmlContent! });

Dispatcher.Run();
});

uiThread.SetApartmentState(ApartmentState.STA);
uiThread.Start(htmlContent);
uiThread.Join();
}
}

Limitation

  1. WebView2 needs a UI thread to run. It seems it cannot use a Task to run the WebView2.

  2. The Dispatcher is only available on Windows, making the method unavailable on Linux.

other approaches

A popular library in JavaScript is Puppeteer. In C#, an alternative is Puppeteer Sharp.