The Amadeus.NET Blog

Development, Projects, Plans and Ideas on .NET, Visual Studio, Windows XP & Vista

How to dynamically Import/Export settings in Visual Studio 2005.

Since .vssettings files were first introduced in Visual Studio 2005, I've seen many questions about how the Import/Export settings mechanism can be controlled through code. This was something that bothered me too since VS2005 Beta 1 but it didn't take long till I figure out the solution. I have now created two methods that I use in all VSPackages that I create, that do the job.

The Import/Export mechanism in Visual Studio 2005 can be controlled with the use of a number of interfaces, provided with the Visual Studio SDK. These interfaces are not documented and, for some of them, the usual "This interface supports the VSIP infrastructure and is not intended to be used directly from your code." phrase, is the only info in the documentation.

The interfaces used (in managed code) all come from the Microsoft.VisualStudio.Shell.Interop.8.0.dll assembly and these are:

Microsoft.VisualStudio.Shell.Interop.IVsProfileSettingsTree
Microsoft.VisualStudio.Shell.Interop.IVsSettingsErrorInformation
Microsoft.VisualStudio.Shell.Interop.IVsProfileSettingsFileCollection
Microsoft.VisualStudio.Shell.Interop.IVsProfileSettingsFileInfo

Before proceeding, we must:

i) Be sure that our VSPackage implements at least one of the methods needed to provide VS with the settings we want to import or export. This is either a Microsoft.VisualStudio.Shell.DialogPage class implementing a number of properties, or an implementation of Microsoft.VisualStudio.Shell.IProfileManager. For information on how to create a profile manager by implementing Microsoft.VisualStudio.Shell.IProfileManager in managed code, see How to: Export Settings Using the Managed Package Framework in the Visual Studio SDK documentation. The easiest way however is to add additional properties we wish to save, to an existing Microsoft.VisualStudio.Shell.DialogPage and add a System.ComponentModel.BrowsableAttribute to them, set to False, if we don't want them to appear to the user through the Options window. In any case, a Microsoft.VisualStudio.Shell.ProvideProfileAttribute must be added to our VSPackage class (the class implementing Microsoft.VisualStudio.Shell.Interop.IVsPackage). We must also be sure that the SupportsProfiles property of any Microsoft.VisualStudio.Shell.ProvideOptionPageAttribute attributes added to our VSPackage, is set to True.

<MSVSIP.ProvideOptionPageAttribute(GetType(OptionsPageGeneral), "Amadeus IDEWindowsState", "General", 105, 106, True, SupportsProfiles:=True)> _
<MSVSIP.ProvideProfileAttribute(GetType(OptionsPageGeneral), "Amadeus IDEWindowsState", "IDEWindowsState General Settings", 105, 108, True, GroupName:="Amadeus", GroupResourceID:=104, DescriptionResourceID:=103)> _

ii) Know the path of our settings in the settings file. This is almost the same as the value we use to access our settings through automation. To determine the exact path, we use the info we initially provided to the Microsoft.VisualStudio.Shell.ProvideProfileAttribute. This path is:
[GroupName]\categoryName_objectName
In the example above, the path would be "Amadeus\Amadeus IDEWindowsState_IDEWindowsState General Settings". If we have provided a GroupName (which is a group in the tree of options in the Options window, our options pages are grouped under) as in the example above, we can only provide the GroupName as path, in which case all the settings of our VSPackage will be exported.

I will now provide the code of two methods (in Visual Basic) that can be used to import/export settings through code.

The first method is:

 Function ExportSettings(Optional ByVal externalFile As String = "", Optional ByVal settingsPath As String = "") As Boolean
 externalFile: The name of the file where our settings will be saved. Not a path and without the extension. If not defined, settingsPath is ignored and all VS settings will be exported in the default file (usually currentsettings.vssetings).
 settingsPath: The path to our settings in the settings file. (see above)

   1:  #Region " ExportSettings "
   2:      Friend Function ExportSettings(Optional ByVal externalFile As String = "", Optional ByVal settingsPath As String = "") As Boolean
   3:          Try
   4:              ' The profile manager that provides programmatic control of settings.
   5:              Dim prf As IVsProfileDataManager = CType(m_Package.GetVSService(GetType(SVsProfileDataManager)), _
   6:                                                      IVsProfileDataManager)
   7:   
   8:              ' All the settings in a .vssettings file.
   9:              Dim sets As IVsProfileSettingsTree = Nothing
  10:              ' The specific settings we wish to export.
  11:              Dim wl As IVsProfileSettingsTree = Nothing
  12:              ' Interface used to retrieve progress status.
  13:              Dim ret As IVsSettingsErrorInformation = Nothing
  14:              ' Variable that holds the extension of .vssettings files.
  15:              Dim ext As String = ""
  16:              ' The path to the default directory that contains the files.
  17:              Dim path As String = ""
  18:              ' The name of the file were settings will be exported.
  19:              Dim file As String = ""
  20:   
  21:              If prf IsNot Nothing Then
  22:                  m_Package.ShowFeedback("Exporting Settings...")
  23:   
  24:                  With prf
  25:                      ' Get the path to the default directory that contains the settings files.
  26:                      ' This is the one defined by the user in Tools->Options->Environment->
  27:                      ' General->Import and Export Settings.
  28:                      .GetDefaultSettingsLocation(path)
  29:                      ' Get the standard extension of the settings file.
  30:                      .GetSettingsFileExtension(ext)
  31:                      ' Get all the settings that can be exported at this moment.
  32:                      ' This will call all properties in any DialogPage classes of the
  33:                      ' VSPackage.
  34:                      .GetSettingsForExport(sets)
  35:   
  36:                      ' If the caller has provided a filename, we use this.
  37:                      ' Otherwise we use currentsettings which is the default file
  38:                      ' VS uses to store settings.
  39:                      If externalFile <> "" Then
  40:                          If Not PackageUtilities.ContainsInvalidFileNameChars(externalFile) Then
  41:                              file = externalFile
  42:                          Else
  43:                              Throw New ArgumentException("Invalid file name specified.", _
  44:                                                          "externalFile")
  45:                          End If
  46:   
  47:                          ' If the caller only wishes to export specific settings
  48:                          ' (like the settings of this VSPackage), we need to disable
  49:                          ' exporting of any other settings and enable only the settings
  50:                          ' we want to export. Otherwise, all VS settings will be exported.
  51:                          If settingsPath <> "" Then
  52:                              ' First disable all settings.
  53:                              sets.SetEnabled(0, 1)
  54:                              ' Find the specified child tree of settings.
  55:                              ' (The settings file has XML structure.)
  56:                              sets.FindChildTree(settingsPath, wl)
  57:                              If wl IsNot Nothing Then
  58:                                  ' If the child tree is found, enable it.
  59:                                  wl.SetEnabled(1, 1)
  60:                              Else
  61:                                  ' The specified settings path, does not exist in the
  62:                                  ' collection of settings retrieved from all VSPackages.
  63:                                  Throw New ArgumentException("Settings path does not exist.", _
  64:                                                              "settingsPath")
  65:                              End If
  66:                          End If
  67:                      Else
  68:                          ' The caller has not specified a specific filename.
  69:                          ' We will export all settings to the default file. We will
  70:                          ' not use this file for specific settings. VS uses this file
  71:                          ' to store all settings.
  72:                          file = "currentsettings"
  73:                      End If
  74:   
  75:                      ' Export settings. Success/Fail info can be retrieved
  76:                      ' from the IVsSettingsErrorInformation interface we provide
  77:                      ' in the ppsettingsErrorInformation argument. But the call
  78:                      ' will also return an error code we can use to determine errors.
  79:                      Dim retCode As Int32 = .ExportSettings((path & file & ext), sets, ret)
  80:                      ' Now we can check success.
  81:                      Dim ex As Exception = Marshal.GetExceptionForHR(retCode)
  82:   
  83:                      ' You can respond to possible failure, anyway you want.
  84:                      If ex IsNot Nothing Then
  85:                          My.TraceInfo.Write(CStr(ex.Message))
  86:                          m_Package.ShowFeedback("Exporting Settings..." & ex.Message)
  87:                          Return False
  88:                      Else
  89:                          m_Package.ShowFeedback("Exporting Settings...Complete.")
  90:                          Return True
  91:                      End If
  92:                  End With
  93:              Else
  94:                  ' The profile manager was not obtained.
  95:                  Return False
  96:              End If
  97:          Catch exM As Exception
  98:              ' Any other exceptions?
  99:              m_Package.ShowFeedback("Exporting Settings..." & exM.Message)
 100:              My.TraceInfo.Write(CStr(exM.Message))
 101:              Return False
 102:          End Try
 103:      End Function
 104:  #End Region

The second method is:

 Function ImportSettings(Optional ByVal externalFile As String = "", Optional ByVal settingsPath As String = "") As Boolean
 externalFile: The name of the file where our settings will be saved. Not a path and without the extension. If not defined, our settings will be retrieved from the default file VS uses to store settings (usually currentsettings.vssetings).
 settingsPath: The path to our settings in the settings file. (see above)

   1:  #Region " ImportSettings "
   2:      Friend Function ImportSettings(Optional ByVal externalFile As String = "", Optional ByVal settingsPath As String = "") As Boolean
   3:          Try
   4:              ' The profile manager that provides programmatic control of settings.
   5:              Dim prf As IVsProfileDataManager = CType(m_Package.GetVSService(GetType(SVsProfileDataManager)), _
   6:                                                      IVsProfileDataManager)
   7:   
   8:              ' All the settings in a .vssettings file.
   9:              Dim sets As IVsProfileSettingsTree = Nothing
  10:              ' The specific settings we wish to import.
  11:              Dim wl As IVsProfileSettingsTree = Nothing
  12:              ' Interface used to retrieve progress status.
  13:              Dim ret As IVsSettingsErrorInformation = Nothing
  14:              ' The collection of .vssettings files in a directory.
  15:              Dim col As IVsProfileSettingsFileCollection = Nothing
  16:              ' Info of a .vssettings file.
  17:              Dim prfInfo As IVsProfileSettingsFileInfo = Nothing
  18:              ' Variable that holds the extension of settings files.
  19:              Dim ext As String = ""
  20:              ' The number of .vssettings files in directory.
  21:              Dim setFiles As Int32
  22:              ' The path of the .vssettings file that contains the settings we wish to import.
  23:              Dim setFile As String = ""
  24:              ' The name of the file that contains the settings we wish to import.
  25:              Dim file As String = ""
  26:   
  27:              If prf IsNot Nothing Then
  28:                  ' If the caller has provided a filename, we use this.
  29:                  ' Otherwise we use currentsettings which is the default file
  30:                  ' VS uses to store settings.
  31:                  If externalFile <> "" Then
  32:                      file = externalFile
  33:                  Else
  34:                      file = "currentsettings"
  35:                  End If
  36:   
  37:                  ' Get the standard extension of the settings file.
  38:                  prf.GetSettingsFileExtension(ext)
  39:                  ' Get the collection of settings files in the directory defined by the 
  40:                  ' user in Tools->Options->Environment->General->Import and Export Settings.
  41:                  prf.GetSettingsFiles(CUInt(ShellInterop.__VSPROFILELOCATIONS.PFL_SettingsDir), col)
  42:                  ' Get the number of files in the collection.
  43:                  col.GetCount(setFiles)
  44:   
  45:                  ' Enumerate the collection to find the requested file.
  46:                  For ii As Int32 = 0 To setFiles
  47:                      col.GetSettingsFile(ii, prfInfo)
  48:   
  49:                      If prfInfo IsNot Nothing Then
  50:                          prfInfo.GetFilePath(setFile)
  51:                      End If
  52:   
  53:                      If setFile.Contains(file & ext) Then
  54:                          Exit For
  55:                      End If
  56:                  Next
  57:   
  58:                  ' If the file is found, we can proceed.
  59:                  If prfInfo IsNot Nothing Then
  60:                      Dim retCode As Int32
  61:   
  62:                      ' Get all the settings available in the file.
  63:                      prfInfo.GetSettingsForImport(sets)
  64:   
  65:                      ' If the caller does not request for specific settings,
  66:                      ' proceed with importing all the settings in the file.
  67:                      ' Otherwise we will search for the specified child tree of
  68:                      ' settings in the file. (The settings file has XML structure.)
  69:                      If settingsPath = "" Then
  70:                          ' Import settings. Success/Fail info can be retrieved
  71:                          ' from the IVsSettingsErrorInformation interface we provide
  72:                          ' in the ppsettingsErrorInformation argument. But the call
  73:                          ' will also return an error code we can use to determine errors.
  74:                          retCode = prf.ImportSettings(sets, ret)
  75:                      Else
  76:                          ' Find the specified child tree.
  77:                          sets.FindChildTree(settingsPath, wl)
  78:   
  79:                          If wl IsNot Nothing Then
  80:                              ' Import settings. Success/Fail info can be retrieved
  81:                              ' from the IVsSettingsErrorInformation interface we provide
  82:                              ' in the ppsettingsErrorInformation argument. But the call
  83:                              ' will also return an error code we can use to determine errors.
  84:                              retCode = prf.ImportSettings(wl, ret)
  85:                          Else
  86:                              ' The specified settings path, does not exist in this file.
  87:                              Throw New ArgumentException("Settings path does not exist.", _
  88:                                                          "settingsPath")
  89:                          End If
  90:                      End If
  91:   
  92:                      ' Now we can check success.
  93:                      Dim ex As Exception = Marshal.GetExceptionForHR(retCode)
  94:   
  95:                      ' You can respond to possible failure, anyway you want.
  96:                      If ex IsNot Nothing Then
  97:                          My.TraceInfo.Write(CStr(ex.Message))
  98:                          Return False
  99:                      Else
 100:                          Return True
 101:                      End If
 102:                  Else
 103:                      ' The specified settings file could not be found in the settings directory.
 104:                      Throw New ArgumentException("Invalid file name specified.", _
 105:                                                  "externalFile")
 106:                  End If
 107:              Else
 108:                  ' The profile manager was not obtained.
 109:                  Return False
 110:              End If
 111:          Catch exM As Exception
 112:              ' Any other exceptions?
 113:              My.TraceInfo.Write(CStr(exM.Message))
 114:              Return False
 115:          End Try
 116:      End Function
 117:  #End Region


OK, now these will do the job, as long as the settings have been properly implemented in our VSPackage. These may also be changed to accommodate your needs. Some methods seen in these example ( like My.TraceInfo.Write() or m_Package.ShowFeedback() ) are only used for simplicity. They are implemented by the VSPackage implementer and cannot be found in the interop assemblies provided with Visual Studio SDK.

Comments

Yitzhak Gootvilig's Blog said:

# November 23, 2006 7:33 AM

toby said:

THANK YOU for picking up the slack where Microsoft left off! Why is it the simple things that are so hard to document?? :)

# December 12, 2006 1:46 PM

toby said:

I don't suppose you've seen anything in the IVsProfileSettingsTree interface that would allow me to "export" settings into memory, then "import" them again later, without writing them to file first? I want to write a package that allows modifying user settings, then restores them later. Oddly enough, if I call prf.GetSettingsForExport(sets), then later try to call prf.ImportSettings(sets, ret), then when I marshal the HRESULT exception I get "Specified cast is not valid", and no action is taken. Either way, thanks again for your help!
# December 12, 2006 2:42 PM

Alexander Kant said:

Hi,

nice post.

I've found an easier way to import vssettings, e.g. by an addin.

/// <summary>

/// imports the vssettings

/// </summary>

/// <param name="settings">path and filename of the settings, e.g. "C:\Users\[currentuser]\Documents\Visual Studio 2008\Settings\mysettings.vssettings"</param>

/// <param name="application">DTE2-Object (VS 2008)</param>

public void ImportSettings(string settings, DTE2 application) {

   application.ExecuteCommand("Tools.ImportandExportSettings", "/import:" + settings);

}

/// <summary>

/// exports the vssettings

/// </summary>

/// <param name="settings">path and filename of the settings, e.g. "C:\Users\[currentuser]\Documents\Visual Studio 2008\Settings\mysettings.vssettings"</param>

/// <param name="application">DTE2-Object (VS 2008)</param>

public void ExportSettings(string settings, DTE2 application) {

   application.ExecuteCommand("Tools.ImportandExportSettings", "/export:" + settings);

}

# November 18, 2009 8:52 AM
Leave a Comment

(required) 

(required) 

(optional)

(required) 


Please add 6 and 1 and type the answer here: