VSIX扩展如何从IDE启动时就接收VS服务的RDT事件
解决Visual Studio扩展中RDT监听器延迟生效的问题
嘿,这个问题我之前帮不少开发者解决过,核心问题通常出在两个地方——监听器实例的生命周期,或者注册的时机不对。咱们一步步来搞定它:
1. 先搞定监听器的生命周期:别让它被GC回收
很多新手会在Package::Initialize里临时创建RDT事件监听器,方法执行完后这个实例就被垃圾回收了,自然只有第一次执行命令时能生效。你得把监听器对象作为Package类的私有字段存起来,确保它能一直存活:
private RunningDocTableEvents _rdtEvents; private uint _rdtCookie; private uint _shellCookie; // 用于后续取消Shell属性监听 protected override void Initialize() { base.Initialize(); // 先别急着直接注册RDT,先监听VS启动完成事件 var shell = (IVsShell)GetService(typeof(SVsShell)); shell.AdviseShellPropertyChanges(new ShellPropertyChangeListener(this), out _shellCookie); }
这里的RunningDocTableEvents是你实现了IVsRunningDocTableEvents的自定义类,一定要用类字段持有,不能是局部变量!
2. 延迟注册到VS完全就绪时
VS启动过程中,RDT(Running Document Table)可能还没完全初始化,这时候直接注册监听器会错过早期的文档事件。我们可以借助IVsShell监听VS的启动完成状态,等Shell彻底初始化后再注册RDT监听器:
private class ShellPropertyChangeListener : IVsShellPropertyEvents { private readonly YourPackageName _package; public ShellPropertyChangeListener(YourPackageName package) { _package = package; } public int OnShellPropertyChange(int propid, object var) { // 监听VS Shell初始化完成的属性 if (propid == (int)__VSSPROPID.VSSPROPID_ShellInitialized) { bool isShellReady = (bool)var; if (isShellReady) { // 现在注册RDT监听器,这时候VS已经完全启动了 var rdt = (IVsRunningDocumentTable)_package.GetService(typeof(SVsRunningDocumentTable)); _package._rdtEvents = new RunningDocTableEvents(rdt); rdt.AdviseRunningDocTableEvents(_package._rdtEvents, out _package._rdtCookie); // 取消Shell属性监听,避免重复触发 var shell = (IVsShell)_package.GetService(typeof(SVsShell)); shell.UnadviseShellPropertyChanges(_package._shellCookie); } } return VSConstants.S_OK; } }
3. 确保你的RDT事件实现正确
最后,检查一下你实现的IVsRunningDocTableEvents方法,确保关键事件(比如文档显示/隐藏)能正确记录最后使用时间:
public class RunningDocTableEvents : IVsRunningDocTableEvents { // 用字典存储文档路径和最后使用时间 private readonly Dictionary<string, DateTime> _documentLastUsed = new Dictionary<string, DateTime>(); public int OnBeforeDocumentWindowShow(uint docCookie, int fFirstShow, IVsWindowFrame pFrame) { if (pFrame != null) { // 获取文档的路径 pFrame.GetProperty((int)__VSFPROPID.VSFPROPID_pszMkDocument, out object docPathObj); string docPath = docPathObj as string; if (!string.IsNullOrEmpty(docPath)) { // 更新最后使用时间 _documentLastUsed[docPath] = DateTime.Now; } } return VSConstants.S_OK; } // 其他接口方法如果不需要处理,直接返回VSConstants.S_OK即可 public int OnAfterFirstDocumentLock(uint docCookie, uint dwRDTLockType, uint dwReadLocksRemaining, uint dwEditLocksRemaining) => VSConstants.S_OK; public int OnBeforeLastDocumentUnlock(uint docCookie, uint dwRDTLockType, uint dwReadLocksRemaining, uint dwEditLocksRemaining) => VSConstants.S_OK; // 省略其他未实现的接口方法... }
按照这个方式调整后,你的RDT监听器应该就能从VS启动时就开始捕获所有文档事件,准确记录每个文档的最后使用时间了。
内容的提问来源于stack exchange,提问作者Eustace
相关产品推荐
相关产品推荐

