You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

首次访问EnvDTE.DTE.Documents抛异常 二次访问正常问题咨询

问题

我尝试获取Visual Studio环境中已打开的文档集合,相关代码在多数解决方案下可正常运行,但在某一个特定解决方案中每次调用都会抛出异常。我曾尝试等待环境完成初始化,但等待任意时长后问题依旧。只要捕获该异常后再次调用该属性,即可正常返回结果。

出问题的代码如下:

internal static IList<Document> GetOpenedDocuments(DTE service)
{
    ThreadHelper.ThrowIfNotOnUIThread();

    if (service == null)
    {
        throw new ArgumentNullException(nameof(service));
    }

    List<Document> openedDocuments = new List<Document>();

    foreach (Document document in service.Documents)
    {
        if (document != null)
        {
            openedDocuments.Add(document);
        }
    }

    return openedDocuments;
}

首次访问service.Documents时总会触发崩溃,堆栈跟踪顶部信息如下:

at EnvDTE.Documents.GetEnumerator()
at XXX.FileHelper.GetOpenedDocuments(DTE service) in

异常信息:

系统找不到指定的文件。(Exception from HRESULT: 0x80070002)


原因说明

这个0x80070002文件找不到错误是Visual Studio DTE COM接口存在多年的已知问题,触发场景是出问题的解决方案的Documents集合里残留了无效脏条目:通常是上一次关闭解决方案时处于打开状态的文件,后续被删除、移动或者重命名,VS启动加载解决方案时没有把这个失效条目从内存集合中清理掉。

第一次枚举service.Documents时,底层COM组件会逐个加载所有文档的元数据,碰到这个不存在的文件条目就会直接抛出异常。而抛异常的过程中,COM层会自动把这个无效条目从集合里移除,所以第二次再调用枚举时,集合里已经没有脏数据,就能正常返回结果。
等待环境初始化没有任何作用,因为这个问题和初始化流程无关,纯粹是集合内存在无效数据导致的。

修复方案

直接在枚举逻辑上加针对性的异常捕获和重试即可,最多重试2次就可以覆盖这个场景,不需要加额外等待延迟,参考实现如下:

internal static IList<Document> GetOpenedDocuments(DTE service)
{
    ThreadHelper.ThrowIfNotOnUIThread();

    if (service == null)
    {
        throw new ArgumentNullException(nameof(service));
    }

    List<Document> openedDocuments = new List<Document>();
    int retryCount = 0;
    // 最多重试2次,覆盖COM层自动清理无效条目的场景
    while (retryCount < 2)
    {
        openedDocuments.Clear();
        try
        {
            foreach (Document document in service.Documents)
            {
                if (document != null)
                {
                    openedDocuments.Add(document);
                }
            }
            // 枚举成功直接跳出循环
            break;
        }
        catch (System.IO.FileNotFoundException)
        {
            retryCount++;
            // 捕获到目标异常后直接重试,第一次异常抛出时COM已经完成无效条目清理
        }
    }
    
    if (retryCount >= 2)
    {
        // 两次重试都失败才抛出异常,避免吞掉其他非预期错误
        throw new InvalidOperationException("枚举已打开文档集合失败");
    }

    return openedDocuments;
}

注意事项:

  • 这个问题从VS2010到最新版本都偶有出现,没有可以从根源规避的公开API,重试是VS扩展开发社区公认的标准处理方式
  • 不要捕获所有类型的异常,只处理对应0x80070002的FileNotFoundException即可,避免掩盖其他真实的代码错误
  • 不需要在重试之间加延迟,异常抛出的瞬间COM层已经完成了无效条目的清理,立刻重试即可

内容的提问来源于stack exchange,提问作者Shay

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 12:51:18