{"id":3673,"date":"2020-01-29T20:00:48","date_gmt":"2020-01-29T18:00:48","guid":{"rendered":"https:\/\/mariusbancila.ro\/blog\/?p=3673"},"modified":"2023-04-05T08:06:46","modified_gmt":"2023-04-05T06:06:46","slug":"using-microsoft-edge-in-a-native-windows-desktop-app-part-2","status":"publish","type":"post","link":"https:\/\/mariusbancila.ro\/blog\/2020\/01\/29\/using-microsoft-edge-in-a-native-windows-desktop-app-part-2\/","title":{"rendered":"Using Microsoft Edge in a native Windows desktop app \u2013 part 2"},"content":{"rendered":"<p class=\"tip-lite\">This article has been updated for the version of WebView2 that requires Microsoft Edge WebView2 Runtime 88.0.705.50 or newer.<\/p>\n<p>In the second part of this series, we will see how to use the WebView2 control in a C++ Windows desktop application. We will use a single document interface MFC application that features a toolbar where you can specify an address to navigate to and buttons to navigate back and forward as well as reloading the current page or stopping navigation.<\/p>\n<p><!-- more --><\/p>\n<div class=\"tip-lite\">Articles in this series:<\/p>\n<ul>\n<li><a href=\"https:\/\/mariusbancila.ro\/blog\/2020\/01\/28\/using-microsoft-edge-in-a-native-windows-desktop-app-part-1\/\">Part 1: Introduction to Edge and WebView2<\/a><\/li>\n<li>Part 2: Creating a WebView2 component<\/li>\n<li><a href=\"https:\/\/mariusbancila.ro\/blog\/2020\/01\/30\/using-microsoft-edge-in-a-native-windows-desktop-app-part-3\/\">Part 3: Navigation and other events<\/a><\/li>\n<li><a href=\"https:\/\/mariusbancila.ro\/blog\/2021\/01\/27\/using-microsoft-edge-in-a-native-windows-desktop-app-part-4\/\">Part 4: Performing navigation with custom data and headers<\/a><\/li>\n<li><a title=\"\" href=\"https:\/\/mariusbancila.ro\/blog\/2023\/03\/21\/using-microsoft-edge-in-a-native-windows-desktop-app-part-5\/\" target=\"_blank\" rel=\"noopener\">Part 5: Executing JavaScript code<\/a><\/li>\n<li><a title=\"\" href=\"https:\/\/mariusbancila.ro\/blog\/2023\/04\/05\/using-microsoft-edge-in-a-native-windows-desktop-app-part-6\/\" target=\"_blank\" rel=\"noopener\">Part 6: Printing a web page<\/a><\/li>\n<\/ul>\n<\/div>\n<h2>The API overview<\/h2>\n<p>The WebView2 SDK contains the following APIs:<\/p>\n<ul>\n<li>Global functions, such as <a href=\"https:\/\/docs.microsoft.com\/en-us\/microsoft-edge\/hosting\/webview2\/reference\/webview2.idl#createcorewebview2environmentwithoptions\" target=\"_blank\" rel=\"noopener noreferrer\">CreateCoreWebView2EnvironmentWithOptions()<\/a> that creates a WebView2 environment with a custom version of Edge, user data directory and\/or additional options, <a href=\"https:\/\/docs.microsoft.com\/en-us\/microsoft-edge\/hosting\/webview2\/reference\/webview2.idl#getavailablecorewebview2browserversionstring\" target=\"_blank\" rel=\"noopener noreferrer\">GetAvailableCoreWebView2BrowserVersionString()<\/a> that retrieves the browser version (including channel name), or <a href=\"https:\/\/docs.microsoft.com\/en-us\/microsoft-edge\/hosting\/webview2\/reference\/webview2.idl#comparebrowserversions\" target=\"_blank\" rel=\"noopener noreferrer\">CompareBrowserVersion()<\/a> that compares browser version to determine which version is newer, older or same.<\/li>\n<li>Interfaces, such as <a href=\"https:\/\/docs.microsoft.com\/en-us\/microsoft-edge\/hosting\/webview2\/reference\/icorewebview2environment\" target=\"_blank\" rel=\"noopener noreferrer\">ICoreWebView2Environment<\/a> that represents the WebView2 environment, <a href=\"https:\/\/docs.microsoft.com\/en-us\/microsoft-edge\/hosting\/webview2\/reference\/icorewebview2environmentoptions\">ICoreWebView2EnvironmentOptions<\/a> that defines options used to create WebView2 Environment, <a href=\"https:\/\/docs.microsoft.com\/en-us\/microsoft-edge\/hosting\/webview2\/reference\/icorewebview2\" target=\"_blank\" rel=\"noopener noreferrer\">ICoreWebView2<\/a> that represents the actual WebView2 control, <a href=\"https:\/\/docs.microsoft.com\/en-us\/microsoft-edge\/hosting\/webview2\/reference\/icorewebview2controller\" target=\"_blank\" rel=\"noopener noreferrer\">ICoreWebView2Controller<\/a> that is the owner of the CoreWebView2 object, and provides support for resizing, showing and hiding, focusing, and other functionality related to windowing and composition, <a href=\"https:\/\/docs.microsoft.com\/en-us\/microsoft-edge\/hosting\/webview2\/reference\/icorewebview2settings\" target=\"_blank\" rel=\"noopener noreferrer\">ICoreWebView2Settings<\/a> that defines properties that enable, disable, or modify WebView features.<\/li>\n<li>Delegate interfaces, such as <a href=\"https:\/\/docs.microsoft.com\/en-us\/microsoft-edge\/hosting\/webview2\/reference\/icorewebview2navigationstartingeventhandler\" target=\"_blank\" rel=\"noopener noreferrer\">ICoreWebView2NavigationStartingEventHandler<\/a> and <a href=\"https:\/\/docs.microsoft.com\/en-us\/microsoft-edge\/hosting\/webview2\/reference\/icorewebview2navigationcompletedeventhandler\" target=\"_blank\" rel=\"noopener noreferrer\">ICoreWebView2NavigationCompletedEventHandler<\/a>.<\/li>\n<li>Event argument interfaces, such as <a href=\"https:\/\/docs.microsoft.com\/en-us\/microsoft-edge\/hosting\/webview2\/reference\/icorewebview2navigationstartingeventargs\" target=\"_blank\" rel=\"noopener noreferrer\">ICoreWebView2NavigationStartingEventArgs<\/a> and <a href=\"https:\/\/docs.microsoft.com\/en-us\/microsoft-edge\/hosting\/webview2\/reference\/icorewebview2navigationcompletedeventargs\" target=\"_blank\" rel=\"noopener noreferrer\">ICoreWebView2NavigationCompletedEventArgs<\/a>.<\/li>\n<\/ul>\n<p>The environment is a container that runs a specific version of the Edge browser, with optional custom browser arguments, and a user data folder.<\/p>\n<p>In order to create a web view control you must do the following:<\/p>\n<ul>\n<li>Call <tt>CreateCoreWebView2EnvironmentWithOptions()<\/tt> to create the web view environment.<\/li>\n<li>When the environment is available, use the <tt>ICoreWebView2Environment<\/tt> interface to create the web view and its controller by calling <tt>CreateCoreWebView2Controller<\/tt>.<\/li>\n<li>When the web view controller is available, use the <tt>ICoreWebView2Controller<\/tt> interface to retrieve a pointer to the webview, <tt>ICoreWebView2*<\/tt> so you can add and remove event handlers. Also, you can retrieve a pointer to the <tt>ICoreWebView2Settings<\/tt> interface to modify web view features.<\/li>\n<\/ul>\n<h2>The demo app<\/h2>\n<p>To see how the WebView2 control works, we will use a very simple MFC application with SDI support. The WebView2 control will be created and display within the view. The application contains a toolbar with buttons to navigate back and forward, to stop or reload a page, and a control to provide the URL. Navigation to the URL starts when you press the ENTER key. With this minimum functionality, the application mimics a browser.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-3677\" src=\"https:\/\/mariusbancila.ro\/blog\/wp-content\/uploads\/2020\/01\/appscreeshot3.png\" alt=\"\" width=\"579\" height=\"406\" srcset=\"https:\/\/mariusbancila.ro\/blog\/wp-content\/uploads\/2020\/01\/appscreeshot3.png 579w, https:\/\/mariusbancila.ro\/blog\/wp-content\/uploads\/2020\/01\/appscreeshot3-300x210.png 300w\" sizes=\"auto, (max-width: 579px) 100vw, 579px\" \/><\/p>\n<p>The most important classes here are the following:<\/p>\n<ul>\n<li><tt>CMainFrame<\/tt> that represents the main window of the application, that contains the menu, toolbar, and status bar. This is where the toolbar events are handled and processed.<\/li>\n<li><tt>CMfcEdgeDemoView<\/tt> is the view in the SDI architecture. It is a window that contains and displays on top of itself the WebView2 control, implemented by the class <tt>CWebBrowser<\/tt> that we will see below. The class overrides <tt>OnInitialUpdate()<\/tt> to create the web view and <tt>DestroyWindow()<\/tt> to destroy it. It also handles the <tt>WM_SIZE<\/tt> window message to resize the web view control.<\/li>\n<\/ul>\n<p>You can check the attached demo projects to look at the source code details.<\/p>\n<h2>Creating the WebView2 control<\/h2>\n<p>The WebView2 control will be managed by the <tt>CWebBrowser<\/tt> class. This class is derived from <tt>CWnd<\/tt> and has the following interface:<\/p>\n<pre class=\"lang:c++ decode:true \">class CWebBrowser : public CWnd\n{\npublic:\n   enum class CallbackType \n   {\n      CreationCompleted,\n      NavigationCompleted\n   };\n\n   using CallbackFunc = std::function&lt;void()&gt;;\n\npublic:\n   CWebBrowser();\n   virtual ~CWebBrowser();\n\n   virtual BOOL Create(\n      LPCTSTR lpszClassName,\n      LPCTSTR lpszWindowName, \n      DWORD dwStyle,\n      const RECT&amp; rect,\n      CWnd* pParentWnd, \n      UINT nID,\n      CCreateContext* = NULL) override;\n\n   BOOL CreateAsync(\n      DWORD dwStyle,\n      const RECT&amp; rect,\n      CWnd* pParentWnd,\n      UINT nID,\n      CallbackFunc onCreated);\n\n   RECT GetBounds();\n   void SetBounds(LONG const width, LONG const height) { Resize(width, height); }\n   void Resize(LONG const width, LONG const height);\n   bool IsWebViewCreated() const;\n\nprotected:\n   DECLARE_DYNCREATE(CWebBrowser)\n   DECLARE_MESSAGE_MAP()\n\nprivate:\n   CWebBrowserImpl* m_pImpl;\n   std::map&lt;CallbackType, CallbackFunc&gt; m_callbacks;\n\nprivate:\n   void RunAsync(CallbackFunc callback);\n   void CloseWebView();\n   void RegisterEventHandlers();\n   void ResizeToClientArea();\n   void NavigateTo(CString url);\n   CString NormalizeUrl(CString url);\n\n   static CString GetInstallPath();\n   static CString GetInstallPathFromRegistry(bool const searchWebView = true);\n   static CString GetInstallPathFromDisk(bool const searchWebView = true);\n   static CString GetUserDataFolder();\n\n   void InitializeWebView();\n   HRESULT OnCreateEnvironmentCompleted(HRESULT result, ICoreWebView2Environment* environment);\n   HRESULT OnCreateWebViewControllerCompleted(HRESULT result, ICoreWebView2Controller* controller);\n   bool HandleWindowMessage(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT* result);\n   BOOL CreateHostWindow(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT&amp; rect, CWnd* pParentWnd, UINT nID);\n   static PCTSTR GetWindowClass();\n   static LRESULT CALLBACK WndProcStatic(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);\n};<\/pre>\n<p>The <tt>Create()<\/tt> method is an overload from <tt>CWnd<\/tt>. However, you can only use this if you want to initiate the creation of the web view and then forget about it. If you need to do something after the web view was created then you need to properly utilize the asynchronous API of the WebView2. The method <tt>CreateAsync()<\/tt> is initiating the creation of the web view and registers a callback that will be invoked when the web view creation is completed.<\/p>\n<pre class=\"lang:c++ decode:true \">struct CWebBrowserImpl\n{\n   wil::com_ptr&lt;ICoreWebView2Environment&gt; m_webViewEnvironment;\n   wil::com_ptr&lt;ICoreWebView2&gt;            m_webView;\n   wil::com_ptr&lt;ICoreWebView2Controller&gt;  m_webController;\n   wil::com_ptr&lt;ICoreWebView2Settings&gt;    m_webSettings;\n};\n\nCWebBrowser::CWebBrowser():m_pImpl(new CWebBrowserImpl())\n{\n   m_callbacks[CallbackType::CreationCompleted] = nullptr;\n   m_callbacks[CallbackType::NavigationCompleted] = nullptr;\n}\n\nCWebBrowser::~CWebBrowser()\n{\n   SetWindowLongPtr(m_hWnd, GWLP_USERDATA, 0);\n   CloseWebView();\n   delete m_pImpl;\n}\n\nBOOL CWebBrowser::CreateHostWindow(\n   LPCTSTR lpszClassName,\n   LPCTSTR lpszWindowName,\n   DWORD dwStyle,\n   const RECT&amp; rect,\n   CWnd* pParentWnd,\n   UINT nID)\n{\n   if (lpszClassName == nullptr)\n      lpszClassName = GetWindowClass();\n\n   if (!CWnd::Create(lpszClassName, lpszWindowName, dwStyle, rect, pParentWnd, nID))\n      return FALSE;\n\n   ::SetWindowLongPtr(m_hWnd, GWLP_USERDATA, (LONG_PTR)this);\n\n   return TRUE;\n}\n\nBOOL CWebBrowser::CreateAsync(\n   DWORD dwStyle,\n   const RECT&amp; rect,\n   CWnd* pParentWnd, \n   UINT nID,\n   CallbackFunc onCreated)\n{\n   if (!CreateHostWindow(nullptr, nullptr, dwStyle, rect, pParentWnd, nID))\n      return FALSE;\n\n   m_callbacks[CallbackType::CreationCompleted] = onCreated;\n\n   InitializeWebView();\n\n   return TRUE;\n}<\/pre>\n<p>There are three steps here:<\/p>\n<ol>\n<li>Create a parent (host) window. The purpose of this window is have a message queue that we will use to process callbacks. When an event occurs, we post a message to the queue. The window procedure will process the message and invoke the appropriate callback. In this example, we have defined the <tt>CallbackType<\/tt> enumeration that provides two types of callbacks: one for completing the navigation, and one for completing the creation of the view.<\/li>\n<li>Register a callback function to be invoked when the web view has been created.<\/li>\n<li>Initialize the web view.<\/li>\n<\/ol>\n<p>To initialize the web view we must call the <tt>CreateCoreWebView2EnvironmentWithOptions()<\/tt> method with the following arguments:<\/p>\n<ul>\n<li>The path to the installation folder of Edge. If this is null, the component should automatically locate the installation of Edge and use that. In practice, providing null does not work well, and the component is not able to detect the browser.<\/li>\n<li>The patch to the user data folder. If this is null, a subfolder in the current folder will be created. Beware that if your application is installed in Program Files, it will not be able to create it. Invoking this method will result in an access denied error (0x80070005 which is a <tt>HRESULT<\/tt> value for <tt>ERROR_ACCESS_DENIED<\/tt>). Therefore, make sure you provide a user folder to a writable location.<\/li>\n<li>Optional environment options (as <tt>ICoreWebView2EnvironmentOptions*<\/tt>) to change the behavior of the web view.<\/li>\n<li>A handler for the result of the asynchronous operation, that will be invoked if the environment was successfully created.<\/li>\n<\/ul>\n<p>If this function fails, it returns an error code. For instance, 0x80070002 (HRESULT for <tt>ERROR_FILE_NOT_FOUND<\/tt>) means that the WebView2 component was not found.<\/p>\n<p>It is important to note that the WebView2 environment as well as all the other WebView2 objects are single threaded and have dependencies on Windows components that require COM to be initialized for a single-threaded apartment. Therefore, the application is required to call <tt>CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED)<\/tt> before calling the <tt>CreateCoreWebView2EnvironmentWithOptions()<\/tt> function. Failure to do so, will result in an <tt>CO_E_NOTINITIALIZED<\/tt> error. In the attached demo project, this call is performed in the <tt>CMfcEdgeDemoApp::InitInstance()<\/tt> method.<\/p>\n<p>The initialization function of the <tt>CWebBrowser<\/tt> class can be implemented as follows:<\/p>\n<pre class=\"lang:c++ decode:true \">void CWebBrowser::CloseWebView()\n{\n   if (m_pImpl-&gt;m_webView)\n   {\n      m_pImpl-&gt;m_webController-&gt;Close();\n\n      m_pImpl-&gt;m_webController = nullptr;\n      m_pImpl-&gt;m_webView = nullptr;\n      m_pImpl-&gt;m_webSettings = nullptr;\n   }\n\n   m_pImpl-&gt;m_webViewEnvironment = nullptr;\n}\n\nvoid CWebBrowser::InitializeWebView()\n{\n   CloseWebView();\n\n   CString subFolder = GetInstallPath();\n   CString appData = GetUserDataFolder();\n   ICoreWebView2EnvironmentOptions* options = nullptr;\n\n   HRESULT hr = CreateCoreWebView2EnvironmentWithOptions(\n      subFolder, \n      appData,\n      options,\n      Callback&lt;ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler&gt;(\n         this, \n         &amp;CWebBrowser::OnCreateEnvironmentCompleted).Get());\n\n   if (!SUCCEEDED(hr))\n   {\n      CString text;\n      if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))\n      {\n         text = L\"Cannot found the Edge browser.\";\n      }\n      else\n      {\n         text = L\"Cannot create the webview environment.\";\n      }\n\n      ShowFailure(hr, text);\n   }\n}<\/pre>\n<p>When the creation of the environment completes successfully, the provided callback is invoked. The first argument of the handler is a <tt>HRESULT<\/tt>, and the second is a pointer to the <tt>ICoreWebView2Environment<\/tt> interface that defines the environment. This pointer can be used to create the web view by calling <tt>CreateCoreWebView2Controller()<\/tt>. This method has two parameters: the handle of the parent window and a callback that will be invoked when the web view creation completes. The implementation of this function is as follows:<\/p>\n<pre class=\"lang:c++ decode:true \">HRESULT CWebBrowser::OnCreateEnvironmentCompleted(\n   HRESULT result, \n   ICoreWebView2Environment* environment)\n{\n   CHECK_FAILURE(result);\n   CHECK_FAILURE(environment-&gt;QueryInterface(IID_PPV_ARGS(&amp;m_pImpl-&gt;m_webViewEnvironment)));\n   CHECK_FAILURE(m_pImpl-&gt;m_webViewEnvironment-&gt;CreateCoreWebView2Controller(\n      m_hWnd, \n      Callback&lt;ICoreWebView2CreateCoreWebView2ControllerCompletedHandler&gt;(\n         this, \n         &amp;CWebBrowser::OnCreateWebViewControllerCompleted).Get()));\n\n   return S_OK;\n}<\/pre>\n<p>The callback <tt>OnCreateWebViewControllerCompleted<\/tt> is invoked with two arguments: a <tt>HRESULT<\/tt> value that indicates the success of the operation and a pointer to the <tt>ICoreWebView2Controller<\/tt> interfaces that defines the controller for the web view. This pointer can be used to get a pointer to the <tt>ICoreWebView2<\/tt> interface. This, in turn, can be used to add and remove event handlers and invoke various methods such as navigation. The implementation is as follows:<\/p>\n<pre class=\"lang:c++ decode:true \">HRESULT CWebBrowser::OnCreateWebViewControllerCompleted(\n   HRESULT result, \n   ICoreWebView2Controller* controller)\n{\n   if (result == S_OK)\n   {\n      if (controller != nullptr)\n      {\n         m_pImpl-&gt;m_webController = controller;\n         CHECK_FAILURE(controller-&gt;get_CoreWebView2(&amp;m_pImpl-&gt;m_webView));\n\n         CHECK_FAILURE(m_pImpl-&gt;m_webView-&gt;get_Settings(&amp;m_pImpl-&gt;m_webSettings));\n\n         RegisterEventHandlers();\n\n         ResizeToClientArea();\n      }\n\n      auto callback = m_callbacks[CallbackType::CreationCompleted];\n      if (callback != nullptr)\n         RunAsync(callback);\n   }\n   else\n   {\n      ShowFailure(result, L\"Cannot create webview environment.\");\n   }\n\n   return S_OK;\n}<\/pre>\n<p>We will look at handling events in the next installment. What you can see here is that when the creation completes we invoke the callback the user passed when initiating the asynchronous creation of the web view. However, the invocation is not done directly. Instead, a message is posted to the web view&#8217;s parent window message queue. When this message is processed, the callback is actually invoked.<\/p>\n<pre class=\"lang:c++ decode:true \">void CWebBrowser::RunAsync(CallbackFunc callback)\n{\n   auto* task = new CallbackFunc(callback);\n   PostMessage(MSG_RUN_ASYNC_CALLBACK, reinterpret_cast&lt;WPARAM&gt;(task), 0);\n}\n\nLRESULT CALLBACK CWebBrowser::WndProcStatic(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)\n{\n   if (auto app = (CWebBrowser*)::GetWindowLongPtr(hWnd, GWLP_USERDATA))\n   {\n      LRESULT result = 0;\n      if (app-&gt;HandleWindowMessage(hWnd, message, wParam, lParam, &amp;result))\n      {\n         return result;\n      }\n   }\n\n   return ::DefWindowProc(hWnd, message, wParam, lParam);\n}\n\nbool CWebBrowser::HandleWindowMessage(\n   HWND, UINT message, WPARAM wParam, LPARAM lParam, LRESULT* result)\n{\n   *result = 0;\n   \n   switch (message)\n   {\n   case WM_SIZE:\n   {\n      if (lParam != 0)\n      {\n         ResizeToClientArea();\n         return true;\n      }\n   }\n   break;\n   case MSG_RUN_ASYNC_CALLBACK:\n   {\n      auto* task = reinterpret_cast&lt;CallbackFunc*&gt;(wParam);\n      (*task)();\n      delete task;\n      return true;\n   }\n   break;\n   }\n\n   return false;\n}<\/pre>\n<p>Having a pointer to the <tt>ICoreWebView2<\/tt> and <tt>ICoreWebView2Controller<\/tt> interfaces, we can also implement the other methods from the public interface of the <tt>CWebBrowser<\/tt> class.<\/p>\n<pre class=\"lang:c++ decode:true \">RECT CWebBrowser::GetBounds()\n{\n   RECT rc{0,0,0,0};\n   if (m_pImpl-&gt;m_webController)\n   {\n      m_pImpl-&gt;m_webController-&gt;get_Bounds(&amp;rc);\n   }\n\n   return rc;\n}\n\nvoid CWebBrowser::Resize(LONG const width, LONG const height)\n{\n   SetWindowPos(nullptr, 0, 0, width, height, SWP_NOMOVE| SWP_NOREPOSITION);\n}\n\nCString CWebBrowser::GetLocationURL()\n{\n   CString url;\n   if (m_pImpl-&gt;m_webView)\n   {\n      wil::unique_cotaskmem_string uri;\n      m_pImpl-&gt;m_webView-&gt;get_Source(&amp;uri);\n\n      if (wcscmp(uri.get(), L\"about:blank\") == 0)\n      {\n         uri = wil::make_cotaskmem_string(L\"\");\n      }\n\n      url = uri.get();\n   }\n\n   return url;\n}\n\nCString CWebBrowser::NormalizeUrl(CString url)\n{\n   if (url.Find(_T(\":\/\/\")) &lt; 0)\n   {\n      if (url.GetLength() &gt; 1 &amp;&amp; url[1] == ':')\n         url = _T(\"file:\/\/\") + url;\n      else\n         url = _T(\"http:\/\/\") + url;\n   }\n\n   return url;\n}\n\nvoid CWebBrowser::NavigateTo(CString url)\n{\n   m_pImpl-&gt;m_webView-&gt;Navigate(NormalizeUrl(url));\n}\n\nvoid CWebBrowser::Navigate(CString const &amp; url, CallbackFunc onComplete)\n{\n   if (m_pImpl-&gt;m_webView)\n   {      \n      m_callbacks[CallbackType::NavigationCompleted] = onComplete;\n      NavigateTo(url);\n   }\n}\n\nvoid CWebBrowser::Stop()\n{\n   if (m_pImpl-&gt;m_webView)\n   {\n      m_pImpl-&gt;m_webView-&gt;Stop();\n   }\n}\n\nvoid CWebBrowser::Reload()\n{\n   if (m_pImpl-&gt;m_webView)\n   {\n      m_pImpl-&gt;m_webView-&gt;Reload();\n   }\n}\n\nvoid CWebBrowser::GoBack()\n{\n   if (m_pImpl-&gt;m_webView)\n   {\n      BOOL possible = FALSE;\n      m_pImpl-&gt;m_webView-&gt;get_CanGoBack(&amp;possible);\n      if(possible)\n         m_pImpl-&gt;m_webView-&gt;GoBack();\n   }\n}\n\nvoid CWebBrowser::GoForward()\n{\n   if (m_pImpl-&gt;m_webView)\n   {\n      BOOL possible = FALSE;\n      m_pImpl-&gt;m_webView-&gt;get_CanGoForward(&amp;possible);\n      if (possible)\n         m_pImpl-&gt;m_webView-&gt;GoForward();\n   }\n}<\/pre>\n<p>We will discuss the details about events and navigation in the next post.<\/p>\n<p>What is left to show here is how the <tt>CWebBrowser<\/tt> is used from the SDI&#8217;s view, which you can see below:<\/p>\n<pre class=\"lang:c++ decode:true \">void CMfcEdgeDemoView::OnInitialUpdate()\n{\n   CView::OnInitialUpdate();\n\n   this-&gt;ModifyStyleEx(WS_EX_CLIENTEDGE | WS_EX_WINDOWEDGE, 0, 0);\n   this-&gt;ModifyStyle(WS_CAPTION | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_THICKFRAME | WS_BORDER, 0, 0);\n\n   m_pWebBrowser = std::make_unique&lt;CWebBrowser&gt;();\n\n   if (m_pWebBrowser != nullptr)\n   {\n      CRect rectClient;\n      GetClientRect(rectClient);\n\n      m_pWebBrowser-&gt;CreateAsync(\n         WS_VISIBLE | WS_CHILD,\n         rectClient,\n         this,\n         1,\n         [this]() {\n            m_pWebBrowser-&gt;Navigate(L\"https:\/\/bing.com\", nullptr);\n         });\n   }\n}\n\nBOOL CMfcEdgeDemoView::DestroyWindow()\n{\n   m_pWebBrowser.reset();\n\n   return CView::DestroyWindow();\n}\n\nvoid CMfcEdgeDemoView::OnSize(UINT nType, int cx, int cy)\n{\n   CView::OnSize(nType, cx, cy);\n\n   CRect rectClient;\n   GetClientRect(rectClient);\n\n   if (m_pWebBrowser != nullptr)\n      m_pWebBrowser-&gt;Resize(cx, cy);\n}<\/pre>\n<p>Notice that when calling <tt>CreateAsync()<\/tt>, we pass a lambda that, when invoked, triggers navigation to the <a href=\"https:\/\/bing.com\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/bing.com<\/a> web page.<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-3681\" src=\"https:\/\/mariusbancila.ro\/blog\/wp-content\/uploads\/2020\/01\/appscreeshot4.jpg\" alt=\"\" width=\"828\" height=\"532\" srcset=\"https:\/\/mariusbancila.ro\/blog\/wp-content\/uploads\/2020\/01\/appscreeshot4.jpg 828w, https:\/\/mariusbancila.ro\/blog\/wp-content\/uploads\/2020\/01\/appscreeshot4-300x193.jpg 300w, https:\/\/mariusbancila.ro\/blog\/wp-content\/uploads\/2020\/01\/appscreeshot4-768x493.jpg 768w, https:\/\/mariusbancila.ro\/blog\/wp-content\/uploads\/2020\/01\/appscreeshot4-700x450.jpg 700w\" sizes=\"auto, (max-width: 828px) 100vw, 828px\" \/><\/p>\n<h2>Finding the WebView2 location<\/h2>\n<p>In my experience with the <tt>CreateCoreWebView2EnvironmentWithOptions()<\/tt>, passing null for the browser location did not work well and it was unable to find the browser installation, regardless of the version I was using (whether it was the Beta or the RTM version).<\/p>\n<p>The Edge browser is installed under <strong>C:\\Program Files (x86)\\Microsoft\\Edge\\Application<\/strong>. The WebView2 runtime is installed under <strong>C:\\Program Files (x86)\\Microsoft\\EdgeWebView\\Application<\/strong>. This is the case even though Edge (and the runtime) is a 64-bit application. The reason its installation path is under Program Files (x86) and not under Program Files (as it is expected for 64-bit application) is probably historical. Chrome does the same because it was easier for scenarios when users migrated from 32-bit to the 64-bit version of the browser.<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-4156\" src=\"https:\/\/mariusbancila.ro\/blog\/wp-content\/uploads\/2020\/01\/edgewebviewpath.png\" alt=\"\" width=\"548\" height=\"135\" srcset=\"https:\/\/mariusbancila.ro\/blog\/wp-content\/uploads\/2020\/01\/edgewebviewpath.png 548w, https:\/\/mariusbancila.ro\/blog\/wp-content\/uploads\/2020\/01\/edgewebviewpath-300x74.png 300w\" sizes=\"auto, (max-width: 548px) 100vw, 548px\" \/><\/p>\n<p>However, the folder you are expected to provide to <tt>CreateCoreWebView2EnvironmentWithOptions()<\/tt> is not <strong>C:\\Program Files (x86)\\Microsoft\\EdgeWebView\\Application<\/strong> but a subfolder that has the same name as the version of the browser. In the picture above, the version (and folder name) is 88.0.705.50.<\/p>\n<p class=\"tip-lite\">The current version of this implementation works only with Edge Beta, which has a different installation path, <em>c:\\Program Files (x86)\\Microsoft\\Edge Beta\\<\/em>.<\/p>\n<p>To programmatically detect the path of the runtime installation you can do the following:<\/p>\n<ul>\n<li>Search in Windows Registry. Installation location and version information is available under <strong>SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Microsoft EdgeWebView<\/strong>. (For the Edge browser it is under <strong>SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Microsoft Edge<\/strong>.)<br \/>\n<img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-4157\" src=\"https:\/\/mariusbancila.ro\/blog\/wp-content\/uploads\/2020\/01\/edgwebvieweregistry.png\" alt=\"\" width=\"793\" height=\"381\" srcset=\"https:\/\/mariusbancila.ro\/blog\/wp-content\/uploads\/2020\/01\/edgwebvieweregistry.png 793w, https:\/\/mariusbancila.ro\/blog\/wp-content\/uploads\/2020\/01\/edgwebvieweregistry-300x144.png 300w, https:\/\/mariusbancila.ro\/blog\/wp-content\/uploads\/2020\/01\/edgwebvieweregistry-768x369.png 768w, https:\/\/mariusbancila.ro\/blog\/wp-content\/uploads\/2020\/01\/edgwebvieweregistry-700x336.png 700w\" sizes=\"auto, (max-width: 793px) 100vw, 793px\" \/><\/li>\n<li>Search on disk in the default installation location for a folder name of the form 88.0.705.50.<\/li>\n<\/ul>\n<p>In the attached source code, you will find the following implementation for this:<\/p>\n<pre class=\"lang:c++ decode:true \">CString CWebBrowser::GetInstallPath()\n{\n   static CString path = []\n   {  \n      auto installPath = GetInstallPathFromRegistry();   \/\/ check registry for WebView2\n      if (installPath.IsEmpty())\n         installPath = GetInstallPathFromDisk();         \/\/ check disk for WebView2\n      \n      return installPath;\n   }(); \n\n   return path;\n}\n\nCString CWebBrowser::GetInstallPathFromRegistry(bool const searchWebView)\n{\n   CString path;\n\n   HKEY handle = nullptr;\n\n   LSTATUS result = ERROR_FILE_NOT_FOUND;\n\n   if (searchWebView)\n   {\n      result = RegOpenKeyEx(HKEY_LOCAL_MACHINE,\n         LR\"(SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Microsoft EdgeWebView)\",\n         0,\n         KEY_READ,\n         &amp;handle);\n\n      if (result != ERROR_SUCCESS)\n         result = RegOpenKeyEx(HKEY_LOCAL_MACHINE,\n            LR\"(SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Microsoft EdgeWebView)\",\n            0,\n            KEY_READ,\n            &amp;handle);\n   }\n   else \/\/ this is for demo purposes only; don't use this on production environments\n   {\n      result = RegOpenKeyEx(HKEY_LOCAL_MACHINE,\n         LR\"(SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Microsoft Edge)\",\n         0,\n         KEY_READ,\n         &amp;handle);\n\n      if (result != ERROR_SUCCESS)\n         result = RegOpenKeyEx(HKEY_LOCAL_MACHINE,\n            LR\"(SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Microsoft Edge)\",\n            0,\n            KEY_READ,\n            &amp;handle);\n   }\n\n   if (result == ERROR_SUCCESS)\n   {\n      TCHAR buffer[MAX_PATH + 1]{ 0 };\n      DWORD type = REG_SZ;\n      DWORD size = MAX_PATH;\n      result = RegQueryValueEx(handle, L\"InstallLocation\", 0, &amp;type, reinterpret_cast&lt;LPBYTE&gt;(buffer), &amp;size);\n      if (result == ERROR_SUCCESS) \n         path += CString{ buffer };\n\n      TCHAR version[100]{ 0 };\n      size = 100;\n      result = RegQueryValueEx(handle, L\"Version\", 0, &amp;type, reinterpret_cast&lt;LPBYTE&gt;(version), &amp;size);\n      if (result == ERROR_SUCCESS)\n      {\n         if (path.GetAt(path.GetLength() - 1) != L'\\\\')\n            path += L\"\\\\\";\n         path += CString{ version };\n      }\n      else\n         path.Empty();\n\n      RegCloseKey(handle);\n   }\n\n   return path;\n}\n\nCString CWebBrowser::GetInstallPathFromDisk(bool const searchWebView)\n{\n   CString path =\n      searchWebView ?\n      LR\"(c:\\Program Files (x86)\\Microsoft\\EdgeWebView\\Application\\)\" :\n      LR\"(c:\\Program Files (x86)\\Microsoft\\Edge\\Application\\)\"; \/\/ this is for demo purposes only; don't use this on production environments\n   CString pattern = path + L\"*\";\n\n   WIN32_FIND_DATA ffd{ 0 };\n   HANDLE hFind = FindFirstFile(pattern, &amp;ffd);\n   if (hFind == INVALID_HANDLE_VALUE)\n   {\n      [[maybe_unused]] DWORD error = ::GetLastError();\n      return {};\n   }\n\n   do\n   {\n      if (ffd.dwFileAttributes &amp; FILE_ATTRIBUTE_DIRECTORY)\n      {\n         CString name{ ffd.cFileName };\n         int a, b, c, d;\n         if (4 == swscanf_s(ffd.cFileName, L\"%d.%d.%d.%d\", &amp;a, &amp;b, &amp;c, &amp;d))\n         {\n            FindClose(hFind);\n            return path + name;\n         }\n      }\n   } while (FindNextFile(hFind, &amp;ffd) != 0);\n\n   FindClose(hFind);\n\n   return {};\n}<\/pre>\n<h2>A few more words&#8230;<\/h2>\n<p>In the code above, there were references to a <tt>CHECK_FAILURE<\/tt> macro, as well as the function <tt>ShowFailure()<\/tt>. This function displays a message to the user containing information about an error. There is also a function <tt>CheckFailure()<\/tt>, called from the <tt>CHECK_FAILURE<\/tt> macro that logs an error message and then terminates the process. These functions have been adapted from the sample code provided with the WebView2 SDK.<\/p>\n<pre class=\"lang:c++ decode:true \">#define CHECK_FAILURE_STRINGIFY(arg)         #arg\n#define CHECK_FAILURE_FILE_LINE(file, line)  ([](HRESULT hr){ CheckFailure(hr, L\"Failure at \" CHECK_FAILURE_STRINGIFY(file) L\"(\" CHECK_FAILURE_STRINGIFY(line) L\")\"); })\n#define CHECK_FAILURE                        CHECK_FAILURE_FILE_LINE(__FILE__, __LINE__)\n#define CHECK_FAILURE_BOOL(value)            CHECK_FAILURE((value) ? S_OK : E_UNEXPECTED)\n\nvoid ShowFailure(HRESULT hr, CString const &amp; message)\n{\n   CString text;\n   text.Format(L\"%s (0x%08X)\", (LPCTSTR)message, hr);\n\n   ::MessageBox(nullptr, static_cast&lt;LPCTSTR&gt;(text), L\"Failure\", MB_OK);\n}\n\nvoid CheckFailure(HRESULT hr, CString const &amp; message)\n{\n   if (FAILED(hr))\n   {    \n      CString text;\n      text.Format(L\"%s : 0x%08X\", (LPCTSTR)message, hr);\n\n      \/\/ TODO: log text\n     \n      std::exit(hr);\n   }\n}<\/pre>\n<h2>Try the app<\/h2>\n<p>You can download, build, and try the sample app for this series from here: <div class=\"wp-block-button\">\n<a  data-e-Disable-Page-Transition=\"true\" class=\"dlm-download-link dlm-download-default wp-block-button__link wp-element-button\" title=\"Version 6\" href=\"https:\/\/mariusbancila.ro\/blog\/download\/3689\/?tmstv=1792338818\" rel=\"nofollow\" id=\"download-link-3689\" data-redirect=\"false\" >\n\tMfcEdgeDemo.zip\t(<span class=\"dlm-download-count-value\">40583 downloads<\/span>\n\t)\n<\/a>\n<\/div>\n.<\/p>\n<p>Stay tuned for the next part of the series.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In the second part of this series, we will see how to use the WebView2 control in a C++ Windows desktop application. We will use a single document interface MFC application that features a toolbar where you can specify an address to navigate to and buttons to navigate back and forward as well as reloading the current page or stopping navigation.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_uag_custom_page_level_css":"","advgb_blocks_editor_width":"","advgb_blocks_columns_visual_guide":"","_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"jetpack_post_was_ever_published":false},"categories":[7,608,10,19,146],"tags":[263,451,616,617,453,55,459],"class_list":["post-3673","post","type-post","status-publish","format-standard","hentry","category-c","category-edge","category-visual-studio","category-web","category-windows","tag-browser","tag-c","tag-edge","tag-html","tag-mfc","tag-visualstudio","tag-windows"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.1.1 - aioseo.com -->\n\t<meta name=\"description\" content=\"In this article you will learn how to create a WebView2 using Microsoft Edge Chromium-based to render web content in an MFC application.\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Marius Bancila\"\/>\n\t<meta name=\"keywords\" content=\"c++,edge,browser,windows,mfc,html,visualstudio\" \/>\n\t<link rel=\"canonical\" href=\"https:\/\/mariusbancila.ro\/blog\/2020\/01\/29\/using-microsoft-edge-in-a-native-windows-desktop-app-part-2\/\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.1.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"en_US\" \/>\n\t\t<meta property=\"og:site_name\" content=\"Marius Bancila&#039;s Blog | About code. Mostly on C++\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"Using Microsoft Edge in a native Windows desktop app\u2013part 2\" \/>\n\t\t<meta property=\"og:description\" content=\"In this article you will learn how to create a WebView2 using Microsoft Edge Chromium-based to render web content in an MFC application.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/mariusbancila.ro\/blog\/2020\/01\/29\/using-microsoft-edge-in-a-native-windows-desktop-app-part-2\/\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2020-01-29T18:00:48+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2023-04-05T06:06:46+00:00\" \/>\n\t\t<meta name=\"twitter:card\" content=\"summary\" \/>\n\t\t<meta name=\"twitter:title\" content=\"Using Microsoft Edge in a native Windows desktop app\u2013part 2\" \/>\n\t\t<meta name=\"twitter:description\" content=\"In this article you will learn how to create a WebView2 using Microsoft Edge Chromium-based to render web content in an MFC application.\" \/>\n\t\t<script type=\"application\/ld+json\" class=\"aioseo-schema\">\n\t\t\t{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2020\\\/01\\\/29\\\/using-microsoft-edge-in-a-native-windows-desktop-app-part-2\\\/#article\",\"name\":\"Using Microsoft Edge in a native Windows desktop app\\u2013part 2\",\"headline\":\"Using Microsoft Edge in a native Windows desktop app \\u2013 part 2\",\"author\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/author\\\/admin\\\/#author\"},\"publisher\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/#organization\"},\"image\":{\"@type\":\"ImageObject\",\"url\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/wp-content\\\/uploads\\\/2020\\\/01\\\/appscreeshot3.png\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2020\\\/01\\\/29\\\/using-microsoft-edge-in-a-native-windows-desktop-app-part-2\\\/#articleImage\",\"width\":579,\"height\":406},\"datePublished\":\"2020-01-29T20:00:48+02:00\",\"dateModified\":\"2023-04-05T08:06:46+02:00\",\"inLanguage\":\"en-US\",\"commentCount\":7,\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2020\\\/01\\\/29\\\/using-microsoft-edge-in-a-native-windows-desktop-app-part-2\\\/#webpage\"},\"isPartOf\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2020\\\/01\\\/29\\\/using-microsoft-edge-in-a-native-windows-desktop-app-part-2\\\/#webpage\"},\"articleSection\":\"C++, Edge, Visual Studio, Web, Windows, browser, C++, Edge, HTML, MFC, VisualStudio, Windows\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2020\\\/01\\\/29\\\/using-microsoft-edge-in-a-native-windows-desktop-app-part-2\\\/#breadcrumblist\",\"itemListElement\":[{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog#listItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/#listItem\",\"name\":\"IT\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/#listItem\",\"position\":2,\"name\":\"IT\",\"item\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/#listItem\",\"name\":\"Software\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog#listItem\",\"name\":\"Home\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/#listItem\",\"position\":3,\"name\":\"Software\",\"item\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/c\\\/#listItem\",\"name\":\"C++\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/#listItem\",\"name\":\"IT\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/c\\\/#listItem\",\"position\":4,\"name\":\"C++\",\"item\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/c\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2020\\\/01\\\/29\\\/using-microsoft-edge-in-a-native-windows-desktop-app-part-2\\\/#listItem\",\"name\":\"Using Microsoft Edge in a native Windows desktop app \\u2013 part 2\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/#listItem\",\"name\":\"Software\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2020\\\/01\\\/29\\\/using-microsoft-edge-in-a-native-windows-desktop-app-part-2\\\/#listItem\",\"position\":5,\"name\":\"Using Microsoft Edge in a native Windows desktop app \\u2013 part 2\",\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/category\\\/it\\\/software\\\/c\\\/#listItem\",\"name\":\"C++\"}}]},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/#organization\",\"name\":\"Marius Bancila's Blog\",\"description\":\"About code. Mostly on C++\",\"url\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/author\\\/admin\\\/#author\",\"url\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/author\\\/admin\\\/\",\"name\":\"Marius Bancila\",\"image\":{\"@type\":\"ImageObject\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2020\\\/01\\\/29\\\/using-microsoft-edge-in-a-native-windows-desktop-app-part-2\\\/#authorImage\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/a84dd2831d955c38355ddea55df4df260809b88f36408bc14fd4eab8f7f131c9?s=96&d=mm&r=g\",\"width\":96,\"height\":96,\"caption\":\"Marius Bancila\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2020\\\/01\\\/29\\\/using-microsoft-edge-in-a-native-windows-desktop-app-part-2\\\/#webpage\",\"url\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2020\\\/01\\\/29\\\/using-microsoft-edge-in-a-native-windows-desktop-app-part-2\\\/\",\"name\":\"Using Microsoft Edge in a native Windows desktop app\\u2013part 2\",\"description\":\"In this article you will learn how to create a WebView2 using Microsoft Edge Chromium-based to render web content in an MFC application.\",\"inLanguage\":\"en-US\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/#website\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/2020\\\/01\\\/29\\\/using-microsoft-edge-in-a-native-windows-desktop-app-part-2\\\/#breadcrumblist\"},\"author\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/author\\\/admin\\\/#author\"},\"creator\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/author\\\/admin\\\/#author\"},\"datePublished\":\"2020-01-29T20:00:48+02:00\",\"dateModified\":\"2023-04-05T08:06:46+02:00\"},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/\",\"name\":\"Marius Bancila's Blog\",\"description\":\"About code. Mostly on C++\",\"inLanguage\":\"en-US\",\"publisher\":{\"@id\":\"https:\\\/\\\/mariusbancila.ro\\\/blog\\\/#organization\"}}]}\n\t\t<\/script>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"Using Microsoft Edge in a native Windows desktop app\u2013part 2","description":"In this article you will learn how to create a WebView2 using Microsoft Edge Chromium-based to render web content in an MFC application.","canonical_url":"https:\/\/mariusbancila.ro\/blog\/2020\/01\/29\/using-microsoft-edge-in-a-native-windows-desktop-app-part-2\/","robots":"max-image-preview:large","keywords":"c++,edge,browser,windows,mfc,html,visualstudio","webmasterTools":{"miscellaneous":""},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/mariusbancila.ro\/blog\/2020\/01\/29\/using-microsoft-edge-in-a-native-windows-desktop-app-part-2\/#article","name":"Using Microsoft Edge in a native Windows desktop app\u2013part 2","headline":"Using Microsoft Edge in a native Windows desktop app \u2013 part 2","author":{"@id":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/#author"},"publisher":{"@id":"https:\/\/mariusbancila.ro\/blog\/#organization"},"image":{"@type":"ImageObject","url":"https:\/\/mariusbancila.ro\/blog\/wp-content\/uploads\/2020\/01\/appscreeshot3.png","@id":"https:\/\/mariusbancila.ro\/blog\/2020\/01\/29\/using-microsoft-edge-in-a-native-windows-desktop-app-part-2\/#articleImage","width":579,"height":406},"datePublished":"2020-01-29T20:00:48+02:00","dateModified":"2023-04-05T08:06:46+02:00","inLanguage":"en-US","commentCount":7,"mainEntityOfPage":{"@id":"https:\/\/mariusbancila.ro\/blog\/2020\/01\/29\/using-microsoft-edge-in-a-native-windows-desktop-app-part-2\/#webpage"},"isPartOf":{"@id":"https:\/\/mariusbancila.ro\/blog\/2020\/01\/29\/using-microsoft-edge-in-a-native-windows-desktop-app-part-2\/#webpage"},"articleSection":"C++, Edge, Visual Studio, Web, Windows, browser, C++, Edge, HTML, MFC, VisualStudio, Windows"},{"@type":"BreadcrumbList","@id":"https:\/\/mariusbancila.ro\/blog\/2020\/01\/29\/using-microsoft-edge-in-a-native-windows-desktop-app-part-2\/#breadcrumblist","itemListElement":[{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog#listItem","position":1,"name":"Home","item":"https:\/\/mariusbancila.ro\/blog","nextItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/#listItem","name":"IT"}},{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/#listItem","position":2,"name":"IT","item":"https:\/\/mariusbancila.ro\/blog\/category\/it\/","nextItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/#listItem","name":"Software"},"previousItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog#listItem","name":"Home"}},{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/#listItem","position":3,"name":"Software","item":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/","nextItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/#listItem","name":"C++"},"previousItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/#listItem","name":"IT"}},{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/#listItem","position":4,"name":"C++","item":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/","nextItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/2020\/01\/29\/using-microsoft-edge-in-a-native-windows-desktop-app-part-2\/#listItem","name":"Using Microsoft Edge in a native Windows desktop app \u2013 part 2"},"previousItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/#listItem","name":"Software"}},{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/2020\/01\/29\/using-microsoft-edge-in-a-native-windows-desktop-app-part-2\/#listItem","position":5,"name":"Using Microsoft Edge in a native Windows desktop app \u2013 part 2","previousItem":{"@type":"ListItem","@id":"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/#listItem","name":"C++"}}]},{"@type":"Organization","@id":"https:\/\/mariusbancila.ro\/blog\/#organization","name":"Marius Bancila's Blog","description":"About code. Mostly on C++","url":"https:\/\/mariusbancila.ro\/blog\/"},{"@type":"Person","@id":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/#author","url":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/","name":"Marius Bancila","image":{"@type":"ImageObject","@id":"https:\/\/mariusbancila.ro\/blog\/2020\/01\/29\/using-microsoft-edge-in-a-native-windows-desktop-app-part-2\/#authorImage","url":"https:\/\/secure.gravatar.com\/avatar\/a84dd2831d955c38355ddea55df4df260809b88f36408bc14fd4eab8f7f131c9?s=96&d=mm&r=g","width":96,"height":96,"caption":"Marius Bancila"}},{"@type":"WebPage","@id":"https:\/\/mariusbancila.ro\/blog\/2020\/01\/29\/using-microsoft-edge-in-a-native-windows-desktop-app-part-2\/#webpage","url":"https:\/\/mariusbancila.ro\/blog\/2020\/01\/29\/using-microsoft-edge-in-a-native-windows-desktop-app-part-2\/","name":"Using Microsoft Edge in a native Windows desktop app\u2013part 2","description":"In this article you will learn how to create a WebView2 using Microsoft Edge Chromium-based to render web content in an MFC application.","inLanguage":"en-US","isPartOf":{"@id":"https:\/\/mariusbancila.ro\/blog\/#website"},"breadcrumb":{"@id":"https:\/\/mariusbancila.ro\/blog\/2020\/01\/29\/using-microsoft-edge-in-a-native-windows-desktop-app-part-2\/#breadcrumblist"},"author":{"@id":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/#author"},"creator":{"@id":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/#author"},"datePublished":"2020-01-29T20:00:48+02:00","dateModified":"2023-04-05T08:06:46+02:00"},{"@type":"WebSite","@id":"https:\/\/mariusbancila.ro\/blog\/#website","url":"https:\/\/mariusbancila.ro\/blog\/","name":"Marius Bancila's Blog","description":"About code. Mostly on C++","inLanguage":"en-US","publisher":{"@id":"https:\/\/mariusbancila.ro\/blog\/#organization"}}]},"og:locale":"en_US","og:site_name":"Marius Bancila's Blog | About code. Mostly on C++","og:type":"article","og:title":"Using Microsoft Edge in a native Windows desktop app\u2013part 2","og:description":"In this article you will learn how to create a WebView2 using Microsoft Edge Chromium-based to render web content in an MFC application.","og:url":"https:\/\/mariusbancila.ro\/blog\/2020\/01\/29\/using-microsoft-edge-in-a-native-windows-desktop-app-part-2\/","article:published_time":"2020-01-29T18:00:48+00:00","article:modified_time":"2023-04-05T06:06:46+00:00","twitter:card":"summary","twitter:title":"Using Microsoft Edge in a native Windows desktop app\u2013part 2","twitter:description":"In this article you will learn how to create a WebView2 using Microsoft Edge Chromium-based to render web content in an MFC application."},"aioseo_meta_data":{"post_id":"3673","title":"Using Microsoft Edge in a native Windows desktop app\u2013part 2","description":"In this article you will learn how to create a WebView2 using Microsoft Edge Chromium-based to render web content in an MFC application.","keywords":[{"label":"C++","value":"C++"},{"label":"Edge","value":"Edge"},{"label":"browser","value":"browser"},{"label":"Windows","value":"Windows"},{"label":"MFC","value":"MFC"},{"label":"HTML","value":"HTML"},{"label":"VisualStudio","value":"VisualStudio"}],"keyphrases":{"focus":{"keyphrase":"","score":0,"analysis":{"keyphraseInTitle":{"score":0,"maxScore":9,"error":1}}},"additional":[]},"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":"","og_custom_url":null,"og_article_section":null,"og_article_tags":[],"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"Article","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":"-1","robots_max_videopreview":"-1","robots_max_imagepreview":"large","priority":null,"frequency":"default","location":null,"local_seo":null,"breadcrumb_settings":null,"limit_modified_date":false,"ai":null,"created":"2021-03-18 21:03:35","updated":"2025-12-12 07:54:48","seo_analyzer_scan_date":null,"focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"author_meta":{"display_name":"Marius Bancila","author_link":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/"},"featured_img":null,"jetpack_publicize_connections":[],"uagb_featured_image_src":{"full":false,"thumbnail":false,"medium":false,"medium_large":false,"large":false,"1536x1536":false,"2048x2048":false},"uagb_author_info":{"display_name":"Marius Bancila","author_link":"https:\/\/mariusbancila.ro\/blog\/author\/admin\/"},"uagb_comment_info":7,"uagb_excerpt":"In the second part of this series, we will see how to use the WebView2 control in a C++ Windows desktop application. We will use a single document interface MFC application that features a toolbar where you can specify an address to navigate to and buttons to navigate back and forward as well as reloading&hellip;","coauthors":[],"tax_additional":{"categories":{"linked":["<a href=\"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/c\/\" class=\"advgb-post-tax-term\">C++<\/a>","<a href=\"https:\/\/mariusbancila.ro\/blog\/category\/it\/web\/edge\/\" class=\"advgb-post-tax-term\">Edge<\/a>","<a href=\"https:\/\/mariusbancila.ro\/blog\/category\/it\/software\/visual-studio\/\" class=\"advgb-post-tax-term\">Visual Studio<\/a>","<a href=\"https:\/\/mariusbancila.ro\/blog\/category\/it\/web\/\" class=\"advgb-post-tax-term\">Web<\/a>","<a href=\"https:\/\/mariusbancila.ro\/blog\/category\/it\/windows\/\" class=\"advgb-post-tax-term\">Windows<\/a>"],"unlinked":["<span class=\"advgb-post-tax-term\">C++<\/span>","<span class=\"advgb-post-tax-term\">Edge<\/span>","<span class=\"advgb-post-tax-term\">Visual Studio<\/span>","<span class=\"advgb-post-tax-term\">Web<\/span>","<span class=\"advgb-post-tax-term\">Windows<\/span>"]},"tags":{"linked":["<a href=\"https:\/\/mariusbancila.ro\/blog\/category\/it\/windows\/\" class=\"advgb-post-tax-term\">browser<\/a>","<a href=\"https:\/\/mariusbancila.ro\/blog\/category\/it\/windows\/\" class=\"advgb-post-tax-term\">C++<\/a>","<a href=\"https:\/\/mariusbancila.ro\/blog\/category\/it\/windows\/\" class=\"advgb-post-tax-term\">Edge<\/a>","<a href=\"https:\/\/mariusbancila.ro\/blog\/category\/it\/windows\/\" class=\"advgb-post-tax-term\">HTML<\/a>","<a href=\"https:\/\/mariusbancila.ro\/blog\/category\/it\/windows\/\" class=\"advgb-post-tax-term\">MFC<\/a>","<a href=\"https:\/\/mariusbancila.ro\/blog\/category\/it\/windows\/\" class=\"advgb-post-tax-term\">VisualStudio<\/a>","<a href=\"https:\/\/mariusbancila.ro\/blog\/category\/it\/windows\/\" class=\"advgb-post-tax-term\">Windows<\/a>"],"unlinked":["<span class=\"advgb-post-tax-term\">browser<\/span>","<span class=\"advgb-post-tax-term\">C++<\/span>","<span class=\"advgb-post-tax-term\">Edge<\/span>","<span class=\"advgb-post-tax-term\">HTML<\/span>","<span class=\"advgb-post-tax-term\">MFC<\/span>","<span class=\"advgb-post-tax-term\">VisualStudio<\/span>","<span class=\"advgb-post-tax-term\">Windows<\/span>"]}},"comment_count":"7","relative_dates":{"created":"Posted 7 years ago","modified":"Updated 4 years ago"},"absolute_dates":{"created":"Posted on January 29, 2020","modified":"Updated on April 5, 2023"},"absolute_dates_time":{"created":"Posted on January 29, 2020 8:00 pm","modified":"Updated on April 5, 2023 8:06 am"},"featured_img_caption":"","series_order":"","jetpack_shortlink":"https:\/\/wp.me\/pYNdv-Xf","jetpack_sharing_enabled":true,"jetpack_likes_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/posts\/3673","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/comments?post=3673"}],"version-history":[{"count":26,"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/posts\/3673\/revisions"}],"predecessor-version":[{"id":4649,"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/posts\/3673\/revisions\/4649"}],"wp:attachment":[{"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/media?parent=3673"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/categories?post=3673"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mariusbancila.ro\/blog\/wp-json\/wp\/v2\/tags?post=3673"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}