├── .clang-format ├── .gitignore ├── GNUmakefile ├── Include ├── LICENSE.txt ├── WebView2.h └── WebView2EnvironmentOptions.h ├── LICENSE.md ├── OpenWebView2Loader.def ├── OpenWebView2Loader.sln ├── OpenWebView2Loader.vcxproj ├── OpenWebView2Loader.vcxproj.filters ├── Out └── .gitignore ├── README.md └── Source ├── WebView2Loader.cpp └── WebView2Loader.h /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Google -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vs 2 | *.user 3 | -------------------------------------------------------------------------------- /GNUmakefile: -------------------------------------------------------------------------------- 1 | # Recommendations from https://tech.davis-hansson.com/p/make/. 2 | SHELL := bash 3 | .ONESHELL: 4 | .SHELLFLAGS := -eu -o pipefail -c 5 | .DELETE_ON_ERROR: 6 | .PHONY: all clean 7 | MAKEFLAGS += --warn-undefined-variables 8 | MAKEFLAGS += --no-builtin-rules 9 | 10 | all: Out/WebView2Loader.dll 11 | 12 | clean: 13 | @rm -rf Out/WebView2Loader.dll Out/WebView2Loader.o 14 | 15 | Out/WebView2Loader.dll: Out/WebView2Loader.o 16 | $(CXX) -static -static-libstdc++ -shared Out/WebView2Loader.o -lole32 -lversion -Os -s -o $@ 17 | 18 | Out/WebView2Loader.o: Source/WebView2Loader.cpp 19 | $(CXX) -c Source/WebView2Loader.cpp -O3 -o $@ 20 | -------------------------------------------------------------------------------- /Include/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) Microsoft Corporation. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * The name of Microsoft Corporation, or the names of its contributors 14 | may not be used to endorse or promote products derived from this 15 | software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /Include/WebView2EnvironmentOptions.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) Microsoft Corporation. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | #ifndef __core_webview2_environment_options_h__ 6 | #define __core_webview2_environment_options_h__ 7 | 8 | #include 9 | #include 10 | 11 | #include "webview2.h" 12 | #define CORE_WEBVIEW_TARGET_PRODUCT_VERSION L"86.0.622.22" 13 | 14 | #define COREWEBVIEW2ENVIRONMENTOPTIONS_STRING_PROPERTY(p) \ 15 | public: \ 16 | HRESULT STDMETHODCALLTYPE get_##p(LPWSTR* value) override { \ 17 | if (!value) \ 18 | return E_POINTER; \ 19 | *value = m_##p.Copy(); \ 20 | if ((*value == nullptr) && (m_##p.Get() != nullptr)) \ 21 | return HRESULT_FROM_WIN32(GetLastError()); \ 22 | return S_OK; \ 23 | } \ 24 | HRESULT STDMETHODCALLTYPE put_##p(LPCWSTR value) override { \ 25 | LPCWSTR result = m_##p.Set(value); \ 26 | if ((result == nullptr) && (value != nullptr)) \ 27 | return HRESULT_FROM_WIN32(GetLastError()); \ 28 | return S_OK; \ 29 | } \ 30 | \ 31 | protected: \ 32 | AutoCoMemString m_##p; 33 | 34 | #define COREWEBVIEW2ENVIRONMENTOPTIONS_BOOL_PROPERTY(p) \ 35 | public: \ 36 | HRESULT STDMETHODCALLTYPE get_##p(BOOL* value) override { \ 37 | if (!value) \ 38 | return E_POINTER; \ 39 | *value = m_##p; \ 40 | return S_OK; \ 41 | } \ 42 | HRESULT STDMETHODCALLTYPE put_##p(BOOL value) override { \ 43 | m_##p = value; \ 44 | return S_OK; \ 45 | } \ 46 | \ 47 | protected: \ 48 | BOOL m_##p = FALSE; 49 | 50 | // This is a base COM class that implements ICoreWebView2EnvironmentOptions. 51 | template 55 | class CoreWebView2EnvironmentOptionsBase 56 | : public Microsoft::WRL::Implements< 57 | Microsoft::WRL::RuntimeClassFlags, 58 | ICoreWebView2EnvironmentOptions> { 59 | public: 60 | CoreWebView2EnvironmentOptionsBase() { 61 | // Initialize the target compatible browser version value to the version of 62 | // the browser binaries corresponding to this version of the SDK. 63 | m_TargetCompatibleBrowserVersion.Set(CORE_WEBVIEW_TARGET_PRODUCT_VERSION); 64 | } 65 | 66 | protected: 67 | ~CoreWebView2EnvironmentOptionsBase(){}; 68 | 69 | class AutoCoMemString { 70 | public: 71 | AutoCoMemString() {} 72 | ~AutoCoMemString() { Release(); } 73 | void Release() { 74 | if (m_string) { 75 | deallocate_fn(m_string); 76 | m_string = nullptr; 77 | } 78 | } 79 | 80 | LPCWSTR Set(LPCWSTR str) { 81 | Release(); 82 | if (str) { 83 | m_string = MakeCoMemString(str); 84 | } 85 | return m_string; 86 | } 87 | LPCWSTR Get() { return m_string; } 88 | LPWSTR Copy() { 89 | if (m_string) 90 | return MakeCoMemString(m_string); 91 | return nullptr; 92 | } 93 | 94 | protected: 95 | LPWSTR MakeCoMemString(LPCWSTR source) { 96 | const size_t length = wcslen(source); 97 | const size_t bytes = (length + 1) * sizeof(*source); 98 | // Ensure we didn't overflow during our size calculation. 99 | if (bytes <= length) { 100 | return nullptr; 101 | } 102 | 103 | wchar_t* result = reinterpret_cast(allocate_fn(bytes)); 104 | if (result) 105 | memcpy(result, source, bytes); 106 | 107 | return result; 108 | } 109 | 110 | LPWSTR m_string = nullptr; 111 | }; 112 | 113 | COREWEBVIEW2ENVIRONMENTOPTIONS_STRING_PROPERTY(AdditionalBrowserArguments) 114 | COREWEBVIEW2ENVIRONMENTOPTIONS_STRING_PROPERTY(Language) 115 | COREWEBVIEW2ENVIRONMENTOPTIONS_STRING_PROPERTY(TargetCompatibleBrowserVersion) 116 | COREWEBVIEW2ENVIRONMENTOPTIONS_BOOL_PROPERTY( 117 | AllowSingleSignOnUsingOSPrimaryAccount) 118 | }; 119 | 120 | template 124 | class CoreWebView2EnvironmentOptionsBaseClass 125 | : public Microsoft::WRL::RuntimeClass< 126 | Microsoft::WRL::RuntimeClassFlags, 127 | CoreWebView2EnvironmentOptionsBase> { 131 | public: 132 | CoreWebView2EnvironmentOptionsBaseClass() {} 133 | 134 | protected: 135 | ~CoreWebView2EnvironmentOptionsBaseClass() override{}; 136 | }; 137 | 138 | typedef CoreWebView2EnvironmentOptionsBaseClass 142 | CoreWebView2EnvironmentOptions; 143 | 144 | #endif // __core_webview2_environment_options_h__ 145 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # ISC License (ISC) 2 | 3 | Copyright 2020 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 10 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 12 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 14 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15 | PERFORMANCE OF THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /OpenWebView2Loader.def: -------------------------------------------------------------------------------- 1 | LIBRARY WebView2Loader 2 | EXPORTS 3 | CompareBrowserVersions @1 4 | CreateCoreWebView2Environment @2 5 | CreateCoreWebView2EnvironmentWithOptions @3 6 | GetAvailableCoreWebView2BrowserVersionString @4 7 | -------------------------------------------------------------------------------- /OpenWebView2Loader.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30320.27 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OpenWebView2Loader", "OpenWebView2Loader.vcxproj", "{2A00A703-6C60-44BA-A5DD-58A313466E67}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {2A00A703-6C60-44BA-A5DD-58A313466E67}.Debug|x64.ActiveCfg = Debug|x64 17 | {2A00A703-6C60-44BA-A5DD-58A313466E67}.Debug|x64.Build.0 = Debug|x64 18 | {2A00A703-6C60-44BA-A5DD-58A313466E67}.Debug|x86.ActiveCfg = Debug|Win32 19 | {2A00A703-6C60-44BA-A5DD-58A313466E67}.Debug|x86.Build.0 = Debug|Win32 20 | {2A00A703-6C60-44BA-A5DD-58A313466E67}.Release|x64.ActiveCfg = Release|x64 21 | {2A00A703-6C60-44BA-A5DD-58A313466E67}.Release|x64.Build.0 = Release|x64 22 | {2A00A703-6C60-44BA-A5DD-58A313466E67}.Release|x86.ActiveCfg = Release|Win32 23 | {2A00A703-6C60-44BA-A5DD-58A313466E67}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {71C1712D-6FBD-402F-A03B-722A22E44D5C} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /OpenWebView2Loader.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | Win32Proj 24 | {2a00a703-6c60-44ba-a5dd-58a313466e67} 25 | OpenWebView2Loader 26 | 10.0 27 | 28 | 29 | 30 | DynamicLibrary 31 | true 32 | v143 33 | Unicode 34 | 35 | 36 | DynamicLibrary 37 | false 38 | v143 39 | true 40 | Unicode 41 | 42 | 43 | DynamicLibrary 44 | true 45 | v143 46 | Unicode 47 | 48 | 49 | DynamicLibrary 50 | false 51 | v143 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | $(SolutionDir)Out\Bin\$(PlatformShortName)\$(Configuration)\ 76 | $(SolutionDir)Out\Obj\$(PlatformShortName)\$(Configuration)\ 77 | WebView2Loader 78 | $(SolutionDir)Include;$(VC_IncludePath);$(WindowsSDK_IncludePath); 79 | 80 | 81 | false 82 | $(SolutionDir)Out\Bin\$(PlatformShortName)\$(Configuration)\ 83 | $(SolutionDir)Out\Obj\$(PlatformShortName)\$(Configuration)\ 84 | WebView2Loader 85 | $(SolutionDir)Include;$(VC_IncludePath);$(WindowsSDK_IncludePath); 86 | 87 | 88 | true 89 | $(SolutionDir)Out\Bin\$(PlatformShortName)\$(Configuration)\ 90 | $(SolutionDir)Out\Obj\$(PlatformShortName)\$(Configuration)\ 91 | WebView2Loader 92 | $(SolutionDir)Include;$(VC_IncludePath);$(WindowsSDK_IncludePath); 93 | 94 | 95 | false 96 | $(SolutionDir)Out\Bin\$(PlatformShortName)\$(Configuration)\ 97 | $(SolutionDir)Out\Obj\$(PlatformShortName)\$(Configuration)\ 98 | WebView2Loader 99 | $(SolutionDir)Include;$(VC_IncludePath);$(WindowsSDK_IncludePath); 100 | 101 | 102 | 103 | Level3 104 | true 105 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 106 | true 107 | stdcpplatest 108 | 109 | 110 | Console 111 | true 112 | Version.lib;%(AdditionalDependencies) 113 | OpenWebView2Loader.def 114 | 115 | 116 | 117 | 118 | Level3 119 | true 120 | true 121 | true 122 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 123 | true 124 | stdcpplatest 125 | 126 | 127 | Console 128 | true 129 | true 130 | true 131 | Version.lib;%(AdditionalDependencies) 132 | OpenWebView2Loader.def 133 | 134 | 135 | 136 | 137 | Level3 138 | true 139 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 140 | true 141 | stdcpplatest 142 | 143 | 144 | Console 145 | true 146 | Version.lib;%(AdditionalDependencies) 147 | OpenWebView2Loader.def 148 | 149 | 150 | 151 | 152 | Level3 153 | true 154 | true 155 | true 156 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 157 | true 158 | stdcpplatest 159 | 160 | 161 | Console 162 | true 163 | true 164 | true 165 | Version.lib;%(AdditionalDependencies) 166 | OpenWebView2Loader.def 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | -------------------------------------------------------------------------------- /OpenWebView2Loader.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {21206c07-7d27-4bac-96e0-7e483e13dd0c} 14 | 15 | 16 | 17 | 18 | Source Files 19 | 20 | 21 | 22 | 23 | Header Files 24 | 25 | 26 | Header Files\Include 27 | 28 | 29 | Header Files\Include 30 | 31 | 32 | 33 | 34 | Source Files 35 | 36 | 37 | -------------------------------------------------------------------------------- /Out/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OpenWebView2Loader 2 | OpenWebView2Loader is a reimplementation of WebView2Loader. It is intended to 3 | be feature-complete with the original WebView2Loader distributed with the 4 | WebView2 NuGet package. 5 | 6 | For most intents and purposes, you should prefer using the official 7 | WebView2Loader. This implementation may be of interest if you need to 8 | understand how WebView2Loader works and could be used to implement 9 | WebView2Loader in another programming language without needing a C++ 10 | dependency. 11 | -------------------------------------------------------------------------------- /Source/WebView2Loader.cpp: -------------------------------------------------------------------------------- 1 | #include "WebView2Loader.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #ifndef __MINGW32__ 7 | #include 8 | #endif 9 | 10 | #include 11 | #include 12 | 13 | #include "../Include/WebView2.h" 14 | 15 | namespace { 16 | 17 | #pragma region Win32 API Pointer Types 18 | 19 | typedef LONG(WINAPI* GetCurrentPackageInfoProc)(UINT32 flags, 20 | UINT32* bufferLength, 21 | BYTE* buffer, UINT32* count); 22 | 23 | typedef LONG(WINAPI* GetCurrentApplicationUserModelIdProc)( 24 | UINT32* applicationUserModelIdLength, PWSTR applicationUserModelId); 25 | 26 | #pragma endregion 27 | 28 | #pragma region Enumerators 29 | 30 | enum class WebView2RunTimeType { 31 | kInstalled = 0x0, 32 | kRedistributable = 0x1, 33 | }; 34 | 35 | enum class WebView2ReleaseChannelPreference { 36 | kStable = 0x0, 37 | kCanary = 0x1, 38 | }; 39 | 40 | #pragma endregion 41 | 42 | #pragma region Globals 43 | 44 | constexpr int kNumChannels = 5; 45 | 46 | const wchar_t* kChannelName[kNumChannels] = { 47 | L"", L"beta", L"dev", L"canary", L"internal", 48 | }; 49 | 50 | const wchar_t* kChannelUuid[kNumChannels] = { 51 | L"{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}", 52 | L"{2CD8A007-E189-409D-A2C8-9AF4EF3C72AA}", 53 | L"{0D50BFEC-CD6A-4F9A-964C-C7416E3ACB10}", 54 | L"{65C35B14-6C1D-4122-AC46-7148CC9D6497}", 55 | L"{BE59E8FD-089A-411B-A3B0-051D9E417818}"}; 56 | 57 | const wchar_t* kChannelPackageFamilyName[kNumChannels] = { 58 | L"Microsoft.WebView2Runtime.Stable_8wekyb3d8bbwe", 59 | L"Microsoft.WebView2Runtime.Beta_8wekyb3d8bbwe", 60 | L"Microsoft.WebView2Runtime.Dev_8wekyb3d8bbwe", 61 | L"Microsoft.WebView2Runtime.Canary_8wekyb3d8bbwe", 62 | L"Microsoft.WebView2Runtime.Internal_8wekyb3d8bbwe"}; 63 | 64 | constexpr wchar_t kInstallKeyPath[] = 65 | L"Software\\Microsoft\\EdgeUpdate\\ClientState\\"; 66 | 67 | constexpr wchar_t kRedistOverrideKey[] = 68 | L"Software\\Policies\\Microsoft\\Edge\\WebView2\\"; 69 | 70 | constexpr wchar_t kEmbeddedOverrideKey[] = 71 | L"Software\\Policies\\Microsoft\\EmbeddedBrowserWebView\\LoaderOverride\\"; 72 | 73 | constexpr UINT32 kMinimumCompatibleVersion[4] = {86, 0, 616, 0}; 74 | 75 | #if defined(_M_X64) || defined(_M_AMD64) 76 | constexpr wchar_t kEmbeddedWebViewPath[] = 77 | L"EBWebView\\x64\\EmbeddedBrowserWebView.dll"; 78 | #elif defined(_M_IX86) 79 | constexpr wchar_t kEmbeddedWebViewPath[] = 80 | L"EBWebView\\x86\\EmbeddedBrowserWebView.dll"; 81 | #elif defined(_M_ARM64) 82 | constexpr wchar_t kEmbeddedWebViewPath[] = 83 | L"EBWebView\\arm64\\EmbeddedBrowserWebView.dll"; 84 | #endif 85 | 86 | bool gShouldCheckPolicyOverride = true; 87 | bool gShouldCheckRegistryOverride = true; 88 | 89 | #pragma endregion 90 | 91 | #pragma region Structures 92 | 93 | struct WebView2EnvironmentParams { 94 | PCWSTR embeddedEdgeSubFolder; 95 | PCWSTR userDataDir; 96 | ICoreWebView2EnvironmentOptions* environmentOptions; 97 | WebView2ReleaseChannelPreference releaseChannelPreference; 98 | }; 99 | 100 | #pragma endregion 101 | 102 | #pragma region Classes 103 | 104 | /// 105 | /// EnvironmentCreatedRetryHandler is an implementation of 106 | /// ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler that proxies 107 | /// another completion handler but implements retry logic. 108 | /// 109 | #ifndef __MINGW32__ 110 | class EnvironmentCreatedRetryHandler 111 | : public Microsoft::WRL::RuntimeClass< 112 | Microsoft::WRL::RuntimeClassFlags, 113 | ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler> { 114 | public: 115 | #else 116 | // This deviates from actual WebView2Loader, but makes it possible to compile under MinGW. 117 | class EnvironmentCreatedRetryHandler 118 | : public ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler { 119 | unsigned __int64 volatile m_cRef = 1; 120 | public: 121 | HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID *ppvObj) override { 122 | constexpr GUID iidIUnknown = { 123 | 0x00000000, 124 | 0x0000, 125 | 0x0000, 126 | {0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}}; 127 | 128 | constexpr GUID iidICoreICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler = { 129 | 0x4e8a3389, 130 | 0xc9d8, 131 | 0x4bd2, 132 | {0xb6,0xb5,0x12,0x4f,0xee,0x6c,0xc1,0x4d}}; 133 | 134 | if (!ppvObj) return E_INVALIDARG; 135 | *ppvObj = NULL; 136 | if (riid == iidIUnknown || riid == iidICoreICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler) { 137 | *ppvObj = (LPVOID)this; 138 | AddRef(); 139 | return NOERROR; 140 | } 141 | return E_NOINTERFACE; 142 | } 143 | ULONG STDMETHODCALLTYPE AddRef() override { 144 | InterlockedIncrement(&m_cRef); 145 | return m_cRef; 146 | } 147 | ULONG STDMETHODCALLTYPE Release() override { 148 | ULONG ulRefCount = InterlockedDecrement(&m_cRef); 149 | if (m_cRef == 0) { 150 | delete this; 151 | } 152 | return ulRefCount; 153 | } 154 | #endif 155 | EnvironmentCreatedRetryHandler( 156 | WebView2EnvironmentParams environmentParams, 157 | ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler* 158 | originalHandler, 159 | int retries) 160 | : mEnvironmentParams(environmentParams), 161 | mOriginalHandler(originalHandler), 162 | mRetries(retries) {} 163 | 164 | HRESULT STDMETHODCALLTYPE 165 | Invoke(HRESULT result, ICoreWebView2Environment* createdEnvironment) override; 166 | 167 | private: 168 | WebView2EnvironmentParams mEnvironmentParams; 169 | Microsoft::WRL::ComPtr< 170 | ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler> 171 | mOriginalHandler; 172 | int mRetries; 173 | }; 174 | 175 | HRESULT TryCreateWebViewEnvironment( 176 | WebView2EnvironmentParams params, 177 | ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler* 178 | environmentCreatedHandler); 179 | 180 | HRESULT STDMETHODCALLTYPE EnvironmentCreatedRetryHandler::Invoke( 181 | HRESULT result, ICoreWebView2Environment* createdEnvironment) { 182 | constexpr GUID iidICoreWebView2Environment = { 183 | 0xb96d755e, 184 | 0x0319, 185 | 0x4e92, 186 | {0xa2, 0x96, 0x23, 0x43, 0x6f, 0x46, 0xa1, 0xfc}}; 187 | 188 | if (result >= 0 || this->mRetries <= 0) { 189 | if (createdEnvironment) { 190 | // May or may not be necessary, but the official 191 | // WebView2Loader seems to do it. 192 | result = createdEnvironment->QueryInterface( 193 | iidICoreWebView2Environment, 194 | reinterpret_cast(&createdEnvironment)); 195 | } 196 | this->mOriginalHandler->Invoke(result, createdEnvironment); 197 | if (createdEnvironment) { 198 | createdEnvironment->Release(); 199 | createdEnvironment = nullptr; 200 | } 201 | } else { 202 | this->mRetries--; 203 | result = TryCreateWebViewEnvironment(this->mEnvironmentParams, this); 204 | if (result < 0) { 205 | this->mOriginalHandler->Invoke(result, nullptr); 206 | } 207 | } 208 | return S_OK; 209 | } 210 | 211 | /// 212 | /// WString implements a simple UTF-16/WTF-16 string class. It is similar to 213 | /// std::wstring but allows handling allocation failures exceptions. 214 | /// 215 | class WString { 216 | public: 217 | WString() = default; 218 | ~WString() { Release(); } 219 | 220 | WString(const WString&) = delete; 221 | WString(WString&&) = delete; 222 | 223 | WString& operator=(const WString&) = delete; 224 | WString& operator=(WString&&) = delete; 225 | 226 | /// 227 | /// Releases the buffer and clears the string. 228 | /// 229 | void Release() { 230 | this->mLength = 0; 231 | this->mCapacity = 0; 232 | if (this->mData) { 233 | *this->mData = 0; 234 | delete[] this->mData; 235 | this->mData = nullptr; 236 | } 237 | } 238 | 239 | /// 240 | /// Reserves enough buffer to store `len` characters. 241 | /// 242 | /// 243 | /// Length in characters (sans the null terminator.) 244 | /// 245 | /// 246 | /// True if successful, false if an allocation error occurs. 247 | /// 248 | bool Reserve(size_t len) { 249 | if (this->mCapacity >= len) { 250 | return true; 251 | } 252 | 253 | auto* newString = new wchar_t[len + 1]; 254 | if (this->mLength) { 255 | std::memcpy(newString, this->mData, this->mLength * 2 + 2); 256 | } else { 257 | *newString = 0; 258 | } 259 | 260 | delete[] this->mData; 261 | 262 | this->mData = newString; 263 | this->mCapacity = len; 264 | 265 | return true; 266 | } 267 | 268 | /// 269 | /// Assigns the null-terminated string `src` to this instance. 270 | /// 271 | /// A null-terminated string. 272 | /// 273 | /// True if successful, false if an allocation error occurs. 274 | /// 275 | bool Assign(const wchar_t* src) { return Assign(src, src ? wcslen(src) : 0); } 276 | 277 | /// 278 | /// Assigns the string `src` with `len` bytes to this instance. 279 | /// 280 | /// A pointer to string data. 281 | /// Length in characters to copy. 282 | /// 283 | /// True if successful, false if an allocation error occurs. 284 | /// 285 | bool Assign(const wchar_t* src, size_t len) { 286 | this->mLength = 0; 287 | if (this->mData) { 288 | *this->mData = 0; 289 | } 290 | if (!src) { 291 | return true; 292 | } 293 | if (!Reserve(len)) { 294 | return false; 295 | } 296 | std::memcpy(this->mData, src, len * 2); 297 | if (this->mCapacity >= len) { 298 | this->mLength = len; 299 | if (this->mData) { 300 | this->mData[len] = 0; 301 | } 302 | return true; 303 | } 304 | return false; 305 | } 306 | 307 | /// 308 | /// Clears the string without releasing the underlying buffer. 309 | /// 310 | void Clear() { 311 | this->mLength = 0; 312 | if (this->mData) { 313 | *this->mData = 0; 314 | } 315 | } 316 | 317 | /// 318 | /// Resizes the string to the given length. 319 | /// 320 | /// 321 | /// Length in characters (sans the null terminator.) 322 | /// 323 | /// True if successful, false otherwise. 324 | bool Resize(size_t len) { 325 | if (len > this->mCapacity) { 326 | if (!Reserve(len)) { 327 | return false; 328 | } 329 | } 330 | this->mLength = len; 331 | if (this->mData) { 332 | this->mData[len] = 0; 333 | } 334 | return true; 335 | } 336 | 337 | /// 338 | /// Appends the null-terminated string `src` to this instance. 339 | /// 340 | /// A null-terminated string. 341 | /// 342 | /// True if successful, false if an allocation error occurs. 343 | /// 344 | bool Append(const wchar_t* src) { return Append(src, src ? wcslen(src) : 0); } 345 | 346 | /// 347 | /// Appends the string `src` with `len` bytes to this instance. 348 | /// 349 | /// A pointer to string data. 350 | /// Length in characters to copy. 351 | /// 352 | /// True if successful, false if an allocation error occurs. 353 | /// 354 | bool Append(const wchar_t* src, size_t len) { 355 | if (!src) { 356 | return true; 357 | } 358 | size_t newLen = this->mLength + len; 359 | if (!Reserve(newLen)) { 360 | return false; 361 | } 362 | std::memcpy(&this->mData[this->mLength], src, len * 2); 363 | if (this->mCapacity >= newLen) { 364 | this->mLength = newLen; 365 | if (this->mData) { 366 | this->mData[newLen] = 0; 367 | } 368 | return true; 369 | } 370 | return false; 371 | } 372 | 373 | /// 374 | /// Like Reserve, but works on a count of bytes. 375 | /// 376 | /// Number of bytes of storage to reserve. 377 | /// 378 | /// True if successful, false if an allocation error occurs. 379 | /// 380 | bool ReserveBuffer(size_t len) { return Reserve((len >> 1) + 1); } 381 | 382 | [[nodiscard]] size_t Capacity() const { return this->mCapacity; } 383 | 384 | [[nodiscard]] size_t Length() const { return this->mLength; } 385 | 386 | [[nodiscard]] bool Empty() const { return this->mLength == 0; } 387 | 388 | [[nodiscard]] PVOID Data() const { return mData; } 389 | 390 | [[nodiscard]] PWSTR String() const { return mData; } 391 | 392 | private: 393 | size_t mLength = 0; 394 | size_t mCapacity = 0; 395 | wchar_t* mData = nullptr; 396 | }; 397 | 398 | #pragma endregion 399 | 400 | #pragma region Functions 401 | 402 | int GetAppUserModelIdForCurrentProcess(WString* idOut) { 403 | auto* lpGetCurrentApplicationUserModelId = 404 | reinterpret_cast( 405 | GetProcAddress(GetModuleHandleW(L"Kernel32.dll"), 406 | "GetCurrentApplicationUserModelId")); 407 | 408 | // Win8+: Use GetCurrentApplicationUserModelId. 409 | if (lpGetCurrentApplicationUserModelId) { 410 | idOut->Reserve(0x100); 411 | auto idLength = static_cast(idOut->Capacity()); 412 | if (!lpGetCurrentApplicationUserModelId(&idLength, idOut->String())) { 413 | if (!idOut->Resize(idLength - 1)) { 414 | return E_UNEXPECTED; 415 | } 416 | return S_OK; 417 | } 418 | } 419 | 420 | // Win7: Use GetCurrentProcessExplicitAppUserModelID. 421 | PWSTR appId = nullptr; 422 | HRESULT hr = GetCurrentProcessExplicitAppUserModelID(&appId); 423 | if (FAILED(hr)) { 424 | CoTaskMemFree(appId); 425 | appId = nullptr; 426 | return hr; 427 | } 428 | if (!idOut->Assign(appId)) { 429 | CoTaskMemFree(appId); 430 | appId = nullptr; 431 | return HRESULT_FROM_WIN32(GetLastError()); 432 | } 433 | CoTaskMemFree(appId); 434 | appId = nullptr; 435 | return S_OK; 436 | } 437 | 438 | HRESULT GetModulePath(HMODULE hModule, WString* outPath) { 439 | outPath->Reserve(MAX_PATH); 440 | DWORD result = GetModuleFileNameW(hModule, outPath->String(), 441 | static_cast(outPath->Capacity())); 442 | if (result == outPath->Capacity() && 443 | GetLastError() == ERROR_INSUFFICIENT_BUFFER) { 444 | outPath->Reserve(0x1000u); 445 | result = GetModuleFileNameW(hModule, outPath->String(), 446 | static_cast(outPath->Capacity())); 447 | } 448 | if (!result || result >= outPath->Capacity()) { 449 | return HRESULT_FROM_WIN32(GetLastError()); 450 | } 451 | outPath->Resize(result); 452 | return S_OK; 453 | } 454 | 455 | wchar_t* MakeCoMemString(PCWSTR str, SIZE_T len) { 456 | SIZE_T byteLen = len * 2 + 2; 457 | 458 | // Overflow check 459 | if (byteLen < len) return nullptr; 460 | 461 | auto* coMem = static_cast(CoTaskMemAlloc(byteLen)); 462 | if (coMem) CopyMemory(coMem, str, byteLen); 463 | return coMem; 464 | } 465 | 466 | int DoesPolicyExistInRoot(HKEY hKey) { 467 | HKEY phkResult = nullptr; 468 | LSTATUS result = 469 | RegOpenKeyExW(hKey, L"Software\\Policies\\Microsoft\\Edge\\WebView2\\", 0, 470 | 0x20019u, &phkResult); 471 | RegCloseKey(phkResult); 472 | return result == ERROR_SUCCESS; 473 | } 474 | 475 | bool ReadEnvironmentVariable(LPCWSTR lpName, WString* outValue) { 476 | DWORD len = GetEnvironmentVariableW(lpName, nullptr, 0); 477 | if (!len || !outValue->Reserve(len)) return false; 478 | return outValue->Resize( 479 | GetEnvironmentVariableW(lpName, outValue->String(), len)); 480 | } 481 | 482 | bool RegGetString(const WCHAR* lpValue, HKEY key, PCWSTR* outBuf, 483 | WString* outStr) { 484 | DWORD pcbData; 485 | wchar_t pvData[MAX_PATH]; 486 | 487 | memset(pvData, 0, sizeof(pvData)); 488 | pcbData = MAX_PATH; 489 | LSTATUS lStatus = 490 | RegGetValueW(key, nullptr, lpValue, 2u, nullptr, pvData, &pcbData); 491 | if (lStatus != ERROR_SUCCESS) { 492 | return false; 493 | } 494 | outStr->Assign(pvData); 495 | *outBuf = outStr->String(); 496 | return true; 497 | } 498 | 499 | bool ReadOverrideFromRegistry(PCWSTR key, HKEY root, const WCHAR* lpValue, 500 | PCWSTR* outBuf, WString* outStr, DWORD* outInt, 501 | bool redist) { 502 | DWORD pcbData; 503 | HKEY phkResult = nullptr; 504 | if (!key || !*key) return false; 505 | WString pvData; 506 | if (redist) 507 | pvData.Append(kRedistOverrideKey); 508 | else 509 | pvData.Append(kEmbeddedOverrideKey); 510 | pvData.Append(key); 511 | LSTATUS lStatus = 512 | RegOpenKeyExW(root, pvData.String(), 0, KEY_QUERY_VALUE, &phkResult); 513 | if (lStatus != ERROR_SUCCESS) { 514 | return false; 515 | } 516 | gShouldCheckRegistryOverride = true; 517 | 518 | if (outInt) { 519 | char szData[4]; 520 | szData[0] = 0; 521 | pcbData = 4; 522 | if (RegGetValueW(phkResult, nullptr, lpValue, RRF_RT_REG_DWORD, nullptr, 523 | szData, &pcbData) == 0) { 524 | *outInt = szData[0] == 1; 525 | } else { 526 | if (!RegGetString(lpValue, phkResult, outBuf, outStr)) { 527 | RegCloseKey(phkResult); 528 | return false; 529 | } 530 | *outInt = wcstol(*outBuf, nullptr, 10) == 1; 531 | } 532 | RegCloseKey(phkResult); 533 | return true; 534 | } 535 | 536 | if (!RegGetString(lpValue, phkResult, outBuf, outStr)) { 537 | RegCloseKey(phkResult); 538 | return false; 539 | } 540 | 541 | RegCloseKey(phkResult); 542 | return true; 543 | } 544 | 545 | bool UpdateParamsWithRegOverrides(PCWSTR key, HKEY root, PCWSTR* outBuf, 546 | WString* outStr, DWORD* outInt, bool redist) { 547 | WString exeName, aumId, modulePath; 548 | GetAppUserModelIdForCurrentProcess(&aumId); 549 | if (FAILED(GetModulePath(nullptr, &modulePath))) { 550 | exeName.Clear(); 551 | } else { 552 | const wchar_t* lastSlash = wcsrchr(modulePath.String(), L'\\'); 553 | if (!lastSlash) lastSlash = modulePath.String(); 554 | exeName.Assign(lastSlash + 1); 555 | } 556 | 557 | if (gShouldCheckPolicyOverride && redist) { 558 | if (ReadOverrideFromRegistry(key, root, aumId.String(), outBuf, outStr, 559 | outInt, redist)) 560 | return true; 561 | if (ReadOverrideFromRegistry(key, root, exeName.String(), outBuf, outStr, 562 | outInt, redist)) 563 | return true; 564 | if (ReadOverrideFromRegistry(key, root, L"*", outBuf, outStr, outInt, 565 | redist)) 566 | return true; 567 | return false; 568 | } 569 | 570 | if (ReadOverrideFromRegistry(aumId.String(), root, key, outBuf, outStr, 571 | outInt, redist)) 572 | return true; 573 | if (ReadOverrideFromRegistry(exeName.String(), root, key, outBuf, outStr, 574 | outInt, redist)) 575 | return true; 576 | if (ReadOverrideFromRegistry(L"*", root, key, outBuf, outStr, outInt, redist)) 577 | return true; 578 | return false; 579 | } 580 | 581 | bool UpdateParamsWithOverrides(const wchar_t* env, const wchar_t* key, 582 | PCWSTR* outBuf, WString* outStr, DWORD* outInt, 583 | bool checkOverride) { 584 | if (checkOverride) { 585 | gShouldCheckPolicyOverride = true; 586 | gShouldCheckRegistryOverride = true; 587 | } 588 | 589 | if (ReadEnvironmentVariable(env, outStr)) { 590 | *outBuf = outStr->String(); 591 | DWORD intVal = wcstol(outStr->String(), nullptr, 10) == 1; 592 | if (outInt) { 593 | *outInt = intVal; 594 | } 595 | return intVal; 596 | } 597 | 598 | if (!gShouldCheckRegistryOverride && !gShouldCheckPolicyOverride && 599 | !checkOverride) { 600 | return false; 601 | } 602 | 603 | gShouldCheckRegistryOverride = false; 604 | gShouldCheckPolicyOverride = DoesPolicyExistInRoot(HKEY_CURRENT_USER) || 605 | DoesPolicyExistInRoot(HKEY_LOCAL_MACHINE); 606 | 607 | return UpdateParamsWithRegOverrides(key, HKEY_LOCAL_MACHINE, outBuf, outStr, 608 | outInt, true) || 609 | UpdateParamsWithRegOverrides(key, HKEY_CURRENT_USER, outBuf, outStr, 610 | outInt, true) || 611 | UpdateParamsWithRegOverrides(key, HKEY_LOCAL_MACHINE, outBuf, outStr, 612 | outInt, false) || 613 | UpdateParamsWithRegOverrides(key, HKEY_CURRENT_USER, outBuf, outStr, 614 | outInt, false); 615 | } 616 | 617 | void UpdateWebViewEnvironmentParamsWithOverrideValues( 618 | WebView2EnvironmentParams* params, WString* outStrings) { 619 | UpdateParamsWithOverrides( 620 | L"WEBVIEW2_BROWSER_EXECUTABLE_FOLDER", L"browserExecutableFolder", 621 | ¶ms->embeddedEdgeSubFolder, &outStrings[0], nullptr, true); 622 | UpdateParamsWithOverrides(L"WEBVIEW2_USER_DATA_FOLDER", L"userDataFolder", 623 | ¶ms->userDataDir, &outStrings[1], nullptr, 624 | false); 625 | const wchar_t* tmpStr = nullptr; 626 | WString tmpWStr; 627 | UpdateParamsWithOverrides( 628 | L"WEBVIEW2_RELEASE_CHANNEL_PREFERENCE", L"releaseChannelPreference", 629 | &tmpStr, &tmpWStr, 630 | reinterpret_cast(¶ms->releaseChannelPreference), false); 631 | } 632 | 633 | HRESULT CreateWebViewEnvironmentWithClientDll( 634 | PCWSTR lpLibFileName, bool unknown, WebView2RunTimeType runtimeType, 635 | PCWSTR unknown2, IUnknown* unknown3, 636 | ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler* 637 | envCompletedHandler) { 638 | HMODULE clientDll = LoadLibraryW(lpLibFileName); 639 | if (!clientDll) { 640 | return HRESULT_FROM_WIN32(GetLastError()); 641 | } 642 | 643 | // This routine is not documented! 644 | auto createProc = reinterpret_cast( 647 | GetProcAddress(clientDll, "CreateWebViewEnvironmentWithOptionsInternal")); 648 | 649 | auto canUnloadProc = reinterpret_cast( 650 | GetProcAddress(clientDll, "DllCanUnloadNow")); 651 | 652 | if (!createProc) { 653 | return HRESULT_FROM_WIN32(GetLastError()); 654 | } 655 | 656 | HRESULT hr = 657 | createProc(unknown, runtimeType, unknown2, unknown3, envCompletedHandler); 658 | 659 | if (canUnloadProc && SUCCEEDED(canUnloadProc())) { 660 | FreeLibrary(clientDll); 661 | } 662 | 663 | return hr; 664 | } 665 | 666 | bool FindClientDllInFolder(WString* folder) { 667 | folder->Append(L"\\"); 668 | folder->Append(kEmbeddedWebViewPath); 669 | return GetFileAttributesW(folder->String()) != INVALID_FILE_ATTRIBUTES; 670 | } 671 | 672 | void GetInstallKeyPathForChannel(DWORD channel, WString* outRegistryKey) { 673 | const auto* guid = kChannelUuid[channel]; 674 | outRegistryKey->Reserve(wcslen(guid) + wcslen(kInstallKeyPath)); 675 | outRegistryKey->Assign(L"Software\\Microsoft\\EdgeUpdate\\ClientState\\", 676 | wcslen(kInstallKeyPath)); 677 | outRegistryKey->Append(guid, wcslen(guid)); 678 | } 679 | 680 | bool CheckVersionAndFindClientDllInFolder(const UINT32* version, 681 | WString* path) { 682 | for (int component = 0; component < 4; component++) { 683 | if (version[component] < kMinimumCompatibleVersion[component]) { 684 | return false; 685 | } 686 | if (version[component] > kMinimumCompatibleVersion[component]) { 687 | break; 688 | } 689 | } 690 | return FindClientDllInFolder(path); 691 | } 692 | 693 | bool ParseVersionNumbers(PCWSTR versionString, UINT32* outVersion) { 694 | const wchar_t* start = versionString; 695 | wchar_t* end = nullptr; 696 | for (int i = 0; i < 4; i++) { 697 | outVersion[i] = wcstol(start, &end, 10); 698 | if (!end || start == end) { 699 | return false; 700 | } 701 | if (*end == L'.') { 702 | start = end + 1; 703 | } 704 | } 705 | return true; 706 | } 707 | 708 | int FindInstalledClientDllForChannel(PCWSTR lpSubKey, bool system, 709 | WString* versionStr, WString* clientPath) { 710 | HKEY phkResult; 711 | DWORD cbPath = MAX_PATH; 712 | UINT32 version[4]; 713 | wchar_t path[MAX_PATH]; 714 | 715 | if (RegOpenKeyExW(system ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER, lpSubKey, 716 | 0, KEY_READ | KEY_WOW64_32KEY, &phkResult)) { 717 | return false; 718 | } 719 | LSTATUS result = RegQueryValueExW(phkResult, L"EBWebView", nullptr, nullptr, 720 | reinterpret_cast(path), &cbPath); 721 | RegCloseKey(phkResult); 722 | if (result) { 723 | return false; 724 | } 725 | clientPath->Assign(path); 726 | const wchar_t* versionPart = wcsrchr(clientPath->String(), '\\'); 727 | if (!versionPart) { 728 | return false; 729 | } 730 | if (!ParseVersionNumbers(versionPart + 1, version)) { 731 | return false; 732 | } 733 | if (versionStr) { 734 | versionStr->Assign(versionPart + 1); 735 | } 736 | return CheckVersionAndFindClientDllInFolder(version, clientPath); 737 | } 738 | 739 | int FindInstalledClientDll(WString* clientPath, 740 | WebView2ReleaseChannelPreference preference, 741 | WString* versionStr, WString* channelStr) { 742 | DWORD channel = 0; 743 | WString lpSubKey; 744 | UINT32 version[4]; 745 | WString pkgBuf; 746 | 747 | static auto getCurrentPackageInfo = 748 | reinterpret_cast(GetProcAddress( 749 | GetModuleHandleW(L"kernelbase.dll"), "GetCurrentPackageInfo")); 750 | 751 | for (int i = 0; i < kNumChannels; i++) { 752 | channel = 753 | preference == WebView2ReleaseChannelPreference::kCanary ? 4 - i : i; 754 | GetInstallKeyPathForChannel(channel, &lpSubKey); 755 | if (FindInstalledClientDllForChannel(lpSubKey.String(), false, versionStr, 756 | clientPath)) { 757 | break; 758 | } 759 | if (FindInstalledClientDllForChannel(lpSubKey.String(), true, versionStr, 760 | clientPath)) { 761 | break; 762 | } 763 | if (!getCurrentPackageInfo) { 764 | continue; 765 | } 766 | unsigned int cPackages; 767 | unsigned int len = 0; 768 | if (getCurrentPackageInfo(1, &len, nullptr, &cPackages) != 769 | ERROR_INSUFFICIENT_BUFFER) { 770 | continue; 771 | } 772 | if (!len || !pkgBuf.ReserveBuffer(len)) { 773 | continue; 774 | } 775 | if (getCurrentPackageInfo(1, &len, static_cast(pkgBuf.Data()), 776 | &cPackages)) { 777 | continue; 778 | } 779 | if (!cPackages) { 780 | continue; 781 | } 782 | auto* packages = static_cast(pkgBuf.Data()); 783 | PACKAGE_INFO* package = nullptr; 784 | for (UINT32 j = 0; j < cPackages; j++) { 785 | if (_wcsicmp(packages[j].packageFamilyName, 786 | kChannelPackageFamilyName[channel]) == 0) { 787 | package = &packages[j]; 788 | break; 789 | } 790 | } 791 | if (package == nullptr) { 792 | continue; 793 | } 794 | version[0] = package->packageId.version.Major; 795 | version[1] = package->packageId.version.Minor; 796 | version[2] = package->packageId.version.Build; 797 | version[3] = package->packageId.version.Revision; 798 | clientPath->Assign(package->path); 799 | if (CheckVersionAndFindClientDllInFolder(version, clientPath)) { 800 | if (versionStr) { 801 | wchar_t buffer[12] = {0}; 802 | versionStr->Reserve(15); 803 | if (_ultow_s(version[0], buffer, _countof(buffer) - 1, 10)) { 804 | continue; 805 | } 806 | versionStr->Assign(buffer); 807 | if (_ultow_s(version[1], buffer, _countof(buffer) - 1, 10)) { 808 | continue; 809 | } 810 | versionStr->Append(L"."); 811 | versionStr->Append(buffer); 812 | if (_ultow_s(version[2], buffer, _countof(buffer) - 1, 10)) { 813 | continue; 814 | } 815 | versionStr->Append(L"."); 816 | versionStr->Append(buffer); 817 | if (_ultow_s(version[3], buffer, _countof(buffer) - 1, 10)) { 818 | continue; 819 | } 820 | versionStr->Append(L"."); 821 | versionStr->Append(buffer); 822 | } 823 | break; 824 | } 825 | } 826 | if (channelStr) { 827 | channelStr->Assign(kChannelName[channel]); 828 | } 829 | return 0; 830 | } 831 | 832 | HRESULT FindEmbeddedClientDll(const wchar_t* embeddedEdgeSubFolder, 833 | WString* outClientPath) { 834 | outClientPath->Reserve(MAX_PATH); 835 | outClientPath->Assign(embeddedEdgeSubFolder); 836 | const wchar_t* path = outClientPath->String(); 837 | if (outClientPath->Length() >= 3 && 838 | static_cast((path[0] & 0xFFDF) - L'A') < 0x1A && 839 | path[1] == L':' && path[2] == L'\\') { 840 | if (!FindClientDllInFolder(outClientPath)) { 841 | return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); 842 | } 843 | return S_OK; 844 | } 845 | if (outClientPath->Length() < 3 || path[1] == ':' || path[0] != L'\\' || 846 | path[1] != L'\\') { 847 | WString modulePath; 848 | HRESULT hr = GetModulePath(nullptr, &modulePath); 849 | if (hr < 0) { 850 | return hr; 851 | } 852 | outClientPath->Assign(modulePath.String(), modulePath.Length()); 853 | const wchar_t* basenameSlash = wcsrchr(modulePath.String(), L'\\'); 854 | if (!basenameSlash) return E_FAIL; 855 | outClientPath->Assign(modulePath.String(), 856 | basenameSlash - modulePath.String() + 1); 857 | outClientPath->Append(embeddedEdgeSubFolder); 858 | if (!FindClientDllInFolder(outClientPath)) { 859 | return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); 860 | } 861 | return S_OK; 862 | } 863 | if (!FindClientDllInFolder(outClientPath)) { 864 | return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); 865 | } 866 | return S_OK; 867 | } 868 | 869 | HRESULT TryCreateWebViewEnvironment( 870 | WebView2EnvironmentParams params, 871 | ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler* 872 | environmentCreatedHandler) { 873 | WebView2RunTimeType runtimeType; 874 | HRESULT hr; 875 | WString dllPath; 876 | 877 | if (params.embeddedEdgeSubFolder && *params.embeddedEdgeSubFolder) { 878 | runtimeType = WebView2RunTimeType::kRedistributable; 879 | hr = FindEmbeddedClientDll(params.embeddedEdgeSubFolder, &dllPath); 880 | } else { 881 | runtimeType = WebView2RunTimeType::kInstalled; 882 | hr = FindInstalledClientDll(&dllPath, params.releaseChannelPreference, 883 | nullptr, nullptr); 884 | } 885 | if (FAILED(hr)) { 886 | return hr; 887 | } 888 | return CreateWebViewEnvironmentWithClientDll( 889 | dllPath.String(), true, runtimeType, params.userDataDir, 890 | params.environmentOptions, environmentCreatedHandler); 891 | } 892 | 893 | HRESULT FindEmbeddedBrowserVersion(LPCWSTR filename, WString* outBuf) { 894 | unsigned int puLen = 0; 895 | LPWSTR lpBuffer = nullptr; 896 | DWORD dwHandle = 0; 897 | DWORD cbVerInfo = GetFileVersionInfoSizeW(filename, &dwHandle); 898 | WString verInfoBuffer; 899 | 900 | if (!cbVerInfo) { 901 | return HRESULT_FROM_WIN32(GetLastError()); 902 | } 903 | 904 | if (!verInfoBuffer.ReserveBuffer(cbVerInfo)) { 905 | return E_UNEXPECTED; 906 | } 907 | 908 | if (!GetFileVersionInfoW(filename, dwHandle, cbVerInfo, 909 | verInfoBuffer.Data()) || 910 | !VerQueryValueW(verInfoBuffer.Data(), 911 | L"\\StringFileInfo\\040904B0\\ProductVersion", 912 | reinterpret_cast(&lpBuffer), &puLen) || 913 | !lpBuffer) { 914 | return HRESULT_FROM_WIN32(GetLastError()); 915 | } 916 | 917 | outBuf->Assign(lpBuffer); 918 | return S_OK; 919 | } 920 | 921 | #pragma endregion 922 | 923 | } // namespace 924 | 925 | #pragma region API Implementation 926 | 927 | STDAPI CreateCoreWebView2EnvironmentWithOptions( 928 | PCWSTR browserExecutableFolder, PCWSTR userDataFolder, 929 | ICoreWebView2EnvironmentOptions* environmentOptions, 930 | ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler* 931 | environmentCreatedHandler) { 932 | WString outStrings[3]; 933 | WebView2EnvironmentParams params{ 934 | .embeddedEdgeSubFolder = browserExecutableFolder, 935 | .userDataDir = userDataFolder, 936 | .environmentOptions = environmentOptions, 937 | .releaseChannelPreference = WebView2ReleaseChannelPreference::kStable, 938 | }; 939 | if (!environmentCreatedHandler) return E_POINTER; 940 | UpdateWebViewEnvironmentParamsWithOverrideValues(¶ms, outStrings); 941 | #ifndef __MINGW32__ 942 | auto retryHandler = Microsoft::WRL::Make( 943 | params, environmentCreatedHandler, 1); 944 | return TryCreateWebViewEnvironment(params, retryHandler.Get()); 945 | #else 946 | auto retryHandler = new EnvironmentCreatedRetryHandler( 947 | params, environmentCreatedHandler, 1); 948 | return TryCreateWebViewEnvironment(params, environmentCreatedHandler); 949 | #endif 950 | } 951 | 952 | STDAPI GetAvailableCoreWebView2BrowserVersionString( 953 | PCWSTR browserExecutableFolder, LPWSTR* versionInfo) { 954 | HRESULT hr; 955 | WebView2EnvironmentParams params{ 956 | .embeddedEdgeSubFolder = browserExecutableFolder, 957 | .userDataDir = nullptr, 958 | .environmentOptions = nullptr, 959 | .releaseChannelPreference = WebView2ReleaseChannelPreference::kStable, 960 | }; 961 | if (!versionInfo) { 962 | return E_POINTER; 963 | } 964 | WString outStrings[3]; 965 | 966 | UpdateWebViewEnvironmentParamsWithOverrideValues(¶ms, outStrings); 967 | 968 | WString channelStr; 969 | WString versionStr; 970 | WString clientPath; 971 | if (params.embeddedEdgeSubFolder && *params.embeddedEdgeSubFolder) { 972 | hr = FindEmbeddedClientDll(params.embeddedEdgeSubFolder, &clientPath); 973 | if (FAILED(hr)) { 974 | *versionInfo = nullptr; 975 | return hr; 976 | } 977 | hr = FindEmbeddedBrowserVersion(clientPath.String(), &versionStr); 978 | } else { 979 | hr = FindInstalledClientDll(&clientPath, params.releaseChannelPreference, 980 | &versionStr, &channelStr); 981 | } 982 | if (FAILED(hr)) { 983 | *versionInfo = nullptr; 984 | return hr; 985 | } 986 | if (!channelStr.Empty()) { 987 | versionStr.Append(L" "); 988 | versionStr.Append(channelStr.String(), channelStr.Length()); 989 | } 990 | hr = S_OK; 991 | *versionInfo = MakeCoMemString(versionStr.String(), versionStr.Length()); 992 | return hr; 993 | } 994 | 995 | STDAPI CompareBrowserVersions(PCWSTR version1, PCWSTR version2, int* result) { 996 | if (!result) { 997 | return E_POINTER; 998 | } 999 | if (!version1 || !version2) { 1000 | return E_INVALIDARG; 1001 | } 1002 | UINT32 v1[4], v2[4]; 1003 | if (!ParseVersionNumbers(version1, v2) || 1004 | !ParseVersionNumbers(version2, v1)) { 1005 | return E_INVALIDARG; 1006 | } 1007 | for (int i = 0; i < 4; ++i) { 1008 | if (v2[i] > v1[i]) { 1009 | *result = 1; 1010 | return S_OK; 1011 | } 1012 | if (v2[i] < v1[i]) { 1013 | *result = -1; 1014 | return S_OK; 1015 | } 1016 | } 1017 | *result = 0; 1018 | return S_OK; 1019 | } 1020 | 1021 | STDAPI CreateCoreWebView2Environment( 1022 | ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler* 1023 | environmentCreatedHandler) { 1024 | return CreateCoreWebView2EnvironmentWithOptions(nullptr, nullptr, nullptr, 1025 | environmentCreatedHandler); 1026 | } 1027 | 1028 | #pragma endregion 1029 | -------------------------------------------------------------------------------- /Source/WebView2Loader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | struct ICoreWebView2EnvironmentOptions; 5 | struct ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler; 6 | 7 | STDAPI CreateCoreWebView2EnvironmentWithOptions( 8 | PCWSTR browserExecutableFolder, PCWSTR userDataFolder, 9 | ICoreWebView2EnvironmentOptions *environmentOptions, 10 | ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler 11 | *environmentCreatedHandler); 12 | 13 | STDAPI CreateCoreWebView2Environment( 14 | ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler 15 | *environmentCreatedHandler); 16 | 17 | STDAPI GetAvailableCoreWebView2BrowserVersionString( 18 | PCWSTR browserExecutableFolder, LPWSTR *versionInfo); 19 | 20 | STDAPI CompareBrowserVersions(PCWSTR version1, PCWSTR version2, int *result); 21 | --------------------------------------------------------------------------------