LoginSignup
14
10

More than 1 year has passed since last update.

次期HTA(HtmlApplication)としてのPowerShell+XAML+WebView2の利用

Last updated at Posted at 2022-05-08

先日、本記事の前段となる下記記事を投稿したが、これは本記事の検証を無事に進められなかったために途中の結果を記録したものである。

本来やりたかった事について目処がたったので検証結果を本記事にまとめる。内容の差分としては XAML(WPF) を利用するか否かである。

■ 前書き

先日書いた記事で書きたい事は書いてしまったので省略する。
追記するならば、XAML を併用した PowerShell + WebView2 は HTA の代替候補として有用だと感じる。

なお、WebView2 用の DLL ファイルは NuGet より取得しているが、現在最新版として公開されてる 1.0.1210.30 では XAML や WPF との連携が上手くできないため、2022/05/08時点プリリリース版の利用が必要である点ご注意いただきたい。

■ WebView2 とは

まず WebView2 が何かについて Microsoft の HP より引用して紹介する。

Microsoft Edge WebView2 を使用すると、Web テクノロジ (HTML、CSS、JavaScript) をユーザーのネイティブ アプリに埋め込みできます。 WebView2 コントロールは、Microsoft Edge をレンダリング エンジンとして使用して、ネイティブ アプリに Web コンテンツを表示します。
https://docs.microsoft.com/ja-jp/microsoft-edge/webview2/

クライアントアプリの UI 内に MS Edge のレンダラーを埋め込む事ができ、クライアントアプリと WebView2 間でデータの交換や、イベントリスナの登録が可能になる。

■ 本記事で利用する技術

下記の技術を組みわせて検証・実装をする。

■ HTA の利点との比較

HTA のメリットと本記事で利用する技術がカバーしている範囲について表にまとめた。

# 機能 HTA PowerShell XAML WebView2
1 デフォルトの環境で動作可能 o o - o
(DLL要同梱)
2 UIをHTML/JS/CSSで作成可能 o - o
(XML部分のみ)
o
3 JSライブラリの利用で実装の手間を軽減可能 o - - o
4 実行ファイル自体がソースコード o o o -
5 デスクトップアプリとして動作 o o - -
6 外部(アプリ)と連携、通信が可能 o o -
(PowerShellを通して可能)

※ o=対応可能、-=対象(範囲)外

今回の組み合わせ PowerShell + XAML + WebView2 を利用することで、おおよそ HTA の良い点をカバーできていると感じる。
 ※ 表に関する追加の記載項目の助言やご指摘はコメント欄にてお知らせください。

■ 本記事の以降の流れ

下記の流れで検証・実装を進める。

  1. XAML で WebView2 を埋め込んだウィンドウを作成
  2. PowerShell から WebVie2 に対して (A)スクリプトの実行、(B)データの送受信 を行う

なお、先日の記事にあったイベントリスナの登録は ExecuteScriptAsync を使う点で重複し検証の意味がないので省いた。

■ XAML で WebView2 を埋め込んだウィンドウを作成

◇ ファイル構成

構成
xaml_webview01\
  + script01.ps1  (1)
  + ui01.xaml     (2)
  + lib\
    + Microsoft.Web.WebView2.Core.dll
    + Microsoft.Web.WebView2.Wpf.dll
    + WebView2Loader.dll
  + data\  … スクリプト側で自動生成

lib 配下のファイルは Nuget (参考#1) よりパッケージをダウンロードして、実行環境のアーキテクチャのDLLファイルを配置する。
なお、当記事の起稿時点では最新版では WebView2 が適切に起動されないためプレリリース版を利用している。

◇ 各ファイルの内容

(1) script01.ps1
Add-Type -AssemblyName PresentationFramework  # WPF 用

<# WebView2 用アセンブリロード #>
[void][reflection.assembly]::LoadFile((Join-Path $PSScriptRoot "lib\Microsoft.Web.WebView2.Wpf.dll"))  # 本記事時点 microsoft.web.webview2.1.0.1222-prerelease を利用
[void][reflection.assembly]::LoadFile((Join-Path $PSScriptRoot "lib\Microsoft.Web.WebView2.Core.dll")) # 本記事時点 microsoft.web.webview2.1.0.1222-prerelease を利用

<# XAML にて Window 構築 #>
[xml]$xaml  = (Get-Content (Join-Path $PSScriptRoot .\ui01.xaml))
$nodeReader = (New-Object System.XML.XmlNodeReader $xaml)
$window     = [Windows.Markup.XamlReader]::Load($nodeReader)

<# WebView2 初期設定 #>
$webview = $window.findName("webView")
$webview.CreationProperties = New-Object 'Microsoft.Web.WebView2.Wpf.CoreWebView2CreationProperties'
$webview.CreationProperties.UserDataFolder = (Join-Path $PSScriptRoot "data")

[void]$window.ShowDialog()
$window.Close()

先日の記事との相違点として、読み込む DLL を Microsoft.Web.WebView2.WinForms.dll から Microsoft.Web.WebView2.Wpf.dll に変更している。

(2) ui01.xaml
<Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:wv2="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf"
        Title="MainWindow" Height="450" Width="800"
    >
    <DockPanel x:Name="RootDock">
        <wv2:WebView2 Name="webView" Source="http://abehiroshi.la.coocan.jp/" />
    </DockPanel>
</Window>

◇ 実行結果

image.png
無事、WebView2 を介して阿部寛さんの HP を表示することに成功した。
XAML を併用することで記述量を著しく抑える事が出来るのはありがたい。

PowerShell から WebVie2 に対して (A)スクリプトの実行、(B)データの送受信 を行う

先日の記事と同様の実装をこちらでも行う。実装は下記の通り行う。

  1. (A) スクリプトの実行
    WebView の ExecuteScriptAsync で JavaScript の alert(メッセージボックス) を起動する。
  2. (B) データの送受信
    WebView から PowerShell:
     JS の window.chrome.webview.postMessage(...) で PowerShell にデータを送信する。
     PowerShell 側は WebView2 オブジェクトの add_WebMessageReceived で受信時の処理を記載する。
    PowerShell から WebView:
     PowerShell の $webview.CoreWebView2.PostWebMessageAsString でデータを送信する。
     JS 側は window.chrome.webview.addEventListener で受信時の処理を記載する。   

◇ ファイル構成

構成
xaml_webview01\
  + script02.ps1  (1)
  + ui02.xaml     (2)
  + page02.html   (3)
  + lib\
    + Microsoft.Web.WebView2.Core.dll
    + Microsoft.Web.WebView2.Wpf.dll
    + WebView2Loader.dll
  + data\  … スクリプト側で自動生成

lib 配下のファイルは Nuget (参考#1) よりパッケージをダウンロードして、実行環境のアーキテクチャのDLLファイルを配置する。
なお、当記事の起稿時点では最新版では WebView2 が適切に起動されないためプレリリース版を利用している。

◇ 各ファイルの内容

(1) script02.ps1
Add-Type -AssemblyName PresentationFramework  # WPF 用

<# WebView2 用アセンブリロード #>
[void][reflection.assembly]::LoadFile((Join-Path $PSScriptRoot "lib\Microsoft.Web.WebView2.Wpf.dll"))  # 本記事時点 microsoft.web.webview2.1.0.1222-prerelease
[void][reflection.assembly]::LoadFile((Join-Path $PSScriptRoot "lib\Microsoft.Web.WebView2.Core.dll")) # 本記事時点 microsoft.web.webview2.1.0.1222-prerelease

<# XAML にて Window 構築 #>
[xml]$xaml  = (Get-Content (Join-Path $PSScriptRoot .\ui02.xaml))
$nodeReader = (New-Object System.XML.XmlNodeReader $xaml)
$window     = [Windows.Markup.XamlReader]::Load($nodeReader)

<# WebView2 初期設定 #>
$webview = $window.findName("webView")
$webview.CreationProperties = New-Object 'Microsoft.Web.WebView2.Wpf.CoreWebView2CreationProperties'
$webview.CreationProperties.UserDataFolder = (Join-Path $PSScriptRoot "data")
$webview.Source = ([uri]("file:///" + $PSScriptRoot + "/page02.html"))

<# Contols #>
$alertButton        = $window.findName("alertButton")
$changeHeaderButton = $window.findName("changeHeaderButton")
$pageTitle          = $window.findName("pageTitle")

<# for Events ScriptBlock #>
$clickAlertButton = {
    $webview.ExecuteScriptAsync("alert('Hello, World!');")
}

$clickChangeHeaderButton = {
    $webview.CoreWebView2.PostWebMessageAsString('that is the question.');
    # $webview.CoreWebView2.PostWebMessageAsJson((@{a=10} | ConvertTo-Json))  # JSON データ用サンプル行
}

$webview_MessageReceived = {
    param($webview, $message)
    $json = ($message.WebMessageAsJson | ConvertFrom-Json)
    $pageTitle.Text = $json.PageTitle
}

<# add Event Listeners #>
$alertButton.add_Click($clickAlertButton)
$changeHeaderButton.add_Click($clickChangeHeaderButton)
$webview.add_WebMessageReceived($webview_MessageReceived)

<# Window の表示 #>
[void]$window.ShowDialog()
$window.Close()
(2) ui02.xaml
<Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:wv2="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf"
        Title="MainWindow" Height="260" Width="500"
    >
    <DockPanel x:Name="RootDock">
        <DockPanel DockPanel.Dock="Top"  LastChildFill="False">
            <Button x:Name="alertButton"        Content="alert(in WebView2)" Padding="3" Margin="2" />
            <Button x:Name="changeHeaderButton" Content="Change Header"      Padding="3" Margin="2" />
        </DockPanel>
        <DockPanel DockPanel.Dock="Top">
            <TextBox Name="pageTitle" Text="DefaultText" IsReadOnly="True" />
        </DockPanel>
        <wv2:WebView2 Name="webView" />
    </DockPanel>
</Window>
(3) page02.html
<!DOCTYPE html>
<html>
  <head>
    <title>Hogeタイトル</title>
    <script>
        window.addEventListener('DOMContentLoaded', () => {
            window.chrome.webview.addEventListener('message', function(event) {
                document.querySelector('h1').innerText = event.data;
            });
            document.querySelector('#sendToPS').addEventListener('click', () => {
                window.chrome.webview.postMessage({PageTitle: document.querySelector('#sendText').value});
            });
        })
    </script>
  </head>
  <body>
    <h1 id="forTitleChange">To be or not to be,</h1>
    <hr />
    <input id="sendText" type="text" value="sample Text" />
    <button id="sendToPS">テキストの内容をPowerShellに送信</button>
  </body>
</html>

◇ 実行結果

・(A) スクリプトの実行

無事WebView2内でメッセージボックスを起動することに成功した。
image.png

・(B) データの送受信

WebView2 から PowerShell:

"テキストの内容をPowerShellに送信" ボタンを押すことで window.chrome.webview.postMessage が実行され、WebView2外部のTextBoxに送信した文字列を代入することに成功した。
image.png

PowerShell から WebView2:

"Change Header" ボタンを押すことで $webview.CoreWebView2.PostWebMessageAsString が実行され、WebView2内部へのデータ送信に成功し、H1要素の値の変更に成功した。
image.png

■結論と後書き

外部との通信やファイル操作等は PowerShell で行い、アプリケーションとしてのデザインは XAML で行い、取得済みデータのユーザ操作は WebView2 上で実装すれば良い感じに棲み分け出来そうな上、VBS/JScript の中で全てを記述するより簡潔に影響範囲を分離しながら書ける可能性も感じた。

PowerShell + WebView2 に関する日本語の情報が見当たらないため本記事が与することを祈る。

参考

記事内で参照しているもの、参照していないが参考とさせていただいたもの。

  1. NuGet Gallery | Microsoft.Web.WebView2 1.0.1222-prerelease
    https://www.nuget.org/packages/Microsoft.Web.WebView2/1.0.1222-prerelease
  2. 次期HTAとしてのPowerShell+WebView2の利用 - Qiita
    https://qiita.com/tkmry/items/dede9cadc0d2a2df6c1a)
  3. 阿部寛のホームページ
    http://abehiroshi.la.coocan.jp/
  4. WPF アプリでの WebView2 の使用を開始する - Microsoft Edge Development | Microsoft Docs
    https://docs.microsoft.com/ja-jp/microsoft-edge/webview2/get-started/wpf
  5. WebView2 in PowerShell Winform GUI - Stack Overflow
    https://stackoverflow.com/questions/66106927/webview2-in-powershell-winform-gui
  6. PowerShellでWPFしたい Part2 - XAML編 - tech.guitarrapc.cóm
    https://tech.guitarrapc.com/entry/2013/03/14/190337
  7. WPF WebView2 を試す | のい太ろぐ
    https://noitalog.tokyo/wpf-webview2/
  8. WPFでWindows.FormsのWebBrowserを使う - 備忘録
    https://kagasu.hatenablog.com/entry/20150817/1439797327
  9. PowerShellでWPFを使う - Qiita
    https://qiita.com/potimarimo/items/1eca0516bd8c690872dc
  10. WPFでDockPanelを使用する - プログラムを書こう!
    https://www.paveway.info/entry/2020/05/01/wpf_dockpanel

更新履歴

  • 2022/05/11:
    • タグに PS-Edge を追加
14
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
10