WebView2运行时无法读取WinRT组件问题咨询[已废弃]
重要提示:此问题相关信息已废弃,仅作历史参考。尽管问题在回答发布时已解决,但所讨论的开发范式已不再受支持或用于原定用途。
我正在开发一款用于游戏发布的WebView2容器应用,目前遇到阻碍开发的问题——WinRT组件无法被正确读取,导致功能完全失效。例如:Javascript端需读取一个字段,用于容器应用未完全激活时(如Xbox主机打开指南菜单)阻止游戏输入,但该字段在JS端显示为undefined,导致用户输入始终被阻塞。读取用户Xbox账号游戏存档等其他功能也存在同样问题,说明WebView2读取WinRT组件存在严重问题。
字段定义
public static bool GameBarActive { get { return gameBarActive; } }
窗口激活事件处理
private static void Window_Activated(CoreWindow sender, WindowActivatedEventArgs args) { if (args.WindowActivationState == CoreWindowActivationState.Deactivated) { gameBarActive = true; } else { gameBarActive = false; } }
预期逻辑与JS端问题
预期逻辑:当gameBarActive为false时,公开只读的GameBarActive字段返回WinRT组件内部可写版本的值。连接WinRT组件与WebView2容器的C#代码,将其置于名为Xbox的顶级Javascript命名空间下的API类中向游戏暴露,但JS端无法识别该组件,导致检测逻辑等效于始终为true(实际无定义)。
RPG Maker MV输入检测示例
if (Xbox.API.GameBarActive == false) { var lastState = this._gamepadStates[gamepad.index] || []; var newState = []; var buttons = gamepad.buttons; var axes = gamepad.axes; var threshold = 0.5; newState[12] = false; newState[13] = false; newState[14] = false; newState[15] = false; for (var i = 0; i < buttons.length; i++) { newState[i] = buttons[i].pressed; } if (axes[1] < -threshold) { newState[12] = true; // up } else if (axes[1] > threshold) { newState[13] = true; // down } if (axes[0] < -threshold) { newState[14] = true; // left } else if (axes[0] > threshold) { newState[15] = true; // right } for (var j = 0; j < newState.length; j++) { if (newState[j] !== lastState[j]) { var buttonName = this.gamepadMapper[j]; if (buttonName) { this._currentState[buttonName] = newState[j]; } } } this._gamepadStates[gamepad.index] = newState; }
问题根源分析
- WinRT组件暴露规则不满足:WebView2对暴露给JS的WinRT类型有严格要求,静态属性直接暴露通常不被支持——WebView2优先支持实例对象的成员,而非静态类成员。
- 线程上下文不匹配:
Window_Activated运行在UI线程,WebView2的JS交互可能处于不同线程,静态变量的跨线程访问可能导致值未同步,甚至JS端无法获取。 - 命名空间与暴露方式错误:若未正确配置WebView2的
AddHostObjectToScript方法,或命名空间层级未正确映射,JS端会无法找到Xbox.API对象。 - WinRT类型可见性问题:用于暴露的类或属性未标记
[AllowForWeb]特性,导致WebView2脚本引擎无法识别该成员。
可行解决尝试方向
改用实例对象暴露,替换静态类
将GameBarActive改为非静态属性,创建API类实例后通过AddHostObjectToScript暴露:using Microsoft.Web.WebView2.Core; [AllowForWeb] public class XboxAPI { public bool GameBarActive { get; private set; } public XboxAPI() { CoreWindow.GetForCurrentThread().Activated += Window_Activated; } private void Window_Activated(CoreWindow sender, WindowActivatedEventArgs args) { GameBarActive = args.WindowActivationState == CoreWindowActivationState.Deactivated; } } // WebView2初始化时添加: webView.CoreWebView2.AddHostObjectToScript("XboxAPI", new XboxAPI());JS端对应调整访问路径:
// 同步访问需使用sync代理 const xboxAPI = window.chrome.webview.hostObjects.sync.XboxAPI; if (xboxAPI.GameBarActive === false) { // 输入处理逻辑 }添加必要的WinRT特性标记
所有要暴露给JS的类和成员必须添加[AllowForWeb]特性(需引用Microsoft.Web.WebView2.Core命名空间),否则WebView2会屏蔽这些成员。通过CoreDispatcher同步线程上下文
若属性更新涉及跨线程操作,需通过CoreDispatcher确保在UI线程更新,保证WebView2能同步到最新值:private async void Window_Activated(CoreWindow sender, WindowActivatedEventArgs args) { await sender.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { GameBarActive = args.WindowActivationState == CoreWindowActivationState.Deactivated; }); }手动映射自定义JS命名空间
WebView2暴露的宿主对象默认在window.chrome.webview.hostObjects下,若要使用自定义的Xbox.API命名空间,需在JS中手动映射:window.Xbox = { API: window.chrome.webview.hostObjects.sync.XboxAPI };启用WebView2调试工具排查
在WebView2中调用webView.CoreWebView2.OpenDevToolsWindow()启用开发者工具,在控制台直接打印Xbox.API或window.chrome.webview.hostObjects,确认对象是否存在、成员是否可见,快速定位暴露或访问问题。
内容的提问来源于stack exchange,提问作者Jeffrey Davis

