dotnet Roslyn 通过读取 suo 文件获取解决方案的启动项目
本文来告诉大家一个黑科技,通过 .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 文件获取解决方案的启动项目相关推荐
- 使用pandas读取dat文件完整解决方案
使用pandas读取dat文件完整解决方案 import pandas as pd data = pd.read_csv('文件名.dat',header=None,encoding='utf-8', ...
- python读取大文件-强悍的Python读取大文件的解决方案
Python 环境下文件的读取问题,请参见拙文 Python基础之文件读取的讲解 这是一道著名的 Python 面试题,考察的问题是,Python 读取大文件和一般规模的文件时的区别,也即哪些接口不适 ...
- python pandas 把数据保存成csv文件,以及读取csv文件获取指定行、指定列数据
文章目录: 1 数据说明 2 把数据集文件信息使用python pandas保存成csv文件 3 使用python pandas 读取csv的每行.每列数据 1 数据说明 1.在test_data目录 ...
- python读取大文件性能_强悍的Python读取大文件的解决方案
Python 环境下文件的读取问题,请参见拙文 Python基础之文件读取的讲解 这是一道著名的 Python 面试题,考察的问题是,Python 读取大文件和一般规模的文件时的区别,也即哪些接口不适 ...
- python中0xff_强悍的Python读取大文件的解决方案
这是一道著名的 Python 面试题,考察的问题是,Python 读取大文件和一般规模的文件时的区别,也即哪些接口不适合读取大文件. 1. read() 接口的问题 f =open(filename, ...
- python读取超大文件-强悍的Python读取大文件的解决方案
Python 环境下文件的读取问题,请参见拙文 Python基础之文件读取的讲解 这是一道著名的 Python 面试题,考察的问题是,Python 读取大文件和一般规模的文件时的区别,也即哪些接口不适 ...
- python读取大文件太慢_强悍的Python读取大文件的解决方案
Python 环境下文件的读取问题,请参见拙文 Python基础之文件读取的讲解 这是一道著名的 Python 面试题,考察的问题是,Python 读取大文件和一般规模的文件时的区别,也即哪些接口不适 ...
- java读取文件是乱码_java读取txt文件乱码解决方案
因为txt默认的选项是ANSI,即GBK编码.GBK和GB2312都是中文编码,在这里解释一下两者的区别. 总体说来,GBK包括所有的汉字,包括简体和繁体.而gb2312则只包括简体汉字. GBK: ...
- java读取txt文件乱码解决方案
因为txt默认的选项是ANSI,即GBK编码.GBK和GB2312都是中文编码,在这里解释一下两者的区别. 总体说来,GBK包括所有的汉字,包括简体和繁体.而gb2312则只包括简体汉字. GBK: ...
最新文章
- etcd数据库备份与还原
- Element-ui学习笔记3--Form表单(二)
- linux 系统调用 hook 总结
- 2. Python3输入与输出
- linux中如何撤销上次命令,使用git reset命令撤销上次Git提交
- 直播预告 | AAAI 2022论文解读:对称的语义感知的妆容迁移与移除网络
- RGB与YUV相互转换
- sql 百分数_SQL经典50题笔记
- Maven多模块,Dubbo分布式服务框架,SpringMVC,前后端分离项目,基础搭建,搭建过程出现的问题...
- directX 简介
- 【java面试经(架构师设计师)-第4课】java基础常识
- 实验11-1-7 藏头诗 (15 分)
- 快手音乐公布版权生态建设阶段战报:音乐人总结算金额提升480%
- 协同过滤推荐之基于近邻协同过滤(一)
- js强制保留两位小数
- 关于组队学习的一点想法
- VB中操作Excel文档
- 【中英】【吴恩达课后测验】Course 3 -结构化机器学习项目 - 第二周测验
- 新冠病毒爆发如何预防?用Python模拟病毒应对策略(附代码)
- python二分法排序_二分法排序-Python实现
热门文章
- symbian操作系统历史回顾
- There is no getter for property named 'user' in 'class com.jyr.wh.domain.User' 异常
- MySQL《多表连接操作2》
- android下图片
- matlab做kmo检验的代码,急求 KMO测度和Bartlett 的球形度检验的计算原公式
- 存能电气机架式UPS助力高速公路监控、收费系统
- 京东视觉算法部招聘CV实习生
- huoshan xl,xa,xg,xk签名参数
- openjudge 1.8.8 矩阵加法
- 鸿蒙系统有希望像ios一样流畅,华为鸿蒙系统或继承了“IOS系统”大量优点,流畅性不输对手!...