MicrosoftEdge / MicrosoftEdge/WebView2Feedback

Unable to debug .NET app using WebView2 in a C++ DLL: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

Open
#2,095 2 comments 0 reactions 1 assignee View on GitHub

@champnic is already working on this.

Since Jan 27, 2022.

bug
Dominant language
PowerShell
Stars
526
Forks
67
PR merge metrics
No merged PRs in 30d

Description

Description
As the title says, I created a small DLL in C++ that opens WebView2 in a window, to use it as DllImport in the .NET side, mostly because WebView2 is uncompatible with NativeAOT, but I figured I can create windows in DLLs and use them from the .NET side with NativeAOT compatible code, this helps me to keep source code protection by compiling to native code.

Anyways, this only happens when debugging, or if I attach a debugger. When I run this without a debugger, everything works, but then I'm unable to develop and debug my app!

There's no difference if I run a DLL build in Debug or Release mode.

Can't provide a stack trace because VSCode doesn't provide any.

Version
SDK: 1.0.1072.54 ?
Runtime: 1.0.1072.54 ?
Framework: .NET 6 + C++
OS: Win11 ( Microsoft Windows [Version 10.0.22000.434] )

Repro Steps
Use this C++ code in a new Win32 DLL project. It's simple: Create a window and attach WebView2 to it.

#include "pch.h"
#include <stdlib.h>
#include <string>
#include <tchar.h>
#include <windows.h>
#include <wrl.h>
#include <wil/com.h>
#include <WebView2.h>
#define EXTERN_DLL_EXPORT extern "C" __declspec(dllexport)

HMODULE _hInstance;
static wil::com_ptr<ICoreWebView2> webviewWindow;
static wil::com_ptr<ICoreWebView2Controller> webviewController;

BOOL APIENTRY DllMain(HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
	case DLL_THREAD_ATTACH:
		_hInstance = hModule;
	case DLL_THREAD_DETACH:
	case DLL_PROCESS_DETACH:
		break;
	}
	return TRUE;
}

EXTERN_DLL_EXPORT HWND Init() {

	WNDCLASSEX wcex;
	wcex.cbSize = sizeof(WNDCLASSEX);
	wcex.style = CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc = [](HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)->LRESULT {
		switch (message) {
		case WM_SIZE:
			if (webviewController != nullptr)
			{
				RECT bounds;
				GetClientRect(hWnd, &bounds);
				webviewController->put_Bounds(bounds);
			};
			break;
		default:
			return DefWindowProc(hWnd, message, wParam, lParam);
		}
	};
	wcex.cbClsExtra = 0;
	wcex.cbWndExtra = 0;
	wcex.hInstance = _hInstance;
	wcex.hIcon = LoadIcon(_hInstance, IDI_APPLICATION);
	wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	wcex.lpszMenuName = NULL;
	wcex.lpszClassName = _T("TEST");
	wcex.hIconSm = LoadIcon(wcex.hInstance, IDI_APPLICATION);
	if (!RegisterClassEx(&wcex)) { return 0; }

	HWND hWnd = CreateWindow(wcex.lpszClassName, _T("TEST"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 640, 480, NULL, NULL, _hInstance, NULL);
	if (!hWnd) { return 0; }

	ShowWindow(hWnd, SW_SHOW);
	UpdateWindow(hWnd);

	CreateCoreWebView2EnvironmentWithOptions(nullptr, nullptr, nullptr,
		Microsoft::WRL::Callback<ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler>(
			[hWnd](HRESULT result, ICoreWebView2Environment* env) -> HRESULT
			{
				env->CreateCoreWebView2Controller(hWnd, Microsoft::WRL::Callback<ICoreWebView2CreateCoreWebView2ControllerCompletedHandler>(
					[hWnd](HRESULT result, ICoreWebView2Controller* controller) -> HRESULT
					{
						if (controller != nullptr)
						{
							webviewController = controller;
							webviewController->get_CoreWebView2(&webviewWindow);
						}
						RECT bounds;
						GetClientRect(hWnd, &bounds);
						webviewController->put_Bounds(bounds);
						webviewWindow->Navigate(L"https://www.bing.com/");
						return S_OK;
					})
					.Get());
				return S_OK;
			})
		.Get());

	return hWnd;
}

EXTERN_DLL_EXPORT void Loop() {
	MSG msg;
	if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
}

On the .NET side, use this code (IMPORTANT: It's a simple console app, it's not using NativeAOT yet, simple console app from dotnet new console, then paste code and run):

using System.Diagnostics;
using System;
using System.Runtime.InteropServices;

namespace App
{

    public class Program
    {
        const string DLL = @"DllFile.dll";

        [DllImport(DLL)] public static extern IntPtr Init(IntPtr hInstance, IntPtr hPrevInstance, string lpCmdLine, int nCmdShow);
        [DllImport(DLL)] public static extern void Loop();

        [STAThread]
        static void Main(string[] args)
        {
            Init(Process.GetCurrentProcess().Handle, IntPtr.Zero, null, 0);
            while (true) { Loop(); }
        }
    }

}

Additional context

I've also tried with or without STAThread and it doesn't make a difference.

If I run the app manually with dotnet run it all works, until I attach a debugger before CreateCoreWebView2EnvironmentWithOptions is called. If I attach the debugger after the window has been initialized with WebView2, it seems to work, but I haven't gone deeper, as it's crippling my developer experience right now.

I'm filing this as a bug because it's now allowing the debugger to attach / work. However if there is another way to make this work with the debugger attached, I'd greatly appreciate it.

Thanks!

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.