Win32 示例应用

WebView2APISample 应用演示如何使用 WebView2 控件和 WebView2 API 向 Win32 C++ 应用添加功能。

  • 示例名称: WebView2APISample
  • 存储库目录: WebView2APISample
  • 解决方案文件: WebView2Samples.sln (位于父目录中, \SampleApps\)
  • 解决方案资源管理器中的项目名称:WebView2APISample

WebView2APISample 在 Win32 本机应用程序中嵌入 WebView2 控件。

此示例生成为 Win32 Visual Studio 2019 项目。 它在 WebView2 环境中使用C++和 HTML/CSS/JavaScript。

WebView2APISample 展示了一系列 WebView2 事件处理程序和 API 方法,这些处理程序和 API 方法允许本机 Win32 应用程序直接与 WebView2 控件交互,反之亦然。

此示例及其解决方案文件是唯一的:它包含解决方案资源管理器中的其他示例的副本。

WebView2APISample 是使用 Microsoft Edge WebView2 控件生成的混合应用程序;也就是说,此应用结合了本机端和浏览器 Web 应用端。 请参阅 Microsoft Edge WebView2 简介中的混合应用方法

正在运行的 WebView2APISample 应用窗口显示 WebView2 SDK 版本以及 WebView2 运行时版本和路径。 提供了许多有用的菜单和菜单项:

WebView2API 示例应用窗口,其中显示了 WebView2 SDK 版本和 WebView2 运行时版本和路径

如果这是你第一次使用 WebView,我们建议首先遵循 教程 Win32 应用中的 WebView2 入门,其中介绍了如何创建 WebView2 应用并演练一些基本的 WebView2 功能。 该特定教程不是从使用项目模板创建新的 Win32 项目开始的;相反,它从 WebView2Samples 存储库中的已完成项目开始,并引导你完成添加的 WebView2 代码。

有关 WebView2 中的事件和 API 处理程序的详细信息,请参阅 WebView2 API 参考

步骤 1 - 安装 Visual Studio

Microsoft Visual Studio (最低版本:Visual Studio 2019) 。 此示例不支持Microsoft Visual Studio Code。 此存储库示例是 Visual Studio 2019 项目。 还可以使用 Visual Studio 2022 打开示例。

  1. 如果 Visual Studio 尚未安装C++支持,请参阅在单独的窗口或选项卡中 安装 Visual Studio 中的 设置 WebView2 开发环境。 按照该部分中的步骤安装具有C++支持的 Visual Studio,然后返回到此页面并继续执行以下步骤。

如果要使用 Visual Studio 2017,在 Visual Studio 2017 中打开解决方案后,请在“项目属性配置”属性>“”常规>平台工具集“>中更改项目的”平台工具集”。

若要使用 Visual Studio 2017,可能还需要在计算机上安装最近的Windows SDK。

步骤 2 - 克隆 WebView2Samples 存储库

  1. 如果尚未执行此操作,请将 WebView2Samples 存储库克隆到本地驱动器。 在单独的窗口或选项卡中,请参阅为 WebView2设置开发环境中的下载 WebView2 示例存储库。 按照该部分中的步骤操作,然后返回到此页面,然后继续以下操作。

  2. 如果以前克隆过存储库,请将最新的提交拉取到存储库的本地副本。

步骤 3 - 在 Visual Studio 中打开解决方案

  1. 在本地驱动器上,在 Visual Studio 中打开 .sln 文件:

    • <your-repos-directory>/WebView2Samples/SampleApps/WebView2Samples.sln

    或者:

    • <your-repos-directory>/WebView2Samples-main/SampleApps/WebView2Samples.sln

WebView2APISample 示例和项目是 win32 main示例。

与其他一些示例不同,示例存储库目录中没有包含此示例自述文件的专用 .sln 文件。 相反, .sln 此示例的文件 (包括其他示例项目以及) 位于父目录中。

解决方案资源管理器 中解决方案中的所有项目

步骤 4 - 在出现提示时安装工作负载

  1. Visual Studio 工作负载 - 如果出现提示,请安装请求的任何 Visual Studio 工作负载。 在单独的窗口或选项卡中,请参阅为 WebView2 设置开发环境中的安装 Visual Studio 工作负载。 按照该部分中的步骤操作,然后返回到此页面,然后继续以下操作。

继续执行以下步骤。

步骤 5 - 查看打开的项目

  1. 在本地驱动器上,在设置的同一个 Visual Studio 版本中再次打开 WebView2Samples 解决方案:

    • <your-repos-directory>/WebView2Samples/SampleApps/WebView2Samples.sln

    或者:

    • <your-repos-directory>/WebView2Samples-main/SampleApps/WebView2Samples.sln
  2. 单击“ 确定” 按钮。 “ 重定向项目 ”对话框可能会打开:

    “重新定位项目”对话框

    已安装版本的示例:

    重定目标 - 已安装 SDK

  3. 单击“ 确定” 按钮。

解决方案资源管理器显示多个项目,包括 WebView2APISample 项目:

解决方案资源管理器 中的 WebView2APISample 项目

步骤 6 - 使用已安装的 SDK 版本生成项目

在 Visual Studio 的顶部,设置生成目标,如下所示:

  1. “解决方案配置” 下拉列表中,选择“ 调试” 或“ 发布”。

  2. “解决方案平台” 下拉列表中,选择“ x86”、“ x64”或“ ARM64”。

  3. “解决方案资源管理器”中,右键单击“WebView2APISample”项目,然后选择“生成”。

    在 解决方案资源管理器 中选择的 WebView2APISample 项目

    这会生成项目文件 SampleApps/WebView2APISample/WebView2APISample.vcxproj

步骤 7 -) 项目运行 (调试

  1. 选择“ 调试>启动调试 ” (F5) 。

    故障排除:如果跳过生成步骤并立即选择“ 调试> (F5) ,可能会出现对话框“无法启动程序:找不到指定的路径”:

    对话框:无法启动程序:找不到指定的路径

    若要解决此问题:在 解决方案资源管理器 中,右键单击“WebView2APISample”项目,然后选择“生成”。

    此时会打开 “WebView2APISample 应用”窗口:

    WebView2APISample 应用窗口

  2. 在 Visual Studio 中,选择“ 调试>停止调试”。 Visual Studio 关闭应用。

步骤 8 - 更新预发行版 WebView2 SDK

接下来,你将更新 WebView2 SDK,然后重新生成项目。

如果要在 GitHub 上的 WebView2APISample 应用的存储库副本中快速查看安装了哪个版本的 WebView2 SDK,请参阅 packages.config

此示例的存储库版本已安装 WebView2 SDK 的预发行版。 下面,你将它更新到 WebView2 SDK 的最新预发行版,或确认已安装最新的 SDK。 使用预发布 SDK 可以访问最新功能。

检查并更新已安装的 NuGet 包,如下所示:

  1. 在 解决方案资源管理器中,右键单击 WebView2APISample 项目 (而不是其上方) 的解决方案节点,然后选择“管理 NuGet 包”。

    “NuGet 包管理器”面板在 Visual Studio 中打开。

  2. 在搜索文本框右侧,选择“包括预发行检查”框。

  3. NuGet 包管理器中,单击“已安装”选项卡。在每个包的右侧,检查是否列出了更新的版本号以及现有的版本号。

  4. 单击“ 更新 ”选项卡。如果更新可用于 WebView2 或 WIL 包,如果需要,可以在此处更新包。

  5. 在右侧的 “版本 ”下拉列表中,如果希望能够尝试最新的 API,请确保选择了“ 最新预发行版 ”:

    选择了“WebView2 SDK 预发布”的 NuGet 包管理器

  6. 单击“ 更新 ”按钮。

    此时将显示 “预览更改 ”对话框:

    WebView2 NugGet 包的“预览更改”对话框

    上图来自另一个项目,但类似。

  7. 单击“ 确定” 按钮。

现已为此项目安装最新版本的 WebView2 SDK。

步骤 9 - 使用更新的 SDK 生成并运行项目

  1. “解决方案资源管理器”中,右键单击“WebView2APISample”项目,然后选择“生成”。

    在 解决方案资源管理器 中选择的 WebView2APISample 项目

  2. 选择“ 调试>启动调试 ” (F5) 。

    此时会打开 “WebView2APISample 应用”窗口:

    WebView2APISample 应用窗口

  3. 使用 WebView2APISample 应用。

  4. 在 Visual Studio 中,选择“ 调试>停止调试”。 Visual Studio 关闭应用。

这将完成生成和运行 Win32 示例应用的编号步骤。 接下来,在 Visual Studio 代码编辑器中,根据以下部分检查代码。

混合应用体系结构

WebView2APISample 应用是混合应用程序的示例,具有 Win32 本机部件和 WebView 部件。

  • Win32 部件可以直接访问本机 Windows API。
  • WebView 是用于标准 Web 技术的容器, (HTML、CSS 和 JavaScript) 。

混合应用

  • 第 1 部分: WebView2APISample 应用的顶部是用 C++ 编写的 Win32 组件。 应用程序的这一部分接收来自用户的 UI 输入,并使用这些输入来控制 WebView。

  • 第 2 部分:WebView2APISample 应用的main部分是一个 WebView,可以使用标准 Web 技术 (HTML/CSS/JavaScript) 重新调整用途。 可以导航到网站或本地内容。

这种混合方法允许使用 Web 技术更快地创建和迭代,同时仍能够利用本机功能。 WebView2APISample 应用演示 Win32 组件和 WebView 组件如何相互交互。

这个范围广泛的示例应用已发展到包含超过 150 个菜单项,演示了 Win32/C++ 框架中的许多 WebView2 API。 以下部分重点介绍混合应用实现的基础知识。

项目文件

本部分简要介绍存储库中的一些关键文件。 WebView2APISample 应用垂直划分为组件,而不是水平划分为层。 每个组件实现一类示例功能的整个工作流,从侦听菜单命令到调用 WebView API 方法来实现它们。

App.cpp

这是运行 WebView2APISample 应用的顶级文件。 它读取命令行选项、设置进程环境并处理应用的线程模型。

AppWindow.cpp (窗口菜单)

此文件通过执行以下操作实现应用程序窗口:

  1. 设置所有 Win32 控件。

  2. 初始化 WebView 环境和 WebView。

  3. 将事件处理程序添加到 WebView 并创建处理应用程序的各种功能的所有组件。

AppWindow 处理示例应用的 “窗口 ”菜单中的命令。

此文件在以下 AppWindow.cpp 中的键函数中进行了更详细的介绍。

FileComponent.cpp (“文件”菜单)

此组件处理“ 文件 ”菜单中 (除 “退出) ”和 DocumentTitleChanged 事件之外的命令。

ScriptComponent.cpp (脚本菜单)

此组件处理“ 脚本 ”菜单中的命令,这些命令涉及通过注入 JavaScript、发布 WebMessages、将本机对象添加到网页或使用 DevTools 协议与网页通信来与 WebView 交互。

ProcessComponent.cpp (“进程”菜单)

此组件处理来自“ 进程 ”菜单的命令,这些命令涉及与浏览器进程交互。 它还处理 ProcessFailed 事件,以防浏览器进程或其呈现进程之一崩溃或无响应。

SettingsComponent.cpp (“设置”菜单)

此组件处理 “设置” 菜单中的命令。 此组件还负责在创建新 WebView 时从旧 WebView 复制设置。 此处提供了与 接互的 ICoreWebView2Settings 大多数代码。

ViewComponent.cpp (视图菜单)

此组件处理“ 视图 ”菜单中的命令,以及与 WebView 的大小和可见性相关的任何功能。 当应用窗口调整大小、最小化或还原时, ViewComponent 将调整、隐藏或显示 WebView 作为响应。 它还响应事件 ZoomFactorChanged

ScenarioWebMessage.cpp和 ScenarioWebMessage.html (方案菜单)

选择ScenarioWebMessage“方案>Web 消息传递”菜单项时,将创建组件。 此组件实现具有C++部件和 HTML + JavaScript 部件的示例应用程序,这些部件通过异步发布和接收消息相互通信。

下面的 ScenarioWebMessage (.html、.cpp 和 .h) 中对此组件进行了更详细的介绍。

“ScenarioAddHostObject.cpp和 ScenarioAddHostObject.html (方案”菜单)

选择 “方案>主机对象” 菜单项时会创建此组件。 它通过主机对象注入演示本机应用与 HTML 网页之间的通信。 主机对象的接口在 中 HostObjectSample.idl声明,对象本身在 中 HostObjectSampleImpl.cpp实现。

另请参阅:

AppWindow.cpp中的关键功能

AppWindow.cpp 通过执行以下操作实现应用程序窗口:

  1. 设置所有 Win32 控件。

  2. 初始化 WebView 环境和 WebView。

  3. 将事件处理程序添加到 WebView 并创建处理应用程序的各种功能的所有组件。

AppWindow 处理示例应用的 “窗口 ”菜单中的命令。 以下是 中的 AppWindow.cpp一些关键函数。

InitializeWebView ()

在 中 AppWindow.cpp,函数 InitializeWebView() 使用 CreateCoreWebView2EnvironmentWithOptions 创建 WebView2 环境。

若要查看这些 API 调用的运行情况,请从 InitializeWebView()中检查以下代码:

HRESULT hr = CreateCoreWebView2EnvironmentWithOptions(
    subFolder, nullptr, options.Get(),
    Callback<ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler>(
        this, &AppWindow::OnCreateEnvironmentCompleted)
        .Get());
if (!SUCCEEDED(hr))
{
    if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
    {
        MessageBox(
            m_mainWindow,
            L"Couldn't find Edge installation. "
            "Do you have a version installed that's compatible with this "
            "WebView2 SDK version?",
            nullptr, MB_OK);
    }
    else
    {
        ShowFailure(hr, L"Failed to create webview environment");
    }
}

OnCreateEnvironmentCompleted ()

创建环境后,我们使用 创建 WebView CreateCoreWebView2Controller

回调 OnCreateEnvironmentCompleted 函数在 CreateCoreWebView2EnvironmentWithOptionsInitializeWebView()传递给 。 回调存储环境指针,然后使用它创建新的 WebView:

HRESULT AppWindow::OnCreateEnvironmentCompleted(
    HRESULT result, ICoreWebView2Environment* environment)
{
    CHECK_FAILURE(result);

    m_webViewEnvironment = environment;

    CHECK_FAILURE(m_webViewEnvironment->CreateCoreWebView2Controller(
        m_mainWindow, Callback<ICoreWebView2CreateCoreWebView2ControllerCompletedHandler>(
                            this, &AppWindow::OnCreateCoreWebView2ControllerCompleted)
                            .Get()));
    return S_OK;
}

OnCreateCoreWebView2ControllerCompleted ()

回调 OnCreateCoreWebView2ControllerCompleted 函数在 CreateCoreWebView2ControllerInitializeWebView()传递给 。 此回调:

  • 初始化与 WebView 相关的状态。
  • 注册一些事件处理程序。
  • 创建应用组件。

RegisterEventHandlers ()

函数 RegisterEventHandlers 在 中 CreateCoreWebView2Controller调用。 它设置应用程序使用的一些事件处理程序,并将其添加到 WebView。

有关 WebView2 中事件处理程序的详细信息,请参阅 ICoreWebView2

下面是 来自 RegisterEventHandlers()的代码片段,其中我们为 NewWindowRequested 事件设置了事件处理程序。 当网页中的 JavaScript 调用 window.open()时会触发此事件。 ICoreWebView2NewWindowRequestedEventHandler生成一个新的AppWindow,并将新窗口的 WebView 传递回浏览器,以便它可以从调用中window.open()返回它。 与调用 CreateCoreWebView2EnvironmentWithOptionsCreateCoreWebView2Controller不同,我们只是提供C++ lambda,而不是为回调提供方法:

CHECK_FAILURE(m_webView->add_NewWindowRequested(
    Callback<ICoreWebView2NewWindowRequestedEventHandler>(
        [this](
            ICoreWebView2* sender,
            ICoreWebView2NewWindowRequestedEventArgs* args) {
            wil::com_ptr<ICoreWebView2Deferral> deferral;
            CHECK_FAILURE(args->GetDeferral(&deferral));

            auto newAppWindow = new AppWindow(L"");
            newAppWindow->m_isPopupWindow = true;
            newAppWindow->m_onWebViewFirstInitialized = [args, deferral, newAppWindow]() {
                CHECK_FAILURE(args->put_NewWindow(newAppWindow->m_webView.get()));
                CHECK_FAILURE(args->put_Handled(TRUE));
                CHECK_FAILURE(deferral->Complete());
            };

            return S_OK;
        })
        .Get(),
    nullptr));

ScenarioWebMessage (.html、.cpp 和 .h)

这些文件 ScenarioWebMessage 显示 Win32 主机如何修改 WebView、WebView 如何修改 Win32 主机,以及 WebView 如何通过访问 Win32 主机的信息来修改自身。 这是异步完成的。

选择ScenarioWebMessage“方案>Web 消息传递”菜单项时,将创建组件。 组件 ScenarioWebMessage 实现具有C++部件和 HTML+JavaScript 部件的示例应用程序,这些部件通过异步发布和接收消息相互通信:

Web 消息传送:发布和接收消息

以下部分演示了每个离散函数如何使用 WebView2APISample 应用工作,然后说明如何实现此功能。

首先,转到示例应用中的 ScenarioWebMessage Web 应用:

  1. 打开 (webView2APISample 应用) 运行。

  2. “方案 ”菜单中,选择“ Web 消息传递”。

    WebView 显示标题为 WebMessage 示例页 的网页, (ScenarioWebMessage.html) :

    用于发布和接收消息的 Web 消息传送

若要浏览 ScenarioWebMessage 功能,可以按照页面上的说明操作,或按照以下步骤操作。

将消息从 Win32 主机发布到 WebView

以下步骤演示了 Win32 主机如何修改 WebView。 在此示例中,你将将文本变为蓝色:

  1. 如上文所述, (ScenarioWebMessage.html) 打开 WebMessage 示例页

  2. “脚本 ”菜单中,选择“ 发布 Web 消息 JSON”。

    此时将显示一个包含预编写代码 {"SetColor":"blue"} 的对话框。

  3. 单击“确定”

    页面的 “发布消息 ”部分中的文本从黑色更改为蓝色。

运作方式
  1. 在 中 ScriptComponent.cpp,对 PostWebMessageAsJson 的 调用会将用户输入发布到 ScenarioMessage.html Web 应用程序:

    // Prompt the user for some JSON and then post it as a web message.
    void ScriptComponent::SendJsonWebMessage()
    {
       TextInputDialog dialog(
          m_appWindow->GetMainWindow(),
          L"Post Web Message JSON",
          L"Web message JSON:",
          L"Enter the web message as JSON.",
          L"{\"SetColor\":\"blue\"}");
       if (dialog.confirmed)
       {
          m_webView->PostWebMessageAsJson(dialog.input.c_str());
       }
    }
    
  2. 在 Web 应用程序中,事件侦听器用于接收和响应 Web 消息。 下面的代码片段来自 ScenarioWebMessage.html。 如果参数为“SetColor”,则事件侦听器将更改文本的颜色:

    window.chrome.webview.addEventListener('message', arg => {
       if ("SetColor" in arg.data) {
          document.getElementById("colorable").style.color = arg.data.SetColor;
       }
    });
    

接收消息 (从 WebView 发送到 Win32 主机)

以下步骤演示 WebView 如何通过更改 Win32 应用的标题来修改 Win32 主机应用:

  1. 如上文所述, (ScenarioWebMessage.html) 打开 WebMessage 示例页

  2. 请注意显示在窗口左上角图标旁边的 WebView2APISample 应用的标题。 它最初是 WebView2APISample - Microsoft Edge WebView2

  3. 在页面的“ 接收消息 ”部分中,输入新标题,然后单击“ 发送 ”按钮。

  4. 请注意显示在 WebView2APISample 应用的标题栏中的新标题。

运作方式
  1. ScenarioWebMessage.html中, window.chrome.webview.postMessage () 将用户输入发送到主机应用程序:

    function SetTitleText() {
       let titleText = document.getElementById("title-text");
       window.chrome.webview.postMessage(`SetTitleText ${titleText.value}`);
    }
    
  2. 在 中 ScenarioWebMessage.cpp,我们使用 add_WebMessageReceived 来注册事件处理程序。 收到事件时,验证输入后,将应用窗口的标题更改为 (m_appWindow) :

    // Setup the web message received event handler before navigating to
    // ensure we don't miss any messages.
    CHECK_FAILURE(m_webview->add_WebMessageReceived(
       Microsoft::WRL::Callback<ICoreWebView2WebMessageReceivedEventHandler>(
          [this](ICoreWebView2* sender, ICoreWebView2WebMessageReceivedEventArgs* args)
    {
       wil::unique_cotaskmem_string uri;
       CHECK_FAILURE(args->get_Source(&uri));
    
       // Always validate that the origin of the message is what you expect.
       if (uri.get() != m_sampleUri)
       {
          return S_OK;
       }
       wil::unique_cotaskmem_string messageRaw;
       CHECK_FAILURE(args->TryGetWebMessageAsString(&messageRaw));
       std::wstring message = messageRaw.get();
    
       if (message.compare(0, 13, L"SetTitleText ") == 0)
       {
          m_appWindow->SetTitleText(message.substr(13).c_str());
       }
       return S_OK;
    }).Get(), &m_webMessageReceivedToken));
    

从 WebView 到主机的往返消息 (返回到 WebView)

以下步骤演示 WebView 如何从 Win32 主机获取信息并通过显示 Win32 应用的大小来修改自身。

  1. 如上文所述, (ScenarioWebMessage.html) 打开 WebMessage 示例页

  2. 在页面的“ 往返 ”部分中,单击“ GetWindowBounds ”按钮。

    按钮下方的文本框显示 WebView2APISample 应用的边界。

运作方式
  1. 单击“ 获取窗口边界 ”按钮时, GetWindowBounds 将调用 中的 ScenarioWebMessage.html 函数。 GetWindowBounds 调用 window.chrome.webview.postMessage () 向主机应用程序发送消息:

    function GetWindowBounds() {
        window.chrome.webview.postMessage("GetWindowBounds");
    }
    
  2. 在 中 ScenarioWebMessage.cpp,我们使用 add_WebMessageReceived 注册收到的事件处理程序。 验证输入后,事件处理程序从应用窗口获取窗口边界。 PostWebMessageAsJson 将边界发送到 Web 应用程序:

    if (message.compare(L"GetWindowBounds") == 0)
    {
       RECT bounds = m_appWindow->GetWindowBounds();
       std::wstring reply =
          L"{\"WindowBounds\":\"Left:" + std::to_wstring(bounds.left)
          + L"\\nTop:" + std::to_wstring(bounds.top)
          + L"\\nRight:" + std::to_wstring(bounds.right)
          + L"\\nBottom:" + std::to_wstring(bounds.bottom)
          + L"\"}";
       CHECK_FAILURE(sender->PostWebMessageAsJson(reply.c_str()));
    }
    
  3. 在 中 ScenarioWebMessage.html,事件侦听器响应 WindowBounds 消息并显示窗口的边界:

    window.chrome.webview.addEventListener('message', arg => {
       if ("WindowBounds" in arg.data) {
          document.getElementById("window-bounds").value = arg.data.WindowBounds;
       }
    });
    

另请参阅