MicrosoftEdge / WebView2Browser

A web browser built with the Microsoft Edge WebView2 control.
Other
402 stars 80 forks source link

Unable to compile WebView2Browser App #54

Open Jenifer-TheCoder opened 5 months ago

Jenifer-TheCoder commented 5 months ago

I had cloned github repo and just built. Errors related to nuget packages were fixed by providing online package source. But I am still getting the errors below: 1>C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Microsoft\VC\v170\Microsoft.CppBuild.targets(517,5): warning MSB8004: Output Directory does not end with a trailing slash. This build instance will add the slash as it is required to allow proper evaluation of the Output Directory. 1>BrowserWindow.cpp 1>..WebView2Browser\framework.h(17,10): error C1083: Cannot open include file: 'cpprest/json.h': No such file or directory 1>(compiling source file 'BrowserWindow.cpp') 1>Tab.cpp 1>..WebView2Browser\framework.h(17,10): error C1083: Cannot open include file: 'cpprest/json.h': No such file or directory 1>(compiling source file 'Tab.cpp') 1>WebViewBrowserApp.cpp 1>..WebView2Browser\framework.h(17,10): error C1083: Cannot open include file: 'cpprest/json.h': No such file or directory 1>(compiling source file 'WebViewBrowserApp.cpp') 1>Generating Code... 1>Done building project "WebViewBrowserApp.vcxproj" -- FAILED.

I am not able to see cpprest folder either, please advise!

Is it specific to the nuget package version otherwise?

Thanks

Stefan-Jauch commented 1 month ago

I also experienced the same error:

Error C1083 Cannot open include file: 'cpprest/json.h': No such file or directory WebView2Browser \WebView2Browser\framework.h 17

After adding the following entry to Project Properties -> VC++ Directories -> Include Directories, compiling the source succeeded:

$(ProjectDir)packages\cpprestsdk.v141.2.10.12.1\build\native\include

But linking failed with:

1>BrowserWindow.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: class web::json::value & __cdecl web::json::value::operator[](class std::basic_string<wchar_t,struct std::char_traits<wchar_t>,class std::allocator<wchar_t> > const &)" (__imp_??Avalue@json@web@@QEAAAEAV012@AEBV?$basic_string@_WU?$char_traits@_W@std@@V?$allocator@_W@2@@std@@@Z)
1>BrowserWindow.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: class web::json::value & __cdecl web::json::value::at(class std::basic_string<wchar_t,struct std::char_traits<wchar_t>,class std::allocator<wchar_t> > const &)" (__imp_?at@value@json@web@@QEAAAEAV123@AEBV?$basic_string@_WU?$char_traits@_W@std@@V?$allocator@_W@2@@std@@@Z)
1>BrowserWindow.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: void __cdecl web::json::value::erase(class std::basic_string<wchar_t,struct std::char_traits<wchar_t>,class std::allocator<wchar_t> > const &)" (__imp_?erase@value@json@web@@QEAAXAEBV?$basic_string@_WU?$char_traits@_W@std@@V?$allocator@_W@2@@std@@@Z)
1>BrowserWindow.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: class std::basic_string<wchar_t,struct std::char_traits<wchar_t>,class std::allocator<wchar_t> > const & __cdecl web::json::value::as_string(void)const " (__imp_?as_string@value@json@web@@QEBAAEBV?$basic_string@_WU?$char_traits@_W@std@@V?$allocator@_W@2@@std@@XZ)
1>BrowserWindow.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: bool __cdecl web::json::value::as_bool(void)const " (__imp_?as_bool@value@json@web@@QEBA_NXZ)
1>BrowserWindow.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: class web::json::number const & __cdecl web::json::value::as_number(void)const " (__imp_?as_number@value@json@web@@QEBAAEBVnumber@23@XZ)
1>BrowserWindow.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: int __cdecl web::json::value::as_integer(void)const " (__imp_?as_integer@value@json@web@@QEBAHXZ)
1>BrowserWindow.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: void __cdecl web::json::value::serialize(class std::basic_ostream<wchar_t,struct std::char_traits<wchar_t> > &)const " (__imp_?serialize@value@json@web@@QEBAXAEAV?$basic_ostream@_WU?$char_traits@_W@std@@@std@@@Z)
1>BrowserWindow.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: static class web::json::value __cdecl web::json::value::parse(class std::basic_string<wchar_t,struct std::char_traits<wchar_t>,class std::allocator<wchar_t> > const &)" (__imp_?parse@value@json@web@@SA?AV123@AEBV?$basic_string@_WU?$char_traits@_W@std@@V?$allocator@_W@2@@std@@@Z)
1>BrowserWindow.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: static class web::json::value __cdecl web::json::value::boolean(bool)" (__imp_?boolean@value@json@web@@SA?AV123@_N@Z)
1>BrowserWindow.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: static class web::json::value __cdecl web::json::value::number(unsigned __int64)" (__imp_?number@value@json@web@@SA?AV123@_K@Z)
1>BrowserWindow.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: class web::json::value & __cdecl web::json::value::operator=(class web::json::value const &)" (__imp_??4value@json@web@@QEAAAEAV012@AEBV012@@Z)
1>BrowserWindow.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: class web::json::value & __cdecl web::json::value::operator=(class web::json::value &&)" (__imp_??4value@json@web@@QEAAAEAV012@$$QEAV012@@Z)
1>BrowserWindow.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: __cdecl web::json::value::value(int)" (__imp_??0value@json@web@@QEAA@H@Z)
1>BrowserWindow.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: __cdecl web::json::value::value(wchar_t const *)" (__imp_??0value@json@web@@QEAA@PEB_W@Z)
1>BrowserWindow.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: __cdecl web::json::value::value(class web::json::value const &)" (__imp_??0value@json@web@@QEAA@AEBV012@@Z)

To fix this, add the following entry to Project Properties -> VC++ Directories -> Library Directories

$(ProjectDir)packages\cpprestsdk.v141.2.10.12.1\build\native\x64\lib

And the following entry to Project Properties -> Linker -> Additional Dependencies

cpprest141_2_10.lib

Then the complete build should succeed. But when starting the app, the DLL cpprest141_2_10.dll (or cpprest141_2_10d.dll) cannot be found. So, copy it from the directory packages\cpprestsdk.v141.2.10.12.1\build\native\x64\bin to the folder where the app executable is located.

(For x86, above mentioned paths must be adapted).

laultman commented 1 month ago

Same problem... @Stefan-Jauch, thanks for the fix post. I'll try it. Any idea how to get an ARM64 build of this and the supporting packages?

laultman commented 1 month ago

@Stefan-Jauch I added a little bit of code to the project file to copy the cpprest141_2_10.dll (or cpprest141_2_10d.dll - debug version) instead of having to manually copy the file. In the project in each configuration there is a "PostBuildEvent" section in the XML of the project. Below is a snippet from the Debug X64. While it makes no difference here, I modified the XML to follow the "normal" VS project pattern where the .bin folder is the head in the output.

This is the debug section, notice the dll file name ends with the letter "d". In the release you would omit the "d". Also in my debug I want to get a list of the files copied during the debug build from the XCOPY command. This is done by adding the "/F" to the command. In release section I don't have that command switch. `

xcopy "$(ProjectDir)wvbrowser_ui\" "$(OutDir)wvbrowser_ui\" /S /I /Y /F xcopy "$(ProjectDir)packages\cpprestsdk.v141.2.10.12.1\build\native\x64\bin\cpprest141_2_10d.dll" "$(OutDir)\" /S /I /Y /F
</PostBuildEvent>

`

laultman commented 1 month ago

To change the output directory is very simple. In each configuration section e.g Debug|Win32 etc., you simply have to add "bin\" to the XML . Before change: <OutDir>$(Configuration)_$(Platform)</OutDir> After change: <OutDir>bin\$(Configuration)_$(Platform)</OutDir>

After making this change in each configuration your project directory with have a bin folder and all your builds will be there.

laultman commented 2 weeks ago

It's been literally a month and still no resolution. I cannot get WebView to initialize outside of a Windows UI framework. It always fails in the create controller black box of the edge code. On arm64 chip I get "procedure not found" same code on x64 I get a failure in the hookup to the HWND parent. Both of these are fatal errors. On the arm64 it is an unhandled exception in the KernelBase on intel it is a failure in the shell.

laultman commented 2 weeks ago

I solved my problem, getting the WebView2 to initialize. There is a litany of issues to get it to work. First and foremost is the HWND. In my specific case I am targeting WinRT in a UWP app on ARM64 building an OpenXR application for HoloLens 2. After a month of experimentation/frustration, I decided to make it work on my Surface Pro 9 i7 Intel x64. I made double sure that everything was up to date and I used Visual Studio 2022 17.11. Note, Windows Update installs the current version of WebView2 along with the Edge Browser and they share the same DLL files. For HoloLens this is great, it significantly reduces the package size.

In my application I am NOT using any of the Windows UI frameworks - HoloLens has its own UI. I am instead, using the C++ VS template, "Core App (C++/WinRT). This produces an App for a Core Window App (no Windows UIs) for UWP it minimal features. I have no desire for backward compatibility, so I set my project to use C++20 as opposed to C++14. Once this worked and ran on my x64, I then took about the prospect of adding in the WebView2 control. Skipping the pain points...

Add two nuget packages to the project: Microsoft.Web.WebView2 (I used the prerelease) and then the Microsoft.Windows.CppWinRT (current version). Run the solution to restore the nuget packages. This should create a "packages" folder in your solution directory. In this directory tree is a critical DLL that you MUST manually include in your application package: WebView2Loader.dll. Here was my relative path, ..\packages\Microsoft.Web.WebView2.1.0.2783-prerelease\build\native\x64\WebView2Loader.dll. If this is not set correctly the app will immediately fail.

Like UWP's manifest where you declare capabilities, the same is true for both C++/WinRT and WebView2. You find these in the project properties/Common properties. I used the defaults for now.

Now is where the fun begins. UWP apps follow an order during startup as all apps do but there are specifics in UWP. The thing to know is that WebView2 requires a valid HWND to parent from that must be the UI thread. Until your app calls the windows.Activate() and if finishes the activation, you cannot get a usable HWND. Because there are so many things that can go wrong during the activation, like the VS debugger stealing the active window before WebView2 can initialize, the code has to be async by design.

The only reliable place to grab the CoreWindow (it is the HWND) is in the SetWindow(CoreWindow window) where the IFramework is creating the main window and passing it to the SetWindow function. In this method I added an event to capture the window activated event from Windows window.Activated({ this, &App::OnWindowActivated });. I added some bool flags to ensure I only initialize once since this event is triggered each time your app window becomes active. I added an event handler void OnWindowActivated(CoreWindow const& sender, WindowActivatedEventArgs const& args) where the sender is the CoreWindow. After testing that the window is activated by checking CoreWindowActivationState and checking that the control is not already initialized, I call my InitializeWebView2(sender); function.

If your read the WebView2 docs you will know that you create an environment for the control and then you create a controller instance of a control. Use the winrt library for this. Do not use the Microsoft.WRL library. There is a complete winrt wrapper for the WebView2 control. Do not mix old and new, unless you know exactly what you are doing. You must implement the create functions as async. winrt::Windows::Foundation::IAsyncAction CreateWV2EnvironmentAsync() for the environment.

Here is what you have been waiting to see, how to get the HWND. First pass in the CoreWindow, that you get when the OnWindowsActived event fires and calls the WebView2 initialize function that in turn calls this function: void CreateCompositionController(CoreWindow const& window, winrt::Microsoft::Web::WebView2::Core::CoreWebView2Environment webViewEnvironment). Now you have the CoreWindow in a function that will async create the controller but you must extract the HWND in a compatible format, auto windowReference = winrt::Microsoft::Web::WebView2::Core::CoreWebView2ControllerWindowReference::CreateFromCoreWindow(window);. Next with the elusive windowReference you do the dirty work. webViewEnvironment.CreateCoreWebView2CompositionControllerAsync(windowReference) .Completed([this, webViewEnvironment](auto const& asyncInfo, winrt::Windows::Foundation::AsyncStatus asyncStatus) { if (asyncStatus == winrt::Windows::Foundation::AsyncStatus::Completed) { m_compositionController = asyncInfo.GetResults(); // Proceed with further initialization if needed m_WV2CtrlInitialized = true; OutputDebugString(L"***JLA CreateCompositionController completed successfully.\n"); } else { OutputDebugString(L"***JLA CreateCompositionController failed.\n"); } }); Sorry the editor here did not honor the line feeds, but you can figure that out.