Поделиться через


Вызов собственного кода из веб-кода

WebView2 позволяет приложениям преодолеть разрыв между веб-интерфейсом и собственной стороной приложения, позволяя передавать объект в Интернет. Вы предоставляете выбранные собственные API на веб-странице JavaScript через промежуточный собственный объект узла, определенный в машинном коде. Api на стороне машинного кода проецируются в JavaScript с помощью API WebView2 AddHostObjectToScript .

В этой статье рассматриваются главным образом Win32/C++, а также некоторые аспекты .NET/C# в кадрах. Сведения о WinRT см. в статье Вызов собственного кода WinRT из веб-кода.

Зачем использовать AddHostObjectToScript?

  • При разработке приложения WebView2 может возникнуть собственный объект, методы или свойства которого считаются полезными. Вы можете активировать эти собственные методы объектов из веб-кода в результате взаимодействия с пользователем на веб-стороне приложения. Кроме того, может не потребоваться повторно реализовывать методы собственных объектов в веб-коде. API AddHostObjectToScript позволяет повторно использовать собственный код с помощью веб-кода.

  • Например, может быть собственный API веб-камеры, который потребует повторной записи большого объема кода на веб-стороне. Возможность вызова методов собственного объекта быстрее и эффективнее, чем повторное кодирование методов объекта на веб-стороне приложения. В этом случае собственный код может передать объект в веб-код JavaScript приложения, чтобы код JavaScript можно было повторно использовать методы собственного API.

Сценарии, которые могут извлечь выгоду из использования объектов узла в скрипте:

  • Существует API клавиатуры, и вы хотите вызвать функцию 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 . Репозиторий содержит пример приложения WebView2 для Win32. Инструкции в новом окне или вкладке см. в разделе Пример приложения Win32.

  3. Откройте Microsoft Visual Studio. Рекомендуется сначала открыть пример Win32 с помощью Visual Studio 2019.

  4. В локальной копии клонированного WebView2Samples репозитория откройтеSampleApps>WebView2Samples>WebView2Samples.sln. WebView2Samples.sln включает проект WebView2APISample , который является примером приложения Win32. Не закрывайте пример решения для приложений, чтобы продолжить работу с остальной частью этой статьи.

Шаг 2. Определение COM-интерфейса объекта узла с помощью IDL

Определите COM-интерфейс объекта узла в .idl файле, например HostObjectSample.idl, чтобы описать методы и свойства объекта узла.

Сначала используйте язык определения интерфейса (IDL) для определения COM-интерфейса объекта узла. Это определение объекта узла в idl файле описывает предоставляемые (или "упакованные") собственные свойства и методы. IDL-файл (.idl) определяет интерфейс, но не реализует его.

  1. В Visual Studio Обозреватель решений разверните узел WebView2APISample>Source Files и дважды щелкните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 тип . В этой статье мы сосредоточимся на этом демонстрационном свойстве date.

Шаг 3. Определение совместного класса объекта узла

Далее в примере определяется HostObjectSample класс для включения IHostObjectSample и IDispatch.

  1. В HostObjectSample.idlизучите HostObjectSampleкласс ( класс объектов компонента), который включает IHostObjectSample интерфейсы и IDispatch :

        [uuid(637abc45-11f7-4dde-84b4-317d62a638d3)]
        coclass HostObjectSample
        {
            [default] interface IHostObjectSample;
            interface IDispatch;
        };
    }
    
  2. Класс HostObjectSample включает в себя interface IDispatch, который необходим для работы ведущего объекта с AddHostObjectToScript.

Шаг 4. Реализация членов объекта C++

В примере кода приложения Win32 HostObjectSampleImpl.cpp принимает скелет, созданный в ФАЙЛЕ COM IDL, и реализует каждый член объекта C++. Этот файл C++ (.cpp) реализует определенный интерфейс (и также реализует IDispatch).

Реализуйте все функции, определенные в интерфейсе объекта, как описано в IDL-файле. Обязательно реализуйте функции, необходимые для IDispatch. Компилятор выдает ошибку, если эти функции не определены.

Далее мы рассмотрим два конкретных свойства, которые были определены в IDL, чтобы показать, как 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

Объект узла должен реализовать IDispatch , чтобы WebView2 могла проецировать собственный объект узла в веб-код приложения.

IDispatch позволяет динамически вызывать методы и свойства. Как правило, для вызова объектов требуются статические вызовы, но для динамического создания вызовов объектов можно использовать JavaScript. В примере кода приложения Win32 HostObjectSampleImpl.cpp реализует IDispatch, что означает реализацию следующих методов:

  • GetIDsOfNames
  • GetTypeInfo
  • GetTypeInfoCount
  • Invoke

Реализуйте IDispatch , как описано в разделах Библиотеки типов и язык описания объектов. Дополнительные сведения о IDispatch наследовании и методах см. в разделе Интерфейс IDispatch (oaidl.h).

Если объект, который требуется добавить в JavaScript, еще не реализует IDispatch, необходимо написать оболочку IDispatch класса для объекта, который требуется предоставить.

Для этого могут быть библиотеки, которые будут выполняться автоматически. Дополнительные сведения о шагах, необходимых для написания IDispatch оболочки класса для объекта, который требуется предоставить, см. в разделе Автоматизация.

  1. Затем сохраните все изменения, внесенные в проект.

  2. В Обозреватель решений щелкните правой кнопкой мыши webView2APISample (пример приложения Win32) и выберите команду Сборка. При этом создается файл библиотеки .tlb типов COM. Необходимо ссылаться на .tlb файл из исходного кода C++. Дополнительные сведения см. в статье Библиотека типов в com, DCOM и библиотеках типов.

Шаг 6. Вызов метода AddHostObjectToScript для передачи объекта узла в веб-код

На данный момент мы создали наш интерфейс и реализовали собственный объект узла. Теперь мы готовы использовать AddHostObjectToScript для передачи собственного объекта узла в код JavaScript веб-приложения. Пример приложения Win32 вызывает AddHostObjectToScript в ScenarioAddHostObject.cpp, как показано ниже.

  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 показывает, как создать экземпляр HostObjectSample COM-объекта, определенного в IDL-файле. Это объект, который мы будем использовать позже при вызове 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 событий строка (ниже) приводит только что созданный com-объект к типуIDispatch, query_to а затем преобразует объект в 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++, собственный код примера приложения готов к передаче ведущего объекта в веб-код приложения.

  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 событий передается AddHostObjectToScriptв , используя имя sample.

Шаг 7. Доступ к элементам объекта узла с веб-страницы JavaScript

На описанных выше шагах собственный код примера приложения создал объект узла, который реализует IDispatch. Этот машинный код также вызывает API ICoreWebView2::AddHostObjectToScript WebView2 или ICoreWebView2Frame::AddHostObjectToScriptWithOrigins передает объект узла в веб-код приложения.

Теперь веб-код приложения может обращаться к собственным API, которые предоставляются объектом узла. Инструкции JavaScript в .html элементе веб-страницы script или в файле JavaScript, на который ссылается .js ссылка, могут обращаться к экспортированным API на стороне собственного кода.

Веб-код примера приложения Win32 теперь может получить доступ к свойствам и методам собственного объекта узла для доступа к собственным API. Чтобы продемонстрировать это, мы будем использовать элементы управления веб-страницы примера приложения на веб-страницеобъектов размещениясценариев> приложения.

  1. В Microsoft Visual Studio выберите Файл>Сохранить все (CTRL+SHIFT+S), чтобы сохранить проект.

  2. В Обозреватель решений откройте WebView2APISample>ScenarioAddHostObject.html. Мы сравним этот файл с соответствующей веб-страницей в работающем примере приложения Win32.

  3. В Обозреватель решений щелкните правой кнопкой мыши webView2APISample (пример приложения Win32) и выберите команду Сборка.

  4. Нажмите клавишу F5 , чтобы запустить проект в режиме отладки.

  5. В примере приложения Win32 (в строке заголовка WebView2APISample) щелкните меню Сценарий , а затем выберите пункт Меню Объекты узла . Откроется пример веб-страницы AddHostObjectToScript , определяемой параметром ScenarioAddHostObject.html:

    Начало демонстрационной страницы объектов узла

  6. На веб-странице предлагается использовать консоль средства DevTools для запуска операторов JavaScript для chrome.webview.hostObjects.sample объекта . Если вы хотите открыть DevTools из примера приложения, щелкните страницу правой кнопкой мыши и выберите пункт Проверить. Затем перейдите на вкладку Консоль . Дополнительные сведения см. в разделе Общие сведения о консоли.

    Чтобы открыть средства разработки, нажатие клавиши F12 может не работать в этом контексте и вызвать исключение. Если это так, в Visual Studio выберите Остановить отладку и нажмите клавишу F5 , чтобы перезапустить отладку. В примере приложения снова выберите Объекты размещения сценариев>. Дополнительные сведения см. в статье Открытие средств разработки с помощью подхода, отличного от F12 , в статье Отладка приложений WebView2 с помощью Visual Studio.

    В нижней части демонстрационной страницы "Объекты узла" дублируются члены демонстрационного объекта в :<iframe>

    Внизу демонстрационной страницы объектов узла

  7. На отображаемой демонстрационной странице в примере приложения прочитайте текст метки, объясняющий кнопки Даты .

  8. Нажмите кнопки Дата . Под кнопками отображается строка даты, например:

    sample.dateProperty: Tue Nov 01 2022 12:45:25 GMT-0700 (Pacific Daylight Time)
    
  9. Изучите свойства и методы, нажав кнопки на демонстрационной веб-странице и введя значения, чтобы узнать, как работает пример кода. Кнопки демонстрируют доступ к свойствам и методам ведущего объекта из веб-кода приложения.

  10. Чтобы получить представление о том, что происходит в JavaScript, изучите следующий код в ScenarioAddHostObject.html.

    Следующий код представляет собой демонстрационное Date свойство непосредственно в элементе body :

    <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 определяется как часть объекта host.

Использование примера приложения

Вы можете поэкспериментировать с использованием и изменением примера приложения Win32. Затем следуйте той же схеме в собственном приложении:

  1. Создайте объект узла в собственном коде приложения.
  2. Передайте объект узла в веб-код приложения.
  3. Используйте объект host из веб-кода приложения.

Сведения о других API-интерфейсах, которые существуют в экосистеме объектов узла, см. в статье WebView2 Win32 C++ ICoreWebView2.

Обзор справочника по API

См. раздел Общий доступ к объектам узла и веб-сайта в обзоре функций и API WebView2.

См. также

Github: