
如何在 WebView2 环境中将 .NET(C#)对象"投影"(Project)到 JavaScript 全局环境中,让 H5 页面中的 JS 可以直接调用原生能力。基本原理与机制HostObject 投影WebView2 提供了一种机制,允许将 .NET 对象直接暴露给 JavaScript 运行时,使其成为window.chrome.webview.hostObjects下的属性。这种"投影"机制本质上是 COM 互操作的封装:┌─────────────────┐ COM/IDispatch ┌─────────────────┐ │ .NET Object │ ◄─────────────────────────────► │ JS Proxy │ │ (C# Class) │ │ (hostObjects) │ └─────────────────┘ └─────────────────┘ │ │ │ [ComVisible] │ │ [ClassInterface] │ ▼ ▼ 注册到 WebView2 JS 直接调用方法 AddHostObjectToScript window.chrome.webview.hostObjects.xxx核心组件组件职责IWebViewHost抽象层,封装 WebView2 操作WebViewHost实现层,调用CoreWebView2.AddHostObjectToScriptIXxxBridgeCOM 接口定义,声明暴露给 JS 的方法XxxBridge具体实现,标记[ComVisible(true)]调用路径定义 HostObject接口定义(COM 契约)usingSystem.Runtime.InteropServices;namespaceShellClient.CommHelper.XTest;/// summary/// XTestObj interface exposed to WebView2./// All methods must be COM-visible and use compatible types./// /summary[ComVisible(true)][Guid("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX")]// 生成唯一 GUID[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]publicinterfaceIXTestObj{/// summary/// Synchronous call - returns JSON string directly./// /summarystringTestSync(stringaction,stringdataJson);/// summary/// Asynchronous call - invokes callback with response./// /summaryvoidTestAsync(stringaction,stringdataJson,[MarshalAs(UnmanagedType.IDispatch)]objectcallback);/// summary/// Read-only property: app version./// /summarystringVersion{get;}/// summary/// Read-only property: app name./// /summarystringName{get;}}关键点:[ComVisible(true)]- 允许 COM 访问[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]- 使用 IDispatch 接口,支持 JavaScript 动态调用[MarshalAs(UnmanagedType.IDispatch)]- 将 JavaScript 函数作为 COM 对象传递所有方法返回string类型(JSON 字符串),便于跨语言边界实现类usingSystem.Runtime.InteropServices;usingSystem.Text.Json;usingSerilog;namespaceShellClient.CommHelper.XTest;/// summary/// Implementation of XTestObj for WebView2 bridge./// /summary[ComVisible(true)][Guid("YYYYYYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY")][ClassInterface(ClassInterfaceType.None)][ComDefaultInterface(typeof(IXTestObj))]publicsealedclassXTestObj:IXTestObj,IDisposable{privatestaticreadonlySerilog.ILogger_log=Log.ForContextXTestObj();privatebool_disposed;// Read-only dataprivatereadonlystring_version;privatereadonly