共用方式為


從 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.sln WebView2APISample包含專案,也就是 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 方案總管中,展開 [WebView2API][範>例來源檔案],然後按兩下 [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 檔案。 您必須從原始 .tlb 程式碼C++參考檔案。 如需詳細資訊,請參閱 COM、DCOM 和類型連結庫中的類型連結庫

步驟 6:呼叫 AddHostObjectToScript 將主機對象傳遞至網頁端程式代碼

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

  1. 在 Visual Studio 方案總管中,開啟 WebView2APISample>來源檔案>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 句示範如何具現化 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: