最近,偶然发现了一个博客文章,提到了Visual Studio 2005 C++解决方案中一个未被文档记录的开关(/MP),它允许在多线程中编译源文件。很好奇,如果30个项目解决方案启用这个开关,能节省多少时间。不幸的是,VS2005 C++项目设置中允许开启构建计时,但只能计时每个单独项目的构建时间,而不是整个解决方案的构建时间。经过两次使用秒表手动测量总构建时间(记录显示,不使用开关需要28分26秒,使用开关需要17分42秒),开始寻找一种方法,让IDE告诉总构建时间。
通过搜索,发现其他博客和论坛上也有人抱怨这个缺失的功能(这个功能在Visual Studio 6时代是存在的)。解决方案是利用Visual Studio 2005中的IDE自动化,来恢复这个非常受欢迎的功能。
插件最初是通过Visual Studio 2005 C# AddIn向导生成的。这为代码的其余部分提供了框架。IDE自动化模型暴露了两个事件,OnBuildBegin和OnBuildDone,它们非常适合在插件中使用,因为它们在构建开始和结束时被触发。
在插件的OnConnection事件中,获取了打算发送输出的窗口窗格(在这种情况下是构建窗口),并为自己添加了事件处理程序到IDE:
public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom)
{
_applicationObject = (DTE2)application;
_addInInstance = (AddIn)addInInst;
// 希望输出在构建窗口
OutputWindow outputWindow = (OutputWindow)_applicationObject.Windows.Item(Constants.vsWindowKindOutput).Object;
outputWindowPane = outputWindow.OutputWindowPanes.Item("Build");
// 添加自己作为OnBuildBegin/OnBuildDone处理程序
EnvDTE.Events events = _applicationObject.Events;
buildEvents = (EnvDTE.BuildEvents)events.BuildEvents;
buildEvents.OnBuildBegin += new _dispBuildEvents_OnBuildBeginEventHandler(this.OnBuildBegin);
buildEvents.OnBuildDone += new _dispBuildEvents_OnBuildDoneEventHandler(this.OnBuildDone);
}
在插件的OnDisconnection事件中,进行清理:
public void OnDisconnection(ext_DisconnectMode disconnectMode, ref Array custom)
{
// 移除自己作为OnBuildBegin/OnBuildEnd处理程序
if (buildEvents != null)
{
buildEvents.OnBuildBegin -= new _dispBuildEvents_OnBuildBeginEventHandler(this.OnBuildBegin);
buildEvents.OnBuildDone -= new _dispBuildEvents_OnBuildDoneEventHandler(this.OnBuildDone);
}
}
插件的核心部分在于在OnConnection事件中添加的处理程序。首先,OnBuildBegin事件:
public void OnBuildBegin(EnvDTE.vsBuildScope Scope, EnvDTE.vsBuildAction Action)
{
// 检查是否为解决方案构建或重建所有
if (EnvDTE.vsBuildScope.vsBuildScopeSolution == Scope &&
(EnvDTE.vsBuildAction.vsBuildActionBuild == Action ||
EnvDTE.vsBuildAction.vsBuildActionRebuildAll == Action))
{
// 标记构建计时器
amTiming = true;
dtStart = DateTime.Now;
outputWindowPane.OutputString(String.Format("Starting timed solution build on {0}\n", dtStart));
}
}
OnBuildBegin事件首先检查否在构建解决方案(而不是项目),并进一步限制计时到构建或重建所有命令(想要忽略清理命令)。如果检查成功,设置一个标志来表示正在计时构建,获取当前的日期和时间,并将该时间发送到构建窗口。
其次,OnBuildDone事件:
public void OnBuildDone(EnvDTE.vsBuildScope Scope, EnvDTE.vsBuildAction Action)
{
// 检查否真的在计时这个构建
if (amTiming)
{
amTiming = false;
dtEnd = DateTime.Now;
outputWindowPane.OutputString(String.Format("Ended timed solution build on {0}\n", dtEnd));
TimeSpan tsElapsed = dtEnd - dtStart;
outputWindowPane.OutputString(String.Format("Total build time: {0}\n", tsElapsed));
}
}
OnBuildDone事件首先检查否真的在计时这个构建。如果正确,获取当前的日期和时间,将该时间发送到构建窗口,计算构建开始和结束之间的经过时间,最后将该时间发送到构建窗口。
发现很难确定IDE中自动化的可用性。MSDN上关于这个主题的帮助对于刚开始接触插件世界的人来说似乎非常缺乏(这仅仅是第三个插件)。不过,发现了一个非常有用的资源,即VS2005自动化样本(可以从Microsoft网站下载),一旦能够浏览源代码,它就给了所有需要的入口点。
当Visual Studio 2013被引入时,注意到现在有一个选项可以为C++解决方案启用构建计时(Tools > Options > Projects and Solutions > VC++ Project Settings > Build Timing)。立即启用了这个选项,并发现IDE开发者在计时输出中提供的细节过多。仍然更喜欢原来解决方案构建计时器的输出,所以想要在VS2013中启用它。Microsoft引入了VSPackages来取代Add Ins,所以在VS2013中创建了一个新的包,并将原始代码迁移过去。结果可以作为SolutionBuildTimer2013.zip下载。这个压缩包包含了扩展的源代码以及一个vsix包,可以通过双击文件来安装。
在安装2015并尝试在VS2015中重建扩展后,发现2013版本可以构建以支持2015。源代码和vsix包已经相应更新。
Visual Studio 2017改变了VSIX格式,这意味着不得不再次重建扩展以支持这个版本。
Visual Studio 2019通过简单地更改VSIX的清单目标版本,增加了对2019的支持。