How To: Use Macros to Configure Publish Settings
There are all sorts of publishing properties available to the Visual Studio developer. Visual Studio tries to set the properties to reasonable default values. But with all things that are defaulted, the values are right for some people, but wrong for others. In my day-to-day work, where I run through publishing quite a bit, there are quite a few values I want to reset. Its possible to click through the UI to change the values, but who has a couple of seconds to spare to open dialogs and click on stuff? Not I; every second lost is a second I could spend writing a blog entry. Fortunately, its easy to create a macro that will set these values for you. In this post, I'll write about you can use a macro to set these default values through the DTE.
Displaying publish properties and values
Let's start by viewing all available publishing properties. Before going too far, let's show some helper functions to accomplish our tasks. Here is some code I copied from my macro explorer:
Function GetPublishProperties() As EnvDTE.Properties
Dim proj As Project = DTE.Solution.Projects.Item(1)
If proj Is Nothing Then
ShowError("Unable to retrieve a project", "GetPublishProperties")
Return Nothing
End If
Dim publishProperty As EnvDTE.Property = proj.Properties.Item("Publish")
If publishProperty IsNot Nothing Then
Dim publishProperties As EnvDTE.Properties = TryCast(publishProperty.Value, EnvDTE.Properties)
Return publishProperties
End If
ShowError("Unable to get publish properties of project " & proj.Name, "GetPublishProperties")
Return Nothing
End Function
Sub ShowError(ByVal message As String, ByVal title As String)
MsgBox(message, MsgBoxStyle.Exclamation Or MsgBoxStyle.OkOnly, title)
End Sub
Sub ShowException(ByVal ex As Exception, ByVal methodName As String)
Dim message As String = ex.Message
If ex.InnerException IsNot Nothing Then
message = ex.InnerException.Message
End If
ShowError(message, methodName)
End Sub
GetPublishProperties
is a function to retrieve the object which contains the publish properties. It grabs the first project in the solution, and selects the "Publish" property from there. Next, the resultant property value is converted to another properties container. Some error handling is also included (although probably not enough).
With that as a baseline, this macro will show all of the publish properties, types, and values:
Sub ShowPublishProperties()
Dim publishProperties As EnvDTE.Properties = GetPublishProperties()
If publishProperties IsNot Nothing Then
Dim sb As New System.Text.StringBuilder()
For Each prop As EnvDTE.Property In publishProperties
sb.Append(String.Format("{0} [{1}]: {2}", prop.Name, prop.Value.GetType().ToString(), prop.Value.ToString()))
sb.Append(vbCrLf)
Next
MsgBox(sb.ToString())
End If
End Sub
This macro simply iterates through all of the publish properties, gathers the property names, types, and values and show them all in a message box. Here is a subset of the output:
PublisherName [System.String]:
OpenBrowserOnPublish [System.Boolean]: True
BootstrapperComponentsLocation [System.Int32]: 0
PublishFiles [System.__ComObject]: System.__ComObject
Most of the values are strings
, ints
, or booleans
. If you were to look in the project file, you would see that these values correspond to properties within the file. The exception are PublishFiles
and BootstrapperPackages
, which correspond to the Items in the project file, as well as the the lists within the Application Files dialog and Prerequisites dialog. These will be explored further in a bit. But first, let's try setting some easy values.
Setting Property values
Setting a value is as easy as reading a value. For example, to set the PublisherName
property, one could write a macro like this:
Sub SetPublisherName()
Dim publishProperties As EnvDTE.Properties = GetPublishProperties()
If publishProperties IsNot Nothing Then
Dim publisherNameProperty As EnvDTE.Property = publishProperties.Item("PublisherName")
publisherNameProperty.Value = "Test Value"
End If
End Sub
Some properties perform validation when the values are set. For example, the PublishUrl
:
Sub SetPublishUrl()
Dim publishProperties As EnvDTE.Properties = GetPublishProperties()
If publishProperties IsNot Nothing Then
Try
publishProperties.Item("PublishUrl").Value = ""
Catch ex As Exception
ShowException(ex, "SetPublishUrl")
End Try
End If
End Sub
In the above example, an attempt is made to blank out the PublishUrl
property. However, the PublishUrl
can not be set to an empty string, as the thrown exception indicates:
An empty string is not allowed for property 'Publish Location'.
The thrown message uses "Publish Location" because that is what the label for the text box which corresponds to this property uses. In fact, the property page is pretty much setting this property in the exact same manner as the macro.
Modifying Items
There are 2 different types of Items
exposed by the Publish
Properties: PublishFiles
and BootstrapperPackages
. Here is a macro that does something with PublishFiles
:
Sub ListFiles()
Dim publishProperties As EnvDTE.Properties = GetPublishProperties()
If publishProperties IsNot Nothing Then
Dim filesObject As Object = publishProperties.Item("PublishFiles")
If filesObject Is Nothing Then
ShowError("Could not get PublishFiles object", "ListFiles")
Return
End If
Try
Dim numFiles As Integer
numFiles = filesObject.Value.Item("Count").Value
Dim sb As New System.Text.StringBuilder()
For i = 0 To numFiles - 1
'display file name and publish status.
sb.AppendLine("File Name=" & filesObject.Object.Item(i).Name & ", Status=" & filesObject.Object.Item(i).PublishStatus)
Next
MsgBox(sb.ToString(), MsgBoxStyle.DefaultButton1, "Application Files")
Catch ex As Exception
ShowException(ex, "ListFiles")
End Try
End If
End Sub
This macro relies on latebinding in doing its work: filesObject
is declared as Object, yet it uses properties from that object. The filesObject.Object
contains an accessor to a collection; there are actually 2 ways to get a value out of the collection: one could use either a string to get a file by name, or an index, like what was done above. Here is some sample output from running this macro:
File Name=WindowsApplication2.exe, Status=0
File Name=WindowsApplication2.pdb, Status=0
File Name=WindowsApplication2.xml, Status=0
The "Status" of the file corresponds to the Publish Status column in the Application Files dialog. 0 corresponds to the "(Auto)" value.
Accessing the bootstrapper packages is similar:
Sub IncludeAllPrerequisites()
Dim publishProperties As EnvDTE.Properties = GetPublishProperties()
Dim bootstrapperPackages As Object = publishProperties.Item("BootstrapperPackages")
If bootstrapperPackages IsNot Nothing Then
Dim numPackages As Integer = bootstrapperPackages.Value.Item("Count").Value
For i As Integer = 0 To numPackages - 1
bootstrapperPackages.Object.Item(i).Install = True
Next
End If
End Sub
Sub ExcludeWindowsInstaller31()
Dim publishProperties As EnvDTE.Properties = GetPublishProperties()
Dim bootstrapperPackages As Object = publishProperties.Item("BootstrapperPackages")
If bootstrapperPackages IsNot Nothing Then
Dim windowsInstaller31Package As Object = bootstrapperPackages.Object.Item("Microsoft.Windows.Installer.3.1")
If windowsInstaller31Package IsNot Nothing Then
windowsInstaller31Package.Install = False
End If
End If
End Sub
The first macro includes all available prerequisites when publishing. In the first example, the packages are accessed by index. The second example gets a bootstrapper package by using a specific product code.
Publishing via a macro
Finally, just like it is possible to build from a macro, it is possible to publish as well. Here is an example that does something similar to what the Publish Now button does on the Publish property page:
Sub Publish()
Dim proj As Project = DTE.Solution.Projects.Item(1)
Dim sb2 As EnvDTE80.SolutionBuild2 = CType(DTE.Solution.SolutionBuild, EnvDTE80.SolutionBuild2)
Dim config2 = CType(sb2.ActiveConfiguration, EnvDTE80.SolutionConfiguration2)
Dim configName = String.Format("{0}|{1}", config2.Name, config2.PlatformName)
sb2.BuildProject(configName, proj.UniqueName, True)
sb2.PublishProject(configName, proj.UniqueName, True)
End Sub
Ideally, the macro should verify that the build succeeded before publishing, but that has been left as an exercise for the reader.
Comments
- Anonymous
May 08, 2009
Hi Im trying this to publish WCF service project but, Im receiving message saying that I need to reinstall Visual Studio to publish my application. When I right click on the project this message do not appear and publish action occurs ok. I saw that this property exists, but I could not get it to change its local path to publish in other locations. Could you help me?