fix: 修复部分用户打不开设置页,WebView2 缺失时仍可正常打字 (#801)
* fix(installer): VC++ 运行库要求提到 14.40 并给出直接下载链接 服务端程序用 MSVC 14.40+ 编译并导入 msvcp140.dll,配上更旧的运行库, std::mutex 首次加锁即访问冲突、进程无声闪退,设置页因此打不开;原先 14.20 的门槛放过了装着 2019 / 早期 2022 运行库的机器。 - 缺失与「已装但过旧」分开提示,并显示当前版本 - 「是」直接下载 VC++ x64 运行库与 WebView2 Evergreen Bootstrapper - 更正 WebView2 缺失的影响:只有设置页需要它,默认 D2D 候选窗照常打字 * fix(server): WebView2 不可用时设置页给出提示,候选窗退回 D2D - 设置进程不再忽略 WebView2 environment / controller 创建失败(运行时缺失 时同步失败、回调不来,界面会一直停在启动画面),改为弹框说明原因、错误码 与下载直链后退出 - ui_backend = webview2 而检测不到运行时时,本次进程改用 D2D 渲染,保证能 正常打字;不改配置,装好运行时后下次启动自动恢复 - WebView2 下载地址统一为 Evergreen Bootstrapper 直链
This commit is contained in:
1 parent
66e0a6dc76
commit
5aed0fcbf0
9 files changed
+136
-32
No files matched your search
+4
-3
@@ -47,12 +47,13 @@ pwsh -File ./Prepare-PackageFiles.ps1 -TargetVersion 1.2.3 -RepoRoot .. `
|
||||
|
||||
## 运行时依赖检查
|
||||
|
||||
安装包不携带 WebView2 Runtime 和 VC 运行库——前者有自己的 Evergreen 更新通道,后者是系统级共享组件。但缺了任何一个,输入法装完就是坏的,而故障出现在安装结束之后(Server 起不来,或者候选窗一片空白),用户看不到任何解释。所以 `msime_setup.iss` 的 `InitializeSetup` 在安装开始前先查一遍,缺哪个就当场说清楚,并给出下载地址。
|
||||
安装包不携带 WebView2 Runtime 和 VC 运行库——前者有自己的 Evergreen 更新通道,后者是系统级共享组件。缺 VC 运行库 Server 起不来;缺 WebView2 设置页打不开,但默认的 D2D 候选窗(以及表情 / 键盘 / 手写面板,都是 `msimeui` 原生绘制)照常可用,选了 `appearance.ui_backend = "webview2"` 的 Server 启动时检测不到运行时会临时退回 D2D(`FallBackToD2dIfWebView2RuntimeMissing`,不改配置,装好后下次启动自动恢复)。这些故障都出现在安装结束之后,用户看不到任何解释。所以 `msime_setup.iss` 的 `InitializeSetup` 在安装开始前先查一遍,缺哪个就当场说清楚,并给出下载地址。
|
||||
|
||||
- **Microsoft Edge WebView2 Runtime**:读 EdgeUpdate 客户端键 `{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}` 的 `pv`。机器级安装由 32 位的 EdgeUpdate 写入,所以主查 `HKLM32`,再看 `HKLM64` 和 `HKCU`(用户级安装)。`pv` 缺失或残留成 `0.0.0.0` 都算没装
|
||||
- **Visual C++ 2015–2022 可再发行组件包(x64)**:读 `Software\Microsoft\VisualStudio\14.0\VC\Runtimes\x64` 的 `Installed` / `Major` / `Minor`,`HKLM32` 和 `HKLM64` 两个视图都查。要求 **14.20 以上**(VC++ 2019 起):`server_exe\` 里每个 exe 都导入了 `vcruntime140_1.dll`,它从那一版才开始随 redist 分发,只装了 2015 / 2017 版的机器同样有 `Installed=1`,却照样起不来。32 位 TSF DLL 用静态 CRT(`windows/CMakeLists.txt` 的 `CMAKE_MSVC_RUNTIME_LIBRARY`),所以不检查 x86 redist
|
||||
- **Visual C++ 2015–2022 可再发行组件包(x64)**:读 `Software\Microsoft\VisualStudio\14.0\VC\Runtimes\x64` 的 `Installed` / `Major` / `Minor`,`HKLM32` 和 `HKLM64` 两个视图都查。要求 **14.40 以上**(常量 `VCRuntimeMinMinor`):`server_exe\` 里的 exe 用 MSVC 14.40+(VS 2022 17.10 起)编译并导入 `msvcp140.dll`,这一版起 `std::mutex` 的构造函数是 constexpr,配上更旧的 `msvcp140.dll` 会在第一次加锁时访问冲突、无声闪退——设置页「点了没反应」就是这个现象。装了 2019 / 早期 2022 运行库的机器同样有 `Installed=1`,所以必须比版本号。构建工具链升级到要求更高 redist 时同步调高这个常量。32 位 TSF DLL 用静态 CRT(`windows/CMakeLists.txt` 的 `CMAKE_MSVC_RUNTIME_LIBRARY`),所以不检查 x86 redist
|
||||
- **不要改成查 `System32` 里的 DLL。** Setup.exe 是 32 位进程,Pascal Script 的 `FileExists` 会被 WOW64 重定向到 `SysWOW64`,查到的是 x86 运行库;只装了 x64 redist 的机器会被误判成缺少 x64 运行库。绕开重定向的办法不可移植:`EnableFsRedirection` 在 Inno Setup 7 里已被移除,而它的替代品在 CI 用的 6.x 里还不存在
|
||||
- 缺组件时弹一次对话框:选「是」打开下载页并结束安装,选「否」继续装。**不阻断安装**——用户可能马上就要去补组件,也可能只是在准备环境。静默安装(CI、批量部署)用 `SuppressibleMsgBox` 默认「否」直接继续,缺什么已经写进安装日志(`/LOG=`)
|
||||
- 缺组件或版本过旧时弹一次对话框,区分「没装」和「已装 x.y 但过旧」,列出直接下载链接(VC++:`https://aka.ms/vc14/vc_redist.x64.exe`;WebView2 Evergreen Bootstrapper:`https://go.microsoft.com/fwlink/p/?LinkId=2124703`)和对应的说明页:选「是」直接下载这些安装包并结束安装,选「否」继续装。**不阻断安装**——用户可能马上就要去补组件,也可能只是在准备环境。静默安装(CI、批量部署)用 `SuppressibleMsgBox` 默认「否」直接继续,缺什么已经写进安装日志(`/LOG=`)
|
||||
- 安装时检查只是第一道关:用户可能选了「否」,也可能事后卸掉或损坏了 WebView2。所以设置进程在 WebView2 environment / controller 创建失败(含运行时缺失时的同步失败)时会弹框说明原因和错误码、给出同一条 WebView2 直链并退出,而不是留一个空白窗口
|
||||
|
||||
## 依赖
|
||||
|
||||
|
||||
+45
-21
@@ -200,9 +200,15 @@ const
|
||||
下载页也统一给简体中文版,省得用户落到英文页上再自己切。}
|
||||
WebView2DownloadUrl =
|
||||
'https://developer.microsoft.com/zh-cn/microsoft-edge/webview2';
|
||||
{ Evergreen Bootstrapper 直链,点开就是安装器。}
|
||||
WebView2DirectUrl = 'https://go.microsoft.com/fwlink/p/?LinkId=2124703';
|
||||
VCRuntimeKey = 'Software\Microsoft\VisualStudio\14.0\VC\Runtimes\x64';
|
||||
VCRuntimeDownloadUrl =
|
||||
'https://learn.microsoft.com/zh-cn/cpp/windows/latest-supported-vc-redist?view=msvc-170';
|
||||
{ 微软给的「最新受支持 v14 x64 运行库」固定直链。}
|
||||
VCRuntimeDirectUrl = 'https://aka.ms/vc14/vc_redist.x64.exe';
|
||||
{ 见 VCRuntimeInstalledInView 上方的说明。}
|
||||
VCRuntimeMinMinor = 40;
|
||||
|
||||
var
|
||||
VersionDirName: String;
|
||||
@@ -215,9 +221,10 @@ var
|
||||
DataDirAdoptedEmpty: Boolean;
|
||||
|
||||
{ WebView2 Runtime 与 VC 运行库都不随包分发:前者有自己的 Evergreen 更新通道,
|
||||
后者是系统级共享组件,安装器不该替用户装。但缺了任何一个,输入法装完就是坏的,
|
||||
而故障现场在安装结束之后——Server 直接起不来,或者候选窗一片空白,用户看不到
|
||||
任何解释。所以在安装开始前查一遍,把原因当场说清楚。
|
||||
后者是系统级共享组件,安装器不该替用户装。缺 VC 运行库 Server 直接起不来;
|
||||
缺 WebView2 设置页打不开(默认 D2D 候选窗照常可用,选了 WebView2 渲染的
|
||||
Server 启动时会临时退回 D2D)。故障现场都在安装结束之后,用户看不到任何解释,
|
||||
所以在安装开始前查一遍,把原因当场说清楚。
|
||||
|
||||
返回空串表示没装;否则是 pv 里的版本号。}
|
||||
function ReadWebView2Version: String;
|
||||
@@ -244,9 +251,11 @@ end;
|
||||
// {sysnative} 对 64 位安装器又不可用。注册表访问没有这个问题,HKLM32 / HKLM64
|
||||
// 两个视图都是显式指定的。
|
||||
//
|
||||
// x64 redist 的安装状态就记在这个键里。要求 14.20 以上(VC++ 2019 起):server_exe
|
||||
// 里每个 exe 都导入了 vcruntime140_1.dll,而它是从那一版才开始随 redist 分发的,
|
||||
// 只装了 2015/2017 版的机器同样有 Installed=1,却照样起不来。
|
||||
// x64 redist 的安装状态就记在这个键里。要求 14.40 以上:server_exe 里的 exe 都用
|
||||
// MSVC 14.40+(VS 2022 17.10 起)编译并导入 msvcp140.dll,这一版起 std::mutex 的
|
||||
// 构造函数改成了 constexpr,配上 14.40 之前的 msvcp140.dll 会在第一次加锁时访问冲突,
|
||||
// 进程无声闪退(设置页打不开就是这个样子)。装了 2019 / 早期 2022 运行库的机器
|
||||
// 同样有 Installed=1,所以必须比版本号,不能只看装没装。
|
||||
// 32 位 TSF DLL 用的是静态 CRT(windows/CMakeLists.txt 里的
|
||||
// CMAKE_MSVC_RUNTIME_LIBRARY),所以不必检查 x86 redist。
|
||||
// 注意:Inno Setup 的花括号注释不能嵌套,上面提到的常量会提前闭合 { } 注释,
|
||||
@@ -262,7 +271,7 @@ begin
|
||||
(Installed = 1) and
|
||||
RegQueryDWordValue(RootKey, VCRuntimeKey, 'Major', Major) and
|
||||
RegQueryDWordValue(RootKey, VCRuntimeKey, 'Minor', Minor) and
|
||||
((Major > 14) or ((Major = 14) and (Minor >= 20)));
|
||||
((Major > 14) or ((Major = 14) and (Minor >= VCRuntimeMinMinor)));
|
||||
end;
|
||||
|
||||
{ redist 的安装包是 32 位的,键通常落在 WOW6432Node 下,但较新的版本两个视图都写,
|
||||
@@ -273,7 +282,7 @@ begin
|
||||
VCRuntimeInstalledInView(HKLM32) or VCRuntimeInstalledInView(HKLM64);
|
||||
end;
|
||||
|
||||
{ 只用于写日志,不参与判定。}
|
||||
{ 只用于日志和提示文字,不参与判定。}
|
||||
function ReadVCRuntimeVersion: String;
|
||||
begin
|
||||
Result := '';
|
||||
@@ -298,6 +307,7 @@ var
|
||||
WebView2Version: String;
|
||||
NeedsWebView2: Boolean;
|
||||
NeedsVCRuntime: Boolean;
|
||||
VCRuntimeVersion: String;
|
||||
Prompt: String;
|
||||
begin
|
||||
Result := True;
|
||||
@@ -310,29 +320,43 @@ begin
|
||||
Log('WebView2 Runtime found: ' + WebView2Version);
|
||||
|
||||
NeedsVCRuntime := not VCRuntimeIsInstalled;
|
||||
{ 有版本号却没通过判定,就是装了但太旧,提示里要和「没装」分开说。}
|
||||
VCRuntimeVersion := ReadVCRuntimeVersion;
|
||||
if NeedsVCRuntime then
|
||||
Log('Prerequisite missing: Visual C++ 2015-2022 Redistributable (x64).')
|
||||
Log('Prerequisite missing or outdated: Visual C++ 2015-2022 Redistributable (x64), found "' +
|
||||
VCRuntimeVersion + '", need 14.' + IntToStr(VCRuntimeMinMinor) + ' or later.')
|
||||
else
|
||||
Log('Visual C++ runtime found: ' + ReadVCRuntimeVersion);
|
||||
Log('Visual C++ runtime found: ' + VCRuntimeVersion);
|
||||
|
||||
if not (NeedsWebView2 or NeedsVCRuntime) then
|
||||
exit;
|
||||
|
||||
Prompt := '这台电脑缺少输入法运行所需的系统组件:' + #13#10;
|
||||
Prompt := '这台电脑缺少输入法用到的系统组件,或版本过旧:' + #13#10;
|
||||
if NeedsVCRuntime then
|
||||
begin
|
||||
Prompt := Prompt + #13#10 +
|
||||
'● Visual C++ 2015-2022 可再发行组件包(x64)' + #13#10 +
|
||||
' 缺少它输入法主程序根本无法启动(提示找不到 VCRUNTIME140.dll 之类)。' + #13#10 +
|
||||
' 下载:' + VCRuntimeDownloadUrl + #13#10 +
|
||||
' 直链:https://aka.ms/vs/17/release/vc_redist.x64.exe' + #13#10;
|
||||
'● Visual C++ 2015-2022 可再发行组件包(x64),需要 14.' +
|
||||
IntToStr(VCRuntimeMinMinor) + ' 或更高版本' + #13#10;
|
||||
if VCRuntimeVersion <> '' then
|
||||
Prompt := Prompt +
|
||||
' 当前已安装 ' + VCRuntimeVersion + ',版本过旧,设置页等程序会启动即闪退。' + #13#10
|
||||
else
|
||||
Prompt := Prompt +
|
||||
' 缺少它输入法主程序根本无法启动(提示找不到 VCRUNTIME140.dll 之类)。' + #13#10;
|
||||
Prompt := Prompt +
|
||||
' 直接下载:' + VCRuntimeDirectUrl + #13#10 +
|
||||
' 说明页面:' + VCRuntimeDownloadUrl + #13#10;
|
||||
end;
|
||||
if NeedsWebView2 then
|
||||
Prompt := Prompt + #13#10 +
|
||||
'● Microsoft Edge WebView2 Runtime' + #13#10 +
|
||||
' 缺少它候选窗口、设置页和表情 / 手写 / 键盘面板都打不开。' + #13#10 +
|
||||
' 下载:' + WebView2DownloadUrl + #13#10;
|
||||
' 缺少它设置页面打不开。默认的 D2D 候选窗不依赖它,打字不受影响;' + #13#10 +
|
||||
' 选了 WebView2 渲染的候选窗会临时改用 D2D,装好后自动恢复。' + #13#10 +
|
||||
' 直接下载:' + WebView2DirectUrl + #13#10 +
|
||||
' 说明页面:' + WebView2DownloadUrl + #13#10;
|
||||
Prompt := Prompt + #13#10 +
|
||||
'点击「是」打开下载页面并结束本次安装,装好组件后重新运行本安装程序;' + #13#10 +
|
||||
'点击「否」继续安装(装完仍需自行补齐上述组件,输入法才能正常工作)。';
|
||||
'点击「是」直接下载上述组件并结束本次安装,装好后重新运行本安装程序;' + #13#10 +
|
||||
'点击「否」继续安装(装完仍需自行补齐上述组件,对应功能才能正常使用)。';
|
||||
|
||||
{ 静默安装(CI、企业批量部署)不该被一个对话框卡住:默认继续,缺什么已经写进日志了。}
|
||||
if
|
||||
@@ -340,9 +364,9 @@ begin
|
||||
then
|
||||
begin
|
||||
if NeedsVCRuntime then
|
||||
OpenDownloadPage(VCRuntimeDownloadUrl);
|
||||
OpenDownloadPage(VCRuntimeDirectUrl);
|
||||
if NeedsWebView2 then
|
||||
OpenDownloadPage(WebView2DownloadUrl);
|
||||
OpenDownloadPage(WebView2DirectUrl);
|
||||
Result := False;
|
||||
end;
|
||||
end;
|
||||
|
||||
@@ -535,6 +535,9 @@ const std::string &GetConfiguredUiBackend();
|
||||
bool SetConfiguredUiBackend(const std::string &backend);
|
||||
// Process-lifetime renderer chosen at InitImeConfig. Independent of later reloads.
|
||||
bool UseD2dSmallWindowUi();
|
||||
// Switch this process to the D2D renderer without touching the configured value, so the user's
|
||||
// choice comes back on the next start. Only valid before the candidate window is created.
|
||||
void FallBackToD2dSmallWindowUi();
|
||||
// "fluent" | "wechat" | "graphite" | "willow_green" | "autumn_osmanthus" | "microsoft"
|
||||
// — candidate-window and floating-toolbar skin.
|
||||
const std::string &GetConfiguredCandidateSkin();
|
||||
|
||||
@@ -231,6 +231,11 @@ bool UseD2dSmallWindowUi()
|
||||
UiBackendPolicy::Backend::Native;
|
||||
}
|
||||
|
||||
void FallBackToD2dSmallWindowUi()
|
||||
{
|
||||
g_ui_backend_active = "d2d";
|
||||
}
|
||||
|
||||
const std::string &GetConfiguredCandidateSkin()
|
||||
{
|
||||
return g_candidate_skin;
|
||||
|
||||
@@ -116,6 +116,7 @@ int CALLBACK WinMain(_In_ HINSTANCE hInstance, _In_ HINSTANCE /*hPrevInstance*/,
|
||||
|
||||
// Initialize config
|
||||
InitImeConfig();
|
||||
FallBackToD2dIfWebView2RuntimeMissing();
|
||||
VoiceInput::Initialize();
|
||||
Global::candidate_ui.page_size = GetConfiguredCandidatePageSize();
|
||||
|
||||
|
||||
@@ -125,6 +125,36 @@ void TraceStartup(const char *stage)
|
||||
}
|
||||
}
|
||||
|
||||
// WebView2 起不来时设置窗口只剩一块空白(运行时缺失时同步失败、回调根本不来,splash 会一直转),
|
||||
// 用户看不出是怎么回事。把原因和下载地址说清楚,然后退出,不留一个空窗口。
|
||||
void ReportWebViewStartupFailure(HWND hwnd, const wchar_t *stage, HRESULT hr)
|
||||
{
|
||||
static bool reported = false;
|
||||
if (reported)
|
||||
return;
|
||||
reported = true;
|
||||
TraceStartup("ui: webview startup failed");
|
||||
SettingsSplash::Dismiss();
|
||||
|
||||
wchar_t message[1024]{};
|
||||
swprintf_s(message,
|
||||
L"设置页面需要 Microsoft Edge WebView2 Runtime,但它没能启动(%s,错误码 0x%08X)。\r\n"
|
||||
L"The settings page needs the Microsoft Edge WebView2 Runtime, but it could not start "
|
||||
L"(%s, error 0x%08X).\r\n\r\n"
|
||||
L"输入法打字不受影响,只有设置页面需要它。\r\n"
|
||||
L"Typing is not affected; only the settings page needs it.\r\n\r\n"
|
||||
L"请安装或修复 WebView2 Runtime 后重新打开设置:\r\n"
|
||||
L"Install or repair the WebView2 Runtime, then open the settings again:\r\n"
|
||||
L"%s\r\n\r\n"
|
||||
L"是否现在打开下载链接?\r\nOpen the download link now?",
|
||||
stage, static_cast<unsigned>(hr), stage, static_cast<unsigned>(hr),
|
||||
CommonUtils::kWebView2RuntimeDownloadUrl);
|
||||
if (MessageBoxW(hwnd, message, L"水杉输入法 / Metasequoia IME", MB_YESNO | MB_ICONERROR | MB_SETFOREGROUND) ==
|
||||
IDYES)
|
||||
ShellExecuteW(hwnd, L"open", CommonUtils::kWebView2RuntimeDownloadUrl, nullptr, nullptr, SW_SHOWNORMAL);
|
||||
DestroyWindow(hwnd);
|
||||
}
|
||||
|
||||
nlohmann::json CandidateColorsToJson(const CandidateSkinCatalog::CandidateColors &colors)
|
||||
{
|
||||
nlohmann::json json = nlohmann::json::object();
|
||||
@@ -1737,31 +1767,42 @@ void InitWebView(HWND hwnd)
|
||||
L"--disable-sync "
|
||||
L"--disable-prompt-on-repost "
|
||||
L"--no-first-run");
|
||||
CreateCoreWebView2EnvironmentWithOptions(
|
||||
const HRESULT create_result = CreateCoreWebView2EnvironmentWithOptions(
|
||||
nullptr, user_data.c_str(), options.Get(),
|
||||
Callback<ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler>(
|
||||
[hwnd](HRESULT result, ICoreWebView2Environment *environment) -> HRESULT {
|
||||
TraceStartup("ui: environment created");
|
||||
if (FAILED(result) || !environment)
|
||||
{
|
||||
SettingsSplash::Dismiss();
|
||||
return FAILED(result) ? result : E_FAIL;
|
||||
const HRESULT hr = FAILED(result) ? result : E_FAIL;
|
||||
ReportWebViewStartupFailure(hwnd, L"environment", hr);
|
||||
return hr;
|
||||
}
|
||||
// ICoreWebView2Environment3 需要较新的运行时,过旧的 WebView2 会停在这里。
|
||||
ComPtr<ICoreWebView2Environment3> environment3;
|
||||
if (FAILED(environment->QueryInterface(IID_PPV_ARGS(&environment3))))
|
||||
{
|
||||
SettingsSplash::Dismiss();
|
||||
ReportWebViewStartupFailure(hwnd, L"environment3", E_NOINTERFACE);
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
return environment3->CreateCoreWebView2CompositionController(
|
||||
const HRESULT controller_request = environment3->CreateCoreWebView2CompositionController(
|
||||
hwnd,
|
||||
Callback<ICoreWebView2CreateCoreWebView2CompositionControllerCompletedHandler>(
|
||||
[hwnd](HRESULT controller_result, ICoreWebView2CompositionController *controller) -> HRESULT {
|
||||
return OnControllerCreated(hwnd, controller_result, controller);
|
||||
const HRESULT hr = OnControllerCreated(hwnd, controller_result, controller);
|
||||
if (FAILED(hr))
|
||||
ReportWebViewStartupFailure(hwnd, L"controller", hr);
|
||||
return hr;
|
||||
})
|
||||
.Get());
|
||||
if (FAILED(controller_request))
|
||||
ReportWebViewStartupFailure(hwnd, L"controller", controller_request);
|
||||
return controller_request;
|
||||
})
|
||||
.Get());
|
||||
// 运行时缺失时这里同步返回失败,上面的回调不会被调用。
|
||||
if (FAILED(create_result))
|
||||
ReportWebViewStartupFailure(hwnd, L"environment", create_result);
|
||||
}
|
||||
|
||||
UINT32 MouseKeys(WPARAM w_param)
|
||||
|
||||
@@ -12,6 +12,8 @@ void SendImeInputs(std::wstring words);
|
||||
|
||||
namespace CommonUtils
|
||||
{
|
||||
// Evergreen Bootstrapper 的官方直链,点开即下载安装器,用户不用在文档页里自己找。
|
||||
inline constexpr wchar_t kWebView2RuntimeDownloadUrl[] = L"https://go.microsoft.com/fwlink/p/?LinkId=2124703";
|
||||
std::wstring get_local_appdata_path_w();
|
||||
std::wstring get_ime_data_path_w();
|
||||
// Directory holding config.toml. Same as the data directory unless METASEQUOIA_IME_CONFIG_DIR
|
||||
|
||||
@@ -494,9 +494,9 @@ void ReportSmallWindowWebviewUnavailable(HRESULT last_hr, SmallWindowWebviewFail
|
||||
L"shown for now: {}.\r\n\r\n"
|
||||
L"请安装或修复 Microsoft Edge WebView2 Runtime 后重新登录:\r\n"
|
||||
L"Install or repair the Microsoft Edge WebView2 Runtime, then sign in again:\r\n"
|
||||
L"https://developer.microsoft.com/microsoft-edge/webview2/\r\n\r\n"
|
||||
L"{}\r\n\r\n"
|
||||
L"错误码 / error: {:#x}",
|
||||
hosts_zh, hosts_en, static_cast<unsigned>(last_hr))
|
||||
hosts_zh, hosts_en, CommonUtils::kWebView2RuntimeDownloadUrl, static_cast<unsigned>(last_hr))
|
||||
: fmt::format(L"WebView2 运行时已启动,但{}没能创建,暂时无法显示。\r\n"
|
||||
L"The WebView2 Runtime started, but the following could not be created and cannot be "
|
||||
L"shown for now: {}.\r\n\r\n"
|
||||
@@ -1292,6 +1292,30 @@ void InitSmallWindowWebviews(HWND candHwnd, HWND menuHwnd, HWND ftbHwnd)
|
||||
BeginSmallWindowWebviewEnvironmentCreate();
|
||||
}
|
||||
|
||||
// The WebView2 hosts are shown cloaked and only uncloak after their first paint, so without a
|
||||
// Runtime the candidate window never appears and the IME cannot be used at all. The D2D renderer
|
||||
// has no such dependency. The configured value is left alone: once the Runtime is installed, the
|
||||
// next Server start goes back to WebView2 on its own.
|
||||
void FallBackToD2dIfWebView2RuntimeMissing()
|
||||
{
|
||||
if (UseD2dSmallWindowUi())
|
||||
{
|
||||
return;
|
||||
}
|
||||
LPWSTR version = nullptr;
|
||||
const HRESULT hr = GetAvailableCoreWebView2BrowserVersionString(nullptr, &version);
|
||||
const bool available = SUCCEEDED(hr) && version && version[0] != L'\0';
|
||||
CoTaskMemFree(version);
|
||||
if (available)
|
||||
{
|
||||
return;
|
||||
}
|
||||
FallBackToD2dSmallWindowUi();
|
||||
TraceSmallWindowWebview(fmt::format(L"edge webview runtime not found (hr={:#x}); ui_backend=webview2 falls back "
|
||||
L"to d2d for this session",
|
||||
static_cast<unsigned>(hr)));
|
||||
}
|
||||
|
||||
bool PrepareTrayMenuWebviewForShow()
|
||||
{
|
||||
if (TrayMenuPresenter::Instance().IsBound())
|
||||
|
||||
@@ -102,6 +102,9 @@ void InflateCandWnd(const CandidateWindowContent &content, std::function<void()>
|
||||
std::pair<double, double> LastCandidateSlotMeasuredSize();
|
||||
void InflateMeasureDivCandWnd(const CandidateWindowContent &content, std::function<void()> onComplete = nullptr);
|
||||
void InitSmallWindowWebviews(HWND candHwnd, HWND menuHwnd, HWND ftbHwnd);
|
||||
// appearance.ui_backend = "webview2" but no usable WebView2 Runtime: run this process on the D2D
|
||||
// renderer so typing still works. Call after InitImeConfig and before CreateCandidateWindow.
|
||||
void FallBackToD2dIfWebView2RuntimeMissing();
|
||||
void ShutdownWebviews();
|
||||
void UpdateSmallWindowWebviewVisibility(HWND hwnd, bool visible);
|
||||
// Kick (or retry) shared small-window WebView2 init. Returns true when the menu
|
||||
|
||||
Reference in new issue
Block a user