Win32 的官方 WebView2 示例未显示正确的结果

问题描述 投票:0回答:1

为了学习如何使用 MS 的 WebView2,我尝试从这里编译并执行示例代码:https://github.com/MicrosoftEdge/WebView2Samples/blob/main/GettingStartedGuides/Win32_GettingStarted/HelloWebView.cpp

我尝试了几乎相同的代码,除了最小限度的修改以消除依赖性。 结果只显示空窗口。

这是我的实际代码:

#include <windows.h>
#include <stdlib.h>
#include <string>
#include <tchar.h>
#include <wrl.h>
#include "WebView2.h"

// The main window class name.
static TCHAR szWindowClass[] = _T("DesktopApp");

// The string that appears in the application's title bar.
static TCHAR szTitle[] = _T("WebView sample");

HINSTANCE hInst = 0;

using namespace Microsoft::WRL;

// don't care memory leak for now
static ICoreWebView2Controller *webviewController = nullptr;
static ICoreWebView2 *webview = nullptr;

static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    TCHAR greeting[] = _T("Hello, Windows desktop!");

    switch (message)
    {
    case WM_SIZE:
        if (webviewController != nullptr) {
            RECT bounds;
            GetClientRect(hWnd, &bounds);
            webviewController->put_Bounds(bounds);
        };
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
        break;
    }

    return 0;
}

int CALLBACK WinMain(
    _In_ HINSTANCE hInstance,
    _In_ HINSTANCE hPrevInstance,
    _In_ LPSTR     lpCmdLine,
    _In_ int       nCmdShow
    )
{
    WNDCLASSEX wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.hIcon = LoadIcon(hInstance, IDI_APPLICATION);
    wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcex.lpszMenuName = NULL;
    wcex.lpszClassName = szWindowClass;
    wcex.hIconSm = LoadIcon(wcex.hInstance, IDI_APPLICATION);

    if (!RegisterClassEx(&wcex))
    {
        MessageBox(NULL,
                   _T("Call to RegisterClassEx failed!"),
                   _T("Windows Desktop Guided Tour"),
                   NULL);

        return 1;
    }

    // Store instance handle in our global variable
    hInst = hInstance;

    // The parameters to CreateWindow explained:
    // szWindowClass: the name of the application
    // szTitle: the text that appears in the title bar
    // WS_OVERLAPPEDWINDOW: the type of window to create
    // CW_USEDEFAULT, CW_USEDEFAULT: initial position (x, y)
    // 500, 100: initial size (width, length)
    // NULL: the parent of this window
    // NULL: this application does not have a menu bar
    // hInstance: the first parameter from WinMain
    // NULL: not used in this application
    HWND hWnd = CreateWindow(
        szWindowClass,
        szTitle,
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT,
        1200, 900,
        NULL,
        NULL,
        hInstance,
        NULL
        );

    if (!hWnd)
    {
        MessageBox(NULL,
                   _T("Call to CreateWindow failed!"),
                   _T("Windows Desktop Guided Tour"),
                   NULL);

        return 1;
    }

    // The parameters to ShowWindow explained:
    // hWnd: the value returned from CreateWindow
    // nCmdShow: the fourth parameter from WinMain
    ShowWindow(hWnd,
               nCmdShow);
    UpdateWindow(hWnd);

    CreateCoreWebView2EnvironmentWithOptions(
        nullptr,
        nullptr,
        nullptr,
        Callback<ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler>(
            [hWnd](HRESULT result, ICoreWebView2Environment *env) -> HRESULT {
            // Create a CoreWebView2Controller and get the associated CoreWebView2 whose parent is the main window hWnd
            env->CreateCoreWebView2Controller(
                hWnd,
                Callback<ICoreWebView2CreateCoreWebView2ControllerCompletedHandler>(
                    [hWnd](HRESULT result, ICoreWebView2Controller *controller) -> HRESULT {
                        if (controller != nullptr) {
                            webviewController = controller;
                            webviewController->get_CoreWebView2(&webview);
                        }

                        // Add a few settings for the webview
                        // The demo step is redundant since the values are the default settings
                        ICoreWebView2Settings *settings;
                        webview->get_Settings(&settings);
                        settings->put_IsScriptEnabled(TRUE);
                        settings->put_AreDefaultScriptDialogsEnabled(TRUE);
                        settings->put_IsWebMessageEnabled(TRUE);
                        // settings->Release();

                        // Resize WebView to fit the bounds of the parent window
                        RECT bounds;
                        GetClientRect(hWnd, &bounds);
                        webviewController->put_Bounds(bounds);

                        // Schedule an async task to navigate to Bing
                        auto hr = webview->Navigate(L"https://www.bing.com/");


                        // <NavigationEvents>
                        // Step 4 - Navigation events
                        // register an ICoreWebView2NavigationStartingEventHandler to cancel any non-https navigation
                        EventRegistrationToken token;
                        webview->add_NavigationStarting(
                            Callback<ICoreWebView2NavigationStartingEventHandler>(
                                [](ICoreWebView2 *webview,
                                   ICoreWebView2NavigationStartingEventArgs *args) -> HRESULT {
                                    wchar_t *uri;
                                    args->get_Uri(&uri);
                                    std::wstring source(uri);
                                    // CoTaskMemFree(uri);
                                    if (source.substr(0, 5) != L"https") {
                                        args->put_Cancel(true);
                                    }
                                    return S_OK;
                                })
                                .Get(),
                            &token);
                        // </NavigationEvents>

                        // <Scripting>
                        // Step 5 - Scripting
                        // Schedule an async task to add initialization script that freezes the Object object
                        webview->AddScriptToExecuteOnDocumentCreated(L"Object.freeze(Object);",
                                                                     nullptr);
                        // Schedule an async task to get the document URL
                        webview->ExecuteScript(L"window.document.URL;",
                                               Callback<ICoreWebView2ExecuteScriptCompletedHandler>(
                                                   [](HRESULT errorCode,
                                                      LPCWSTR resultObjectAsJson) -> HRESULT {
                                                       LPCWSTR URL = resultObjectAsJson;
                                                       //doSomethingWithURL(URL);
                                                       return S_OK;
                                                   })
                                                   .Get());
                        // </Scripting>

                        // <CommunicationHostWeb>
                        // Step 6 - Communication between host and web content
                        // Set an event handler for the host to return received message back to the web content
                        webview->add_WebMessageReceived(
                            Callback<ICoreWebView2WebMessageReceivedEventHandler>(
                                [](ICoreWebView2 *webview,
                                   ICoreWebView2WebMessageReceivedEventArgs *args) -> HRESULT {
                                    wchar_t *message;
                                    args->TryGetWebMessageAsString(&message);
                                    // processMessage(&message);
                                    webview->PostWebMessageAsString(message);
                                    // CoTaskMemFree(message);
                                    return S_OK;
                                })
                                .Get(),
                            &token);

                        // Schedule an async task to add initialization script that
                        // 1) Add an listener to print message from the host
                        // 2) Post document URL to the host
                        webview->AddScriptToExecuteOnDocumentCreated(
                            L"window.chrome.webview.addEventListener(\'message\', event => "
                            L"alert(event.data));"
                            L"window.chrome.webview.postMessage(window.document.URL);",
                            nullptr);
                        // </CommunicationHostWeb>

                        return S_OK;
                    })
                    .Get());
            return S_OK;
        }).Get());
    // Main message loop:
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return (int)msg.wParam;
}

这是我的 CMakeLists.txt 来构建此文件:

cmake_minimum_required(VERSION 3.16)

project(webview-sample VERSION 0.1 LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_executable(webview-sample
    WIN32
    main.cpp
)

target_include_directories(webview-sample
    PRIVATE
    ../../3rdparty/Microsoft.Web.WebView2/build/native/include)


target_link_libraries(webview-sample
    PRIVATE
    ${CMAKE_CURRENT_SOURCE_DIR}/../../3rdparty/Microsoft.Web.WebView2/build/native/x64/WebView2LoaderStatic.lib
)

而且,这就是我得到的:

我猜它应该显示 bing 页面。 从终端收到的唯一消息是:

WebView2 Warning: Using default User Data Folder is not recommended, please see documentation.  https://go.microsoft.com/fwlink/?linkid=2187341 

这似乎与我的问题无关。我做错了什么?

c++ winapi webview2
1个回答
0
投票

这是一个 COM 问题。官方示例工作正常,因为它使用智能指针,所以在它的情况下:

static wil::com_ptr<ICoreWebView2Controller> webviewController;
...
webviewController = controller; // calls controller->AddRef();
// and Release will be called when the smart pointer will release it

在你的代码中

static ICoreWebView2Controller* webviewController = nullptr;
...
webviewController = controller; // just a raw copy

所以解决方案就是在传递了一个有效的指针后调用

controller->AddRef

(HRESULT result, ICoreWebView2Controller* controller) -> HRESULT {
if (controller != nullptr) {
    controller->AddRef();
    webviewController = controller;
    webviewController->get_CoreWebView2(&webview);
}

使用完毕后调用Release(在本例中是在执行结束时)

PS:我建议始终在 COM 代码中使用任何类型的智能指针。

© www.soinside.com 2019 - 2024. All rights reserved.