PowerShellにてNuGetで取得したWebView2を直接利用し、ExecuteScriptAsyncの実行結果を取得する

Microsoft.Web.WebView2 パッケージの管理を自分でやりたくないため、NuGet によって取得したパッケージを参照してみました。
参照方法は非公式な方法ですが、動いてしまったのでサンプルとして公開してみます。

大半は使用しないファイルがあるため、プログラムと一緒に配布するのが正しそうです。
https://docs.microsoft.com/ja-jp/microsoft-edge/webview2/get-started/wpf

# $PSVersionTable
```
Name                           Value
----                           -----
PSVersion                      5.1.19041.610
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.19041.610
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
```
32 ビット版や ARM64 ビット版でも動きそうな雰囲気はありますが、こちらでは 64 ビット版でのみ確認済みです。

# 事前準備
下記のプログラムをインストールしてください。
- Microsoft Edge WebView2 エバーグリーン ブートストラップ
https://developer.microsoft.com/microsoft-edge/webview2/
- Microsoft Edge
https://www.microsoft.com/edge#evergreen

また、NuGet パッケージ取得に ExecutionPolicy の変更が必要です。PowerShell にコピペする場合はこのプロセスだけでも許可してください。
```PowerShell
If ((Get-ExecutionPolicy) -eq "Restricted"){
    Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process
}
```

# サンプル

![PowerShell ウィンドウに 770 と値が書き込まれている画像](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/667945/94b211de-ca50-2e24-8773-9d734626720b.png)
下記スクリプトを実行すると、上記のような画面が表示されます。
左上の"横幅取得"ボタンを押すと、PowerShell ウィンドウに 770 と値が書き込まれたことを確認できます。

重要な行は行頭に + を付けて強調表示しています。(本来の用途とは違いますが…)
このままでは実行できませんので、PowerShell で実行する前にテキストエディタで、+ を空の文字列に置換してください。

```diff_powershell
# 環境を確認し、
# -> Microsoft.Web.WebView2 が無ければ NuGet からパッケージを取得する
# -> Microsoft Edge WebView2 エバーグリーン ブートストラップ がインストールされていなければ例外をスローする
# -> Microsoft Edge がインストールされていなければ例外をスローする
Function Test-WebView2Runtime{
    Function Get-WebView2Assembly([Switch]$Retry){
        Try{
+            $SetDllDirectory = Add-Type -PassThru -Name "WindowsNtBaseApiClientDll" -MemberDefinition "[DllImport(""kernel32.dll"", SetLastError = true)]
+            public static extern bool SetDllDirectory(string lpPathName);"

            # DLL のパスを取得する
            $WebView2RuntimesPath = "$env:ProgramFiles\PackageManagement\NuGet\Packages\Microsoft.Web.WebView*\runtimes"
            If (Test-Path $WebView2RuntimesPath -PathType Container){
                Switch ((Get-WmiObject -Class Win32_ComputerSystem).SystemType.ToLower()){
                    ("x64-based pc"){
+                        $WebView2LoaderPath = (Get-Item "$WebView2RuntimesPath\win-x64\native\WebView2Loader.dll")[0]
                    }
                    ("x86-based pc"){
                        $WebView2LoaderPath = (Get-Item "$WebView2RuntimesPath\win-x86\native\WebView2Loader.dll")[0]
                    }
                    ("arm64-based pc"){
                        $WebView2LoaderPath = (Get-Item "$WebView2RuntimesPath\win-arm64\native\WebView2Loader.dll")[0]
                    }
                }
+                $MicrosoftWebWebView2WpfForDotnet45Path = (Get-Item "$env:ProgramFiles\PackageManagement\NuGet\Packages\Microsoft.Web.WebView*\lib\net45\Microsoft.Web.WebView2.Wpf.dll")[0]
+                $MicrosoftWebWebView2CoreForDotnet45Path = (Get-Item "$env:ProgramFiles\PackageManagement\NuGet\Packages\Microsoft.Web.WebView*\lib\net45\Microsoft.Web.WebView2.Core.dll")[0]
            }


            # DLL のパスが空であれば警告を表示する
            If (($WebView2LoaderPath -eq $Null) -or ($WebView2LoaderPath -eq $Null) -or ($WebView2LoaderPath -eq $Null)){
                Write-Warning "Get the Microsoft.Web.WebView2: Install-Package Microsoft.Web.WebView2"
            }

            # DLL を読み込む
+            $Path = (Split-Path $WebView2LoaderPath -Parent)
+            $SetDllDirectory::SetDllDirectory($Path) | Out-Null
+            [Reflection.Assembly]::LoadFile($MicrosoftWebWebView2WpfForDotnet45Path) | Out-Null
+            [Reflection.Assembly]::LoadFile($MicrosoftWebWebView2CoreForDotnet45Path) | Out-Null

            # XAML を読み込むために使用する
+            Add-Type -AssemblyName System.Windows.Forms,PresentationFramework,PresentationCore,WindowsBase,WindowsFormsIntegration,System.Xml.Linq | Out-Null
            # PowerShell では Await が実装できないので、代わりに VB.NET で定義しておく
+            Add-Type -Language VisualBasic -ReferencedAssemblies PresentationFramework,PresentationCore,WindowsBase,System.Xaml,$MicrosoftWebWebView2WpfForDotnet45Path,$MicrosoftWebWebView2CoreForDotnet45Path -TypeDefinition '
+            Public Class WebView2ExtendFunctions
+
+                Public Shared Async Sub ExecuteScript(WebView2 As Microsoft.Web.WebView2.Wpf.WebView2, JavaScript As String, Action As System.Action(Of String))
+                    Dim Value As String = Await WebView2.ExecuteScriptAsync(JavaScript)
+                    Action(Value)
+                End Sub
+
+            End Class
+            '
        }
        Catch{
            # 再試行のフラグがあれば例外としてスローする
            If ($Retry){
                Write-Error -Exception ([System.PlatformNotSupportedException]"Get the Microsoft.Web.WebView2: Install-Package Microsoft.Web.WebView2") -ErrorAction Stop
                Break
            }
            # NuGet から Microsoft.Web.WebView2 をインストールする
            [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12
            Import-Module PackageManagement
            Install-PackageProvider -Name NuGet -Force | Out-Null
            Install-Module PowerShellGet -AllowClobber -Force | Out-Null
            If ((Get-PackageSource | Where Name -eq nuget.org) -eq $Null){
                Register-PackageSource -Name nuget.org -Location https://www.nuget.org/api/v2 -ProviderName NuGet -Trusted | Out-Null
            }
            Get-PackageSource -Name nuget.org | Set-PackageSource -Trusted | Out-Null
            Install-Package Microsoft.Web.WebView2 | Out-Null
            # 再試行する
            Return Get-WebView2Assembly -Retry
        }
    }
    $Value = $False
    Get-WebView2Assembly

    Try{
        $WebView2Version = [Microsoft.Web.WebView2.Core.CoreWebView2Environment]::GetAvailableBrowserVersionString()
    }
    Catch{
        # GetAvailableBrowserVersionString が実行できなければ、Microsoft Edge WebView2 エバーグリーン ブートストラップ がインストールされていないと判定する
        Write-Error -Exception ([System.PlatformNotSupportedException]"Get the WebView2 Runtime Evergreen Bootstrapper: https://developer.microsoft.com/microsoft-edge/webview2/") -ErrorAction Stop
    }
    Try{
        $WebView2Version = [Version]$WebView2Version
        # 関数の判定として、バージョンが 0.0.0.0 以上か判定する
        $Value = $WebView2Version -gt [Version]"0.0.0.0"
    }
    Catch{
        # GetAvailableBrowserVersionString から取得した値が Version 型でない場合は、Microsoft Edge がインストールされていないと判定する
        Write-Error -Exception ([System.PlatformNotSupportedException]"Get the Microsoft Edge: https://www.microsoft.com/edge#evergreen") -ErrorAction Stop
    }

    Return $Value
}


If (-not (Test-WebView2Runtime)){
    # Microsoft Edge が 0.0.0.0 以下の場合
    Break
}

# ウィンドウの定義
+$XamlDocument = [System.Xml.Linq.XDocument]::Parse('<Window
+        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+        xmlns:Wpf="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf"
+        Title="WebView2 テスト" Height="450" Width="800">
+    <Grid>
+        <Grid.RowDefinitions>
+            <RowDefinition Height="Auto"/>
+            <RowDefinition Height="*"/>
+        </Grid.RowDefinitions>
+        <StackPanel Grid.Row="0" Orientation="Horizontal">
+            <Button x:Name="GetClientWidthButton" Content="横幅取得" />
+        </StackPanel>
+        <Wpf:WebView2 x:Name="WebView2" Source="https://www.google.co.jp" Grid.Row="1"/>
+    </Grid>
+</Window>')
+$MainWindow = [Windows.Markup.XamlReader]::Load($XamlDocument.CreateReader())

# WebView2 の初期化
+$MainWindow.FindName("WebView2").CreationProperties = New-Object Microsoft.Web.WebView2.Wpf.CoreWebView2CreationProperties
+$MainWindow.FindName("WebView2").CreationProperties.UserDataFolder = "$env:LocalAppData\YOUR APP NAME"

# 横幅取得ボタンを押したときに、コンソールに値を書きこむ
+$MainWindow.FindName("GetClientWidthButton").Add_Click({
+[WebView2ExtendFunctions]::ExecuteScript($MainWindow.FindName("WebView2"), "document.body.clientWidth", +[Action[String]]{param ($Value) Write-Host $Value})
+})

# ウィンドウの表示
+$MainWindow.ShowDialog()
```

# クリーンアップ
WebView2 の UserData フォルダーを指定しています。
```
%LocalAppData%\YOUR APP NAME
```
実行後は不要となりますので削除してください。

# 簡単な解説
余分な行が多いため、強調表示された行・コメント等を見ながら照らし合わせてください。

1. SetDllDirectory で WebView2Loader.dll があるディレクトリーを設定する
1. .NET Framework 4.5 向けの Microsoft.Web.WebView2.Core.dll と Microsoft.Web.WebView2.Wpf.dll を参照
1. XAML の読み込みに使用している System.Windows.Forms,PresentationFramework,PresentationCore,WindowsBase,WindowsFormsIntegration,System.Xml.Linq を参照
1. ExecuteScriptAsync を Await している関数を定義
1. XAML の定義
1. WebView2 の実行に必要な CreationProperties を定義
1. ボタンを押したときのイベントハンドラーを登録
1. ウィンドウを表示