從 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 存放庫,然後開啟解決方案
下載並安裝 Microsoft Visual Studio 2019 (16.11.10 版) 或更新版本,以及 Win32 範例應用程式中所述的其他必要條件。 Win32 範例應用程式是使用 Visual Studio 2019 所建立,因此若要遵循本文中的範例步驟,建議您從 Visual Studio 2019 開始,而不是 Visual Studio 2022。
複製 WebView2Samples 存放庫。 存放庫包含 Win32 特定的 WebView2 範例應用程式。 如需指示,請參閱新視窗或索引標籤中的 Win32 範例應用程式。
開Microsoft Visual Studio。 建議您一開始使用 Visual Studio 2019 開啟 Win32 範例。
在複製
WebView2Samples
存放庫的本地副本中,開SampleApps
>>WebView2Samples
啟 WebView2Samples.sln。WebView2Samples.sln
WebView2APISample
包含專案,也就是 Win32 範例應用程式。 讓範例應用程式解決方案保持開啟,以遵循本文的其餘部分。
步驟 2:使用 IDL 定義主機物件的 COM 介面
在檔案中 .idl
定義主機物件的 COM 介面,例如 HostObjectSample.idl,以描述主物件上的方法和屬性。
首先,使用介面定義語言 (IDL) 來定義主機物件的 COM 介面。 檔案中的 idl
這個主機物件定義會描述公開的 (或「包裝」) 原生端屬性和方法。 IDL () .idl
檔案 會定義 介面,但不會實作它。
在 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(); };
在上面,請注意
DateProperty
使用 型別的DATE
。 本文將著重於此日期示範屬性。
步驟 3:定義主機物件 coclass
接下來,此範例會 HostObjectSample
定義要包含 IHostObjectSample
和 IDispatch
的coclass。
在
HostObjectSample.idl
中,HostObjectSample
檢查 coclass (元件物件類別) ,其中包含IHostObjectSample
和IDispatch
介面:[uuid(637abc45-11f7-4dde-84b4-317d62a638d3)] coclass HostObjectSample { [default] interface IHostObjectSample; interface IDispatch; }; }
coclass
HostObjectSample
包含interface IDispatch
,這是主物件使用 所需的 。AddHostObjectToScript
步驟 4:實作C++對象的成員
在 Win32 範例應用程式程式代碼 中,HostObjectSampleImpl.cpp 會採用在 COM IDL 檔案中建立的基本架構,並實作C++物件的每個成員。 這個C++ (.cpp
) 檔案 會實 作定義的介面 (,也會實 IDispatch
作) 。
實作物件介面中定義的所有函式,如我們在IDL檔案中所述。 請務必實作 所需的函式 IDispatch
。 如果未定義這些函式,編譯程式會擲回錯誤。
接下來,我們會檢查IDL中定義的兩個特定屬性,以顯示IDL與檔案的 .cpp
關聯性。
在 Visual Studio 方案總管中,展開 [WebView2API][範>例來源檔案],然後按兩下 [HostObjectSampleImpl.cpp ] 加以開啟。
檢查 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();
在 下 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; }
檢查
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
類別包裝函式所需的步驟,請參閱 自動化。
接下來,儲存您在專案中所做的任何變更。
在 [方案總管] 中,以滑鼠右鍵按兩下 WebView2APISample (這是 Win32 範例應用程式) ,然後選取 [ 建置]。 這會建立 COM 類型庫
.tlb
檔案。 您必須從原始.tlb
程式碼C++參考檔案。 如需詳細資訊,請參閱 COM、DCOM 和類型連結庫中的類型連結庫。
步驟 6:呼叫 AddHostObjectToScript 將主機對象傳遞至網頁端程式代碼
到目前為止,我們已建置介面並實作原生主機物件。 現在我們已準備好使用 AddHostObjectToScript
將原生主機對象傳遞至應用程式的 Web 端 JavaScript 程式代碼。 Win32 範例應用程式會在 ScenarioAddHostObject.cpp 中呼叫AddHostObjectToScript
,如下所示。
在 Visual Studio 方案總管中,開啟 WebView2APISample>來源檔案>ScenarioAddHostObject.cpp。
移至類別實
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); });
語
Make
句示範如何具現化HostObjectSample
IDL檔案中定義的 COM物件。 這是我們稍後在呼叫AddHostObjectToScript
時將使用的物件。 語Make
句會為我們取得在 HostObjectSampleImpl.cpp 中實作之介面的 指標。接下來,我們會新增事件處理程式來接
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());
在
NavigationStarting
事件處理程式中query_to
,下面的行 () 將新建立的 COM 物件轉換成IDispatch
類型,然後將物件轉換成VARIANT
。VARIANT
類型可讓您使用整數和數位等數據結構,以及更複雜的類型,例如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 端程式代碼。
在上方的下行中
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(); }
在上述事件處理程式中
NavigationStarting
,VARIANT
會使用名稱sample
傳遞到AddHostObjectToScript
。
步驟 7:從網頁 JavaScript 存取主機對象成員
在上述步驟中,範例應用程式的原生端程序代碼建立了實作的 IDispatch
主機物件。 此原生程式代碼也會呼叫 WebView2 API ICoreWebView2::AddHostObjectToScript
或 ICoreWebView2Frame::AddHostObjectToScriptWithOrigins
,並將主機對象傳遞至應用程式的 Web 端程式代碼。
現在,應用程式的 Web 端程式代碼可以存取主機物件所公開的原生端 API。 網頁.html
元素或參考 .js
JavaScript 檔案中的 JavaScript 語script
句可以存取導出的原生端 API。
Win32 範例應用程式的 Web 端程式代碼現在可以存取原生主機對象的屬性和方法,以存取原生 API。 我們將在應用程式的 [案例主機物件] 網頁中>,使用範例應用程式的網頁控件來示範這一點。
在 Microsoft Visual Studio 中,選取 [ 檔案>全部 儲存 (Ctrl+Shift+S) 以儲存專案。
在 [方案總管] 中,開啟 WebView2APISample>ScenarioAddHostObject.html。 我們將比較此檔案與執行中 Win32 範例應用程式中的對應網頁。
在 [方案總管] 中,以滑鼠右鍵按兩下 WebView2APISample (這是 Win32 範例應用程式) ,然後選取 [ 建置]。
按 F5 以偵錯模式執行專案。
在具有 WebView2APISample 標題列 (的 Win32 範例應用程式) 中,單擊 [案例 ] 功能表, 然後選取 [ 主機物件 ] 功能表項。 AddHostObjectToScript 範例網頁隨即出現,由 定義:
ScenarioAddHostObject.html
網頁建議使用DevTools的 控制台 工具,在對象上
chrome.webview.hostObjects.sample
執行JavaScript語句。 如果您想要從範例應用程式開啟 DevTools,請以滑鼠右鍵按鍵按鍵,然後選取 [ 檢查]。 然後選取 [ 控制台] 索引 標籤。如需詳細資訊,請參閱 主控台概觀。若要開啟 DevTools,按 F12 可能無法在此內容中運作,而且可能會觸發例外狀況。 若是如此,請在 Visual Studio 中選取 [ 停止偵錯],然後按 F5 重新啟動偵錯。 在範例應用程式中,再次選 取 [案例>主機物件] 。 如需詳細資訊,請參閱在使用Visual Studio偵錯WebView2應用程式中使用 F12 以外的方法開啟DevTools。
[主機物件] 示範頁面底部會複製 中的示範對象成員
<iframe>
:在範例應用程式的轉譯示範頁面中,閱讀說明 [日期 ] 按鈕的標籤文字。
按兩下 [ 日期 ] 按鈕。 日期字串會顯示在按鈕下方,例如:
sample.dateProperty: Tue Nov 01 2022 12:45:25 GMT-0700 (Pacific Daylight Time)
按兩下示範網頁中的按鈕並輸入值來探索屬性和方法,以查看範例程式代碼的行為。 這些按鈕示範如何從應用程式的 Web 端程式代碼存取主機物件的屬性和方法。
若要深入瞭解 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>
您也可以在範例應用程式的轉譯示範頁面中讀取上述標籤文字,說明 日期 按鈕程序代碼。
下列程式代碼是一個示範
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; });
表示式
chrome.webview.hostObjects.sync.sample.dateProperty
是dateProperty
原生主機物件的 。.idl
在先前所述的 HostObjectSample.idl 檔案中,date 屬性會定義為主機物件的一部分。
使用範例應用程式
您可以試驗如何使用和修改 Win32 範例應用程式。 然後在您自己的應用程式中遵循相同的模式:
- 在應用程式的原生端程式代碼中建立主機物件。
- 將主機物件傳遞至應用程式的 Web 端程式代碼。
- 從應用程式的 Web 端程式代碼使用主機物件。
若要瞭解主機對象生態系統中有哪些其他 API,請參閱 WebView2 Win32 C++ ICoreWebView2。
API 參考概觀
請參閱 WebView2 功能和 API 概觀中的主機/Web 物件共用。
另請參閱
- WebView2 功能和 API 概觀中的 Web/原生 Interop。
- 在 WebView2 應用程式中使用框架
- 從 Web 端程式代碼呼叫原生端 WinRT 程式代碼
GitHub: