本文来告诉大家一个黑科技,通过 .suo 文件读取 VisualStudio 的启动项目。在 sln 项目里面,都会生成对应的 suo 文件,这个文件是 OLE 格式的文件,文件的格式没有公开,本文的方法适合用在 VisualStudio 2019 上,对于其他版本的 VisualStudio 也许会不适合

感谢 Simon Cropp 大佬提供的方法

默认在 sln 解决方案文件的相同文件夹里面,将会存放 .vs\{解决方案名}\v{VS版本}\.suo 文件,如解决方案文件名为 HairhechallchujurKairbilairlem.sln 在 VisualStudio 2019 下将会存放 .vs\HairhechallchujurKairbilairlem\v16\.suo 文件

这个 .suo 文件是包含了 VisualStudio 解决方案的一些配置,如启动项目。关多关于此文件,请参阅 Solution User Options (.Suo) File 文档

预计这个 suo 格式文件基本不会更改,在 1995 年的时候就开始使用这个格式

读取 .suo 需要使用到 Open MCDF 库。这是一个完全由 C# 实现的读取 OLE 格式文档的库,我在做 OFFICE 组件也用到这个库

在 suo 文件里面,通过 SolutionConfiguration 内容存放当前的启动项,这里面的内容是使用 UTF-16 编码的字符串,读取的方法如下

            using (var fileStream = new FileStream(suoFilePath, FileMode.Open)){using CompoundFile compoundFile = new CompoundFile(fileStream, CFSUpdateMode.ReadOnly, CFSConfiguration.SectorRecycle | CFSConfiguration.EraseFreeSectors);var cfStream = compoundFile.RootStorage.GetStream("SolutionConfiguration");var byteList = cfStream.GetData();var encoding = Encoding.GetEncodings().Single(x => string.Equals(x.Name, "utf-16", StringComparison.OrdinalIgnoreCase));var text = encoding.GetEncoding().GetString(byteList);}

这里的 text 的内容大概如下

"\u0011\0MultiStartupProj\0=\u0003\0\0;4\0{45171CDC-EDAC-4D0B-BDF8-63DE2D4F947B}.dwStartupOpt\0=\u0003\0\0;\u000f\0StartupProject\0=\b&\0{45171CDC-EDAC-4D0B-BDF8-63DE2D4F947B};A\0{45171CDC-EDAC-4D0B-BDF8-63DE2D4F947B}.Release|Any CPU.fBatchBld\0=\u0003\0\0;?\0{45171CDC-EDAC-4D0B-BDF8-63DE2D4F947B}.Debug|Any CPU.fBatchBld\0=\u0003\0\0;4\0{AE3577E5-5D4E-44F8-B181-88A31B92584A}.dwStartupOpt\0=\u0003\0\0;A\0{AE3577E5-5D4E-44F8-B181-88A31B92584A}.Release|Any CPU.fBatchBld\0=\u0003\0\0;?\0{AE3577E5-5D4E-44F8-B181-88A31B92584A}.Debug|Any CPU.fBatchBld\0=\u0003\0\0;4\0{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}.dwStartupOpt\0=\u0003\0\0;\n\0ActiveCfg\0=\b\r\0Debug|Any CPU;"

通过读取 StartupProject 后续的内容即可找到当前的启动项目的 GUID 值,以下是我写的正则

                var text = encoding.GetEncoding().GetString(byteList);const char nul = '\u0000';const char dc1 = '\u0011';const char etx = '\u0003';const char soh = '\u0001';var startupProjectRegex = new Regex(@$"StartupProject{nul}={'\b'}&{nul}(.{'{'}{38}{'}'});A");var startupProjectMatch = startupProjectRegex.Match(text);if (startupProjectMatch.Success){var guid = Guid.Parse(startupProjectMatch.Groups[1].Value);}

上面代码拿到的 guid 就是启动项目的 guid 内容

咱可以采用 Simon Cropp 大佬的开源项目 https://github.com/SimonCropp/SetStartupProjects 来辅助读取当前 sln 里面包含的 csproj 的 GUID 和路径

代码如下

var projectList = SetStartupProjects.SolutionProjectExtractor.GetAllProjectFiles(solutionFile.FullName).ToList();

通过 guid 获取当前的 csproj 项目文件路径方法如下

                    var guid = Guid.Parse(startupProjectMatch.Groups[1].Value);var project = projectList.FirstOrDefault(temp => new Guid(temp.Guid) == guid);

我封装了方法,传入的是 sln 文件,返回启动项目的路径

        private static FileInfo GetStartupProject(FileInfo solutionFile){var solutionFilePath = solutionFile.FullName;var solutionDirectory = solutionFile.DirectoryName;var solutionName = Path.GetFileNameWithoutExtension(solutionFilePath);var suoDirectoryPath = Path.Combine(solutionDirectory, ".vs", solutionName, "v16");Directory.CreateDirectory(suoDirectoryPath);var suoFilePath = Path.Combine(suoDirectoryPath, ".suo");var projectList = SetStartupProjects.SolutionProjectExtractor.GetAllProjectFiles(solutionFile.FullName).ToList();using (var fileStream = new FileStream(suoFilePath, FileMode.Open)){using CompoundFile compoundFile = new CompoundFile(fileStream, CFSUpdateMode.ReadOnly, CFSConfiguration.SectorRecycle | CFSConfiguration.EraseFreeSectors);var cfStream = compoundFile.RootStorage.GetStream("SolutionConfiguration");var byteList = cfStream.GetData();var encoding = Encoding.GetEncodings().Single(x => string.Equals(x.Name, "utf-16", StringComparison.OrdinalIgnoreCase));var text = encoding.GetEncoding().GetString(byteList);const char nul = '\u0000';const char dc1 = '\u0011';const char etx = '\u0003';const char soh = '\u0001';var startupProjectRegex = new Regex(@$"StartupProject{nul}={'\b'}&{nul}(.{'{'}{38}{'}'});A");var startupProjectMatch = startupProjectRegex.Match(text);if (startupProjectMatch.Success){var guid = Guid.Parse(startupProjectMatch.Groups[1].Value);var project = projectList.FirstOrDefault(temp => new Guid(temp.Guid) == guid);return new FileInfo(project.FullPath);}}return null;}

需要先在项目安装 SetStartupProjects 库,才能使用这个方法

本文所有代码放在 github 和 gitee 欢迎小伙伴访问

除了读取启动项目,还可以读取断点等内容,读取 suo 里面的所有内容的方法如下

                compoundFile.RootStorage.VisitEntries(item =>{if (item.IsStream){Console.WriteLine(item.Name);var stream = item as CFStream;byteList = stream.GetData();text = encoding.GetEncoding().GetString(byteList);}}, true);

当然了,获取到的内容不一定使用 UTF-16 编码格式,还需要自己尝试,里面的数据只是二进制而是,上面代码的转换字符串只是用来调试

更多请看

SimonCropp/SetStartupProjects: Setting Visual Studio startup projects by hacking the suo

Solution User Options (.Suo) File

更多编译相关请看手把手教你写 Roslyn 修改编译

我搭建了自己的博客 https://blog.lindexi.com/ 欢迎大家访问,里面有很多新的博客。只有在我看到博客写成熟之后才会放在csdn或博客园,但是一旦发布了就不再更新

如果在博客看到有任何不懂的,欢迎交流,我搭建了 dotnet 职业技术学院 欢迎大家加入

如有不方便在博客评论的问题,可以加我 QQ 2844808902 交流


本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名林德熙(包含链接:http://blog.csdn.net/lindexi_gd ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系。

dotnet Roslyn 通过读取 suo 文件获取解决方案的启动项目相关推荐

  1. 使用pandas读取dat文件完整解决方案

    使用pandas读取dat文件完整解决方案 import pandas as pd data = pd.read_csv('文件名.dat',header=None,encoding='utf-8', ...

  2. python读取大文件-强悍的Python读取大文件的解决方案

    Python 环境下文件的读取问题,请参见拙文 Python基础之文件读取的讲解 这是一道著名的 Python 面试题,考察的问题是,Python 读取大文件和一般规模的文件时的区别,也即哪些接口不适 ...

  3. python pandas 把数据保存成csv文件,以及读取csv文件获取指定行、指定列数据

    文章目录: 1 数据说明 2 把数据集文件信息使用python pandas保存成csv文件 3 使用python pandas 读取csv的每行.每列数据 1 数据说明 1.在test_data目录 ...

  4. python读取大文件性能_强悍的Python读取大文件的解决方案

    Python 环境下文件的读取问题,请参见拙文 Python基础之文件读取的讲解 这是一道著名的 Python 面试题,考察的问题是,Python 读取大文件和一般规模的文件时的区别,也即哪些接口不适 ...

  5. python中0xff_强悍的Python读取大文件的解决方案

    这是一道著名的 Python 面试题,考察的问题是,Python 读取大文件和一般规模的文件时的区别,也即哪些接口不适合读取大文件. 1. read() 接口的问题 f =open(filename, ...

  6. python读取超大文件-强悍的Python读取大文件的解决方案

    Python 环境下文件的读取问题,请参见拙文 Python基础之文件读取的讲解 这是一道著名的 Python 面试题,考察的问题是,Python 读取大文件和一般规模的文件时的区别,也即哪些接口不适 ...

  7. python读取大文件太慢_强悍的Python读取大文件的解决方案

    Python 环境下文件的读取问题,请参见拙文 Python基础之文件读取的讲解 这是一道著名的 Python 面试题,考察的问题是,Python 读取大文件和一般规模的文件时的区别,也即哪些接口不适 ...

  8. java读取文件是乱码_java读取txt文件乱码解决方案

    因为txt默认的选项是ANSI,即GBK编码.GBK和GB2312都是中文编码,在这里解释一下两者的区别. 总体说来,GBK包括所有的汉字,包括简体和繁体.而gb2312则只包括简体汉字. GBK: ...

  9. java读取txt文件乱码解决方案

    因为txt默认的选项是ANSI,即GBK编码.GBK和GB2312都是中文编码,在这里解释一下两者的区别. 总体说来,GBK包括所有的汉字,包括简体和繁体.而gb2312则只包括简体汉字. GBK: ...

最新文章

  1. etcd数据库备份与还原
  2. Element-ui学习笔记3--Form表单(二)
  3. linux 系统调用 hook 总结
  4. 2. Python3输入与输出
  5. linux中如何撤销上次命令,使用git reset命令撤销上次Git提交
  6. 直播预告 | AAAI 2022论文解读:对称的语义感知的妆容迁移与移除网络
  7. RGB与YUV相互转换
  8. sql 百分数_SQL经典50题笔记
  9. Maven多模块,Dubbo分布式服务框架,SpringMVC,前后端分离项目,基础搭建,搭建过程出现的问题...
  10. directX 简介
  11. 【java面试经(架构师设计师)-第4课】java基础常识
  12. 实验11-1-7 藏头诗 (15 分)
  13. 快手音乐公布版权生态建设阶段战报:音乐人总结算金额提升480%
  14. 协同过滤推荐之基于近邻协同过滤(一)
  15. js强制保留两位小数
  16. 关于组队学习的一点想法
  17. VB中操作Excel文档
  18. 【中英】【吴恩达课后测验】Course 3 -结构化机器学习项目 - 第二周测验
  19. 新冠病毒爆发如何预防?用Python模拟病毒应对策略(附代码)
  20. python二分法排序_二分法排序-Python实现

热门文章

  1. symbian操作系统历史回顾
  2. There is no getter for property named 'user' in 'class com.jyr.wh.domain.User' 异常
  3. MySQL《多表连接操作2》
  4. android下图片
  5. matlab做kmo检验的代码,急求 KMO测度和Bartlett 的球形度检验的计算原公式
  6. 存能电气机架式UPS助力高速公路监控、收费系统
  7. 京东视觉算法部招聘CV实习生
  8. huoshan xl,xa,xg,xk签名参数
  9. openjudge 1.8.8 矩阵加法
  10. 鸿蒙系统有希望像ios一样流畅,华为鸿蒙系统或继承了“IOS系统”大量优点,流畅性不输对手!...