为C# WinForms应用附加VSCode调试器调试TypeScript/JavaScript
实现VSCode调试Jint执行的TS转译JS(WinForms环境)
我之前折腾过类似的场景,给你一步步拆解具体怎么做,分为C#端开发、VSCode配置两个核心部分,最后再提些现成的参考案例:
整体核心思路
Jint本身没有直接对接VSCode调试器的能力,但VSCode的JavaScript调试器支持Chrome DevTools Protocol(CDP)——这是Chrome调试器使用的标准协议。我们的核心思路就是:在WinForms应用里搭建一个兼容CDP的WebSocket服务器,把Jint的调试事件(断点命中、步进等)和VSCode的调试指令(设置断点、继续执行等)通过CDP协议打通,同时利用内联源映射让VSCode直接映射到原始TypeScript代码。
C# WinForms端需完成的开发工作
1. 引入必要的依赖包
首先通过NuGet安装几个关键包:
Jint:选最新稳定版(v3+),确保它的调试API可用WebSocketSharp-netcore:用来快速搭建WebSocket服务器(也可以用.NET自带的System.Net.WebSockets,但前者更省心)SourceMap.NET:用来解析内联的Source Map,实现TS和JS代码的位置映射
2. 初始化Jint引擎并启用调试模式
在你的WinForms应用中,初始化Jint引擎时要开启调试模式,同时拿到调试器实例:
using Jint; using Jint.Debugger; // 初始化带调试模式的Jint引擎 var engine = new Engine(options => { options.DebugMode = true; // 必须开启,否则调试API无法工作 options.AllowDebuggerStatement = true; // 支持JS中的debugger语句触发断点 }); // 获取Jint调试器实例,后续用来处理断点、步进等操作 var jintDebugger = engine.Debugger;
3. 搭建CDP兼容的WebSocket服务器
我们需要监听一个端口(默认CDP用9229),接收VSCode的调试连接,然后处理CDP协议的命令和事件:
using WebSocketSharp; using WebSocketSharp.Server; // 启动WebSocket服务器,监听9229端口 var wssv = new WebSocketServer("ws://localhost:9229"); // 注册自定义的调试会话处理类 wssv.AddWebSocketService<JintCdpDebugSession>(); wssv.Start(); // WinForms关闭时记得停止服务器,避免端口占用 FormClosing += (s, e) => wssv.Stop();
4. 实现CDP调试会话处理类
这个类是核心,需要完成:
- 解析VSCode发来的CDP命令(比如设置断点、继续执行)
- 调用Jint的调试API执行对应操作
- 监听Jint的调试事件(比如断点命中),转换成CDP格式发回给VSCode
- 解析内联Source Map,把JS代码位置映射回原始TS位置
举个核心逻辑的简化示例:
public class JintCdpDebugSession : WebSocketBehavior { private Engine _engine; private Debugger _jintDebugger; private SourceMap _sourceMap; // 解析后的内联源映射 protected override void OnOpen() { // 这里可以从外部传入已经初始化好的Jint引擎实例 _engine = YourGlobalEngineInstance; _jintDebugger = _engine.Debugger; // 监听Jint的断点命中事件 _jintDebugger.BreakpointHit += OnBreakpointHit; } protected override void OnMessage(MessageEventArgs e) { // 解析VSCode发来的CDP JSON消息 var cdpMessage = JsonSerializer.Deserialize<CdpMessage>(e.Data); switch (cdpMessage.Method) { case "Debugger.enable": // 启用Jint调试,回复成功 SendResponse(cdpMessage.Id, new { }); break; case "Debugger.setBreakpointByUrl": // 解析请求中的TS文件路径和行号,通过源映射找到对应的JS位置 var @params = cdpMessage.Params as SetBreakpointByUrlParams; var jsPosition = _sourceMap.FindGeneratedPosition(@params.URL, @params.LineNumber); // 在Jint中设置断点 _jintDebugger.SetBreakpoint(jsPosition.Line, jsPosition.Column); // 回复断点信息给VSCode SendResponse(cdpMessage.Id, new { breakpointId = "bp1", locations = new[] { new { url = @params.URL, lineNumber = @params.LineNumber } } }); break; case "Debugger.continue": _jintDebugger.Resume(); SendResponse(cdpMessage.Id, new { }); break; // 其他CDP命令(stepOver、stepInto等)类似处理 } } private void OnBreakpointHit(object sender, BreakpointEventArgs e) { // 通过源映射把JS断点位置转换回TS位置 var tsPosition = _sourceMap.FindOriginalPosition(e.Breakpoint.Line, e.Breakpoint.Column); // 发送CDP的paused事件给VSCode,触发调试暂停 SendEvent("Debugger.paused", new { callFrames = new[] { new { functionName = "", location = new { url = tsPosition.Source, lineNumber = tsPosition.Line } } } }); } private void SendResponse(long id, object result) { Send(JsonSerializer.Serialize(new { id, result })); } private void SendEvent(string method, object @params) { Send(JsonSerializer.Serialize(new { method, @params })); } }
5. 解析内联Source Map
在加载TS转译后的JS代码时,要提取并解析内联的sourceMappingURL:
using SourceMapDotNet; // 假设jsCode是从TS转译来的带内联源映射的JS字符串 var sourceMapParser = new SourceMapParser(); // 提取内联的源映射内容(通常在JS末尾的//# sourceMappingURL=data:application/json;base64,...) var sourceMapContent = ExtractInlineSourceMap(jsCode); _sourceMap = sourceMapParser.Parse(sourceMapContent); // 自定义方法:提取内联源映射内容 private string ExtractInlineSourceMap(string jsCode) { var lines = jsCode.Split('\n'); foreach (var line in lines.Reverse()) { if (line.TrimStart().StartsWith("//# sourceMappingURL=data:")) { var base64Part = line.Split(',')[1]; return Encoding.UTF8.GetString(Convert.FromBase64String(base64Part)); } } return null; }
VSCode端的调试配置
打开你的TS源文件所在的VSCode工作区,创建.vscode/launch.json文件,配置成连接到我们的CDP服务器:
{ "version": "0.2.0", "configurations": [ { "type": "chrome", "request": "attach", "name": "Attach to WinForms Jint Debugger", "port": 9229, "webRoot": "${workspaceFolder}", // 你的TS源文件根目录 "sourceMaps": true, "skipFiles": ["<node_internals>/**"] // 跳过无关文件 } ] }
配置好后,启动WinForms应用,然后在VSCode中点击调试按钮,就能连接到应用,在TS代码上设置断点进行调试了。
相关实践案例参考
目前社区里有一些现成的参考可以借鉴:
- 开源项目
Jint.Debugger:虽然没有直接实现CDP,但它封装了Jint的调试API,你可以基于它扩展CDP对接逻辑 - 一些.NET脚本编辑器/调试器(比如
ScriptCS的调试插件)也采用了类似的CDP对接思路,你可以参考它们的实现 - 微软官方的
Debug Adapter Protocol文档里,也有关于自定义调试适配器的示例,不过用CDP会比从零实现DAP更高效
内容的提问来源于stack exchange,提问作者user11324063
相关产品推荐
相关产品推荐

