Silverlight OOB模式:让浏览器应用以桌面客户端运行的完整指南
Silverlight 这名字在现在的开发圈里提起来多少带点“考古”的味道。但如果你手里还压着一套 Silverlight 写的内部业务系统或者当年为了在线播放加密视频部署过 Silverlight 应用那你大概率也遇到过同一个头疼的问题浏览器对插件的支持越来越差用户打开网页就弹警告业务方却天天追着要求“离线也能用”“像普通桌面软件一样双击就能开”。这时候Silverlight 本身的 Out-of-Browser简称 OOB也就是标题里说的“在浏览器外运行”能力反而成了最靠谱的出路。我前几年接手维护过一套基于 Silverlight 构建的报表展示系统就是靠 OOB 模式把应用从浏览器里“捞”出来改成桌面客户端运行才算是彻底绕开了浏览器封锁插件的问题。这篇东西我不打算写成教科书就按我实际折腾下来的经验把这套“在浏览器外运行 Silverlight 应用”的完整链路拆开讲讲它是什么、怎么配、怎么装、怎么更新以及发布前后有哪些坑。Silverlight 虽然已经不是主流技术但这套设计思路和运维方法对还在维护存量系统的人来说应该还能省下不少排查时间。1. 为什么要把 Silverlight 从浏览器里请出来OOB 模式的真实价值1.1 OOB 不是新东西但很多人从头到尾都没用过Silverlight 的 Out-of-Browser 功能其实很早就有了早在 Silverlight 3 的时候就已经引入Silverlight 4 做了大量增强到 Silverlight 5 基本成熟。我第一次接触 OOB 是当时某个项目需要在展厅大屏上循环播放产品演示展厅那台机器 IE 版本很老又不想为了一个演示去折腾浏览器设置于是想到直接把 Silverlight 应用安装成桌面应用。当时第一反应是“这不就是个快捷方式吗”后来发现远不止如此。OOB 模式的核心是应用安装后会生成一个独立的启动入口用户可以像打开普通桌面程序一样双击运行不需要先启动浏览器、输入网址、等待插件加载。应用虽然还是在 Silverlight 运行时上跑但界面、逻辑、资源文件全部从本地加载网络请求才真正走 HTTP。这个特性在浏览器插件被厂商陆续放弃支持之后反而成了唯一还活得不错的“后门”。不过这里要分清一个概念OOB 不是把 XAP 文件下载下来用浏览器打开也不是简单地创建一个网页快捷方式。应用安装后实际运行的是一个经过签名的、独立的 Silverlight 应用程序实例它有自己的窗口、自己的生命周期甚至可以被固定在任务栏上。理解了这一点你后面配置权限、管理版本的时候才不会走错方向。1.2 哪些场景必须脱离浏览器别等吃了亏才想起来Silverlight 的浏览器内运行模式是在一个受限的沙箱里运行的很多操作系统能力访问不到。而 OOB 模式尤其是开启提升权限ElevatedPermissions之后能做的事情比浏览器内多得多。根据我接触过的实际项目下面几类场景属于“不脱离浏览器根本没法用”的典型。第一类是离线业务。比如仓库盘点、客户现场演示、内网隔离环境里的数据录入这些场景要么没有外网要么不允许开浏览器访问特定端口。把应用安装成本地程序后界面资源都在本地数据可以通过独立服务接口同步系统至少在“打开就能用”这件事上不再依赖网络状态。第二类是需要本地文件读写和拖放支持的场景。浏览器内运行的 Silverlight 应用默认只能通过 OpenFileDialog 和 SaveFileDialog 和用户文件系统打交道而且每次都得让用户手动选文件。OOB 模式配合提升权限后可以直接读取指定目录的文件支持把文件从资源管理器拖进应用窗口。我做过一个批量导入 Excel 数据的工具就是靠这个特性把操作效率一下提上来的。第三类是需要在桌面上“驻留”的应用比如实时监控大屏、客服工作台。这类应用希望有自己的任务栏图标、独立的窗口位置记忆甚至希望在用户关闭浏览器之后仍然继续运行。OOB 模式虽然不能像 Windows 服务那样常驻后台但至少做到了“窗口即应用”用户不再需要忍受一堆标签页里的某一个。2. 开启脱离浏览器运行能力工程配置与 OutOfBrowserSettings.xml2.1 csproj 和配置文件的关系要把 Silverlight 项目调到 OOB 模式第一步是在工程文件里打开开关。在 Visual Studio 中打开 Silverlight 项目属性找到 “Silverlight” 选项卡勾选 “Enable running application out of the browser” 复选框。勾上之后Visual Studio 会自动生成一个叫 OutOfBrowserSettings.xml 的文件并把它登记到项目的 csproj 配置里。这个文件是整个 OOB 行为的核心配置很多排错最后都追到了这个文件头上。我见过不少项目开发的时候能跑发给用户装不上最后发现就是 OutOfBrowserSettings.xml 里 SourceList 写错了。所以先把这个文件的典型结构看明白。?xml version1.0 encodingutf-8? OutOfBrowserSettings ShortNameMyReports DisplayName公司报表客户端 5.0 InstallURLhttps://reports.internal.example/Client/setup.exe EnableGPUAccelerationfalse ShowInstallMenuItemtrue Permissions Permission NameElevatedPermissions / /Permissions WindowSettings Window Title公司报表客户端 Height720 Width1280 Left50 Top50 Resizabletrue WindowStyleBorderlessRoundCornersWindow / /WindowSettings SourceList Sourcehttps://reports.internal.example/Client/Reports.xap/Source /SourceList /OutOfBrowserSettings注意几个关键点。ShortName 是安装后生成的快捷方式和进程相关的标识DiscplayName 是显示给用户看的应用名。最容易被忽略的是 SourceList 里的 Source 地址它必须指向一个可访问的 .xap 文件地址否则应用安装完后无法正常启动运行。InstallURL 则是给用户下载独立安装程序的备用入口如果只靠右键安装这个值可以省略但做企业内部分发时建议一定配上。这个文件在 csproj 里的登记方式一般是这样的OutOfBrowserSettingsFileProperties\OutOfBrowserSettings.xml/OutOfBrowserSettingsFile如果你在项目目录里找不到这个文件或者改了 XML 后构建出来行为没变化优先检查 csproj 里这一行是否存在、路径是否正确。真实项目里经常出现多人协作时某台机器上这个文件被清理掉结果构建出来还是普通的浏览器模式运行。2.2 配置权限和窗口形态OutOfBrowserSettings.xml 里最需要认真理解的是 Permissions 节点。默认情况下应用程序没有配置任何权限节点此时 OOB 应用的权限等级和浏览器内模式类似仍然受沙箱限制。一旦加入了上面示例里的 ElevatedPermissions应用就获得了“提升权限”。提升权限具体能干什么以 Silverlight 5 的实际行为为准主要包括直接读写用户指定目录下的文件、创建和访问隔离存储以外的本地文件、调用 COM 组件比如访问 Excel 对象模型、使用完整的网络访问能力而不受跨域策略文件限制以及使用本机的剪贴板和拖放事件。注意这些能力仍然不能等同于传统 .NET 桌面应用比如它不能直接读写任意系统目录、不能注册全局系统服务而且 Windows 版本不同实际行为也有差异。所以任何时候都要先验证目标环境不要想当然。窗口设置方面Resizable 控制用户能否拖动调整窗口大小WindowStyle 有 StandardWindow 和 BorderlessRoundCornersWindow 两种常用取值后者适合做全屏风格的应用。EnableGPUAcceleration 建议在图形界面复杂的项目里开启但在低配终端上反而可能出现渲染兼容问题我一般默认 false。还有一个细节ShowInstallMenuItem 控制右键菜单中是否显示“在我的计算机上安装此应用程序”菜单项。开发测试阶段建议设为 true正式分发如果走自定义安装器也可以不依赖这个入口。3. 让用户装得上三种安装路径与体验差异配置做完应用构建出来下一步考虑的是用户怎么装上这个“浏览器外的应用”。Silverlight OOB 的安装路径实际有三条每条的适用环境和坑不一样放在一起说。3.1 右键菜单安装最直接但往往最被低估当 ShowInstallMenuItem 设置为 true 且应用通过网页加载运行后用户在 Silverlight 应用区域点击右键会看到“在我的计算机上安装此应用程序”选项。点击之后系统会弹出确认框确认后应用即被安装到本机桌面和开始菜单会生成快捷方式。这条路径用起来最简单但它有一个隐藏的依赖应用必须先在浏览器里成功加载并运行起来才能触发右键菜单。换句话说如果浏览器端插件已经被禁用或无法加载这条路径就等于死的。所以右键安装只适合那些“浏览器还能用只是不想一直开浏览器”的场景。另外右键安装会利用当前已经运行的应用实例如果你当前是通过 localhost 调试页面加载的 XAP那么安装下来的应用 SourceList 里的地址很可能也指向 localhost换到别的机器上就彻底跑不起来。这一点我在测试时踩过发布给现场人员前一定要确认 SourceList 是正式环境的地址。3.2 编程方式触发安装和更新检查Silverlight 提供的编程接口可以在应用代码里主动调用安装逻辑。典型场景是网页里嵌入一个按钮用户点击后在业务逻辑校验通过的情况下通过 JavaScript 调用 Silverlight 的托管代码接口执行安装流程。核心代码类似这样private void InstallLocalApplication() { if (Application.InstallState InstallState.NotInstalled) { bool installAccepted Application.Current.Install(); if (installAccepted) { statusText.Text 应用已安装请从桌面快捷方式启动; } } }这里 Application.Install() 会返回一个布尔值表明用户是否在系统弹窗里确认了安装。想要判断当前应用是否已安装可以用 Application.InstallState 属性它在应用启动时就能拿到状态。如果你的应用已经在运行还可以用 Application.Current.CheckAndDownloadUpdateAsync() 来主动触发更新检查。这个方法会向 SourceList 里的地址请求最新版本如果有新版本会自动下载并提示用户重启应用。这个接口在 OOB 应用里非常关键因为 OOB 应用不会像网页那样每次刷新都拿到最新代码更新机制完全靠这个。3.3 自定义安装器企业内部批量部署的靠谱路径第三条路径适合 IT 部门统一推送的场景。Silverlight 允许你构建一个独立的安装引导程序本质上是利用在网页中嵌入一段 HTML/JavaScript调用 Silverlight 运行时提供的安装相关接口或者更常见的是直接用静默安装参数配合 system center 之类的工具推送。实际项目里大多数企业内部是先把 XAP 和 OutOfBrowserSettings.xml 打包再做成一个 setup 引导程序引导程序检查目标机器是否已安装 Silverlight 运行时可手动安装、是否需要更新应用、然后用编程方式安装 OOB 应用。因为 OOB 应用安装后本质是在 AppData 下生成了用户级安装目录和快捷方式所以不需要管理员权限这也是它比传统桌面软件分发简单的地方。需要注意自定义安装器触发安装时同样必须经过用户权限确认弹窗。OOB 应用无法做到完全不弹窗的静默安装除非使用企业级系统管理工具提前配置好相关策略。这一点要提前跟业务方说清楚避免“双击就能装好”的预期落空。下面这个表格可以直观对比三条路径安装方式触发条件适合场景常见限制右键菜单应用已在浏览器内成功运行少量用户、临时安装浏览器插件不可用时失效编程安装应用内部按钮、JS 调用网页内自定义安装流程需要用户确认弹窗自定义安装器独立引导程序运行企业批量部署、现场分发仍需确认弹窗需管理权限策略4. OOB 运行时和浏览器内的真实分工权限、文件与桌面集成4.1 提升权限带来的本地能力究竟有多大前面提到 ElevatedPermissions 是 OOB 应用能力的分水岭这里展开说。很多从 Silverlight 转过来的人会惯性以为“提升权限 完全信任 能随便读写 C 盘”这是最大的误解。开启提升权限后OOB 应用默认可以访问“文档”“图片”“音乐”“视频”等用户库目录也可以读写当前用户的临时目录和隔离存储但普通系统目录仍然不能直接操作除非用户明确通过文件选择对话框授权。Silverlight 5 还支持通过类 P/Invoke 的机制调用 COM 组件但这个能力在部署时容易踩到 64 位系统兼容问题建议能不用就不用。另一个明显区别是网络访问。浏览器内 Silverlight 应用默认必须遵循客户端的跨域策略clientaccesspolicy.xml否则无法直接调用第三方接口。OOB 提升权限后则可以绕过这个限制这对于内网里一堆没有部署跨域策略文件的旧系统来说是救命稻草。我在某个项目里就是靠 OOB 直接对接内部数据接口省掉了统一给几十个服务加跨域文件的脏活。本地拖放也值得一说。Silverlight 5 的 OOB 模式支持把系统里的文件直接拖入窗口进而通过 DragEventArgs 拿到文件路径并读取内容。这个能力对批量导入、文件转码场景非常实用。我第一次做出来拖放导入时业务方直接说“这下终于像桌面软件了”。4.2 更新、回滚与缓存清理企业里真正会遇到的运维题目OOB 应用部署后的更新问题比浏览器模式下要麻烦得多因为没有“刷新网页”这个操作。Silverlight 提供了两套更新粒度最常用的是启动时调用 CheckAndDownloadUpdateAsync() 做前台更新检查。应用每次启动就向服务器请求一次 SourceList 中的 XAP 地址对比版本号有新版则自动下载。这个方法的坑在于如果服务器返回的 XAP 解析失败或网络超时异常会抛到未处理事件里导致应用启动时卡住或者崩溃。稳妥做法是包一层 try-catch并做好异常后的降级策略比如提示用户稍后重试而不是直接退出。另一种是精准控制版本的更新方式让自定义安装器直接检查服务器上某个固定地址的版本信息文件决定是否强制重新安装。适用于那种“必须统一版本不允许用户用旧版”的内部系统。更新之后本地是会保存旧版本缓存的。Silverlight OOB 应用安装后的数据缓存在当前用户目录的 AppData 区域里与浏览器缓存分开。如果出现“配置明明改了但运行起来还是旧界面”的情况优先怀疑是不是本地缓存了旧 XAP。手动清理方式是把应用彻底卸载后删掉对应缓存目录再重新安装一次。虽然粗暴但在存量环境里往往最有效。5. 维护这些存量应用我踩过的坑兼容、缓存、发布检查5.1 最容易翻车的配置组合Exe 兼容性、浏览器版本、运行时版本三座大山是 Silverlight OOB 维护绕不开的痛。下面几个坑我基本都踩过一遍。第一个坑是把 SourceList 地址写成相对路径或者不可访问的地址。OOB 应用安装后启动时仍然依赖 SourceList 中的地址去加载运行时和更新组件。如果这个地址失效应用启动就直接黑屏崩溃。绝对不要在配置里写 “localhost” 或者局域网临时 IP尤其不要在开发机上勾选好配置后直接把文件打包忘了改地址。第二个坑是 Visual Studio 的“调试”行为。F5 调试 OOB 应用时Visual Studio 往往直接以浏览器模式启动调试导致你写的代码里 Application.InstallState 的判断在测试时永远走不到 OOB 分支。需要手动右键项目选择“在浏览器外运行”才能真实模拟最终用户的环境。很多开发者代码写对了但一直没以 OOB 方式验证发出去就出问题。第三个坑是跨平台预期不一致。Silverlight OOB 在老版本 macOS 上的安装逻辑和 Windows 存在明显差异比如窗口样式支持范围、快捷键行为等。如果团队里有 Mac 用户尽量统一在 Windows 上进行最终验证至少不要拿 macOS 上的表现去推断 Windows 用户看到的界面。第四个坑是权限提升与杀毒软件的冲突。部分终端安全软件会把 Silverlight 的隔离存储目录或 COM 调用行为当成可疑操作导致应用启动时被拦截。这类问题定位起来非常隐蔽因为报错信息往往是通用的“应用程序无法启动”。排查时先让用户在终端安全软件里查看拦截日志比反复改代码有效得多。5.2 一份发布前必做的验证清单经过这套流程我整理了一份 OOB 应用发布前检查清单每次发版前照着走一遍能省掉至少两轮现场返工。检查项检查方法参考标准SourceList 地址打开 OutOfBrowserSettings.xml 核对必须是正式环境域名非 localhostXAP 包完整性用解压工具打开 XAP确认入口程序集存在能正常解出 .dll 和 AppManifest.xaml版本号递增对比服务器上旧版 XAP 的版本号必须高于旧版本否则不会触发更新首次安装在一台干净的测试机上走完整安装流程从安装到双击启动全程无报错更新流程先装旧版再部署新版启动触发更新提示更新并在重启后进入新版离线启动断网后启动应用观察加载行为应能进入界面并提示网络异常而非崩溃提升权限在代码里测试文件读写、拖放功能表现符合预期杀毒软件在安装有企业终端的机器上实测无拦截提示或已在白名单做完这一遍OOB 应用基本可以比较放心地交付给用户了。最后分享一个我个人的排查习惯当你怀疑 OOB 应用“更新了但没变化”时先去本地缓存目录看一下实际加载的 XAP 包大小和时间戳而不是急着改服务器配置。很多时候问题就出在缓存没有清干净应用一直在跑老代码。先清缓存、再重装、再检查配置按这个顺序排查能在十分钟内解决绝大多数存量 Silverlight 的“灵异问题”。