How to: Upgrade Project Systems
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 Visual Studio Extensibility 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 — for example, Visual Studio .NET 2003 — 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 not 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 QEF_ReportOnly for the rgfQueryEdit parameter. The project then needs to do the following:
If the VSQueryEditResult value returned in the pfEditCanceled parameter is QER_EditOK, then the upgrade can proceed because the project file can be written.
If the VSQueryEditResult value returned in the pfEditCanceled parameter is QER_EditNotOK and the VSQueryEditResult 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 the VSQueryEditResultFlags 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 in VSQueryEditResultFlags.
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. See Basic Project for an implementation of IVsProjectUpgrade::UpgradeProject.
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.