简体   繁体   English

基本 Win32 WebView2 示例在纯 C 中不起作用

[英]Basic Win32 WebView2 example not working in pure C

A new problem blows my mind lately: a very simple code using the WebView2 library from Microsoft works if compiled as C++ but not as C.最近有一个新问题让我大吃一惊:如果编译为 C++ 而不是 C,则使用 Microsoft 的 WebView2 库的一个非常简单的代码可以工作。 What could be causing the problem?什么可能导致问题? I tried all sorts of fixes, like using an older version of the WebView2 library, using Edge Canary or beta or a different version of WebView2 Runtime, it simply refuses to work.我尝试了各种修复,例如使用旧版本的 WebView2 库、使用 Edge Canary 或 beta 或不同版本的 WebView2 Runtime,它只是拒绝工作。

Here is the sample code in C:以下是 C 中的示例代码:

#include <initguid.h>
#include <Windows.h>
#include <stdio.h>
#include <conio.h>
#include <shlwapi.h>
#pragma comment(lib, "Shlwapi.lib")
#include <Shlobj_core.h>
#include "WebView2.h"

#define APPLICATION_NAME TEXT("WebView2")

#define error_printf printf

ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler* envHandler;
ICoreWebView2CreateCoreWebView2ControllerCompletedHandler* completedHandler;
HWND hWnd = NULL;
ICoreWebView2Controller* webviewController = NULL;
ICoreWebView2* webviewWindow = NULL;
BOOL bEnvCreated = FALSE;

ULONG HandlerRefCount = 0;
ULONG HandlerAddRef(IUnknown* This)
{
    return ++HandlerRefCount;
}
ULONG HandlerRelease(IUnknown* This)
{
    --HandlerRefCount;
    if (HandlerRefCount == 0)
    {
        if (completedHandler)
        {
            free(completedHandler->lpVtbl);
            free(completedHandler);
        }
        if (envHandler)
        {
            free(envHandler->lpVtbl);
            free(envHandler);
        }
    }
    return HandlerRefCount;
}
HRESULT HandlerQueryInterface(
    IUnknown* This,
    IID* riid,
    void** ppvObject
)
{
    *ppvObject = This;
    HandlerAddRef(This);
    return S_OK;
}
HRESULT HandlerInvoke(
    IUnknown* This,
    HRESULT errorCode,
    void* arg
)
{
    if (!bEnvCreated)
    {
        bEnvCreated = TRUE;
        char ch;
        completedHandler = malloc(sizeof(ICoreWebView2CreateCoreWebView2ControllerCompletedHandler));
        if (!completedHandler)
        {
            error_printf(
                "%s:%d: %s (0x%x).\n",
                __FILE__,
                __LINE__,
                "Cannot allocate ICoreWebView2CreateCoreWebView2ControllerCompletedHandler",
                GetLastError()
            );
            ch = _getch();
            return GetLastError();
        }
        completedHandler->lpVtbl = malloc(sizeof(ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerVtbl));
        if (!completedHandler->lpVtbl)
        {
            error_printf(
                "%s:%d: %s (0x%x).\n",
                __FILE__,
                __LINE__,
                "Cannot allocate ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerVtbl",
                GetLastError()
            );
            ch = _getch();
            return GetLastError();
        }
        completedHandler->lpVtbl->AddRef = HandlerAddRef;
        completedHandler->lpVtbl->Release = HandlerRelease;
        completedHandler->lpVtbl->QueryInterface = HandlerQueryInterface;
        completedHandler->lpVtbl->Invoke = HandlerInvoke;

        ICoreWebView2Environment* env = arg;
        env->lpVtbl->CreateCoreWebView2Controller(
            env,
            hWnd,
            completedHandler
        );
    }
    else
    {
        ICoreWebView2Controller* controller = arg;

        if (controller != NULL) {
            webviewController = controller;
            webviewController->lpVtbl->get_CoreWebView2(
                webviewController,
                &webviewWindow
            );
        }

        ICoreWebView2Settings* Settings;
        webviewWindow->lpVtbl->get_Settings(
            webviewWindow,
            &Settings
        );
        Settings->lpVtbl->put_IsScriptEnabled(
            Settings,
            TRUE
        );
        Settings->lpVtbl->put_AreDefaultScriptDialogsEnabled(
            Settings,
            TRUE
        );
        Settings->lpVtbl->put_IsWebMessageEnabled(
            Settings,
            TRUE
        );
        Settings->lpVtbl->put_AreDevToolsEnabled(
            Settings,
            FALSE
        );
        Settings->lpVtbl->put_AreDefaultContextMenusEnabled(
            Settings,
            TRUE
        );
        Settings->lpVtbl->put_IsStatusBarEnabled(
            Settings,
            TRUE
        );

        RECT bounds;
        GetClientRect(hWnd, &bounds);
        webviewController->lpVtbl->put_Bounds(
            webviewController,
            bounds
        );

        webviewWindow->lpVtbl->Navigate(
            webviewWindow,
            L"https://google.com/"
        );
    }

    return S_OK;
}

LRESULT CALLBACK WindowProc(
    _In_ HWND   hWnd,
    _In_ UINT   uMsg,
    _In_ WPARAM wParam,
    _In_ LPARAM lParam
)
{
    switch (uMsg)
    {
    /*case WM_NCCALCSIZE:
    {
        return 0;
    }*/
    case WM_DPICHANGED:
    {
        RECT* const newWindowSize = (RECT*)(lParam);
        SetWindowPos(
            hWnd,
            NULL,
            newWindowSize->left,
            newWindowSize->top,
            newWindowSize->right - newWindowSize->left,
            newWindowSize->bottom - newWindowSize->top,
            SWP_NOZORDER | SWP_NOACTIVATE);
        return TRUE;
    }
    case WM_SIZE:
    {
        if (webviewController != NULL) {
            RECT bounds;
            GetClientRect(hWnd, &bounds);
            webviewController->lpVtbl->put_Bounds(
                webviewController,
                bounds
            );
        };
        break;
    }
    default:
    {
        return DefWindowProc(
            hWnd,
            uMsg,
            wParam,
            lParam
        );
    }
    }
    return 0;
}

int WINAPI wWinMain(
    _In_ HINSTANCE hInstance,
    _In_opt_ HINSTANCE hPrevInstance,
    _In_ LPWSTR lpCmdLine,
    _In_ int nShowCmd
)
{
    int ch;

    FILE* conout;
    AllocConsole();
    freopen_s(
        &conout,
        "CONOUT$",
        "w",
        stdout
    );

    HRESULT hr;

    if (!SetProcessDpiAwarenessContext(
        DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2
    ))
    {
        error_printf(
            "%s:%d: %s (0x%x).\n",
            __FILE__,
            __LINE__,
            "SetProcessDpiAwarenessContext",
            GetLastError()
        );
        ch = _getch();
        return GetLastError();
    }

    hr = CoInitialize(NULL);
    if (FAILED(hr))
    {
        error_printf(
            "%s:%d: %s (0x%x).\n",
            __FILE__,
            __LINE__,
            "CoInitialize",
            hr
        );
        ch = _getch();
        return hr;
    }

    WNDCLASS wndClass = { 0 };
    wndClass.style = CS_HREDRAW | CS_VREDRAW;
    wndClass.lpfnWndProc = WindowProc;
    wndClass.cbClsExtra = 0;
    wndClass.cbWndExtra = 0;
    wndClass.hInstance = hInstance;
    wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
    wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wndClass.lpszMenuName = NULL;
    wndClass.lpszClassName = APPLICATION_NAME;

    hWnd = CreateWindowEx(
        0,
        (LPCWSTR)(
            MAKEINTATOM(
                RegisterClass(&wndClass)
            )
            ),
        APPLICATION_NAME,
        WS_OVERLAPPEDWINDOW,
        100, 100, 800, 800,
        NULL,
        NULL,
        hInstance,
        NULL
    );
    if (!hWnd)
    {
        error_printf(
            "%s:%d: %s (0x%x).\n",
            __FILE__,
            __LINE__,
            "CreateWindowEx",
            GetLastError()
        );
        ch = _getch();
        return GetLastError();
    }

    ShowWindow(hWnd, nShowCmd);

    envHandler = malloc(sizeof(ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler));
    if (!envHandler)
    {
        error_printf(
            "%s:%d: %s (0x%x).\n",
            __FILE__,
            __LINE__,
            "Cannot allocate ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler",
            GetLastError()
        );
        ch = _getch();
        return GetLastError();
    }
    envHandler->lpVtbl = malloc(sizeof(ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerVtbl));
    if (!envHandler->lpVtbl)
    {
        error_printf(
            "%s:%d: %s (0x%x).\n",
            __FILE__,
            __LINE__,
            "Cannot allocate ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerVtbl",
            GetLastError()
        );
        ch = _getch();
        return GetLastError();
    }
    envHandler->lpVtbl->AddRef = HandlerAddRef;
    envHandler->lpVtbl->Release = HandlerRelease;
    envHandler->lpVtbl->QueryInterface = HandlerQueryInterface;
    envHandler->lpVtbl->Invoke = HandlerInvoke;
    
    UpdateWindow(hWnd);

    CreateCoreWebView2EnvironmentWithOptions(
        NULL,
        NULL,
        NULL,
        envHandler
    );

    MSG msg;
    BOOL bRet;
    while ((bRet = GetMessage(
        &msg,
        NULL,
        0,
        0)) != 0)
    {
        // An error occured
        if (bRet == -1)
        {
            break;
        }
        else
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return 0;
}

As you can see, I simply used lpVtbl where appropiate and provided proper callbacks.如您所见,我只是在适当的地方使用lpVtbl并提供了适当的回调。 The code is a bit more compact in C++: C++ 中的代码更紧凑:

#include <Windows.h>
#include <stdio.h>
#include <wrl.h>
#include <wil/com.h>
#include "WebView2.h"

#define APPLICATION_NAME TEXT("WebView2")

static wil::com_ptr<ICoreWebView2Controller> webviewController;
static wil::com_ptr<ICoreWebView2> webviewWindow;

LRESULT CALLBACK WindowProc(
    _In_ HWND   hwnd,
    _In_ UINT   uMsg,
    _In_ WPARAM wParam,
    _In_ LPARAM lParam
)
{
    switch (uMsg)
    {
    case WM_DPICHANGED:
    {
        RECT* const newWindowSize = (RECT*)(lParam);
        SetWindowPos(hwnd,
            NULL,
            newWindowSize->left,
            newWindowSize->top,
            newWindowSize->right - newWindowSize->left,
            newWindowSize->bottom - newWindowSize->top,
            SWP_NOZORDER | SWP_NOACTIVATE);
        return TRUE;
    }
    case WM_SIZE:
    {
        if (webviewController != NULL) {
            RECT bounds;
            GetClientRect(hwnd, &bounds);
            webviewController->put_Bounds(bounds);
        };
        break;
    }
    case WM_DESTROY:
    {
        PostQuitMessage(0);
        break;
    }
    default:
    {
        return DefWindowProc(
            hwnd,
            uMsg,
            wParam,
            lParam
        );
    }
    }
    return 0;
}

int WINAPI wWinMain(
    HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    PWSTR pCmdLine,
    int nCmdShow
)
{
    SetProcessDpiAwarenessContext(
        DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2
    );

    WNDCLASS wndClass = { 0 };
    wndClass.style = CS_HREDRAW | CS_VREDRAW;
    wndClass.lpfnWndProc = WindowProc;
    wndClass.cbClsExtra = 0;
    wndClass.cbWndExtra = 0;
    wndClass.hInstance = hInstance;
    wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
    wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wndClass.lpszMenuName = NULL;
    wndClass.lpszClassName = APPLICATION_NAME;
    RegisterClass(&wndClass);

    HWND hWnd = CreateWindowEx(
        0,
        APPLICATION_NAME,
        APPLICATION_NAME,
        WS_OVERLAPPEDWINDOW,
        100, 100, 800, 800,
        NULL,
        NULL,
        hInstance,
        NULL
    );

    ShowWindow(
        hWnd,
        nCmdShow
    );
    UpdateWindow(hWnd);

    CreateCoreWebView2EnvironmentWithOptions(NULL, NULL, NULL,
        Microsoft::WRL::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, Microsoft::WRL::Callback<ICoreWebView2CreateCoreWebView2ControllerCompletedHandler>(
                    [hWnd](HRESULT result, ICoreWebView2Controller* controller) -> HRESULT {
                        if (controller != nullptr) {
                            webviewController = controller;
                            webviewController->get_CoreWebView2(&webviewWindow);
                        }

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

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

                        // Schedule an async task to navigate to Bing
                        webviewWindow->Navigate(HOME_PAGE);

                        // Step 4 - Navigation events

                        // Step 5 - Scripting

                        // Step 6 - Communication between host and web content

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

    MSG msg;
    BOOL bRet;
    while ((bRet = GetMessage(
        &msg,
        NULL,
        0,
        0)) != 0)
    {
        // An error occured
        if (bRet == -1)
        {
            break;
        }
        else
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return (int)msg.wParam;
}

Since I have now stumbled upon this, I am really curious what may be causing this.由于我现在偶然发现了这一点,我真的很好奇可能是什么原因造成的。 Why does it matter in the end?为什么最后很重要? Thanks for any pointers.感谢您的任何指示。

It does not work means the web page is not displayed.它不起作用意味着不显示 web 页面。 The window is blank. window 是空白的。 The WebView2 actually is not displayed on the window. WebView2 实际上并没有显示在 window 上。

Edit:编辑:

What are wil::com_ptr s actually? wil::com_ptr实际上是什么? If I change this:如果我改变这个:

static wil::com_ptr<ICoreWebView2Controller> webviewController;
static wil::com_ptr<ICoreWebView2> webviewWindow;

To this:对此:

static ICoreWebView2Controller* webviewController;
static ICoreWebView2* webviewWindow;

In C++, I break it.在 C++ 中,我打破了它。 Why?为什么? Just why?只是为什么? (I replaced the callbacks Microsoft::WRL::Callback with standalone classes and, naturally, it still worked, but getting rid of the COM pointers and using regular pointers breaks it. Why...? (我用独立的类替换了回调Microsoft::WRL::Callback ,当然,它仍然有效,但是去掉 COM 指针并使用常规指针会破坏它。为什么......?

The solution is simple, if one actually takes a minute to look a bit on the correctness and logic of the code, rather than suggesting generics and commenting just so that the patch cables have some bytes to carry around.解决方案很简单,如果实际上需要花一点时间来看看代码的正确性和逻辑性,而不是建议 generics 和评论只是为了让跳线有一些字节可以随身携带。 This section has to be modified a bit: since I use controller after the function returns, and without using smart pointers, I have to increase its reference count so that the library knows I use it and does not free it after the Invoke function body is executed.本节必须稍作修改:由于我在 function 返回后使用controller ,并且不使用智能指针,因此我必须增加其引用计数,以便库知道我使用它并且在Invoke ZC14C425274C178A 后不释放它执行。 Of course, when you do the assignment in the version with smart pointers, this is automatically done in the background.当然,当您在带有智能指针的版本中进行赋值时,这会在后台自动完成。 Unfortunately, I overlooked this, not being that aware this happens.不幸的是,我忽略了这一点,并没有意识到会发生这种情况。 Things like these is the reason I write these ports in C, so that I take an in-depth look and better understand how stuff works from top to bottom.像这样的事情是我在 C 中编写这些端口的原因,以便我深入了解并更好地了解这些东西是如何从上到下工作的。 'It can't be done' without an actual reason is not a valid answer for me.没有实际原因的“无法完成”对我来说不是一个有效的答案。

Anyway, here is the fixed version:无论如何,这是固定版本:

if (controller != NULL) {
    webviewController = controller;
    webviewController->lpVtbl->get_CoreWebView2(
        webviewController,
         &webviewWindow
    );
    webviewController->lpVtbl->AddRef(webviewController); // <-- here, increase the reference count for the webviewController
}

That's it.而已。 many thanks to the guy who helped me out: https://github.com/MicrosoftEdge/WebView2Feedback/issues/1124非常感谢帮助我的人: https://github.com/MicrosoftEdge/WebView2Feedback/issues/1124

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM