Visual Studio 2005 自动版本控制插件
一个在特定条件下处理程序集和文件版本号递增的插件。
引言
很多时候,我发现自己无法区分几个较小程序的版本,因为我很少在每次进行微小代码更改时费心去修改版本号。Visual Basic 6 提供了在每次编译时自动递增生成/修订版本号的选项,我认为这有时是个好主意……但是,我找不到任何真正令人满意的解决方案。
背景
我确实找到了一些技巧(包括预/后生成命令等),这些技巧有时比手动更改版本号更麻烦,还有一两个插件的创意是通过编程进行更改。其中一个名为 VAV,它是 SourceForge 上一个预 alpha 版项目;我的源代码使用了 VAVE 这个名字,我承认我厚颜无耻地在其名称中添加了“Enhanced”(增强),代表“VisualStudio Assembly Versionner”(Visual Studio 程序集版本号生成器)。
然而,那个代码的做法完全不合我意。核心思想很完美——钩取来自环境的命令。但实际工作方式却很奇怪且不寻常,而 Visual Studio 的对象模型一直都在那里处理细节。
Using the Code
实际代码集中在插件类中,Visual Studio 默认将其命名为 'Connect
'。该类实现了 IDTExtensibility2
和 IDTCommandTarget
,分别允许它作为插件加载并响应 IDE 的调用。
最重要的是,插件的主要工作是每次生成时递增修订号。但既然我属于那种即使是“Hello world”应用程序也会提供选项的类型,这还不够。提供一个递增生成号(版本号的第三部分)并重置修订号的命令相对简单,对次版本号(版本信息的第二部分)也可以进行同样的操作。
递增修订号是通过钩取 Visual Studio 提供的某些命令自动完成的;首先,Build.BuildSolution
和 Build.RebuildSolution
,以及 Build.BuildOnlyProject
和 Build.RebuildOnlyProject
命令是比较明显的。Build.BuildSelection
和 Build.RebuildSelection
用于生成选定的项目,即当前在解决方案资源管理器中突出显示的项目(或当前打开文件所属的项目)——这是“Build <project name>”按钮和菜单项发出的命令。此外,在启动调试会话时也可以递增修订号,因为这也会执行生成(Debug.Start
和 Debug.StartWithoutDebugging
),但这仅限于开发人员实际需要时。
钩取命令是通过持有对该命令的引用(尽管目前并非真正必要,但我认为可以稍后使用,稍后详述)以及一个关联的 CommandEvents
对象来完成的。
Private _cmdBuildSolution As Command
Private WithEvents cmdBuildSolutionEvents As CommandEvents
当类连接到 IDE 时,即在 OnConnection
子程序中,对象被设置为各自的实例。
_cmdBuildSolution = commands.Item("Build.BuildSolution")
cmdBuildSolutionEvents = _applicationObject.Events.CommandEvents(_cmdBuildSolution.Guid, _
_cmdBuildSolution.ID)
并且 CommandEvent
的两个事件被指向我们的两个处理程序。
AddHandler cmdBuildSolutionEvents.BeforeExecute, _
New _dispCommandEvents_BeforeExecuteEventHandler(AddressOf OnBeforeBuildAny)
AddHandler cmdBuildSolutionEvents.AfterExecute, _
New _dispCommandEvents_AfterExecuteEventHandler(AddressOf OnAfterBuild)
一旦所有命令都指向正确方向,在 IDE 中发出该命令就会执行我们的 OnBeforeBuildAny
子程序,然后是实际命令,然后是我们的 OnAfterBuild
子程序。我只使用 OnAfterBuild
将消息输出到输出窗口,因为在输出工具窗口中还没有默认窗格之前输出任何内容都有些麻烦。OnBeforeBuildAny
仅访问 IDE 的解决方案对象,遍历所有项目,并递增每个项目的版本属性('AssemblyVersion' 和 'AssemblyFileVersion')——就是这么简单。当然,它还会执行一些额外的检查并考虑我添加的一些额外选项。
Private Sub Update(ByVal part As VersionParts)
If Not _enabled Then Exit Sub
Dim build as SolutionBuild = _applicationObject.Solution.SolutionBuild
If _release AndAlso build.ActiveConfiguration.Name <> "Release" Then
Exit Sub
End If
Dim pp As Project
_queuedMsg = ""
For j As Integer = 1 To _applicationObject.Solution.Projects.Count
Try
pp = _applicationObject.Solution.Projects.Item(j)
Dim v1 As New Version(pp.Properties.Item("AssemblyVersion").Value.ToString)
Dim v2 As New Version(pp.Properties.Item("AssemblyFileVersion").Value.ToString)
Dim vv1 As Version, vv2 As Version
Select Case part
Case VersionParts.Build
vv1 = New Version(v1.Major, v1.Minor, v1.Build + 1, 0)
vv2 = New Version(v2.Major, v2.Minor, v2.Build + 1, 0)
Case VersionParts.Minor
vv1 = New Version(v1.Major, v1.Minor + 1, 0, 0)
vv2 = New Version(v2.Major, v2.Minor + 1, 0, 0)
Case VersionParts.Major
vv1 = New Version(v1.Major + 1, 0, 0, 0)
vv2 = New Version(v2.Major + 1, 0, 0, 0)
Case VersionParts.None
Exit Sub
Case Else 'revision is also default for unknown values
vv1 = New Version(v1.Major, v1.Minor, v1.Build, v1.Revision + 1)
vv2 = New Version(v2.Major, v2.Minor, v2.Build, v2.Revision + 1)
End Select
pp.Properties.Item("AssemblyVersion").Value = vv1.ToString
_queuedMsg &= " ---> VAVE: Updated assembly version for project " & pp.Name & _
" to " & vv1.ToString & " ---" & vbCrLf
pp.Properties.Item("AssemblyFileVersion").Value = vv2.ToString
_queuedMsg &= " ---> VAVE: Updated output file version for project " & pp.Name & _
" to " & vv2.ToString & " ---" & vbCrLf
pp.Save()
Catch
_queuedMsg &= " ---> VAVE: Failed to increment version for one
---> of the projects. ---" & vbCrLf
End Try
Next
End Sub
如我所说,我忍不住添加了几个额外的选项;该插件处理两个额外的命令,“Increment Build”(递增生成号)和“Increment Minor”(递增次版本号),一个帮助设置选项的对话框,以及实际的选项:启用/禁用所有功能,仅在生成时递增修订号,或在生成**和**调试(带或不带调试)时递增,以及仅当选定配置为 Release 时才递增。
关注点
Visual Studio 拥有一个扩展的扩展性模型,但其文档却极为稀少。这几行代码花费了大量的试错、反射和跟踪,但最终我达到了一个对其功能满意的程度。
当然,有几件事可能不会让所有人都满意:由于 BeforeExecute
事件发生在编译之前,即使生成因某种原因失败,版本号也会被递增(嗯,除非我们想直接修改结果程序集,否则似乎没有办法解决这个问题,对吧?);将命令添加到工具菜单的做法实际上相当笨拙,但我厌倦了不一致的行为——如果有人能让它像预期的那样工作,请给我一个提示;递增修订号时,当前解决方案中的所有项目都会被处理,这确实不太好;当然,如果有人在此基础上添加更多选项/功能,请在此发布,我将在可能的情况下整合更改。
另请注意,从源代码编译和测试插件可能需要调整生成设置,因为生成后,结果应被复制到 Visual Studio 的一个插件位置,并使用 /resetaddin <addin name> 开关启动 IDE。我的后置生成命令如下所示:
xcopy "$(TargetPath)" "C:\Documents and Settings\[user]\My Documents\
Visual Studio 2005\Addins\" /Y &
xcopy "$(ProjectDir)VAVE.Addin" "C:\Documents and Settings\[user]\My Documents\
Visual Studio 2005\Addins\" /Y
历史
- 2007-10-21:初稿。