diff --git a/WebView2/Extension.cpp b/WebView2/Extension.cpp new file mode 100644 index 0000000..fd3829d --- /dev/null +++ b/WebView2/Extension.cpp @@ -0,0 +1,54 @@ +/* +** Copyright (C) 2025 nstechbytes. All rights reserved. +*/ + +#include "Extension.h" +#include "Plugin.h" +#include "../API/RainmeterAPI.h" +#include + +bool g_extensions_checked = false; + +std::vector GetExtensionsID(const std::wstring& input) +{ + std::vector result; + std::wstringstream ss(input); + std::wstring token; + + while (std::getline(ss, token, L',')) { + token.erase(0, token.find_first_not_of(L" \t")); + token.erase(token.find_last_not_of(L" \t") + 1); + + if (!token.empty()) { + result.push_back(token); + } + } + return result; +} + +void EnableExtension(ICoreWebView2BrowserExtension* extension, BOOL enable) +{ + extension->Enable( + enable, + Callback( + [](HRESULT hr) -> HRESULT + { + if (FAILED(hr)) + ShowFailure(hr, L"Enable extension failed"); + return S_OK; + }).Get()); +} + +void RemoveExtension(void* rm, ICoreWebView2BrowserExtension* extension, const std::wstring& name) +{ + extension->Remove( + Callback( + [](HRESULT hr) -> HRESULT + { + if (FAILED(hr)) + ShowFailure(hr, L"Uninstall extension failed"); + return S_OK; + }).Get()); + + RmLogF(rm, LOG_DEBUG, L"WebView2: \"%s\" extension removed.", name.c_str()); +} diff --git a/WebView2/Extension.h b/WebView2/Extension.h new file mode 100644 index 0000000..a214aa0 --- /dev/null +++ b/WebView2/Extension.h @@ -0,0 +1,18 @@ +/* +** Copyright (C) 2025 nstechbytes. All rights reserved. +*/ + +#pragma once + +#include +#include +#include +#include + +// Global flag for extension checking +extern bool g_extensions_checked; + +// Extension utility functions +std::vector GetExtensionsID(const std::wstring& input); +void EnableExtension(ICoreWebView2BrowserExtension* extension, BOOL enable); +void RemoveExtension(void* rm, ICoreWebView2BrowserExtension* extension, const std::wstring& name); diff --git a/WebView2/HostObject.idl b/WebView2/HostObject.idl index c524103..de32a36 100644 --- a/WebView2/HostObject.idl +++ b/WebView2/HostObject.idl @@ -1,4 +1,6 @@ -// Copyright (C) 2025 nstechbytes. All rights reserved. +/* +** Copyright (C) 2025 nstechbytes. All rights reserved. +*/ import "oaidl.idl"; import "ocidl.idl"; diff --git a/WebView2/HostObjectRmAPI.cpp b/WebView2/HostObjectRmAPI.cpp index 1a22678..b688cf5 100644 --- a/WebView2/HostObjectRmAPI.cpp +++ b/WebView2/HostObjectRmAPI.cpp @@ -1,5 +1,5 @@ -/** -* Copyright (C) 2025 nstechbytes. All rights reserved. +/* +** Copyright (C) 2025 nstechbytes. All rights reserved. */ #include "HostObjectRmAPI.h" diff --git a/WebView2/HostObjectRmAPI.h b/WebView2/HostObjectRmAPI.h index adc3076..13eae13 100644 --- a/WebView2/HostObjectRmAPI.h +++ b/WebView2/HostObjectRmAPI.h @@ -1,6 +1,6 @@ -/** -* Copyright (C) 2025 nstechbytes. All rights reserved. -*/ +/* +** Copyright (C) 2025 nstechbytes. All rights reserved. +*/ #pragma once #include "HostObject_h.h" diff --git a/WebView2/PathUtils.cpp b/WebView2/PathUtils.cpp new file mode 100644 index 0000000..f4bce71 --- /dev/null +++ b/WebView2/PathUtils.cpp @@ -0,0 +1,172 @@ +/* +** Copyright (C) 2025 nstechbytes. All rights reserved. +*/ + +#include "PathUtils.h" +#include "Utils.h" +#include "../API/RainmeterAPI.h" +#include + +// Path and URI utilities +std::wstring NormalizeUri(const std::wstring& uri) +{ + const std::wstring scheme_sep = L"://"; + auto scheme_pos = uri.find(scheme_sep); + if (scheme_pos == std::wstring::npos) + return uri; + + const std::wstring scheme = uri.substr(0, scheme_pos); + const size_t after_scheme = scheme_pos + scheme_sep.length(); + + if (scheme == L"file") + { + size_t last_slash = uri.find_last_of(L'/'); + if (last_slash != std::wstring::npos) + { + return uri.substr(0, last_slash + 1); + } + return uri; + } + + size_t path_start = uri.find(L'/', after_scheme); + if (path_start == std::wstring::npos) + { + return uri + L"/"; + } + + return uri.substr(0, path_start + 1); +} + +std::wstring NormalizePath(void* rm, LPCWSTR path) +{ + if (!path || !*path) + return {}; + + std::wstring value = path; + + // Relative path - absolute + if (value[0] != L'/' && (value.length() < 2 || value[1] != L':')) + { + if (LPCWSTR absolutePath = RmPathToAbsolute(rm, value.c_str())) + { + value = absolutePath; + } + } + + // Normalize slashes + for (wchar_t& ch : value) + { + if (ch == L'\\') ch = L'/'; + } + + // Enforce extension if required + auto dotPos = value.find_last_of(L'.'); + if (dotPos == std::wstring::npos) + { + RmLog(rm, LOG_ERROR, L"Execute: File extension is missing, use '.js'."); + return {}; + } + + std::wstring extension = value.substr(dotPos); + for (wchar_t& ch : extension) + ch = towlower(ch); + + if (_wcsicmp(extension.c_str(), L".js") != 0) + { + std::wstring msg = L"Execute: The file extension '"; + msg.append(extension); + msg += L"' is not supported. Use '.js' extension."; + RmLog(rm, LOG_ERROR, msg.c_str()); + return {}; + } + return value; +} + +bool IsFilePathSyntax(LPCWSTR input) +{ + if (!input || input[0] == L'\0') return false; + + bool hasExtension = false; + bool hasSeparator = false; + + const wchar_t* lastDot = nullptr; + const wchar_t* p = input; + + while (*p) { + if (wcschr(L"<>:\"|?*();'", *p)) { + if (!(*p == L':' && p == input + 1)) { + return false; + } + } + + if (*p == L'\\' || *p == L'/') { + hasSeparator = true; + lastDot = nullptr; + } + else if (*p == L'.') { + lastDot = p; + } + p++; + } + + if (lastDot != nullptr && lastDot != input) { + if (!iswspace(*(lastDot + 1)) && *(lastDot + 1) != L'\0') { + hasExtension = true; + } + } + + bool hasSpace = (wcschr(input, L' ') != nullptr); + + if (hasSpace && !hasSeparator) { + return false; + } + + return hasSeparator || hasExtension; +} + +// File reading utilities +std::wstring ReadScriptFile(const std::wstring& path) +{ + std::ifstream is(path, std::ios::binary); + if (!is) { + throw std::runtime_error("Failed to open file"); + } + + // Read all bytes + std::vector bytes((std::istreambuf_iterator(is)), std::istreambuf_iterator()); + size_t n = bytes.size(); + if (n == 0) return std::wstring(); + + const unsigned char* ub = reinterpret_cast(bytes.data()); + + // Detect BOMs + if (n >= 3 && ub[0] == 0xEFu && ub[1] == 0xBBu && ub[2] == 0xBFu) { + // UTF-8 with BOM -> skip BOM then convert + return Utf8ToWstring(reinterpret_cast(ub + 3), static_cast(n - 3)); + } + + if (n >= 2 && ub[0] == 0xFFu && ub[1] == 0xFEu) { + // UTF-16 LE with BOM + std::wstring out; + out.reserve((n - 2) / 2); + for (size_t i = 2; i + 1 < n; i += 2) { + wchar_t ch = static_cast(ub[i] | (ub[i + 1] << 8)); + out.push_back(ch); + } + return out; + } + + if (n >= 2 && ub[0] == 0xFEu && ub[1] == 0xFFu) { + // UTF-16 BE with BOM -> swap bytes + std::wstring out; + out.reserve((n - 2) / 2); + for (size_t i = 2; i + 1 < n; i += 2) { + wchar_t ch = static_cast((ub[i] << 8) | ub[i + 1]); + out.push_back(ch); + } + return out; + } + + // No BOM: assume UTF-8 and convert + return Utf8ToWstring(reinterpret_cast(ub), static_cast(n)); +} diff --git a/WebView2/PathUtils.h b/WebView2/PathUtils.h new file mode 100644 index 0000000..0df825c --- /dev/null +++ b/WebView2/PathUtils.h @@ -0,0 +1,17 @@ +/* +** Copyright (C) 2025 nstechbytes. All rights reserved. +*/ + +#pragma once + +#include +#include +#include + +// Path and URI utilities +std::wstring NormalizeUri(const std::wstring& uri); +std::wstring NormalizePath(void* rm, LPCWSTR path); +bool IsFilePathSyntax(LPCWSTR input); + +// File reading utilities +std::wstring ReadScriptFile(const std::wstring& path); diff --git a/WebView2/Plugin.cpp b/WebView2/Plugin.cpp index ae4d1e6..fca7ea5 100644 --- a/WebView2/Plugin.cpp +++ b/WebView2/Plugin.cpp @@ -1,5 +1,10 @@ -// Copyright (C) 2025 nstechbytes. All rights reserved. +/* +** Copyright (C) 2025 nstechbytes. All rights reserved. +*/ + #include "Plugin.h" +#include "Utils.h" +#include "PathUtils.h" #include "../API/RainmeterAPI.h" #include #include @@ -25,15 +30,6 @@ static std::atomic g_hookAlive{ false }; static std::mutex g_skinMapMutex; -std::wstring ToLower(std::wstring s) -{ - if (!s.empty()) - { - CharLowerBuffW(s.data(), static_cast(s.size())); - } - return s; -} - // DllMain to load TypeLib from embedded resources BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { @@ -138,6 +134,37 @@ void FeatureNotAvailable() L"Feature Not Available", MB_OK); } +std::wstring GetHostName(const std::wstring& input, bool origin) +{ + std::wstring result; + result.reserve(input.size()); + + for (wchar_t ch : input) + { + if (origin && (ch == L'/' || ch == L'\\')) + { + break; // stop processing at first '/' + } + + if ((ch >= L'a' && ch <= L'z') || + (ch >= L'A' && ch <= L'Z') || + (ch >= L'0' && ch <= L'9')) + { + result.push_back(ch); + } + else + { + result.push_back(L'-'); + } + } + + // convert to lowercase + result = ToLower(result); + + return result; +} + +// WebView2 helper functions void UpdateWindowBounds(Measure* measure) { if (!measure || !measure->webViewController) @@ -175,36 +202,6 @@ void UpdateChildWindowState(Measure* measure, bool enabled, bool shouldDefocus) } } -std::wstring GetHostName(const std::wstring& input, bool origin) -{ - std::wstring result; - result.reserve(input.size()); - - for (wchar_t ch : input) - { - if (origin && (ch == L'/' || ch == L'\\')) - { - break; // stop processing at first '/' - } - - if ((ch >= L'a' && ch <= L'z') || - (ch >= L'A' && ch <= L'Z') || - (ch >= L'0' && ch <= L'9')) - { - result.push_back(ch); - } - else - { - result.push_back(L'-'); - } - } - - // convert to lowercase - result = ToLower(result); - - return result; -} - // Skin subclass procedure LRESULT CALLBACK SkinSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) { @@ -414,158 +411,6 @@ void RemoveKeyboardHook() } } -static std::wstring Utf8ToWstring(const char* data, int len) -{ - if (len <= 0) return std::wstring(); - - // First call to get required buffer size - int required = MultiByteToWideChar(CP_UTF8, 0, data, len, nullptr, 0); - if (required == 0) { - throw std::runtime_error("Utf8ToWstring: MultiByteToWideChar failed (size query)"); - } - - std::wstring out; - out.resize(required); - int res = MultiByteToWideChar(CP_UTF8, 0, data, len, &out[0], required); - if (res == 0) { - throw std::runtime_error("Utf8ToWstring: MultiByteToWideChar failed (conversion)"); - } - return out; -} - -std::wstring ReadScriptFile(const std::wstring& path) -{ - std::ifstream is(path, std::ios::binary); - if (!is) { - throw std::runtime_error("Failed to open file"); - } - - // Read all bytes - std::vector bytes((std::istreambuf_iterator(is)), std::istreambuf_iterator()); - size_t n = bytes.size(); - if (n == 0) return std::wstring(); - - const unsigned char* ub = reinterpret_cast(bytes.data()); - - // Detect BOMs - if (n >= 3 && ub[0] == 0xEFu && ub[1] == 0xBBu && ub[2] == 0xBFu) { - // UTF-8 with BOM -> skip BOM then convert - return Utf8ToWstring(reinterpret_cast(ub + 3), static_cast(n - 3)); - } - - if (n >= 2 && ub[0] == 0xFFu && ub[1] == 0xFEu) { - // UTF-16 LE with BOM - std::wstring out; - out.reserve((n - 2) / 2); - for (size_t i = 2; i + 1 < n; i += 2) { - wchar_t ch = static_cast(ub[i] | (ub[i + 1] << 8)); - out.push_back(ch); - } - return out; - } - - if (n >= 2 && ub[0] == 0xFEu && ub[1] == 0xFFu) { - // UTF-16 BE with BOM -> swap bytes - std::wstring out; - out.reserve((n - 2) / 2); - for (size_t i = 2; i + 1 < n; i += 2) { - wchar_t ch = static_cast((ub[i] << 8) | ub[i + 1]); - out.push_back(ch); - } - return out; - } - - // No BOM: assume UTF-8 and convert - return Utf8ToWstring(reinterpret_cast(ub), static_cast(n)); -} - -bool IsFilePathSyntax(LPCWSTR input) { - if (!input || input[0] == L'\0') return false; - - bool hasExtension = false; - bool hasSeparator = false; - bool hasIllegalChar = false; - - const wchar_t* lastDot = nullptr; - const wchar_t* p = input; - - while (*p) { - if (wcschr(L"<>:\"|?*();'", *p)) { - if (!(*p == L':' && p == input + 1)) { - return false; - } - } - - if (*p == L'\\' || *p == L'/') { - hasSeparator = true; - lastDot = nullptr; - } - else if (*p == L'.') { - lastDot = p; - } - p++; - } - - if (lastDot != nullptr && lastDot != input) { - if (!iswspace(*(lastDot + 1)) && *(lastDot + 1) != L'\0') { - hasExtension = true; - } - } - - bool hasSpace = (wcschr(input, L' ') != nullptr); - - if (hasSpace && !hasSeparator) { - return false; - } - - return hasSeparator || hasExtension; -} - -std::wstring NormalizePath(void* rm, LPCWSTR path) -{ - if (!path || !*path) - return {}; - - std::wstring value = path; - - // Relative path - absolute - if (value[0] != L'/' && (value.length() < 2 || value[1] != L':')) - { - if (LPCWSTR absolutePath = RmPathToAbsolute(rm, value.c_str())) - { - value = absolutePath; - } - } - - // Normalize slashes - for (wchar_t& ch : value) - { - if (ch == L'\\') ch = L'/'; - } - - // Enforce extension if required - auto dotPos = value.find_last_of(L'.'); - if (dotPos == std::wstring::npos) - { - RmLog(rm, LOG_ERROR, L"Execute: File extension is missing, use '.js'."); - return {}; - } - - std::wstring extension = value.substr(dotPos); - for (wchar_t& ch : extension) - ch = towlower(ch); - - if (_wcsicmp(extension.c_str(), L".js") != 0) - { - std::wstring msg = L"Execute: The file extension '"; - msg.append(extension); - msg += L"' is not supported. Use '.js' extension."; - RmLog(rm, LOG_ERROR, msg.c_str()); - return {}; - } - return value; -} - // Rainmeter Plugin Exports PLUGIN_EXPORT void Initialize(void** data, void* rm) { diff --git a/WebView2/Plugin.h b/WebView2/Plugin.h index f675ff1..53fc227 100644 --- a/WebView2/Plugin.h +++ b/WebView2/Plugin.h @@ -1,6 +1,6 @@ -/** -* Copyright (C) 2025 nstechbytes. All rights reserved. -*/ +/* +** Copyright (C) 2025 nstechbytes. All rights reserved. +*/ #pragma once @@ -28,8 +28,6 @@ extern wil::com_ptr g_typeLib; struct SkinSubclassData; -extern bool g_extensions_checked; - // Measure structure containing WebView2 state struct Measure { @@ -126,14 +124,13 @@ void CreateWebView2(Measure* measure); void StopWebView2(Measure* measure); void RestartWebView2(Measure* measure); void UpdateChildWindowState(Measure* measure, bool enabled, bool shouldDefocus = true); +void UpdateWindowBounds(Measure* measure); -// Taken from: https://github.com/MicrosoftEdge/WebView2Samples/blob/main/SampleApps/WebView2APISample/CheckFailure.h -// Notify the user of a failure with a message box. +// Helper functions void ShowFailure(HRESULT hr, const std::wstring& message = L"Error"); -// If something failed, show the error code and fail fast. void CheckFailure(HRESULT hr, const std::wstring& message = L"Error"); -// Notify the user that a feature is not available void FeatureNotAvailable(); +std::wstring GetHostName(const std::wstring& input, bool origin); #define CHECK_FAILURE_STRINGIFY(arg) #arg #define CHECK_FAILURE_FILE_LINE(file, line) ([](HRESULT hr){ CheckFailure(hr, L"Failure at " CHECK_FAILURE_STRINGIFY(file) L"(" CHECK_FAILURE_STRINGIFY(line) L")"); }) diff --git a/WebView2/Utils.cpp b/WebView2/Utils.cpp new file mode 100644 index 0000000..cd61fea --- /dev/null +++ b/WebView2/Utils.cpp @@ -0,0 +1,77 @@ +/* +** Copyright (C) 2025 nstechbytes. All rights reserved. +*/ + +#include "Utils.h" +#include +#include + +// String utilities +std::wstring ToLower(std::wstring s) +{ + if (!s.empty()) + { + CharLowerBuffW(s.data(), static_cast(s.size())); + } + return s; +} + +std::wstring Utf8ToWstring(const char* data, int len) +{ + if (len <= 0) return std::wstring(); + + // First call to get required buffer size + int required = MultiByteToWideChar(CP_UTF8, 0, data, len, nullptr, 0); + if (required == 0) { + throw std::runtime_error("Utf8ToWstring: MultiByteToWideChar failed (size query)"); + } + + std::wstring out; + out.resize(required); + int res = MultiByteToWideChar(CP_UTF8, 0, data, len, &out[0], required); + if (res == 0) { + throw std::runtime_error("Utf8ToWstring: MultiByteToWideChar failed (conversion)"); + } + return out; +} + +// INI file utilities +bool ParseBool(const wchar_t* value) +{ + if (!value) return false; + + return _wcsicmp(value, L"true") == 0 || + _wcsicmp(value, L"1") == 0 || + _wcsicmp(value, L"yes") == 0 || + _wcsicmp(value, L"on") == 0; +} + +bool GetIniBool(CSimpleIniW& ini, bool& dirty, const wchar_t* section, const wchar_t* key, bool def) +{ + const wchar_t* value = ini.GetValue(section, key, nullptr); + if (!value) + { + ini.SetValue(section, key, def ? L"true" : L"false"); + dirty = true; + return def; + } + return ParseBool(value); +} + +std::wstring GetIniString(CSimpleIniW& ini, bool& dirty, const wchar_t* section, const wchar_t* key, const wchar_t* def, bool forceDefault) +{ + const wchar_t* value = ini.GetValue(section, key, nullptr); + if (!value) + { + ini.SetValue(section, key, def); + dirty = true; + return def; + } + if (forceDefault && std::wcscmp(value, def) != 0) + { + ini.SetValue(section, key, def); + dirty = true; + return def; + } + return value; +} diff --git a/WebView2/Utils.h b/WebView2/Utils.h new file mode 100644 index 0000000..543549a --- /dev/null +++ b/WebView2/Utils.h @@ -0,0 +1,18 @@ +/* +** Copyright (C) 2025 nstechbytes. All rights reserved. +*/ + +#pragma once + +#include +#include +#include "Ini/SimpleIni.h" + +// String utilities +std::wstring ToLower(std::wstring s); +std::wstring Utf8ToWstring(const char* data, int len); + +// INI file utilities +bool ParseBool(const wchar_t* value); +bool GetIniBool(CSimpleIniW& ini, bool& dirty, const wchar_t* section, const wchar_t* key, bool def); +std::wstring GetIniString(CSimpleIniW& ini, bool& dirty, const wchar_t* section, const wchar_t* key, const wchar_t* def, bool forceDefault = false); diff --git a/WebView2/WebView2.cpp b/WebView2/WebView2.cpp index 40bf952..a46043d 100644 --- a/WebView2/WebView2.cpp +++ b/WebView2/WebView2.cpp @@ -3,103 +3,14 @@ */ #include "Plugin.h" +#include "Utils.h" +#include "PathUtils.h" +#include "Extension.h" #include "HostObjectRmAPI.h" #include "../API/RainmeterAPI.h" #include #include -bool g_extensions_checked = false; - -inline bool ParseBool(const wchar_t* value) -{ - if (!value) return false; - - return _wcsicmp(value, L"true") == 0 || - _wcsicmp(value, L"1") == 0 || - _wcsicmp(value, L"yes") == 0 || - _wcsicmp(value, L"on") == 0; -} - - -inline bool GetIniBool(CSimpleIniW & ini, bool& dirty, const wchar_t* section, const wchar_t* key, bool def) -{ - const wchar_t* value = ini.GetValue(section, key, nullptr); - if (!value) - { - ini.SetValue(section, key, def ? L"true" : L"false"); - dirty = true; - return def; - } - return ParseBool(value); -} - -inline std::wstring GetIniString(CSimpleIniW& ini, bool& dirty, const wchar_t* section, const wchar_t* key, const wchar_t* def, bool forceDefault = false) -{ - const wchar_t* value = ini.GetValue(section, key, nullptr); - if (!value) - { - ini.SetValue(section, key, def); - dirty = true; - return def; - } - if (forceDefault && std::wcscmp(value, def) != 0) - { - ini.SetValue(section, key, def); - dirty = true; - return def; - } - return value; -} - -std::vector GetExtensionsID(const std::wstring& input) -{ - std::vector result; - std::wstringstream ss(input); - std::wstring token; - - while (std::getline(ss, token, L',')) { - token.erase(0, token.find_first_not_of(L" \t")); - token.erase(token.find_last_not_of(L" \t") + 1); - - if (!token.empty()) { - result.push_back(token); - } - } - return result; -} - -static void EnableExtension( - ICoreWebView2BrowserExtension* extension, - BOOL enable) -{ - extension->Enable( - enable, - Callback( - [](HRESULT hr) -> HRESULT - { - if (FAILED(hr)) - ShowFailure(hr, L"Enable extension failed"); - return S_OK; - }).Get()); -} - -static void RemoveExtension(void* rm, - ICoreWebView2BrowserExtension* extension, - const std::wstring& name) -{ - extension->Remove( - Callback( - [](HRESULT hr) -> HRESULT - { - if (FAILED(hr)) - ShowFailure(hr, L"Uninstall extension failed"); - return S_OK; - }).Get()); - - RmLogF(rm, LOG_DEBUG, L"WebView2: \"%s\" extension removed.", name.c_str()); -} - - // Create WebView2 environment and controller void CreateWebView2(Measure* measure) { @@ -288,35 +199,6 @@ HRESULT Measure::CreateEnvironmentHandler(HRESULT result, ICoreWebView2Environme return S_OK; } -std::wstring NormalizeUri(const std::wstring& uri) -{ - const std::wstring scheme_sep = L"://"; - auto scheme_pos = uri.find(scheme_sep); - if (scheme_pos == std::wstring::npos) - return uri; - - const std::wstring scheme = uri.substr(0, scheme_pos); - const size_t after_scheme = scheme_pos + scheme_sep.length(); - - if (scheme == L"file") - { - size_t last_slash = uri.find_last_of(L'/'); - if (last_slash != std::wstring::npos) - { - return uri.substr(0, last_slash + 1); - } - return uri; - } - - size_t path_start = uri.find(L'/', after_scheme); - if (path_start == std::wstring::npos) - { - return uri + L"/"; - } - - return uri.substr(0, path_start + 1); -} - void RegisterFrames(Measure* measure, ICoreWebView2Frame* rawFrame, int level) { wil::com_ptr frame = rawFrame; diff --git a/WebView2/WebView2.vcxproj b/WebView2/WebView2.vcxproj index a8eda65..de7728e 100644 --- a/WebView2/WebView2.vcxproj +++ b/WebView2/WebView2.vcxproj @@ -22,14 +22,20 @@ + + + + + + diff --git a/WebView2/WebView2.vcxproj.filters b/WebView2/WebView2.vcxproj.filters index 35d1dde..f6dcb29 100644 --- a/WebView2/WebView2.vcxproj.filters +++ b/WebView2/WebView2.vcxproj.filters @@ -12,12 +12,18 @@ + + + + + + Ini