为了学习如何使用 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
这似乎与我的问题无关。我做错了什么?
这是一个 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 代码中使用任何类型的智能指针。