title: Basic Win32 WebView2 example not working in pure C
body: <p>A new problem blows my mind lately: a very simple code using the WebView2 library from Microsoft works if compiled as C++ but not as C. What could be causing the problem? I tried all sorts of fixes, like using an older version of the WebView2 library, using Edge Canary or beta or a different version of WebView2 Runtime, it simply refuses to work.</p>
  <p>Here is the sample code in C:</p>
  <pre><code>#include &lt;initguid.h&gt;
  #include &lt;Windows.h&gt;
  #include &lt;stdio.h&gt;
  #include &lt;conio.h&gt;
  #include &lt;shlwapi.h&gt;
  #pragma comment(lib, &quot;Shlwapi.lib&quot;)
  #include &lt;Shlobj_core.h&gt;
  #include &quot;WebView2.h&quot;
  
  #define APPLICATION_NAME TEXT(&quot;WebView2&quot;)
  
  #define error_printf printf
  
  ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler* envHandler;
  ICoreWebView2CreateCoreWebView2ControllerCompletedHandler* completedHandler;
  HWND hWnd = NULL;
  ICoreWebView2Controller* webviewController = NULL;
  ICoreWebView2* webviewWindow = NULL;
  BOOL bEnvCreated = FALSE;
  
  ULONG HandlerRefCount = 0;
  ULONG HandlerAddRef(IUnknown* This)
  {
      return ++HandlerRefCount;
  }
  ULONG HandlerRelease(IUnknown* This)
  {
      --HandlerRefCount;
      if (HandlerRefCount == 0)
      {
          if (completedHandler)
          {
              free(completedHandler-&gt;lpVtbl);
              free(completedHandler);
          }
          if (envHandler)
          {
              free(envHandler-&gt;lpVtbl);
              free(envHandler);
          }
      }
      return HandlerRefCount;
  }
  HRESULT HandlerQueryInterface(
      IUnknown* This,
      IID* riid,
      void** ppvObject
  )
  {
      *ppvObject = This;
      HandlerAddRef(This);
      return S_OK;
  }
  HRESULT HandlerInvoke(
      IUnknown* This,
      HRESULT errorCode,
      void* arg
  )
  {
      if (!bEnvCreated)
      {
          bEnvCreated = TRUE;
          char ch;
          completedHandler = malloc(sizeof(ICoreWebView2CreateCoreWebView2ControllerCompletedHandler));
          if (!completedHandler)
          {
              error_printf(
                  &quot;%s:%d: %s (0x%x).\n&quot;,
                  __FILE__,
                  __LINE__,
                  &quot;Cannot allocate ICoreWebView2CreateCoreWebView2ControllerCompletedHandler&quot;,
                  GetLastError()
              );
              ch = _getch();
              return GetLastError();
          }
          completedHandler-&gt;lpVtbl = malloc(sizeof(ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerVtbl));
          if (!completedHandler-&gt;lpVtbl)
          {
              error_printf(
                  &quot;%s:%d: %s (0x%x).\n&quot;,
                  __FILE__,
                  __LINE__,
                  &quot;Cannot allocate ICoreWebView2CreateCoreWebView2ControllerCompletedHandlerVtbl&quot;,
                  GetLastError()
              );
              ch = _getch();
              return GetLastError();
          }
          completedHandler-&gt;lpVtbl-&gt;AddRef = HandlerAddRef;
          completedHandler-&gt;lpVtbl-&gt;Release = HandlerRelease;
          completedHandler-&gt;lpVtbl-&gt;QueryInterface = HandlerQueryInterface;
          completedHandler-&gt;lpVtbl-&gt;Invoke = HandlerInvoke;
  
          ICoreWebView2Environment* env = arg;
          env-&gt;lpVtbl-&gt;CreateCoreWebView2Controller(
              env,
              hWnd,
              completedHandler
          );
      }
      else
      {
          ICoreWebView2Controller* controller = arg;
  
          if (controller != NULL) {
              webviewController = controller;
              webviewController-&gt;lpVtbl-&gt;get_CoreWebView2(
                  webviewController,
                  &amp;webviewWindow
              );
          }
  
          ICoreWebView2Settings* Settings;
          webviewWindow-&gt;lpVtbl-&gt;get_Settings(
              webviewWindow,
              &amp;Settings
          );
          Settings-&gt;lpVtbl-&gt;put_IsScriptEnabled(
              Settings,
              TRUE
          );
          Settings-&gt;lpVtbl-&gt;put_AreDefaultScriptDialogsEnabled(
              Settings,
              TRUE
          );
          Settings-&gt;lpVtbl-&gt;put_IsWebMessageEnabled(
              Settings,
              TRUE
          );
          Settings-&gt;lpVtbl-&gt;put_AreDevToolsEnabled(
              Settings,
              FALSE
          );
          Settings-&gt;lpVtbl-&gt;put_AreDefaultContextMenusEnabled(
              Settings,
              TRUE
          );
          Settings-&gt;lpVtbl-&gt;put_IsStatusBarEnabled(
              Settings,
              TRUE
          );
  
          RECT bounds;
          GetClientRect(hWnd, &amp;bounds);
          webviewController-&gt;lpVtbl-&gt;put_Bounds(
              webviewController,
              bounds
          );
  
          webviewWindow-&gt;lpVtbl-&gt;Navigate(
              webviewWindow,
              L&quot;https://google.com/&quot;
          );
      }
  
      return S_OK;
  }
  
  LRESULT CALLBACK WindowProc(
      _In_ HWND   hWnd,
      _In_ UINT   uMsg,
      _In_ WPARAM wParam,
      _In_ LPARAM lParam
  )
  {
      switch (uMsg)
      {
      /*case WM_NCCALCSIZE:
      {
          return 0;
      }*/
      case WM_DPICHANGED:
      {
          RECT* const newWindowSize = (RECT*)(lParam);
          SetWindowPos(
              hWnd,
              NULL,
              newWindowSize-&gt;left,
              newWindowSize-&gt;top,
              newWindowSize-&gt;right - newWindowSize-&gt;left,
              newWindowSize-&gt;bottom - newWindowSize-&gt;top,
              SWP_NOZORDER | SWP_NOACTIVATE);
          return TRUE;
      }
      case WM_SIZE:
      {
          if (webviewController != NULL) {
              RECT bounds;
              GetClientRect(hWnd, &amp;bounds);
              webviewController-&gt;lpVtbl-&gt;put_Bounds(
                  webviewController,
                  bounds
              );
          };
          break;
      }
      default:
      {
          return DefWindowProc(
              hWnd,
              uMsg,
              wParam,
              lParam
          );
      }
      }
      return 0;
  }
  
  int WINAPI wWinMain(
      _In_ HINSTANCE hInstance,
      _In_opt_ HINSTANCE hPrevInstance,
      _In_ LPWSTR lpCmdLine,
      _In_ int nShowCmd
  )
  {
      int ch;
  
      FILE* conout;
      AllocConsole();
      freopen_s(
          &amp;conout,
          &quot;CONOUT$&quot;,
          &quot;w&quot;,
          stdout
      );
  
      HRESULT hr;
  
      if (!SetProcessDpiAwarenessContext(
          DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2
      ))
      {
          error_printf(
              &quot;%s:%d: %s (0x%x).\n&quot;,
              __FILE__,
              __LINE__,
              &quot;SetProcessDpiAwarenessContext&quot;,
              GetLastError()
          );
          ch = _getch();
          return GetLastError();
      }
  
      hr = CoInitialize(NULL);
      if (FAILED(hr))
      {
          error_printf(
              &quot;%s:%d: %s (0x%x).\n&quot;,
              __FILE__,
              __LINE__,
              &quot;CoInitialize&quot;,
              hr
          );
          ch = _getch();
          return hr;
      }
  
      WNDCLASS wndClass = { 0 };
      wndClass.style = CS_HREDRAW | CS_VREDRAW;
      wndClass.lpfnWndProc = WindowProc;
      wndClass.cbClsExtra = 0;
      wndClass.cbWndExtra = 0;
      wndClass.hInstance = hInstance;
      wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
      wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
      wndClass.lpszMenuName = NULL;
      wndClass.lpszClassName = APPLICATION_NAME;
  
      hWnd = CreateWindowEx(
          0,
          (LPCWSTR)(
              MAKEINTATOM(
                  RegisterClass(&amp;wndClass)
              )
              ),
          APPLICATION_NAME,
          WS_OVERLAPPEDWINDOW,
          100, 100, 800, 800,
          NULL,
          NULL,
          hInstance,
          NULL
      );
      if (!hWnd)
      {
          error_printf(
              &quot;%s:%d: %s (0x%x).\n&quot;,
              __FILE__,
              __LINE__,
              &quot;CreateWindowEx&quot;,
              GetLastError()
          );
          ch = _getch();
          return GetLastError();
      }
  
      ShowWindow(hWnd, nShowCmd);
  
      envHandler = malloc(sizeof(ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler));
      if (!envHandler)
      {
          error_printf(
              &quot;%s:%d: %s (0x%x).\n&quot;,
              __FILE__,
              __LINE__,
              &quot;Cannot allocate ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler&quot;,
              GetLastError()
          );
          ch = _getch();
          return GetLastError();
      }
      envHandler-&gt;lpVtbl = malloc(sizeof(ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerVtbl));
      if (!envHandler-&gt;lpVtbl)
      {
          error_printf(
              &quot;%s:%d: %s (0x%x).\n&quot;,
              __FILE__,
              __LINE__,
              &quot;Cannot allocate ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandlerVtbl&quot;,
              GetLastError()
          );
          ch = _getch();
          return GetLastError();
      }
      envHandler-&gt;lpVtbl-&gt;AddRef = HandlerAddRef;
      envHandler-&gt;lpVtbl-&gt;Release = HandlerRelease;
      envHandler-&gt;lpVtbl-&gt;QueryInterface = HandlerQueryInterface;
      envHandler-&gt;lpVtbl-&gt;Invoke = HandlerInvoke;
      
      UpdateWindow(hWnd);
  
      CreateCoreWebView2EnvironmentWithOptions(
          NULL,
          NULL,
          NULL,
          envHandler
      );
  
      MSG msg;
      BOOL bRet;
      while ((bRet = GetMessage(
          &amp;msg,
          NULL,
          0,
          0)) != 0)
      {
          // An error occured
          if (bRet == -1)
          {
              break;
          }
          else
          {
              TranslateMessage(&amp;msg);
              DispatchMessage(&amp;msg);
          }
      }
  
      return 0;
  }
  </code></pre>
  <p>As you can see, I simply used <code>lpVtbl</code> where appropiate and provided proper callbacks. The code is a bit more compact in C++:</p>
  <pre><code>#include &lt;Windows.h&gt;
  #include &lt;stdio.h&gt;
  #include &lt;wrl.h&gt;
  #include &lt;wil/com.h&gt;
  #include &quot;WebView2.h&quot;
  
  #define APPLICATION_NAME TEXT(&quot;WebView2&quot;)
  
  static wil::com_ptr&lt;ICoreWebView2Controller&gt; webviewController;
  static wil::com_ptr&lt;ICoreWebView2&gt; webviewWindow;
  
  LRESULT CALLBACK WindowProc(
      _In_ HWND   hwnd,
      _In_ UINT   uMsg,
      _In_ WPARAM wParam,
      _In_ LPARAM lParam
  )
  {
      switch (uMsg)
      {
      case WM_DPICHANGED:
      {
          RECT* const newWindowSize = (RECT*)(lParam);
          SetWindowPos(hwnd,
              NULL,
              newWindowSize-&gt;left,
              newWindowSize-&gt;top,
              newWindowSize-&gt;right - newWindowSize-&gt;left,
              newWindowSize-&gt;bottom - newWindowSize-&gt;top,
              SWP_NOZORDER | SWP_NOACTIVATE);
          return TRUE;
      }
      case WM_SIZE:
      {
          if (webviewController != NULL) {
              RECT bounds;
              GetClientRect(hwnd, &amp;bounds);
              webviewController-&gt;put_Bounds(bounds);
          };
          break;
      }
      case WM_DESTROY:
      {
          PostQuitMessage(0);
          break;
      }
      default:
      {
          return DefWindowProc(
              hwnd,
              uMsg,
              wParam,
              lParam
          );
      }
      }
      return 0;
  }
  
  int WINAPI wWinMain(
      HINSTANCE hInstance,
      HINSTANCE hPrevInstance,
      PWSTR pCmdLine,
      int nCmdShow
  )
  {
      SetProcessDpiAwarenessContext(
          DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2
      );
  
      WNDCLASS wndClass = { 0 };
      wndClass.style = CS_HREDRAW | CS_VREDRAW;
      wndClass.lpfnWndProc = WindowProc;
      wndClass.cbClsExtra = 0;
      wndClass.cbWndExtra = 0;
      wndClass.hInstance = hInstance;
      wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
      wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
      wndClass.lpszMenuName = NULL;
      wndClass.lpszClassName = APPLICATION_NAME;
      RegisterClass(&amp;wndClass);
  
      HWND hWnd = CreateWindowEx(
          0,
          APPLICATION_NAME,
          APPLICATION_NAME,
          WS_OVERLAPPEDWINDOW,
          100, 100, 800, 800,
          NULL,
          NULL,
          hInstance,
          NULL
      );
  
      ShowWindow(
          hWnd,
          nCmdShow
      );
      UpdateWindow(hWnd);
  
      CreateCoreWebView2EnvironmentWithOptions(NULL, NULL, NULL,
          Microsoft::WRL::Callback&lt;ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler&gt;(
              [hWnd](HRESULT result, ICoreWebView2Environment* env) -&gt; HRESULT {
  
                  // Create a CoreWebView2Controller and get the associated CoreWebView2 whose parent is the main window hWnd
                  env-&gt;CreateCoreWebView2Controller(hWnd, Microsoft::WRL::Callback&lt;ICoreWebView2CreateCoreWebView2ControllerCompletedHandler&gt;(
                      [hWnd](HRESULT result, ICoreWebView2Controller* controller) -&gt; HRESULT {
                          if (controller != nullptr) {
                              webviewController = controller;
                              webviewController-&gt;get_CoreWebView2(&amp;webviewWindow);
                          }
  
                          // Add a few settings for the webview
                          // The demo step is redundant since the values are the default settings
                          ICoreWebView2Settings* Settings;
                          webviewWindow-&gt;get_Settings(&amp;Settings);
                          Settings-&gt;put_IsScriptEnabled(TRUE);
                          Settings-&gt;put_AreDefaultScriptDialogsEnabled(TRUE);
                          Settings-&gt;put_IsWebMessageEnabled(TRUE);
                          Settings-&gt;put_AreDevToolsEnabled(FALSE);
                          //Settings-&gt;put_AreDefaultContextMenusEnabled(FALSE);
                          Settings-&gt;put_IsStatusBarEnabled(FALSE);
  
                          // Resize WebView to fit the bounds of the parent window
                          RECT bounds;
                          GetClientRect(hWnd, &amp;bounds);
                          webviewController-&gt;put_Bounds(bounds);
                          webviewController-&gt;put_ZoomFactor(0.8);
  
                          // Schedule an async task to navigate to Bing
                          webviewWindow-&gt;Navigate(HOME_PAGE);
  
                          // Step 4 - Navigation events
  
                          // Step 5 - Scripting
  
                          // Step 6 - Communication between host and web content
  
                          return S_OK;
                      }).Get());
                  return S_OK;
              }).Get());
  
      MSG msg;
      BOOL bRet;
      while ((bRet = GetMessage(
          &amp;msg,
          NULL,
          0,
          0)) != 0)
      {
          // An error occured
          if (bRet == -1)
          {
              break;
          }
          else
          {
              TranslateMessage(&amp;msg);
              DispatchMessage(&amp;msg);
          }
      }
  
      return (int)msg.wParam;
  }
  </code></pre>
  <p>Since I have now stumbled upon this, I am really curious what may be causing this. Why does it matter in the end? Thanks for any pointers.</p>
  <p>It does not work means the web page is not displayed. The window is blank. The WebView2 actually is not displayed on the window.</p>
  <p>Edit:</p>
  <p>What are <code>wil::com_ptr</code>s actually? If I change this:</p>
  <pre><code>static wil::com_ptr&lt;ICoreWebView2Controller&gt; webviewController;
  static wil::com_ptr&lt;ICoreWebView2&gt; webviewWindow;
  </code></pre>
  <p>To this:</p>
  <pre><code>static ICoreWebView2Controller* webviewController;
  static ICoreWebView2* webviewWindow;
  </code></pre>
  <p>In C++, I break it. Why? Just why? (I replaced the callbacks <code>Microsoft::WRL::Callback</code> with standalone classes and, naturally, it still worked, but getting rid of the COM pointers and using regular pointers breaks it. Why...?</p>