從 Web 端程式碼呼叫原生端程式碼

WebView2 可讓應用程式藉由讓物件傳遞至 Web,以橋接應用程式的 Web 和原生端之間的間距。 您可以透過原生程式碼中定義的中繼原生主機物件,將選取的原生端 API 公開至網頁 JavaScript。 原生端 API 會使用 WebView2 AddHostObjectToScript API 投影到 JavaScript 中。

本文主要涵蓋 Win32/C++,也涵蓋框架內 .NET/C# 的某些層面。 針對 WinRT,請 參閱從 Web 端程式碼呼叫原生端 WinRT 程式碼

為什麼要使用 AddHostObjectToScript

  • 開發 WebView2 應用程式時,您可能會遇到原生物件,而您發現其方法或屬性很有用。 您可能想要從 Web 端程式碼觸發這些原生物件方法,因為使用者在應用程式的 Web 端互動。 此外,您可能不想在 Web 端程式碼中重新實作原生物件的方法。 API AddHostObjectToScript 可讓 Web 端程式碼重複使用原生端程式碼。

  • 例如,可能有原生網路攝影機 API,需要在 Web 端重新撰寫大量程式碼。 相較于在應用程式的 Web 端重新撰寫物件方法的程式碼,能夠呼叫原生物件的方法會更快且更有效率。 在此情況下,原生端程式碼可以將 物件傳遞至應用程式的 Web 端 JavaScript 程式碼,讓您的 JavaScript 程式碼可以重複使用原生 API 的方法。

可能受益于在腳本中使用主機物件的案例:

  • 有鍵盤 API,而且您想要從 Web 端呼叫 keyboardObject.showKeyboard 函式。

  • 透過 JavaScript 存取檔案系統,而不只是網頁沙箱。 JavaScript 是沙箱化的,可防止它直接存取檔案系統。 藉由使用 AddHostObjectToScript 建立向 JavaScript 公開的原生物件,您可以使用主機物件來操作檔案系統上的檔案,而不只是在網頁沙箱中。

本文使用 Win32 範例應用程式 來示範 的 AddHostObjectToScript 一些實際應用程式。

步驟 1:安裝 Visual Studio、安裝 git、複製 WebView2Samples 存放庫,然後開啟解決方案

  1. 下載並安裝 Microsoft Visual Studio 2019 (16.11.10 版) 或更新版本,以及 Win32 範例應用程式中所述的其他必要條件。 Win32 範例應用程式是使用 Visual Studio 2019 所建立,因此若要遵循本文中的範例步驟,建議您從 Visual Studio 2019 開始,而不是 Visual Studio 2022。

  2. 複製 WebView2Samples 存放庫。 存放庫包含 Win32 特定的 WebView2 範例應用程式。 如需指示,請參閱新視窗或索引標籤中的 Win32 範例應用程式

  3. 開啟 Microsoft Visual Studio。 建議您一開始使用 Visual Studio 2019 開啟 Win32 範例。

  4. 在複製 WebView2Samples 存放庫的本機複本中,開啟 SampleApps>WebView2Samples>WebView2Samples.slnWebView2Samples.slnWebView2APISample包含專案,也就是 Win32 範例應用程式。 讓範例應用程式解決方案保持開啟,以遵循本文的其餘部分。

步驟 2:使用 IDL 定義主機物件的 COM 介面

在檔案中 .idl 定義主機物件的 COM 介面,例如 HostObjectSample.idl,以描述主物件上的方法和屬性。

首先,使用介面定義語言 (IDL) 來定義主機物件的 COM 介面。 檔案中的 idl 這個主機物件定義描述公開的 (或「包裝」) 原生端屬性和方法。 IDL () .idl 檔案 會定義 介面,但不會實作它。

  1. 在 Visual Studio方案總管中,展開[WebView2API][范>例來源檔案],然後按兩下 HostObjectSample.idl 以開啟它。

    下列程式碼會 IHostObjectSample 定義 介面,此介面繼承 IUnknown 為 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. 在上面,請注意 DateProperty 使用 型別的 DATE 。 本文將著重于此日期示範屬性。

步驟 3:定義主機物件 coclass

接下來,此範例會 HostObjectSample 定義要包含 IHostObjectSampleIDispatch 的 coclass。

  1. HostObjectSample.idl 中, HostObjectSample 檢查coclass (元件物件類別) ,其中包含 IHostObjectSampleIDispatch 介面:

        [uuid(637abc45-11f7-4dde-84b4-317d62a638d3)]
        coclass HostObjectSample
        {
            [default] interface IHostObjectSample;
            interface IDispatch;
        };
    }
    
  2. coclass HostObjectSample 包含 interface IDispatch ,這是主物件使用 所需的 。 AddHostObjectToScript

步驟 4:實作 C++ 物件的成員

在 Win32 範例應用程式程式碼中, HostObjectSampleImpl.cpp 會採用在 COM IDL 檔案中建立的基本架構,並實作 C++ 物件的每個成員。 這個 C++ (.cpp) 檔案會實作定義 介面 (,也會實 IDispatch 作) 。

實作物件介面中定義的所有函式,如我們在 IDL 檔案中所述。 請務必實作 所需的函式 IDispatch 。 如果未定義這些函式,編譯器會擲回錯誤。

接下來,我們會檢查 IDL 中定義的兩個特定屬性,以顯示 IDL 與檔案的 .cpp 關聯性。

  1. 在 Visual Studio方案總管中,展開[WebView2APISample>來源檔案],然後按兩下[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

主機物件必須實作 IDispatch ,讓 WebView2 可以將原生主機物件投影到應用程式的 Web 端程式碼。

IDispatch 可讓您動態叫用方法和屬性。 一般而言,呼叫物件需要靜態調用,但您可以使用 JavaScript 動態建立物件呼叫。 在 Win32 範例應用程式程式碼中, HostObjectSampleImpl.cpp 會實作 IDispatch ,這表示實作下列方法:

  • GetIDsOfNames
  • GetTypeInfo
  • GetTypeInfoCount
  • Invoke

IDispatch 作 ,如 型別程式庫和物件描述語言中所述。 如需繼承和方法的詳細資訊 IDispatch ,請 參閱 iDispatch 介面 (oaidl.h)

如果您想要新增至 JavaScript 的物件尚未實 IDispatch 作 ,您必須為想要公開的物件撰寫 IDispatch 類別包裝函式。

可能有程式庫可以自動執行這項操作。 若要深入瞭解為您想要公開的物件撰寫 IDispatch 類別包裝函式所需的步驟,請參閱 自動化

  1. 接下來,儲存您在專案中所做的任何變更。

  2. 在方案總管中,以滑鼠右鍵按一下WebView2APISample (這是 Win32 範例應用程式) ,然後選取 [建置]。 這會建立 COM 型別程式庫 .tlb 檔案。 您需要從 C++ 原始程式碼參考 .tlb 檔案。 如需詳細資訊,請參閱COM、DCOM 和類型程式庫中的類型程式庫

步驟 6:呼叫 AddHostObjectToScript 將主機物件傳遞至網頁端程式碼

到目前為止,我們已建置介面並實作原生主機物件。 現在我們已準備好使用 AddHostObjectToScript 將原生主機物件傳遞至應用程式的 Web 端 JavaScript 程式碼。 ScenarioAddHostObject.cpp中的 Win32 範例應用程式呼叫 AddHostObjectToScript ,如下所示。

  1. 在 Visual Studio方案總管中,開啟WebView2APISample>來源檔案>案例AddHostObject.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 句示範如何具現化 HostObjectSample IDL 檔案中定義的 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 類型,然後將物件轉換成 VARIANTVARIANT 類型可讓您使用整數和陣列等資料結構,以及更複雜的類型,例如 IDispatch

    如需支援資料類型的完整清單,請 參閱 (oaidl.h) 的 VARIANT 結構 。 並非聯集 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. 在上述事件處理常式中 NavigationStartingVARIANT 會使用名稱 sample 傳遞至 AddHostObjectToScript

步驟 7:從網頁 JavaScript 存取主機物件成員

在上述步驟中,範例應用程式的原生端程式碼建立了實作 的 IDispatch 主機物件。 此原生程式碼也會呼叫 WebView2 API ICoreWebView2::AddHostObjectToScriptICoreWebView2Frame::AddHostObjectToScriptWithOrigins ,並將主機物件傳遞至應用程式的 Web 端程式碼。

現在,應用程式的 Web 端程式碼可以存取主機物件所公開的原生端 API。 網頁 .html 元素或參考 .js JavaScript 檔案中的 JavaScript 語 script 句可以存取匯出的原生端 API。

Win32 範例應用程式的 Web 端程式碼現在可以存取原生主機物件的屬性和方法,以存取原生 API。 我們將在應用程式的 [案例主機物件] 網頁中> ,使用範例應用程式的網頁控制項來示範這一點。

  1. 在 Microsoft Visual Studio 中,選取[檔案>全部儲存 (Ctrl+Shift+S) 以儲存專案。

  2. 在 方案總管 中,開啟WebView2APISample>ScenarioAddHostObject.html。 我們將比較此檔案與執行中 Win32 範例應用程式中的對應網頁。

  3. 在方案總管中,以滑鼠右鍵按一下WebView2APISample (這是 Win32 範例應用程式) ,然後選取 [建置]

  4. F5 以偵錯模式執行專案。

  5. 在具有 WebView2APISample 標題列 (的 Win32 範例應用程式) 中,按一下 [案例 ] 功能表, 然後選取 [ 主機物件 ] 功能表項目。 AddHostObjectToScript 範例網頁隨即出現,由 定義: ScenarioAddHostObject.html

    主機物件示範頁面頂端

  6. 網頁建議使用 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 端程式碼存取主機物件的屬性和方法。

  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. 下列程式碼是一個示範 Date 屬性,會包裝在 iframe 元素內建立的專案中 script

    // 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.datePropertydateProperty 原生主機物件的 。

    .idl在先前所述的HostObjectSample.idl檔案中,date 屬性會定義為主機物件的一部分。

使用範例應用程式

您可以試驗如何使用和修改 Win32 範例應用程式。 然後在您自己的應用程式中遵循相同的模式:

  1. 在應用程式的原生端程式碼中建立主機物件。
  2. 將主機物件傳遞至應用程式的 Web 端程式碼。
  3. 從應用程式的 Web 端程式碼使用主機物件。

若要瞭解主機物件生態系統中有哪些其他 API,請參閱 WebView2 Win32 C++ ICoreWebView2

API 參考概觀

請參閱WebView2 功能和 API 概觀中的主機/Web 物件共用

另請參閱

GitHub: