-
Notifications
You must be signed in to change notification settings - Fork 62
Description
What happened?
Our legacy Win32 app is configured as DPI unaware. We switched from Windowed to Window-to-Visual hosting in order to resolve an issue where two instances would freeze each other if one was busy. The WebView2 control renders and receives mouse input correctly, however the context menu on right click and popups from <select> form input fields appear at an offset if rendered on a screen with scale other than 100%.
Importance
Moderate. My app's user experience is affected, but still usable.
Runtime Channel
Stable release (WebView2 Runtime)
Runtime Version
142.0.3595.94
SDK Version
1.0.3595.46
Framework
Win32
Operating System
Windows 11
OS Version
26100.6584
Repro steps
This is a minimal example, generated with CoPilot, that reproduces the problem. If executed on a monitor with scale larger than 100%, popups appear offset as in the attached images (in this case, scale = 125%). Popups appear normally on a screen with 100% scaling. Attached Wv2PopupIssue.zip.
// WebView2 Window-to-Visual issue on DPI-unaware app
#include <windows.h>
#include <wrl.h>
#include <wil/com.h>
#include <WebView2.h>
#include <WebView2EnvironmentOptions.h>
#include <ShellScalingApi.h>
using namespace Microsoft::WRL;
#pragma comment(lib, "user32.lib")
#pragma comment(lib, "gdi32.lib")
#pragma comment(lib, "shcore.lib")
HINSTANCE g_hInstance;
HWND g_hwndMain = nullptr;
HWND g_hwndLeft = nullptr;
HWND g_hwndWebView = nullptr;
wil::com_ptr<ICoreWebView2Controller> g_webViewController;
const wchar_t* CLASS_NAME = L"WV2TestWindow";
const wchar_t* LEFT_CLASS = L"LeftPanel";
const wchar_t* WEBVIEW_CLASS = L"WebViewPanel";
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK LeftPanelProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK WebViewPanelProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void UpdateLayout(HWND hwnd);
void CreateWebView2();
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
g_hInstance = hInstance;
// Set process to DPI unaware
SetProcessDpiAwareness(PROCESS_DPI_UNAWARE);
// Set environment variable for WebView2 to use visual hosting
SetEnvironmentVariable(
L"COREWEBVIEW2_FORCED_HOSTING_MODE",
L"COREWEBVIEW2_HOSTING_MODE_WINDOW_TO_VISUAL");
// Register main window class
WNDCLASS wc = {};
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
RegisterClass(&wc);
// Register left panel class
WNDCLASS wcLeft = {};
wcLeft.lpfnWndProc = LeftPanelProc;
wcLeft.hInstance = hInstance;
wcLeft.lpszClassName = LEFT_CLASS;
wcLeft.hbrBackground = CreateSolidBrush(RGB(255, 255, 224)); // Pale yellow
wcLeft.hCursor = LoadCursor(nullptr, IDC_ARROW);
RegisterClass(&wcLeft);
// Register webview panel class
WNDCLASS wcWebView = {};
wcWebView.lpfnWndProc = WebViewPanelProc;
wcWebView.hInstance = hInstance;
wcWebView.lpszClassName = WEBVIEW_CLASS;
wcWebView.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcWebView.hCursor = LoadCursor(nullptr, IDC_ARROW);
RegisterClass(&wcWebView);
// Create main window
g_hwndMain = CreateWindowEx(
0,
CLASS_NAME,
L"WebView2 DPI Issue Test",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 1024, 768,
nullptr,
nullptr,
hInstance,
nullptr
);
if (g_hwndMain == nullptr)
{
return 0;
}
// Create left panel
g_hwndLeft = CreateWindowEx(
0,
LEFT_CLASS,
nullptr,
WS_CHILD | WS_VISIBLE,
0, 0, 0, 0,
g_hwndMain,
nullptr,
hInstance,
nullptr
);
// Create webview panel
g_hwndWebView = CreateWindowEx(
0,
WEBVIEW_CLASS,
nullptr,
WS_CHILD | WS_VISIBLE,
0, 0, 0, 0,
g_hwndMain,
nullptr,
hInstance,
nullptr
);
ShowWindow(g_hwndMain, nCmdShow);
UpdateLayout(g_hwndMain);
// Create WebView2
CreateWebView2();
// Message loop
MSG msg = {};
while (GetMessage(&msg, nullptr, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_SIZE:
UpdateLayout(hwnd);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
LRESULT CALLBACK LeftPanelProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
LRESULT CALLBACK WebViewPanelProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
void UpdateLayout(HWND hwnd)
{
RECT rc;
GetClientRect(hwnd, &rc);
int width = rc.right - rc.left;
int height = rc.bottom - rc.top;
if (width > 800)
{
// Show left panel (200px wide)
MoveWindow(g_hwndLeft, 0, 0, 200, height, TRUE);
MoveWindow(g_hwndWebView, 200, 0, width - 200, height, TRUE);
ShowWindow(g_hwndLeft, SW_SHOW);
}
else
{
// Hide left panel, full width for webview
ShowWindow(g_hwndLeft, SW_HIDE);
MoveWindow(g_hwndWebView, 0, 0, width, height, TRUE);
}
// Update WebView2 bounds if it exists
if (g_webViewController)
{
RECT rcWebView;
GetClientRect(g_hwndWebView, &rcWebView);
g_webViewController->put_Bounds(rcWebView);
}
}
void CreateWebView2()
{
// Get user data folder path
wchar_t userDataFolder[MAX_PATH];
GetEnvironmentVariable(L"TEMP", userDataFolder, MAX_PATH);
wcscat_s(userDataFolder, L"\\WV2TestUserData");
CreateCoreWebView2EnvironmentWithOptions(
nullptr,
userDataFolder,
nullptr,
Callback<ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler>(
[](HRESULT result, ICoreWebView2Environment* env) -> HRESULT
{
if (FAILED(result))
{
return result;
}
// Create controller
env->CreateCoreWebView2Controller(
g_hwndWebView,
Callback<ICoreWebView2CreateCoreWebView2ControllerCompletedHandler>(
[](HRESULT result, ICoreWebView2Controller* controller) -> HRESULT
{
if (FAILED(result))
{
return result;
}
g_webViewController = controller;
// Get WebView2 core
wil::com_ptr<ICoreWebView2> webView;
g_webViewController->get_CoreWebView2(&webView);
// Set bounds
RECT rc;
GetClientRect(g_hwndWebView, &rc);
g_webViewController->put_Bounds(rc);
// Navigate to URL
webView->Navigate(L"https://ace.c9.io/build/kitchen-sink.html");
return S_OK;
}
).Get()
);
return S_OK;
}
).Get()
);
}
Repros in Edge Browser
No, issue does not reproduce in the corresponding Edge version
Regression
Don't know
Last working version (if regression)
No response