如何将 WebView2 控件添加到 MFC 项目中的 CDialog 资源?
How do I add a WebView2 control to a CDialog resource in a MFC project?
我已经能够从 Microsoft 下载并构建示例项目。我可以 运行 Win32 项目,它在视图中显示一个 WebView2 对象并显示功能。
对于我的情况,我想使用 CDialog
作为 WebView2 控件的父控件,但我不知道该怎么做。当我按照说明 here 进行操作时,它基于视图样式对象。在说明中它说:
Step 3 - Create a single WebView within the parent window
Add a WebView to the main window.
我在这里迷路了,不知道如何将控件添加到我的基本 CDialog
项目中。
感谢您指导如何处理这个问题。
这个选择 tutorial 帮助了我。我下载了示例项目,对其进行了编译并成功运行。同样,它基于从 CView
派生的应用程序。但是,我已经设法制定出所需的原则。
毫无疑问,随着我继续调整我正在制作的测试应用程序,还会涉及更多内容。
- 使用锅炉位置代码创建基于对话框的应用程序。
- 将 C++ 语言标准设置为 ISO C++ 17。
- 安装两个 NuGet 包:
Microsoft.Web.WebView2
NuGet 包
Microsoft.Windows.ImplementationLibrary
NuGet 包
- 将
InitInstance
更改为使用 CoInitialize
。
- 添加
ExitInstance
处理程序并调用 UnCoinitialize
。
出于测试目的,我只使用了对话框的尺寸。首先向对话框添加一个变量 class:
std::unique_ptr<CWebBrowser> m_pWebBrowser;
然后,在OnInitDialog
中你可以这样做:
m_pWebBrowser = std::make_unique<CWebBrowser>();
if (m_pWebBrowser != nullptr)
{
CRect rectClient;
GetClientRect(rectClient);
m_pWebBrowser->CreateAsync(
WS_VISIBLE | WS_CHILD,
rectClient,
this,
1,
[this]() {
m_pWebBrowser->SetParent(this);
m_pWebBrowser->DisablePopups();
m_pWebBrowser->Navigate(L"https://jw.org", nullptr);
m_pWebBrowser->RegisterCallback(CWebBrowser::CallbackType::TitleChanged, [this]() {
CString title = m_pWebBrowser->GetTitle();
AfxGetMainWnd()->SetWindowText(title);
});
});
}
为了测试,我还添加了 OnSize
处理程序:
void CMFCTestWebView2Dlg::OnSize(UINT nType, int cx, int cy)
{
CDialogEx::OnSize(nType, cx, cy);
CRect rectClient;
if (m_pWebBrowser != nullptr)
{
m_staticBrowser.GetClientRect(rectClient);
m_staticBrowser.ClientToScreen(rectClient);
ScreenToClient(rectClient);
m_pWebBrowser->Resize(rectClient.Width(), rectClient.Height());
}
}
它的核心是 CWebBrowser
class。这在链接教程中可用,我所做的只是调整 SetParentView
方法,使其接受 CWnd
指针。
哦,你在对话框中添加 DestroyWindow
class:
BOOL CMFCTestWebView2Dlg::DestroyWindow()
{
m_pWebBrowser.reset();
return CDialogEx::DestroyWindow();
}
CWebBrowser
Header
#pragma once
#include <EventToken.h>
#include <functional>
#include <map>
struct ICoreWebView2Environment;
struct ICoreWebView2Controller;
struct CWebBrowserImpl;
class CView;
class CWebBrowser : public CWnd
{
public:
enum class CallbackType
{
CreationCompleted,
NavigationCompleted,
TitleChanged,
};
using CallbackFunc = std::function<void()>;
public:
CWebBrowser();
virtual ~CWebBrowser();
virtual BOOL Create(
LPCTSTR lpszClassName,
LPCTSTR lpszWindowName,
DWORD dwStyle,
const RECT& rect,
CWnd* pParentWnd,
UINT nID,
CCreateContext* = NULL) override;
BOOL CreateAsync(
DWORD dwStyle,
const RECT& rect,
CWnd* pParentWnd,
UINT nID,
CallbackFunc onCreated);
void RegisterCallback(CallbackType const type, CallbackFunc callback);
RECT GetBounds();
void SetBounds(LONG const width, LONG const height) { Resize(width, height); }
void Resize(LONG const width, LONG const height);
CString GetLocationURL();
void Navigate(CString const & url, CallbackFunc onComplete);
void NavigatePost(CString const& url, CString const& content, CString const& headers, CallbackFunc onComplete = nullptr);
void GoBack();
void GoForward();
void Reload();
void Stop();
bool IsNavigating() const { return m_isNavigating; }
void DisablePopups();
void PrintDocument();
CString GetTitle() const { return m_strTitle; }
void SetParent(CWnd* pParent) { m_pParent = pParent; }
bool IsWebViewCreated() const;
protected:
DECLARE_DYNCREATE(CWebBrowser)
DECLARE_MESSAGE_MAP()
private:
CWebBrowserImpl* m_pImpl;
std::map<CallbackType, CallbackFunc> m_callbacks;
EventRegistrationToken m_navigationCompletedToken = {};
EventRegistrationToken m_navigationStartingToken = {};
EventRegistrationToken m_documentTitleChangedToken = {};
bool m_isNavigating = false;
CWnd* m_pParent = nullptr;
CString m_strTitle;
private:
void RunAsync(CallbackFunc callback);
void CloseWebView();
void RegisterEventHandlers();
void ResizeToClientArea();
void NavigateTo(CString url);
CString NormalizeUrl(CString url);
static CString GetInstallPath();
static CString GetInstallPathFromRegistry(bool const searchWebView = true);
static CString GetInstallPathFromDisk(bool const searchWebView = true);
static CString GetUserDataFolder();
void InitializeWebView();
HRESULT OnCreateEnvironmentCompleted(HRESULT result, ICoreWebView2Environment* environment);
HRESULT OnCreateWebViewControllerCompleted(HRESULT result, ICoreWebView2Controller* controller);
static PCTSTR GetWindowClass();
static LRESULT CALLBACK WndProcStatic(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
bool HandleWindowMessage(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT* result);
BOOL CreateHostWindow(
LPCTSTR lpszClassName,
LPCTSTR lpszWindowName,
DWORD dwStyle,
const RECT& rect,
CWnd* pParentWnd,
UINT nID);
};
CWebBrowser
来源
#include "pch.h"
#include <wrl.h>
#include "wil/com.h"
using namespace Microsoft::WRL;
#include "EdgeWebBrowser.h"
#include "Messages.h"
#include "WebView2.h"
#include <sstream>
#include <iomanip>
#include <shlwapi.h>
#pragma comment(lib,"shlwapi.lib")
#include "shlobj.h"
#pragma comment(lib,"Version.lib")
#include <string>
#define CHECK_FAILURE_STRINGIFY(arg) #arg
#define CHECK_FAILURE_FILE_LINE(file, line) ([](HRESULT hr){ CheckFailure(hr, L"Failure at " CHECK_FAILURE_STRINGIFY(file) L"(" CHECK_FAILURE_STRINGIFY(line) L")"); })
#define CHECK_FAILURE CHECK_FAILURE_FILE_LINE(__FILE__, __LINE__)
#define CHECK_FAILURE_BOOL(value) CHECK_FAILURE((value) ? S_OK : E_UNEXPECTED)
struct CWebBrowserImpl
{
wil::com_ptr<ICoreWebView2Environment> m_webViewEnvironment;
wil::com_ptr<ICoreWebView2Environment2> m_webViewEnvironment2;
wil::com_ptr<ICoreWebView2> m_webView;
wil::com_ptr<ICoreWebView2_2> m_webView2;
wil::com_ptr<ICoreWebView2Controller> m_webController;
wil::com_ptr<ICoreWebView2Settings> m_webSettings;
};
void ShowFailure(HRESULT hr, CString const & message)
{
CString text;
text.Format(L"%s (0x%08X)", (LPCTSTR)message, hr);
::MessageBox(nullptr, static_cast<LPCTSTR>(text), L"Failure", MB_OK);
}
void CheckFailure(HRESULT hr, CString const & message)
{
if (FAILED(hr))
{
CString text;
text.Format(L"%s : 0x%08X", (LPCTSTR)message, hr);
// TODO: log text
std::exit(hr);
}
}
/////////////////////////////////////////////////////////////////////////////
// CWebBrowser
IMPLEMENT_DYNCREATE(CWebBrowser, CWnd)
/////////////////////////////////////////////////////////////////////////////
// CWebBrowser properties
BEGIN_MESSAGE_MAP(CWebBrowser, CWnd)
END_MESSAGE_MAP()
CWebBrowser::CWebBrowser():m_pImpl(new CWebBrowserImpl())
{
m_callbacks[CallbackType::CreationCompleted] = nullptr;
m_callbacks[CallbackType::NavigationCompleted] = nullptr;
}
CWebBrowser::~CWebBrowser()
{
SetWindowLongPtr(m_hWnd, GWLP_USERDATA, 0);
CloseWebView();
delete m_pImpl;
}
BOOL CWebBrowser::CreateHostWindow(
LPCTSTR lpszClassName,
LPCTSTR lpszWindowName,
DWORD dwStyle,
const RECT& rect,
CWnd* pParentWnd,
UINT nID)
{
if (lpszClassName == nullptr)
lpszClassName = GetWindowClass();
if (!CWnd::Create(lpszClassName, lpszWindowName, dwStyle, rect, pParentWnd, nID))
return FALSE;
::SetWindowLongPtr(m_hWnd, GWLP_USERDATA, (LONG_PTR)this);
return TRUE;
}
BOOL CWebBrowser::Create(
LPCTSTR lpszClassName,
LPCTSTR lpszWindowName,
DWORD dwStyle,
const RECT& rect,
CWnd* pParentWnd,
UINT nID,
CCreateContext*)
{
if (!CreateHostWindow(lpszClassName, lpszWindowName, dwStyle, rect, pParentWnd, nID))
return FALSE;
InitializeWebView();
return TRUE;
}
BOOL CWebBrowser::CreateAsync(
DWORD dwStyle,
const RECT& rect,
CWnd* pParentWnd,
UINT nID,
CallbackFunc onCreated)
{
if (!CreateHostWindow(nullptr, nullptr, dwStyle, rect, pParentWnd, nID))
return FALSE;
m_callbacks[CallbackType::CreationCompleted] = onCreated;
InitializeWebView();
return TRUE;
}
void CWebBrowser::RegisterCallback(CallbackType const type, CallbackFunc callback)
{
m_callbacks[type] = callback;
}
void CWebBrowser::CloseWebView()
{
if (m_pImpl->m_webView)
{
m_pImpl->m_webView->remove_NavigationCompleted(m_navigationCompletedToken);
m_pImpl->m_webView->remove_NavigationStarting(m_navigationStartingToken);
m_pImpl->m_webView->remove_DocumentTitleChanged(m_documentTitleChangedToken);
m_pImpl->m_webController->Close();
m_pImpl->m_webController = nullptr;
m_pImpl->m_webView = nullptr;
m_pImpl->m_webView2 = nullptr;
m_pImpl->m_webSettings = nullptr;
}
m_pImpl->m_webViewEnvironment2 = nullptr;
m_pImpl->m_webViewEnvironment = nullptr;
}
void CWebBrowser::InitializeWebView()
{
CloseWebView();
CString subFolder = GetInstallPath();
CString appData = GetUserDataFolder();
ICoreWebView2EnvironmentOptions* options = nullptr;
HRESULT hr = CreateCoreWebView2EnvironmentWithOptions(
subFolder,
appData,
options,
Callback<ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler>(
this,
&CWebBrowser::OnCreateEnvironmentCompleted).Get());
if (!SUCCEEDED(hr))
{
CString text;
if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
{
text = L"Cannot found the WebView2 component.";
}
else
{
text = L"Cannot create the webview environment.";
}
ShowFailure(hr, text);
}
}
HRESULT CWebBrowser::OnCreateEnvironmentCompleted(
HRESULT result,
ICoreWebView2Environment* environment)
{
CHECK_FAILURE(result);
if (!environment)
return E_FAIL;
CHECK_FAILURE(environment->QueryInterface(IID_PPV_ARGS(&m_pImpl->m_webViewEnvironment)));
CHECK_FAILURE(environment->QueryInterface(IID_PPV_ARGS(&m_pImpl->m_webViewEnvironment2)));
CHECK_FAILURE(m_pImpl->m_webViewEnvironment->CreateCoreWebView2Controller(
m_hWnd,
Callback<ICoreWebView2CreateCoreWebView2ControllerCompletedHandler>(
this,
&CWebBrowser::OnCreateWebViewControllerCompleted).Get()));
return S_OK;
}
HRESULT CWebBrowser::OnCreateWebViewControllerCompleted(
HRESULT result,
ICoreWebView2Controller* controller)
{
if (result == S_OK)
{
if (controller != nullptr)
{
m_pImpl->m_webController = controller;
CHECK_FAILURE(controller->get_CoreWebView2(&m_pImpl->m_webView));
if (!m_pImpl->m_webView)
return E_FAIL;
CHECK_FAILURE(m_pImpl->m_webView->QueryInterface(IID_PPV_ARGS(&m_pImpl->m_webView2)));
CHECK_FAILURE(m_pImpl->m_webView->get_Settings(&m_pImpl->m_webSettings));
RegisterEventHandlers();
ResizeToClientArea();
}
auto callback = m_callbacks[CallbackType::CreationCompleted];
if (callback != nullptr)
RunAsync(callback);
}
else
{
ShowFailure(result, L"Cannot create webview environment.");
}
return S_OK;
}
void CWebBrowser::RegisterEventHandlers()
{
// NavigationCompleted handler
CHECK_FAILURE(m_pImpl->m_webView->add_NavigationCompleted(
Callback<ICoreWebView2NavigationCompletedEventHandler>(
[this](
ICoreWebView2*,
ICoreWebView2NavigationCompletedEventArgs* args) -> HRESULT
{
m_isNavigating = false;
BOOL success;
CHECK_FAILURE(args->get_IsSuccess(&success));
if (!success)
{
COREWEBVIEW2_WEB_ERROR_STATUS webErrorStatus{};
CHECK_FAILURE(args->get_WebErrorStatus(&webErrorStatus));
if (webErrorStatus == COREWEBVIEW2_WEB_ERROR_STATUS_DISCONNECTED)
{
// Do something here if you want to handle a specific error case.
// In most cases this isn't necessary, because the WebView will
// display its own error page automatically.
}
}
wil::unique_cotaskmem_string uri;
m_pImpl->m_webView->get_Source(&uri);
if (wcscmp(uri.get(), L"about:blank") == 0)
{
uri = wil::make_cotaskmem_string(L"");
}
auto callback = m_callbacks[CallbackType::NavigationCompleted];
if (callback != nullptr)
RunAsync(callback);
return S_OK;
})
.Get(),
&m_navigationCompletedToken));
// NavigationStarting handler
CHECK_FAILURE(m_pImpl->m_webView->add_NavigationStarting(
Callback<ICoreWebView2NavigationStartingEventHandler>(
[this](
ICoreWebView2*,
ICoreWebView2NavigationStartingEventArgs* args) -> HRESULT
{
wil::unique_cotaskmem_string uri;
CHECK_FAILURE(args->get_Uri(&uri));
m_isNavigating = true;
return S_OK;
}).Get(), &m_navigationStartingToken));
// DocumentTitleChanged handler
CHECK_FAILURE(m_pImpl->m_webView->add_DocumentTitleChanged(
Callback<ICoreWebView2DocumentTitleChangedEventHandler>(
[this](ICoreWebView2* sender, IUnknown* args) -> HRESULT {
wil::unique_cotaskmem_string title;
CHECK_FAILURE(sender->get_DocumentTitle(&title));
m_strTitle = title.get();
auto callback = m_callbacks[CallbackType::TitleChanged];
if (callback != nullptr)
RunAsync(callback);
return S_OK;
})
.Get(), &m_documentTitleChangedToken));
}
void CWebBrowser::ResizeToClientArea()
{
if (m_pImpl->m_webController)
{
RECT bounds;
GetClientRect(&bounds);
m_pImpl->m_webController->put_Bounds(bounds);
}
}
RECT CWebBrowser::GetBounds()
{
RECT rc{0,0,0,0};
if (m_pImpl->m_webController)
{
m_pImpl->m_webController->get_Bounds(&rc);
}
return rc;
}
void CWebBrowser::Resize(LONG const width, LONG const height)
{
SetWindowPos(nullptr, 0, 0, width, height, SWP_NOMOVE| SWP_NOREPOSITION);
}
CString CWebBrowser::GetLocationURL()
{
CString url;
if (m_pImpl->m_webView)
{
wil::unique_cotaskmem_string uri;
m_pImpl->m_webView->get_Source(&uri);
if (wcscmp(uri.get(), L"about:blank") == 0)
{
uri = wil::make_cotaskmem_string(L"");
}
url = uri.get();
}
return url;
}
CString CWebBrowser::NormalizeUrl(CString url)
{
if (url.Find(_T("://")) < 0)
{
if (url.GetLength() > 1 && url[1] == ':')
url = _T("file://") + url;
else
url = _T("http://") + url;
}
return url;
}
void CWebBrowser::NavigateTo(CString url)
{
m_pImpl->m_webView->Navigate(NormalizeUrl(url));
}
void CWebBrowser::Navigate(CString const & url, CallbackFunc onComplete)
{
if (m_pImpl->m_webView)
{
m_callbacks[CallbackType::NavigationCompleted] = onComplete;
NavigateTo(url);
}
}
// The raw request header string delimited by CRLF(optional in last header).
void CWebBrowser::NavigatePost(CString const& url, CString const& content, CString const& headers, std::function<void()> onComplete)
{
if (!m_pImpl->m_webView) return;
CString normalizedUrl{ NormalizeUrl(url) };
m_callbacks[CallbackType::NavigationCompleted] = onComplete;
wil::com_ptr<ICoreWebView2WebResourceRequest> webResourceRequest;
wil::com_ptr<IStream> postDataStream = SHCreateMemStream(
reinterpret_cast<const BYTE*>(static_cast<LPCTSTR>(content)),
content.GetLength() + 1);
CHECK_FAILURE(m_pImpl->m_webViewEnvironment2->CreateWebResourceRequest(
CT2W(normalizedUrl),
L"POST",
postDataStream.get(),
CT2W(headers),
&webResourceRequest));
CHECK_FAILURE(m_pImpl->m_webView2->NavigateWithWebResourceRequest(webResourceRequest.get()));
}
void CWebBrowser::PrintDocument()
{
if (m_pImpl->m_webView)
{
m_pImpl->m_webView->ExecuteScript(L"window.print();", nullptr);
}
}
void CWebBrowser::Stop()
{
if (m_pImpl->m_webView)
{
m_pImpl->m_webView->Stop();
}
}
void CWebBrowser::Reload()
{
if (m_pImpl->m_webView)
{
m_pImpl->m_webView->Reload();
}
}
void CWebBrowser::GoBack()
{
if (m_pImpl->m_webView)
{
BOOL possible = FALSE;
m_pImpl->m_webView->get_CanGoBack(&possible);
if(possible)
m_pImpl->m_webView->GoBack();
}
}
void CWebBrowser::GoForward()
{
if (m_pImpl->m_webView)
{
BOOL possible = FALSE;
m_pImpl->m_webView->get_CanGoForward(&possible);
if (possible)
m_pImpl->m_webView->GoForward();
}
}
void CWebBrowser::DisablePopups()
{
if (m_pImpl->m_webSettings)
{
m_pImpl->m_webSettings->put_AreDefaultScriptDialogsEnabled(FALSE);
}
}
PCTSTR CWebBrowser::GetWindowClass()
{
static PCTSTR windowClass = []
{
static TCHAR const * className = L"EdgeBrowserHost";
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProcStatic;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = AfxGetInstanceHandle();
wcex.hIcon = nullptr;
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = nullptr;
wcex.lpszClassName = className;
wcex.hIconSm = nullptr;
ATOM result = RegisterClassEx(&wcex);
if (result == 0)
{
[[maybe_unused]] DWORD lastError = ::GetLastError();
}
return className;
}();
return windowClass;
}
LRESULT CALLBACK CWebBrowser::WndProcStatic(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
if (auto app = (CWebBrowser*)::GetWindowLongPtr(hWnd, GWLP_USERDATA))
{
LRESULT result = 0;
if (app->HandleWindowMessage(hWnd, message, wParam, lParam, &result))
{
return result;
}
}
return ::DefWindowProc(hWnd, message, wParam, lParam);
}
bool CWebBrowser::HandleWindowMessage(
HWND, UINT message, WPARAM wParam, LPARAM lParam, LRESULT* result)
{
*result = 0;
switch (message)
{
case WM_SIZE:
{
if (lParam != 0)
{
ResizeToClientArea();
return true;
}
}
break;
case MSG_RUN_ASYNC_CALLBACK:
{
auto* task = reinterpret_cast<CallbackFunc*>(wParam);
(*task)();
delete task;
return true;
}
break;
}
return false;
}
void CWebBrowser::RunAsync(CallbackFunc callback)
{
auto* task = new CallbackFunc(callback);
PostMessage(MSG_RUN_ASYNC_CALLBACK, reinterpret_cast<WPARAM>(task), 0);
}
bool CWebBrowser::IsWebViewCreated() const
{
return m_pImpl->m_webView != nullptr;
}
CString CWebBrowser::GetInstallPath()
{
static CString path = []
{
auto installPath = GetInstallPathFromRegistry(); // check registry for WebView2
if (installPath.IsEmpty())
installPath = GetInstallPathFromDisk(); // check disk for WebView2
if (installPath.IsEmpty())
installPath = GetInstallPathFromRegistry(false);// check registry for Edge
if (installPath.IsEmpty())
installPath = GetInstallPathFromDisk(false); // check disk for Edge
return installPath;
}();
return path;
}
CString CWebBrowser::GetInstallPathFromRegistry(bool const searchWebView)
{
CString path;
HKEY handle = nullptr;
LSTATUS result = ERROR_FILE_NOT_FOUND;
if (searchWebView)
{
result = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
LR"(SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Microsoft EdgeWebView)",
0,
KEY_READ,
&handle);
if (result != ERROR_SUCCESS)
result = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
LR"(SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Microsoft EdgeWebView)",
0,
KEY_READ,
&handle);
}
else
{
result = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
LR"(SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Microsoft Edge)",
0,
KEY_READ,
&handle);
if (result != ERROR_SUCCESS)
result = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
LR"(SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Microsoft Edge)",
0,
KEY_READ,
&handle);
}
if (result == ERROR_SUCCESS)
{
TCHAR buffer[MAX_PATH + 1]{ 0 };
DWORD type = REG_SZ;
DWORD size = MAX_PATH;
result = RegQueryValueEx(handle, L"InstallLocation", 0, &type, reinterpret_cast<LPBYTE>(buffer), &size);
if (result == ERROR_SUCCESS)
path += CString{ buffer };
TCHAR version[100]{ 0 };
size = 100;
result = RegQueryValueEx(handle, L"Version", 0, &type, reinterpret_cast<LPBYTE>(version), &size);
if (result == ERROR_SUCCESS)
{
if (path.GetAt(path.GetLength() - 1) != L'\')
path += L"\";
path += CString{ version };
}
else
path.Empty();
RegCloseKey(handle);
}
return path;
}
CString CWebBrowser::GetInstallPathFromDisk(bool const searchWebView)
{
CString path =
searchWebView ?
LR"(c:\Program Files (x86)\Microsoft\EdgeWebView\Application\)" :
LR"(c:\Program Files (x86)\Microsoft\Edge\Application\)";
CString pattern = path + L"*";
WIN32_FIND_DATA ffd{ 0 };
HANDLE hFind = FindFirstFile(pattern, &ffd);
if (hFind == INVALID_HANDLE_VALUE)
{
[[maybe_unused]] DWORD error = ::GetLastError();
return {};
}
do
{
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
CString name{ ffd.cFileName };
int a, b, c, d;
if (4 == swscanf_s(ffd.cFileName, L"%d.%d.%d.%d", &a, &b, &c, &d))
{
FindClose(hFind);
return path + name;
}
}
} while (FindNextFile(hFind, &ffd) != 0);
FindClose(hFind);
return {};
}
CString CWebBrowser::GetUserDataFolder()
{
TCHAR szPath[MAX_PATH]{ 0 };
::SHGetFolderPath(nullptr, CSIDL_LOCAL_APPDATA, nullptr, 0, szPath);
::PathAppend(szPath, LR"(\Demos\)");
::PathAppend(szPath, L"MfcEdgeDemo");
return CString{ szPath };
}
有效。最后的评论,至少对于当前的操作系统......确保你已经安装了 WebView2 Runtime.
我已经能够从 Microsoft 下载并构建示例项目。我可以 运行 Win32 项目,它在视图中显示一个 WebView2 对象并显示功能。
对于我的情况,我想使用 CDialog
作为 WebView2 控件的父控件,但我不知道该怎么做。当我按照说明 here 进行操作时,它基于视图样式对象。在说明中它说:
Step 3 - Create a single WebView within the parent window
Add a WebView to the main window.
我在这里迷路了,不知道如何将控件添加到我的基本 CDialog
项目中。
感谢您指导如何处理这个问题。
这个选择 tutorial 帮助了我。我下载了示例项目,对其进行了编译并成功运行。同样,它基于从 CView
派生的应用程序。但是,我已经设法制定出所需的原则。
毫无疑问,随着我继续调整我正在制作的测试应用程序,还会涉及更多内容。
- 使用锅炉位置代码创建基于对话框的应用程序。
- 将 C++ 语言标准设置为 ISO C++ 17。
- 安装两个 NuGet 包:
Microsoft.Web.WebView2
NuGet 包Microsoft.Windows.ImplementationLibrary
NuGet 包
- 将
InitInstance
更改为使用CoInitialize
。 - 添加
ExitInstance
处理程序并调用UnCoinitialize
。
出于测试目的,我只使用了对话框的尺寸。首先向对话框添加一个变量 class:
std::unique_ptr<CWebBrowser> m_pWebBrowser;
然后,在OnInitDialog
中你可以这样做:
m_pWebBrowser = std::make_unique<CWebBrowser>();
if (m_pWebBrowser != nullptr)
{
CRect rectClient;
GetClientRect(rectClient);
m_pWebBrowser->CreateAsync(
WS_VISIBLE | WS_CHILD,
rectClient,
this,
1,
[this]() {
m_pWebBrowser->SetParent(this);
m_pWebBrowser->DisablePopups();
m_pWebBrowser->Navigate(L"https://jw.org", nullptr);
m_pWebBrowser->RegisterCallback(CWebBrowser::CallbackType::TitleChanged, [this]() {
CString title = m_pWebBrowser->GetTitle();
AfxGetMainWnd()->SetWindowText(title);
});
});
}
为了测试,我还添加了 OnSize
处理程序:
void CMFCTestWebView2Dlg::OnSize(UINT nType, int cx, int cy)
{
CDialogEx::OnSize(nType, cx, cy);
CRect rectClient;
if (m_pWebBrowser != nullptr)
{
m_staticBrowser.GetClientRect(rectClient);
m_staticBrowser.ClientToScreen(rectClient);
ScreenToClient(rectClient);
m_pWebBrowser->Resize(rectClient.Width(), rectClient.Height());
}
}
它的核心是 CWebBrowser
class。这在链接教程中可用,我所做的只是调整 SetParentView
方法,使其接受 CWnd
指针。
哦,你在对话框中添加 DestroyWindow
class:
BOOL CMFCTestWebView2Dlg::DestroyWindow()
{
m_pWebBrowser.reset();
return CDialogEx::DestroyWindow();
}
CWebBrowser
Header
#pragma once
#include <EventToken.h>
#include <functional>
#include <map>
struct ICoreWebView2Environment;
struct ICoreWebView2Controller;
struct CWebBrowserImpl;
class CView;
class CWebBrowser : public CWnd
{
public:
enum class CallbackType
{
CreationCompleted,
NavigationCompleted,
TitleChanged,
};
using CallbackFunc = std::function<void()>;
public:
CWebBrowser();
virtual ~CWebBrowser();
virtual BOOL Create(
LPCTSTR lpszClassName,
LPCTSTR lpszWindowName,
DWORD dwStyle,
const RECT& rect,
CWnd* pParentWnd,
UINT nID,
CCreateContext* = NULL) override;
BOOL CreateAsync(
DWORD dwStyle,
const RECT& rect,
CWnd* pParentWnd,
UINT nID,
CallbackFunc onCreated);
void RegisterCallback(CallbackType const type, CallbackFunc callback);
RECT GetBounds();
void SetBounds(LONG const width, LONG const height) { Resize(width, height); }
void Resize(LONG const width, LONG const height);
CString GetLocationURL();
void Navigate(CString const & url, CallbackFunc onComplete);
void NavigatePost(CString const& url, CString const& content, CString const& headers, CallbackFunc onComplete = nullptr);
void GoBack();
void GoForward();
void Reload();
void Stop();
bool IsNavigating() const { return m_isNavigating; }
void DisablePopups();
void PrintDocument();
CString GetTitle() const { return m_strTitle; }
void SetParent(CWnd* pParent) { m_pParent = pParent; }
bool IsWebViewCreated() const;
protected:
DECLARE_DYNCREATE(CWebBrowser)
DECLARE_MESSAGE_MAP()
private:
CWebBrowserImpl* m_pImpl;
std::map<CallbackType, CallbackFunc> m_callbacks;
EventRegistrationToken m_navigationCompletedToken = {};
EventRegistrationToken m_navigationStartingToken = {};
EventRegistrationToken m_documentTitleChangedToken = {};
bool m_isNavigating = false;
CWnd* m_pParent = nullptr;
CString m_strTitle;
private:
void RunAsync(CallbackFunc callback);
void CloseWebView();
void RegisterEventHandlers();
void ResizeToClientArea();
void NavigateTo(CString url);
CString NormalizeUrl(CString url);
static CString GetInstallPath();
static CString GetInstallPathFromRegistry(bool const searchWebView = true);
static CString GetInstallPathFromDisk(bool const searchWebView = true);
static CString GetUserDataFolder();
void InitializeWebView();
HRESULT OnCreateEnvironmentCompleted(HRESULT result, ICoreWebView2Environment* environment);
HRESULT OnCreateWebViewControllerCompleted(HRESULT result, ICoreWebView2Controller* controller);
static PCTSTR GetWindowClass();
static LRESULT CALLBACK WndProcStatic(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
bool HandleWindowMessage(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT* result);
BOOL CreateHostWindow(
LPCTSTR lpszClassName,
LPCTSTR lpszWindowName,
DWORD dwStyle,
const RECT& rect,
CWnd* pParentWnd,
UINT nID);
};
CWebBrowser
来源
#include "pch.h"
#include <wrl.h>
#include "wil/com.h"
using namespace Microsoft::WRL;
#include "EdgeWebBrowser.h"
#include "Messages.h"
#include "WebView2.h"
#include <sstream>
#include <iomanip>
#include <shlwapi.h>
#pragma comment(lib,"shlwapi.lib")
#include "shlobj.h"
#pragma comment(lib,"Version.lib")
#include <string>
#define CHECK_FAILURE_STRINGIFY(arg) #arg
#define CHECK_FAILURE_FILE_LINE(file, line) ([](HRESULT hr){ CheckFailure(hr, L"Failure at " CHECK_FAILURE_STRINGIFY(file) L"(" CHECK_FAILURE_STRINGIFY(line) L")"); })
#define CHECK_FAILURE CHECK_FAILURE_FILE_LINE(__FILE__, __LINE__)
#define CHECK_FAILURE_BOOL(value) CHECK_FAILURE((value) ? S_OK : E_UNEXPECTED)
struct CWebBrowserImpl
{
wil::com_ptr<ICoreWebView2Environment> m_webViewEnvironment;
wil::com_ptr<ICoreWebView2Environment2> m_webViewEnvironment2;
wil::com_ptr<ICoreWebView2> m_webView;
wil::com_ptr<ICoreWebView2_2> m_webView2;
wil::com_ptr<ICoreWebView2Controller> m_webController;
wil::com_ptr<ICoreWebView2Settings> m_webSettings;
};
void ShowFailure(HRESULT hr, CString const & message)
{
CString text;
text.Format(L"%s (0x%08X)", (LPCTSTR)message, hr);
::MessageBox(nullptr, static_cast<LPCTSTR>(text), L"Failure", MB_OK);
}
void CheckFailure(HRESULT hr, CString const & message)
{
if (FAILED(hr))
{
CString text;
text.Format(L"%s : 0x%08X", (LPCTSTR)message, hr);
// TODO: log text
std::exit(hr);
}
}
/////////////////////////////////////////////////////////////////////////////
// CWebBrowser
IMPLEMENT_DYNCREATE(CWebBrowser, CWnd)
/////////////////////////////////////////////////////////////////////////////
// CWebBrowser properties
BEGIN_MESSAGE_MAP(CWebBrowser, CWnd)
END_MESSAGE_MAP()
CWebBrowser::CWebBrowser():m_pImpl(new CWebBrowserImpl())
{
m_callbacks[CallbackType::CreationCompleted] = nullptr;
m_callbacks[CallbackType::NavigationCompleted] = nullptr;
}
CWebBrowser::~CWebBrowser()
{
SetWindowLongPtr(m_hWnd, GWLP_USERDATA, 0);
CloseWebView();
delete m_pImpl;
}
BOOL CWebBrowser::CreateHostWindow(
LPCTSTR lpszClassName,
LPCTSTR lpszWindowName,
DWORD dwStyle,
const RECT& rect,
CWnd* pParentWnd,
UINT nID)
{
if (lpszClassName == nullptr)
lpszClassName = GetWindowClass();
if (!CWnd::Create(lpszClassName, lpszWindowName, dwStyle, rect, pParentWnd, nID))
return FALSE;
::SetWindowLongPtr(m_hWnd, GWLP_USERDATA, (LONG_PTR)this);
return TRUE;
}
BOOL CWebBrowser::Create(
LPCTSTR lpszClassName,
LPCTSTR lpszWindowName,
DWORD dwStyle,
const RECT& rect,
CWnd* pParentWnd,
UINT nID,
CCreateContext*)
{
if (!CreateHostWindow(lpszClassName, lpszWindowName, dwStyle, rect, pParentWnd, nID))
return FALSE;
InitializeWebView();
return TRUE;
}
BOOL CWebBrowser::CreateAsync(
DWORD dwStyle,
const RECT& rect,
CWnd* pParentWnd,
UINT nID,
CallbackFunc onCreated)
{
if (!CreateHostWindow(nullptr, nullptr, dwStyle, rect, pParentWnd, nID))
return FALSE;
m_callbacks[CallbackType::CreationCompleted] = onCreated;
InitializeWebView();
return TRUE;
}
void CWebBrowser::RegisterCallback(CallbackType const type, CallbackFunc callback)
{
m_callbacks[type] = callback;
}
void CWebBrowser::CloseWebView()
{
if (m_pImpl->m_webView)
{
m_pImpl->m_webView->remove_NavigationCompleted(m_navigationCompletedToken);
m_pImpl->m_webView->remove_NavigationStarting(m_navigationStartingToken);
m_pImpl->m_webView->remove_DocumentTitleChanged(m_documentTitleChangedToken);
m_pImpl->m_webController->Close();
m_pImpl->m_webController = nullptr;
m_pImpl->m_webView = nullptr;
m_pImpl->m_webView2 = nullptr;
m_pImpl->m_webSettings = nullptr;
}
m_pImpl->m_webViewEnvironment2 = nullptr;
m_pImpl->m_webViewEnvironment = nullptr;
}
void CWebBrowser::InitializeWebView()
{
CloseWebView();
CString subFolder = GetInstallPath();
CString appData = GetUserDataFolder();
ICoreWebView2EnvironmentOptions* options = nullptr;
HRESULT hr = CreateCoreWebView2EnvironmentWithOptions(
subFolder,
appData,
options,
Callback<ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler>(
this,
&CWebBrowser::OnCreateEnvironmentCompleted).Get());
if (!SUCCEEDED(hr))
{
CString text;
if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
{
text = L"Cannot found the WebView2 component.";
}
else
{
text = L"Cannot create the webview environment.";
}
ShowFailure(hr, text);
}
}
HRESULT CWebBrowser::OnCreateEnvironmentCompleted(
HRESULT result,
ICoreWebView2Environment* environment)
{
CHECK_FAILURE(result);
if (!environment)
return E_FAIL;
CHECK_FAILURE(environment->QueryInterface(IID_PPV_ARGS(&m_pImpl->m_webViewEnvironment)));
CHECK_FAILURE(environment->QueryInterface(IID_PPV_ARGS(&m_pImpl->m_webViewEnvironment2)));
CHECK_FAILURE(m_pImpl->m_webViewEnvironment->CreateCoreWebView2Controller(
m_hWnd,
Callback<ICoreWebView2CreateCoreWebView2ControllerCompletedHandler>(
this,
&CWebBrowser::OnCreateWebViewControllerCompleted).Get()));
return S_OK;
}
HRESULT CWebBrowser::OnCreateWebViewControllerCompleted(
HRESULT result,
ICoreWebView2Controller* controller)
{
if (result == S_OK)
{
if (controller != nullptr)
{
m_pImpl->m_webController = controller;
CHECK_FAILURE(controller->get_CoreWebView2(&m_pImpl->m_webView));
if (!m_pImpl->m_webView)
return E_FAIL;
CHECK_FAILURE(m_pImpl->m_webView->QueryInterface(IID_PPV_ARGS(&m_pImpl->m_webView2)));
CHECK_FAILURE(m_pImpl->m_webView->get_Settings(&m_pImpl->m_webSettings));
RegisterEventHandlers();
ResizeToClientArea();
}
auto callback = m_callbacks[CallbackType::CreationCompleted];
if (callback != nullptr)
RunAsync(callback);
}
else
{
ShowFailure(result, L"Cannot create webview environment.");
}
return S_OK;
}
void CWebBrowser::RegisterEventHandlers()
{
// NavigationCompleted handler
CHECK_FAILURE(m_pImpl->m_webView->add_NavigationCompleted(
Callback<ICoreWebView2NavigationCompletedEventHandler>(
[this](
ICoreWebView2*,
ICoreWebView2NavigationCompletedEventArgs* args) -> HRESULT
{
m_isNavigating = false;
BOOL success;
CHECK_FAILURE(args->get_IsSuccess(&success));
if (!success)
{
COREWEBVIEW2_WEB_ERROR_STATUS webErrorStatus{};
CHECK_FAILURE(args->get_WebErrorStatus(&webErrorStatus));
if (webErrorStatus == COREWEBVIEW2_WEB_ERROR_STATUS_DISCONNECTED)
{
// Do something here if you want to handle a specific error case.
// In most cases this isn't necessary, because the WebView will
// display its own error page automatically.
}
}
wil::unique_cotaskmem_string uri;
m_pImpl->m_webView->get_Source(&uri);
if (wcscmp(uri.get(), L"about:blank") == 0)
{
uri = wil::make_cotaskmem_string(L"");
}
auto callback = m_callbacks[CallbackType::NavigationCompleted];
if (callback != nullptr)
RunAsync(callback);
return S_OK;
})
.Get(),
&m_navigationCompletedToken));
// NavigationStarting handler
CHECK_FAILURE(m_pImpl->m_webView->add_NavigationStarting(
Callback<ICoreWebView2NavigationStartingEventHandler>(
[this](
ICoreWebView2*,
ICoreWebView2NavigationStartingEventArgs* args) -> HRESULT
{
wil::unique_cotaskmem_string uri;
CHECK_FAILURE(args->get_Uri(&uri));
m_isNavigating = true;
return S_OK;
}).Get(), &m_navigationStartingToken));
// DocumentTitleChanged handler
CHECK_FAILURE(m_pImpl->m_webView->add_DocumentTitleChanged(
Callback<ICoreWebView2DocumentTitleChangedEventHandler>(
[this](ICoreWebView2* sender, IUnknown* args) -> HRESULT {
wil::unique_cotaskmem_string title;
CHECK_FAILURE(sender->get_DocumentTitle(&title));
m_strTitle = title.get();
auto callback = m_callbacks[CallbackType::TitleChanged];
if (callback != nullptr)
RunAsync(callback);
return S_OK;
})
.Get(), &m_documentTitleChangedToken));
}
void CWebBrowser::ResizeToClientArea()
{
if (m_pImpl->m_webController)
{
RECT bounds;
GetClientRect(&bounds);
m_pImpl->m_webController->put_Bounds(bounds);
}
}
RECT CWebBrowser::GetBounds()
{
RECT rc{0,0,0,0};
if (m_pImpl->m_webController)
{
m_pImpl->m_webController->get_Bounds(&rc);
}
return rc;
}
void CWebBrowser::Resize(LONG const width, LONG const height)
{
SetWindowPos(nullptr, 0, 0, width, height, SWP_NOMOVE| SWP_NOREPOSITION);
}
CString CWebBrowser::GetLocationURL()
{
CString url;
if (m_pImpl->m_webView)
{
wil::unique_cotaskmem_string uri;
m_pImpl->m_webView->get_Source(&uri);
if (wcscmp(uri.get(), L"about:blank") == 0)
{
uri = wil::make_cotaskmem_string(L"");
}
url = uri.get();
}
return url;
}
CString CWebBrowser::NormalizeUrl(CString url)
{
if (url.Find(_T("://")) < 0)
{
if (url.GetLength() > 1 && url[1] == ':')
url = _T("file://") + url;
else
url = _T("http://") + url;
}
return url;
}
void CWebBrowser::NavigateTo(CString url)
{
m_pImpl->m_webView->Navigate(NormalizeUrl(url));
}
void CWebBrowser::Navigate(CString const & url, CallbackFunc onComplete)
{
if (m_pImpl->m_webView)
{
m_callbacks[CallbackType::NavigationCompleted] = onComplete;
NavigateTo(url);
}
}
// The raw request header string delimited by CRLF(optional in last header).
void CWebBrowser::NavigatePost(CString const& url, CString const& content, CString const& headers, std::function<void()> onComplete)
{
if (!m_pImpl->m_webView) return;
CString normalizedUrl{ NormalizeUrl(url) };
m_callbacks[CallbackType::NavigationCompleted] = onComplete;
wil::com_ptr<ICoreWebView2WebResourceRequest> webResourceRequest;
wil::com_ptr<IStream> postDataStream = SHCreateMemStream(
reinterpret_cast<const BYTE*>(static_cast<LPCTSTR>(content)),
content.GetLength() + 1);
CHECK_FAILURE(m_pImpl->m_webViewEnvironment2->CreateWebResourceRequest(
CT2W(normalizedUrl),
L"POST",
postDataStream.get(),
CT2W(headers),
&webResourceRequest));
CHECK_FAILURE(m_pImpl->m_webView2->NavigateWithWebResourceRequest(webResourceRequest.get()));
}
void CWebBrowser::PrintDocument()
{
if (m_pImpl->m_webView)
{
m_pImpl->m_webView->ExecuteScript(L"window.print();", nullptr);
}
}
void CWebBrowser::Stop()
{
if (m_pImpl->m_webView)
{
m_pImpl->m_webView->Stop();
}
}
void CWebBrowser::Reload()
{
if (m_pImpl->m_webView)
{
m_pImpl->m_webView->Reload();
}
}
void CWebBrowser::GoBack()
{
if (m_pImpl->m_webView)
{
BOOL possible = FALSE;
m_pImpl->m_webView->get_CanGoBack(&possible);
if(possible)
m_pImpl->m_webView->GoBack();
}
}
void CWebBrowser::GoForward()
{
if (m_pImpl->m_webView)
{
BOOL possible = FALSE;
m_pImpl->m_webView->get_CanGoForward(&possible);
if (possible)
m_pImpl->m_webView->GoForward();
}
}
void CWebBrowser::DisablePopups()
{
if (m_pImpl->m_webSettings)
{
m_pImpl->m_webSettings->put_AreDefaultScriptDialogsEnabled(FALSE);
}
}
PCTSTR CWebBrowser::GetWindowClass()
{
static PCTSTR windowClass = []
{
static TCHAR const * className = L"EdgeBrowserHost";
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProcStatic;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = AfxGetInstanceHandle();
wcex.hIcon = nullptr;
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = nullptr;
wcex.lpszClassName = className;
wcex.hIconSm = nullptr;
ATOM result = RegisterClassEx(&wcex);
if (result == 0)
{
[[maybe_unused]] DWORD lastError = ::GetLastError();
}
return className;
}();
return windowClass;
}
LRESULT CALLBACK CWebBrowser::WndProcStatic(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
if (auto app = (CWebBrowser*)::GetWindowLongPtr(hWnd, GWLP_USERDATA))
{
LRESULT result = 0;
if (app->HandleWindowMessage(hWnd, message, wParam, lParam, &result))
{
return result;
}
}
return ::DefWindowProc(hWnd, message, wParam, lParam);
}
bool CWebBrowser::HandleWindowMessage(
HWND, UINT message, WPARAM wParam, LPARAM lParam, LRESULT* result)
{
*result = 0;
switch (message)
{
case WM_SIZE:
{
if (lParam != 0)
{
ResizeToClientArea();
return true;
}
}
break;
case MSG_RUN_ASYNC_CALLBACK:
{
auto* task = reinterpret_cast<CallbackFunc*>(wParam);
(*task)();
delete task;
return true;
}
break;
}
return false;
}
void CWebBrowser::RunAsync(CallbackFunc callback)
{
auto* task = new CallbackFunc(callback);
PostMessage(MSG_RUN_ASYNC_CALLBACK, reinterpret_cast<WPARAM>(task), 0);
}
bool CWebBrowser::IsWebViewCreated() const
{
return m_pImpl->m_webView != nullptr;
}
CString CWebBrowser::GetInstallPath()
{
static CString path = []
{
auto installPath = GetInstallPathFromRegistry(); // check registry for WebView2
if (installPath.IsEmpty())
installPath = GetInstallPathFromDisk(); // check disk for WebView2
if (installPath.IsEmpty())
installPath = GetInstallPathFromRegistry(false);// check registry for Edge
if (installPath.IsEmpty())
installPath = GetInstallPathFromDisk(false); // check disk for Edge
return installPath;
}();
return path;
}
CString CWebBrowser::GetInstallPathFromRegistry(bool const searchWebView)
{
CString path;
HKEY handle = nullptr;
LSTATUS result = ERROR_FILE_NOT_FOUND;
if (searchWebView)
{
result = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
LR"(SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Microsoft EdgeWebView)",
0,
KEY_READ,
&handle);
if (result != ERROR_SUCCESS)
result = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
LR"(SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Microsoft EdgeWebView)",
0,
KEY_READ,
&handle);
}
else
{
result = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
LR"(SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Microsoft Edge)",
0,
KEY_READ,
&handle);
if (result != ERROR_SUCCESS)
result = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
LR"(SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Microsoft Edge)",
0,
KEY_READ,
&handle);
}
if (result == ERROR_SUCCESS)
{
TCHAR buffer[MAX_PATH + 1]{ 0 };
DWORD type = REG_SZ;
DWORD size = MAX_PATH;
result = RegQueryValueEx(handle, L"InstallLocation", 0, &type, reinterpret_cast<LPBYTE>(buffer), &size);
if (result == ERROR_SUCCESS)
path += CString{ buffer };
TCHAR version[100]{ 0 };
size = 100;
result = RegQueryValueEx(handle, L"Version", 0, &type, reinterpret_cast<LPBYTE>(version), &size);
if (result == ERROR_SUCCESS)
{
if (path.GetAt(path.GetLength() - 1) != L'\')
path += L"\";
path += CString{ version };
}
else
path.Empty();
RegCloseKey(handle);
}
return path;
}
CString CWebBrowser::GetInstallPathFromDisk(bool const searchWebView)
{
CString path =
searchWebView ?
LR"(c:\Program Files (x86)\Microsoft\EdgeWebView\Application\)" :
LR"(c:\Program Files (x86)\Microsoft\Edge\Application\)";
CString pattern = path + L"*";
WIN32_FIND_DATA ffd{ 0 };
HANDLE hFind = FindFirstFile(pattern, &ffd);
if (hFind == INVALID_HANDLE_VALUE)
{
[[maybe_unused]] DWORD error = ::GetLastError();
return {};
}
do
{
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
CString name{ ffd.cFileName };
int a, b, c, d;
if (4 == swscanf_s(ffd.cFileName, L"%d.%d.%d.%d", &a, &b, &c, &d))
{
FindClose(hFind);
return path + name;
}
}
} while (FindNextFile(hFind, &ffd) != 0);
FindClose(hFind);
return {};
}
CString CWebBrowser::GetUserDataFolder()
{
TCHAR szPath[MAX_PATH]{ 0 };
::SHGetFolderPath(nullptr, CSIDL_LOCAL_APPDATA, nullptr, 0, szPath);
::PathAppend(szPath, LR"(\Demos\)");
::PathAppend(szPath, L"MfcEdgeDemo");
return CString{ szPath };
}
有效。最后的评论,至少对于当前的操作系统......确保你已经安装了 WebView2 Runtime.