Upgrading Projects
Changes to the project model from one version of Visual Studio to the next may require that projects and solutions be upgraded so that they can run on the newer version. The Visual Studio SDK provides interfaces that can be used to implement upgrade support in your own projects.
Upgrade Strategies
To support an upgrade, your project system implementation must define and implement an upgrade strategy. In determining your strategy, you can choose to support side-by-side (SxS) backup, copy backup, or both.
SxS backup means that a project copies only those files that need upgrading in place, adding a suitable file name suffix, for example, ".old".
Copy backup means that a project copies all project items to a user-provided backup location. The relevant files at the original project location are then upgraded.
How Upgrade Works
When a solution created in an earlier version of Visual Studio is opened in a newer version, the IDE checks the solution file to determine if it needs to be upgraded. If upgrading is required, the Upgrade Wizard is automatically launched to walk the user through the upgrade process.
When a solution needs upgrading, it queries each project factory for its upgrade strategy. The strategy determines whether the project factory supports copy backup or SxS backup. The information is sent to the Upgrade Wizard, which collects the information required for the backup and presents the applicable options to the user.
Multi-Project Solutions
If a solution contains multiple projects and the upgrade strategies differ, such as when a C++ project that only supports SxS backup and a Web project that only support copy backup, the project factories must negotiate the upgrade strategy.
The solution queries each project factory for IVsProjectUpgradeViaFactory. It then calls UpgradeProject_CheckOnly to see if global project files need upgrading and to determine the supported upgrade strategies. The Upgrade Wizard is then invoked.
After the user completes the wizard, UpgradeProject is called on each project factory to perform the actual upgrade. To facilitate backup, IVsProjectUpgradeViaFactory methods provide the SVsUpgradeLogger service to log the details of the upgrade process. This service cannot be cached.
After updating all relevant global files, each project factory can choose to instantiate a project. The project implementation must support IVsProjectUpgrade. The UpgradeProject method is then called to upgrade all relevant project items.
Note
The UpgradeProject method does not provide the SVsUpgradeLogger service. This service can be obtained by calling QueryService.
Best Practices
Use the SVsQueryEditQuerySave service to check if you can edit a file before editing it, and can save it before saving it. This will help your backup and upgrade implementations handle project files under source control, files with insufficient permissions, and so forth.
Use the SVsUpgradeLogger service during all phases of backup and upgrade to provide information on the success or failure of the upgrade process.
For more information about backing up and upgrading projects, see the comments for IVsProjectUpgrade in vsshell2.idl.
Upgrading Custom Projects
If you change the information persisted in the project file between different Visual Studio versions of your product, then you need to support upgrading your project file from the old to the new version. To support upgrading that allows you to participate in the Visual Studio Conversion Wizard, implement the IVsProjectUpgradeViaFactory interface. This interface contains the only mechanism available for copy upgrading. The upgrading of the project happens as a part of the solution opens. The IVsProjectUpgradeViaFactory interface is implemented by the project factory, or should at least be obtainable from the project factory.
The old mechanism that uses the IVsProjectUpgrade interface is still supported, but conceptually upgrades the project system as a part of the project open. The IVsProjectUpgrade interface is therefore called by the Visual Studio environment even if the IVsProjectUpgradeViaFactory interface is called or implemented. This approach allows you to use IVsProjectUpgradeViaFactory to implement the copy and project only portions of the upgrade, and delegate the rest of the work to be done in-place (possibly at the new location) by the IVsProjectUpgrade interface.
For a sample implementation of IVsProjectUpgrade, see VSSDK Samples.
The following scenarios arise with project upgrades:
If the file is of a newer format than the project can support, then it must return an error stating this. This assumes that the older version of your product includes code to check for the version.
If the PUVFF_SXSBACKUP flag is specified in the UpgradeProject method, the upgrade is going to be implemented as an in-place upgrade prior to the opening of the project.
If the PUVFF_COPYBACKUP flag is specified in the UpgradeProject method, the upgrade is implemented as a copy upgrade.
If the UPF_SILENTMIGRATE flag is specified in the UpgradeProject call, then the user has been prompted by the environment to upgrade the project file as an in-place upgrade, after the project is opened. For example, the environment prompts the user to upgrade when the user opens an older version of the solution.
If the UPF_SILENTMIGRATE flag is not specified in the UpgradeProject call, then you must prompt the user to upgrade the project file.
The following is an example upgrade prompt message:
"The project '%1' was created with an older version of Visual Studio. If you open it with this version of Visual Studio, you may not be able to open it with older versions of Visual Studio. Do you want to continue and open this project?"
To implement IVsProjectUpgradeViaFactory
Implement the method of the IVsProjectUpgradeViaFactory interface, specifically the UpgradeProject method in your project factory implementation, or make the implementations callable from your project factory implementation.
If you want to do an in-place upgrade as a part of the solution opening, supply the flag PUVFF_SXSBACKUP as the
VSPUVF_FLAGS
parameter in your UpgradeProject implementation.If you want to do an in-place upgrade as a part of the solution opening, supply the flag PUVFF_COPYBACKUP as the
VSPUVF_FLAGS
parameter in your UpgradeProject implementation.For both the steps 2 and 3, the actual file upgrade steps, using IVsQueryEditQuerySave2, can be implemented as described in the "Implementing
IVsProjectUpgade
" section below, or you can delegate the actual file upgrade to IVsProjectUpgrade.Use the methods of IVsUpgradeLogger to post upgrade related messages for the user using Visual Studio Migration Wizard.
IVsFileUpgrade interface is used to implement any kind of file upgrade that needs to happen as part of project upgrade. This interface is not called from IVsProjectUpgradeViaFactory, but is supplied as a mechanism to upgrade files that are part of the project system, but the main project system might not be directly aware of. For example, this situation could arise if the compiler related files and properties are not handled by the same development team that handles the rest of the project system.
IVsProjectUpgrade Implementation
If your project system implements IVsProjectUpgrade only, it can't participate in the Visual Studio Conversion Wizard. However, even if you implement the IVsProjectUpgradeViaFactory interface, you can still delegate the file upgrade to IVsProjectUpgrade implementation.
To implement IVsProjectUpgrade
When a user attempts to open a project, the UpgradeProject method is called by the environment after the project is opened and before any other user action can be taken on the project. If the user had already been prompted to upgrade the solution, then the UPF_SILENTMIGRATE flag is passed in the
grfUpgradeFlags
parameter. If the user opens a project directly, such as by using the Add Existing Project command, then the UPF_SILENTMIGRATE flag is not passed and the project needs to prompt the user to upgrade.In response to the UpgradeProject call, the project must evaluate whether the project file is upgraded. If the project does not need to upgrade the project type to a new version, then it can simply return the S_OK flag.
If the project needs to upgrade the project type to a new version, then it must determine whether the project file can be modified by calling the QueryEditFiles method and passing in a value of tagVSQueryEditFlags for the
rgfQueryEdit
parameter. The project then needs to do the following:If the
VSQueryEditResult
value returned in thepfEditCanceled
parameter is QER_EditOK, then the upgrade can proceed because the project file can be written.If the
VSQueryEditResult
value returned in thepfEditCanceled
parameter is QER_EditNotOK and theVSQueryEditResult
value has the QER_ReadOnlyNotUnderScc bit set, then UpgradeProject must return failure, because users should resolve the permissions issue themselves. The project should then do the following:Report the error to the user by calling ReportErrorInfo and return the VS_E_PROJECTMIGRATIONFAILED error code to IVsProjectUpgrade.
If the
VSQueryEditResult
value is QER_EditNotOK and theVSQueryEditResultFlags
value has the QER_ReadOnlyUnderScc bit set, then the project file should be checked out by calling QueryEditFiles (QEF_ForceEdit_NoPrompting, QEF_DisallowInMemoryEdits,...).
If the QueryEditFiles call on the project file causes the file to be checked out, and the latest version to be retrieved, then the project is unloaded and reloaded. The UpgradeProject method is called again once another instance of the project is created. On this second call, the project file can be written to disk; it is recommended that the project save a copy of the project file in the previous format with an .OLD extension, make its necessary upgrade changes, and save the project file in the new format. Again, if any part of the upgrade process fails, the method must indicate failure by returning VS_E_PROJECTMIGRATIONFAILED. This causes the project to be unloaded in Solution Explorer.
It is important to understand the complete process that occurs in the environment for the case in which the call to the QueryEditFiles method (specifying a value of ReportOnly) returns the QER_EditNotOK and the QER_ReadOnlyUnderScc flags.
The user attempts to open the project file.
The environment calls your CanCreateProject implementation.
If CanCreateProject returns
true
, then the environment calls your CanCreateProject implementation.The environment calls your Load implementation to open the file and initialize the project object, for example, Project1.
The environment calls your
IVsProjectUpgrade::UpgradeProject
implementation to determine whether the project file needs to be upgraded.You call QueryEditFiles and pass in a value of QEF_ReportOnly for the
rgfQueryEdit
parameter.The environment returns QER_EditNotOK for
VSQueryEditResult
and the QER_ReadOnlyUnderScc bit is set inVSQueryEditResultFlags
.Your IVsProjectUpgrade implementation calls
IVsQueryEditQuerySave::QueryEditFiles
(QEF_ForceEdit_NoPrompting, QEF_DisallowInMemoryEdits).
This call may cause a new copy of your project file to be checked out and the latest version retrieved, as well as a need to reload your project file. At this point, one of two things happen:
If you handle your own project reload, then the environment calls your ReloadItem (VSITEMID_ROOT) implementation. When you receive this call, reload the first instance of your project (Project1) and continue upgrading your project file. The environment knows that you handle your own project reload if you return
true
for GetProperty (VSHPROPID_HandlesOwnReload).If you do not handle your own project reload, then you return
false
for GetProperty (VSHPROPID_HandlesOwnReload). In this case, before QueryEditFiles(QEF_ForceEdit_NoPrompting, QEF_DisallowInMemoryEdits) returns, the environment creates another new instance of your project, for example, Project2, as follows:The environment calls Close on your first project object, Project1, thus placing this object in the inactive state.
The environment calls your
IVsProjectFactory::CreateProject
implementation to create a second instance of your project, Project2.The environment calls your
IPersistFileFormat::Load
implementation to open the file and initialize the second project object, Project2.The environment calls
IVsProjectUpgrade::UpgradeProject
for a second time to determine whether the project object should be upgraded. However, this call is made on a new, second, instance of the project, Project2. This is the project that is opened in the solution.Note
In the instance that your first project, Project1, is placed in the inactive state, then you must return S_OK from the first call to your UpgradeProject implementation.
You call QueryEditFiles and pass in a value of QEF_ReportOnly for the
rgfQueryEdit
parameter.The environment returns QER_EditOK and the upgrade can proceed because the project file can be written.
If you fail to upgrade, return VS_E_PROJECTMIGRATIONFAILED from IVsProjectUpgrade::UpgradeProject
. If no upgrade is necessary or you choose not to upgrade, treat the IVsProjectUpgrade::UpgradeProject
call as a no-op. If you return VS_E_PROJECTMIGRATIONFAILED, a placeholder node is added to the solution for your project.
Upgrading Project Items
If you add or manage items inside project systems you do not implement, you may need to participate in the project upgrade process. Crystal Reports is an example of an item that can be added to the project system.
Typically, project item implementers want to leverage an already fully instantiated and upgraded project because they need to know what the project references are and what other project properties are there to make an upgrade decision.
To get the project upgrade notification
Set the SolutionOrProjectUpgrading flag (defined in vsshell80.idl) in your project item implementation. This causes your project item VSPackage to auto load when the Visual Studio shell determines that a project system is in the process of upgrading.
Advise the IVsSolutionEventsProjectUpgrade interface via the AdviseSolutionEvents method.
The IVsSolutionEventsProjectUpgrade interface is fired after the project system implementation has completed its upgrade operations and the new upgraded project is created. Depending on the scenario, the IVsSolutionEventsProjectUpgrade interface is fired after the OnAfterOpenSolution, the OnAfterOpenProject, or the OnAfterLoadProject methods.
To upgrade the project item files
You must carefully manage the file backup process in your project item implementation. This applies in particular for a side-by-side backup, where the
fUpgradeFlag
parameter of the UpgradeProject method is set to PUVFF_SXSBACKUP, where files that had been backed up are placed along side files that are designated as ".old". The backed up files older than the system time when the project was upgraded can be designated as stale. Furthermore, they might be overwritten unless you take specific steps to prevent this.At the time your project item gets a notification of the project upgrade, the Visual Studio Conversion Wizard is still displayed. Therefore, you should use the methods of the IVsUpgradeLogger interface to provide upgrade messages to the wizard UI.