Web 側コードからネイティブ側コードを呼び出す

WebView2 を使用すると、オブジェクトを Web に渡すことにより、アプリケーションがアプリケーションの Web 側とネイティブ側の間のギャップを埋められます。 ネイティブ コードで定義されている中間ネイティブ ホスト オブジェクトを使用して、選択したネイティブ側 API を Web ページ JavaScript に公開します。 ネイティブ側 API は、WebView2 AddHostObjectToScript API を使用して JavaScript に投影されます。

この記事では主に Win32/C++ について説明し、フレーム内の .NET/C# のいくつかの側面についても説明します。 WinRT については、「 Web 側コードからネイティブ側の WinRT コードを呼び出す」を参照してください。

AddHostObjectToScriptを使用する理由

  • WebView2 アプリを開発するときに、メソッドまたはプロパティが役に立つネイティブ オブジェクトが見つかる場合があります。 アプリの Web 側でのユーザー操作の結果として、Web 側のコードからこれらのネイティブ オブジェクト メソッドをトリガーする必要がある場合があります。 さらに、Web 側コードでネイティブ オブジェクトのメソッドを再実装したくない場合もあります。 AddHostObjectToScript API を使用すると、Web 側コードによるネイティブ側コードの再利用が可能になります。

  • たとえば、Web 側で大量のコードを書き直す必要があるネイティブ Web カメラ API が存在する場合があります。 ネイティブ オブジェクトのメソッドを呼び出す機能を持つことは、アプリの Web 側でオブジェクトのメソッドを再コーディングするよりも迅速かつ効率的です。 この場合、ネイティブ側のコードは、JavaScript コードがネイティブ API のメソッドを再利用できるように、アプリの Web 側の JavaScript コードにオブジェクトを渡すことができます。

スクリプトでホスト オブジェクトを使用する利点があるシナリオ:

  • キーボード API があり、web 側から keyboardObject.showKeyboard 関数を呼び出す必要があります。

  • JavaScript を使用して、Web ページのサンドボックスだけでなく、ファイル システムにアクセスする。 JavaScript はサンドボックス化されているため、ファイル システムに直接アクセスできなくなります。 AddHostObjectToScriptを使用して JavaScript に公開されるネイティブ オブジェクトを作成すると、ホスト オブジェクトを使用して、Web ページ サンドボックスだけでなく、ファイル システム上のファイルを操作できます。

この記事では、 Win32 サンプル アプリ を使用して、 AddHostObjectToScriptのいくつかの実用的なアプリケーションを示します。

手順 1: Visual Studio をインストールし、git をインストールし、WebView2Samples リポジトリを複製して、ソリューションを開く

  1. Win32 サンプル アプリで説明されているように、Microsoft Visual Studio 2019 (バージョン 16.11.10) 以降とその他の前提条件をダウンロードしてインストールします。 Win32 サンプル アプリは Visual Studio 2019 を使用して作成されているため、この記事の例の手順に従うために、Visual Studio 2022 ではなく Visual Studio 2019 から開始することをお勧めします。

  2. WebView2Samples リポジトリを複製します。 リポジトリには、Win32 固有の WebView2 サンプル アプリが含まれています。 手順については、新しいウィンドウまたはタブで、「 Win32 サンプル アプリ」を参照してください。

  3. Microsoft Visual Studio を起動します。 最初に Visual Studio 2019 を使用して Win32 サンプルを開くお勧めします。

  4. 複製された WebView2Samples リポジトリのローカル コピーで、 WebView2Samples>SampleApps>WebView2Samples.slnを開きます。 WebView2Samples.sln には、Win32 サンプル アプリである WebView2APISample プロジェクトが含まれています。 サンプル アプリ ソリューションを開いたままにして、この記事の残りの部分に従ってください。

手順 2: IDL を使用してホスト オブジェクトの COM インターフェイスを定義する

HostObjectSample.idl などの.idl ファイルでホスト オブジェクトの COM インターフェイスを定義して、ホスト オブジェクトのメソッドとプロパティを記述します。

まず、インターフェイス定義言語 (IDL) を使用して、ホスト オブジェクトの COM インターフェイスを定義します。 idl ファイル内のこのホスト オブジェクト定義は、公開されている (または "ラップされた") ネイティブ側のプロパティとメソッドについて説明します。 IDL (.idl) ファイルはインターフェイスを 定義 しますが、実装していません。

  1. Visual Studio ソリューション エクスプローラーでWebView2APISample>Source Files を展開し、 HostObjectSample.idl ダブルクリックして開きます。

    次のコードでは、 インターフェイスを定義します。このインターフェイスは、COM の標準としてを継承します。 この IHostObjectSample 定義は、オブジェクトのメソッド、プロパティ、コールバック関数などを定義するためのテンプレートとして使用します。

    import "oaidl.idl";
    import "ocidl.idl";
    
    [uuid(0a7a4655-5660-47d0-8a37-98ae21399e57), version(0.1)]
    library HostObjectSampleLibrary
    {
        [uuid(3a14c9c0-bc3e-453f-a314-4ce4a0ec81d8), object, local]
        interface IHostObjectSample : IUnknown
        {
            // Demonstrates a basic method call with some parameters and a return value.
            HRESULT MethodWithParametersAndReturnValue([in] BSTR stringParameter, [in] INT integerParameter, [out, retval] BSTR* stringResult);
    
            // Demonstrate getting and setting a property.
            [propget] HRESULT Property([out, retval] BSTR* stringResult);
            [propput] HRESULT Property([in] BSTR stringValue);
    
            [propget] HRESULT IndexedProperty(INT index, [out, retval] BSTR * stringResult);
            [propput] HRESULT IndexedProperty(INT index, [in] BSTR stringValue);
    
            // Demonstrate native calling back into JavaScript.
            HRESULT CallCallbackAsynchronously([in] IDispatch* callbackParameter);
    
            // Demonstrates a property which uses Date types.
            [propget] HRESULT DateProperty([out, retval] DATE * dateResult);
            [propput] HRESULT DateProperty([in] DATE dateValue);
    
            // Creates a date object on the native side and sets the DateProperty to it.
            HRESULT CreateNativeDate();
    
        };
    
  2. 上記では、DATE型を使用するDatePropertyに注意してください。 この記事では、この日付デモ プロパティに焦点を当てます。

手順 3: ホスト オブジェクトのコクラスを定義する

次に、この例では、IHostObjectSampleIDispatchを含めるHostObjectSampleコクラスを定義します。

  1. HostObjectSample.idlで、IHostObjectSampleインターフェイスとIDispatch インターフェイスを含む HostObjectSamplecoclass (コンポーネント オブジェクト クラス) を調べます。

        [uuid(637abc45-11f7-4dde-84b4-317d62a638d3)]
        coclass HostObjectSample
        {
            [default] interface IHostObjectSample;
            interface IDispatch;
        };
    }
    
  2. HostObjectSampleコクラスには、ホスト オブジェクトがAddHostObjectToScriptで動作するために必要なinterface IDispatchが含まれています。

手順 4: C++ オブジェクトのメンバーを実装する

Win32 サンプル アプリ コードでは、 HostObjectSampleImpl.cpp COM IDL ファイルで作成されたスケルトンを受け取り、C++ オブジェクトの各メンバーを実装します。 この C++ (.cpp) ファイルは、定義されたインターフェイスを 実装 します (また、 IDispatchも実装します)。

IDL ファイルで説明したように、オブジェクトのインターフェイスで定義されているすべての関数を実装します。 必ず、 IDispatchで必要な関数を実装してください。 これらの関数が定義されていない場合、コンパイラはエラーをスローします。

次に、IDL で定義された 2 つの特定のプロパティを調べて、IDL が .cpp ファイルにどのように関連しているかを示します。

  1. Visual Studio ソリューション エクスプローラーで、[ WebView2APISample>Source Files] を展開し、 HostObjectSampleImpl.cpp ダブルクリックして開きます。

  2. HostObjectSample.idl のプロパティ宣言を調べます。

    // Demonstrate getting and setting a property.
    [propget] HRESULT Property([out, retval] BSTR* stringResult);
    [propput] HRESULT Property([in] BSTR stringValue);
    ...
    // Demonstrate a property which uses Date types
    [propget] HRESULT DateProperty([out, retval] DATE * dateResult);
    [propput] HRESULT DateProperty([in] DATE dateValue);
    
    // Creates a date object on the native side and sets the DateProperty to it.
    HRESULT CreateNativeDate();
    
  3. HostObjectSampleImpl.cppでのオブジェクトのプロパティの実装を調べます。

    STDMETHODIMP HostObjectSample::get_Property(BSTR* stringResult)
    {
        *stringResult = SysAllocString(m_propertyValue.c_str());
        return S_OK;
    }
    
    STDMETHODIMP HostObjectSample::put_Property(BSTR stringValue)
    {
        m_propertyValue = stringValue;
        return S_OK;
    }
    ...
    
    STDMETHODIMP HostObjectSample::get_DateProperty(DATE* dateResult)
    {
        *dateResult = m_date;
        return S_OK;
    }
    
    STDMETHODIMP HostObjectSample::put_DateProperty(DATE dateValue)
    {
        m_date = dateValue;
        SYSTEMTIME systemTime;
        if (VariantTimeToSystemTime(dateValue, &systemTime))
    ...
    }
    
    STDMETHODIMP HostObjectSample::CreateNativeDate()
    {
        SYSTEMTIME systemTime;
        GetSystemTime(&systemTime);
        DATE date;
        if (SystemTimeToVariantTime(&systemTime, &date))
        {
            return put_DateProperty(date);
        }
        return E_UNEXPECTED;
    }
    
  4. この記事全体を通じてトレースする DatePropertyを調べます。

手順 5: IDispatch を実装する

WebView2 がネイティブ ホスト オブジェクトをアプリの Web 側コードに投影できるように、ホスト オブジェクトは IDispatch を実装する必要があります。

IDispatch では、メソッドとプロパティを動的に呼び出すことができます。 通常、オブジェクトを呼び出すには静的呼び出しが必要ですが、JavaScript を使用してオブジェクト呼び出しを動的に作成できます。 Win32 サンプル アプリ コードでは、 HostObjectSampleImpl.cppIDispatchを実装します。つまり、次のメソッドを実装します。

  • GetIDsOfNames
  • GetTypeInfo
  • GetTypeInfoCount
  • Invoke

「タイプ ライブラリとオブジェクト記述言語」の説明に従って、IDispatchを実装します。 IDispatchの継承とメソッドの詳細については、「IDispatch インターフェイス (oaidl.h)」を参照してください。

JavaScript に追加するオブジェクトに IDispatchがまだ実装されていない場合は、公開するオブジェクトの IDispatch クラス ラッパーを記述する必要があります。

これを自動的に実行するライブラリが存在する可能性があります。 公開するオブジェクトの IDispatch クラス ラッパーを記述するために必要な手順の詳細については、「 Automation」を参照してください。

  1. 次に、プロジェクトで行った変更を保存します。

  2. ソリューション エクスプローラーで、 WebView2APISample (Win32 サンプル アプリ) を右クリックし、[ ビルド] を選択します。 これにより、ファイル .tlb COM タイプ ライブラリが作成されます。 C++ ソース コードから .tlb ファイルを参照する必要があります。 詳細については、「COM、DCOM、およびタイプライブラリのタイプ ライブラリ」を参照してください。

手順 6: AddHostObjectToScript を呼び出して、ホスト オブジェクトを Web 側のコードに渡す

ここまでは、インターフェイスを構築し、ネイティブ ホスト オブジェクトを実装しました。 これで、 AddHostObjectToScript を使用して、ネイティブ ホスト オブジェクトをアプリの Web 側の JavaScript コードに渡す準備ができました。 Win32 サンプル アプリは、次に示すように、ScenarioAddHostObject.cppでAddHostObjectToScriptを呼び出します。

  1. Visual Studio ソリューション エクスプローラーでWebView2APISample>Source Files>ScenarioAddHostObject.cpp を開きます。

  2. ScenarioAddHostObject クラスの実装に移動します。 このクラスは HTML を表示し、ナビゲーションを処理します。

    ScenarioAddHostObject::ScenarioAddHostObject(AppWindow* appWindow)
        : m_appWindow(appWindow), m_webView(appWindow->GetWebView())
    {
        std::wstring sampleUri = m_appWindow->GetLocalUri(L"ScenarioAddHostObject.html");
    
        m_hostObject = Microsoft::WRL::Make<HostObjectSample>(
            [appWindow = m_appWindow](std::function<void(void)> callback)
        {
            appWindow->RunAsync(callback);
        });
    
  3. Make ステートメントは、IDL ファイルで定義されたHostObjectSample COM オブジェクトをインスタンス化する方法を示しています。 これは、後で AddHostObjectToScript を呼び出すときに使用するオブジェクトです。 Make ステートメントは、HostObjectSampleImpl.cppで実装されているインターフェイスへのポインターを取得します。

  4. 次に、 NavigationStarting イベントをリッスンするイベント ハンドラーを追加します。

        CHECK_FAILURE(m_webView->add_NavigationStarting(
            Microsoft::WRL::Callback<ICoreWebView2NavigationStartingEventHandler>(
                [this, sampleUri](ICoreWebView2* sender, ICoreWebView2NavigationStartingEventArgs* args) -> HRESULT
        {
            wil::unique_cotaskmem_string navigationTargetUri;
            CHECK_FAILURE(args->get_Uri(&navigationTargetUri));
            std::wstring uriTarget(navigationTargetUri.get());
    
  5. NavigationStarting イベント ハンドラーでは、query_to行 (下) によって、新しく作成された COM オブジェクトがIDispatch型にキャストされ、オブジェクトがVARIANTに変換されます。 VARIANT 型を使用すると、整数や配列などのデータ構造だけでなく、 IDispatchなどのより複雑な型を使用できます。

    サポートされているデータ型の完全な一覧については、「 VARIANT 構造体 (oaidl.h)」を参照してください。 VARIANT共用体のすべての型がAddHostObjectToScriptでサポートされているわけではありません。 詳細については、「 ICoreWebView2::AddHostObjectToScript メソッド」を参照してください。

            if (AreFileUrisEqual(sampleUri, uriTarget))
            {
                VARIANT remoteObjectAsVariant = {};
                m_hostObject.query_to<IDispatch>(&remoteObjectAsVariant.pdispVal);
                remoteObjectAsVariant.vt = VT_DISPATCH;
    

    C++ コードフレンドリなオブジェクトのバリアントが作成されたので、サンプル アプリのネイティブ側コードで、ホスト オブジェクトをアプリの Web 側コードに渡す準備ができました。

  6. 上の一番下の行では、 NavigationStarting イベント ハンドラーによってリモート オブジェクトのバリアント型が IDispatchとして設定されます。

                // We can call AddHostObjectToScript multiple times in a row without
                // calling RemoveHostObject first. This will replace the previous object
                // with the new object. In our case this is the same object and everything
                // is fine.
                CHECK_FAILURE(
                    m_webView->AddHostObjectToScript(L"sample", &remoteObjectAsVariant));
                remoteObjectAsVariant.pdispVal->Release();
            }
    
  7. 上記のNavigationStarting イベント ハンドラーでは、sampleという名前を使用して、VARIANTAddHostObjectToScriptに渡されます。

手順 7: Web ページ JavaScript からホスト オブジェクト メンバーにアクセスする

上記の手順では、サンプル アプリのネイティブ側コードによって、 IDispatchを実装するホスト オブジェクトが作成されました。 このネイティブ コードは、WebView2 API ICoreWebView2::AddHostObjectToScript または ICoreWebView2Frame::AddHostObjectToScriptWithOrigins も呼び出し、ホスト オブジェクトをアプリの Web 側コードに渡します。

これで、アプリの Web 側コードが、ホスト オブジェクトによって公開されるネイティブ側 API にアクセスできるようになりました。 .html Web ページscript要素内の JavaScript ステートメント、または参照.js JavaScript ファイル内の JavaScript ステートメントは、エクスポートされたネイティブ側 API にアクセスできます。

Win32 サンプル アプリの Web 側コードは、ネイティブ ホスト オブジェクトのプロパティとメソッドにアクセスして、ネイティブ API にアクセスできるようになりました。 サンプル アプリの Web ページ コントロールを使用して、アプリの Scenario>Host Objects Web ページでこれを示します。

  1. Microsoft Visual Studio で、[ファイル] >[すべて保存] (Ctrl + Shift + S) を選択してプロジェクトを保存します。

  2. ソリューション エクスプローラーで、 WebView2APISample>ScenarioAddHostObject.htmlを開きます。 このファイルを、実行中の Win32 サンプル アプリの対応する Web ページと比較します。

  3. ソリューション エクスプローラーで、 WebView2APISample (Win32 サンプル アプリ) を右クリックし、[ ビルド] を選択します。

  4. F5 キーを押して、プロジェクトをデバッグ モードで実行します。

  5. Win32 サンプル アプリ ( WebView2APISample のタイトル バーがある) で、[ シナリオ ] メニューをクリックし、[ ホスト オブジェクト ] メニュー項目を選択します。 ScenarioAddHostObject.htmlによって定義された AddHostObjectToScript サンプル Web ページが表示されます。

    ホスト オブジェクトのデモ ページの上部

  6. Web ページでは、DevTools の コンソール ツールを使用して、 chrome.webview.hostObjects.sample オブジェクトで JavaScript ステートメントを実行することを提案しています。 サンプル アプリから DevTools を開く場合は、ページを右クリックし、[ 検査] を選択します。 次に、[ コンソール ] タブを選択します。詳細については、「コンソールの 概要」を参照してください。

    DevTools を開くには、 F12 キーを押してもこのコンテキストでは機能せず、例外がトリガーされる可能性があります。 その場合は、Visual Studio で [ デバッグの停止] を選択し、 F5 キーを押してデバッグを再開します。 サンプル アプリで、もう一度 [ シナリオ>ホスト オブジェクト] を選択します。 詳細については、「Visual Studio で WebView2 アプリをデバッグする」で F12 以外の方法を使用して DevTools を開く方法に関するページを参照してください。

    [ホスト オブジェクト] デモ ページの下部には、 <iframe>内のデモ オブジェクト メンバーが複製されています。

    ホスト オブジェクトのデモ ページの下部

  7. サンプル アプリのレンダリングされたデモ ページで、[ 日付] ボタンを説明するラベル テキストを読み取ります。

  8. [ 日付 ] ボタンをクリックします。 日付文字列は、次のようなボタンの下に表示されます。

    sample.dateProperty: Tue Nov 01 2022 12:45:25 GMT-0700 (Pacific Daylight Time)
    
  9. デモ Web ページのボタンをクリックし、値を入力してプロパティとメソッドを調べて、サンプル コードの動作を確認します。 ボタンは、アプリの Web 側コードからホスト オブジェクトのプロパティとメソッドにアクセスする方法を示しています。

  10. JavaScript で何が起こっているのかを把握するには、ScenarioAddHostObject.htmlで次のコード 調べます。

    次のコードは、body 要素内の直接のデモ Date プロパティです。

    <h2>Date Objects</h2>
    <button id="setDateButton">Set Date to Now</button>
    <label for="setDateButton">Sets <code>chrome.webview.hostObjects.options.shouldSerializeDates = true</code> 
        and then runs <code>chrome.webview.hostObjects.sample.dateProperty = new Date()</code></label>
    <br />
    <button id="createRemoteDateButton">Set Remote Date</button>
    <label for="createRemoteDateButton">Calls <code>chrome.webview.hostObjects.sample.createNativeDate()</code> 
        to have the native object create and set the current time to the DateProperty</label>
    <code><pre><span id="dateOutput"></span></pre></code>
    
    <div id="div_iframe" style="display: none;">
        <h2>IFrame</h2>
    </div>
    

    また、サンプル アプリのレンダリングされたデモ ページで上記のラベル テキストを読み上げ、 日付 ボタン コードについて説明することもできます。

  11. 次のコードは、script 要素内に作成されたiframe要素にラップされるデモ Date プロパティです。

    // Date property 
    document.getElementById("setDateButton").addEventListener("click", () => { 
        chrome.webview.hostObjects.options.shouldSerializeDates = true; 
        chrome.webview.hostObjects.sync.sample.dateProperty = new Date(); 
        document.getElementById("dateOutput").textContent = 
            "sample.dateProperty: " + chrome.webview.hostObjects.sync.sample.dateProperty;
    }); 
    document.getElementById("createRemoteDateButton").addEventListener("click", () => { 
        chrome.webview.hostObjects.sync.sample.createNativeDate(); 
        document.getElementById("dateOutput").textContent = 
            "sample.dateProperty: " + chrome.webview.hostObjects.sync.sample.dateProperty; 
    });
    
  12. chrome.webview.hostObjects.sync.sample.dateProperty は、ネイティブ ホスト オブジェクトの dateProperty です。

    前に説明した .idl ファイル HostObjectSample.idl では、date プロパティはホスト オブジェクトの一部として定義されています。

サンプル アプリの使用

Win32 サンプル アプリの使用と変更を試すことができます。 次に、独自のアプリで同じパターンに従います。

  1. アプリのネイティブ側コードでホスト オブジェクトを作成します。
  2. ホスト オブジェクトをアプリの Web 側コードに渡します。
  3. アプリの Web 側コードからホスト オブジェクトを使用します。

ホスト オブジェクト エコシステムに含まれる他の API については、「 WebView2 Win32 C++ ICoreWebView2」を参照してください。

API リファレンスの概要

WebView2 の機能と API の概要」の「ホスト/Web オブジェクトの共有」を参照してください。

関連項目

GitHub: