升级项目

将项目模型从一个版本的 Visual Studio 更改为下一个版本时,可能需要升级项目和解决方案,以便它们可以在较新版本上运行。 Visual Studio SDK 提供可用于在你自己的项目中实现升级支持的接口。

升级策略

若要支持升级,项目系统实现必须定义和实施升级策略。 在确定策略时,可以选择支持并行 (SxS) 备份、复制备份或同时支持这两种备份方式。

  • SxS 备份是指项目仅复制需要就地升级的文件,并添加合适的文件名后缀,例如“.old”。

  • 复制备份是指项目将所有项目项复制到用户提供的备份位置, 然后升级原始项目位置的相关文件。

升级的运作方式

在较新版本中打开在较早版本的 Visual Studio 中创建的解决方案时,IDE 会检查解决方案文件以确定是否需要升级。 如果需要升级,升级向导将会自动启动,引导用户完成升级过程。

当解决方案需要升级时,它会查询每个项目工厂的升级策略。 根据该策略确定项目工厂是支持复制备份还是 SxS 备份。 此信息将发送到升级向导,该向导收集备份所需的信息,并向用户显示适用的选项。

多项目解决方案

如果解决方案包含多个项目且升级策略有所不同,例如,包含仅支持 SxS 备份的 C++ 项目和仅支持复制备份的 Web 项目时,项目工厂必须协商升级策略。

解决方案会查询每个项目工厂的 IVsProjectUpgradeViaFactory。 然后,它会调用 UpgradeProject_CheckOnly,以查看全局项目文件是否需要升级并确定支持的升级策略。 然后调用升级向导

用户完成向导后,将对每个项目工厂调用 UpgradeProject 以执行实际升级。 为了便于备份,IVsProjectUpgradeViaFactory 方法提供 SVsUpgradeLogger 服务来记录升级过程的详细信息。 无法缓存此服务。

更新所有相关的全局文件后,每个项目工厂可以选择实例化项目。 项目实现必须支持 IVsProjectUpgrade。 然后调用 UpgradeProject 方法以升级所有相关的项目项。

注意

UpgradeProject 方法不提供 SVsUpgradeLogger 服务。 可以通过调用 QueryService 来获取此服务。

最佳方案

使用 SVsQueryEditQuerySave 服务检查你是否可以在编辑之前编辑文件,以及在保存之前保存文件。 这样有助于备份和升级实现处理源控件下的项目文件以及权限不足的文件等。

在备份和升级的所有阶段使用 SVsUpgradeLogger 服务,以提供升级过程成功或失败的相关信息。

有关备份和升级项目的详细信息,请参阅 IVsProjectUpgrade in vsshell2.idl 的注释。

升级自定义项目

如果你更改保留在产品的不同 Visual Studio 版本之间的项目文件中的信息,则你需要支持将项目文件从旧版本升级到新版本。 若要支持可让你使用 Visual Studio 转换向导的升级,请实现 IVsProjectUpgradeViaFactory 接口。 此接口包含可用于复制升级的唯一机制。 项目的升级作为解决方案打开的一部分而发生。 IVsProjectUpgradeViaFactory 接口由项目工厂实现或至少应从项目工厂获得。

仍支持使用 IVsProjectUpgrade 接口的旧机制,但该机制将会把项目系统作为项目打开的一部分进行概念上的升级。 因此即使调用或实现了 IVsProjectUpgradeViaFactory 接口,IVsProjectUpgrade 接口仍由 Visual Studio 环境调用。 此方法允许你使用 IVsProjectUpgradeViaFactory 来实现复制、仅规划升级的部分并通过 IVsProjectUpgrade 接口委派要就地(可能在新位置)完成的剩余工作。

有关实现 IVsProjectUpgrade 的示例,请参阅 VSSDK 示例

项目升级时出现下列情况:

  • 如果文件是项目不能支持的较新格式,则它必须返回一个指出此情况的错误。 这假定你产品的较旧版本包含检查版本的代码。

  • 如果在 UpgradeProject 方法中指定 PUVFF_SXSBACKUP 标志 ,则升级将在打开项目前作为就地升级而实现。

  • 如果在 UpgradeProject 方法中指定 PUVFF_COPYBACKUP 标志,则升级将作为复制升级而实现。

  • 如果在 UpgradeProject 调用中指定 UPF_SILENTMIGRATE 标志,则在项目打开后环境会提示用户以就地升级的方式对项目文件进行升级。 例如,当用户打开解决方案的较旧版本时,环境会提示用户升级。

  • 如果在 UpgradeProject 调用中未指定 UPF_SILENTMIGRATE 标志,则必须提示用户升级项目文件。

    下面是一个升级提示消息示例:

    “项目“%1”由 Visual Studio 的较旧版本创建。 如果使用此版本的 Visual Studio 打开它,你将不能用较旧版本的 Visual Studio 打开它。 是否要继续并打开此项目?”

实现 IVsProjectUpgradeViaFactory

  1. 实现 IVsProjectUpgradeViaFactory 接口的方法,特别是项目工厂实现中的 UpgradeProject 方法,或使该实现可从项目工厂实现中调用。

  2. 如果你想要将就地升级作为解决方案打开的一部分而进行,则在 UpgradeProject 实现中提供 PUVFF_SXSBACKUP 标志作为 VSPUVF_FLAGS 参数。

  3. 如果你想要将就地升级作为解决方案打开的一部分而进行,则在 UpgradeProject 实现中提供 PUVFF_COPYBACKUP 标志作为 VSPUVF_FLAGS 参数。

  4. 对于步骤 2 和步骤 3 而言,实际文件升级步骤(使用 IVsQueryEditQuerySave2)可按以下“实现 IVsProjectUpgade”部分中所述进行实现,或可将实际文件升级委派到 IVsProjectUpgrade

  5. 使用 IVsUpgradeLogger 的方法来为使用 Visual Studio 迁移向导的用户发布升级相关消息。

  6. IVsFileUpgrade 接口用于实现需作为项目升级一部分而发生的任何类型的文件升级。 此接口不从 IVsProjectUpgradeViaFactory 调用,但会将其作为一种机制用来对属于项目系统、但主项目系统可能不会直接感知到的文件进行升级。 例如,如果编译器相关文件和属性未由处理项目系统其余部分的同一开发团队处理,则可能会发生这种情况。

IVsProjectUpgrade 实现

如果项目系统仅实现 IVsProjectUpgrade ,则无法使用 Visual Studio 转换向导。 但即使你实现 IVsProjectUpgradeViaFactory 接口,仍可将文件升级委派到 IVsProjectUpgrade 实现。

实现 IVsProjectUpgrade

  1. 用户尝试打开一个项目时,UpgradeProject 方法将由环境在项目打开后、在项目上执行任何其他用户操作前调用。 如果已提示用户升级解决方案,则 UPF_SILENTMIGRATE 标志将传入 grfUpgradeFlags 参数。 如果用户直接打开项目,例如使用添加现有项目命令,则不会传递 UPF_SILENTMIGRATE 标志,并且项目需要提示用户升级。

  2. 在响应 UpgradeProject 调用时,该项目必须评估是否升级该项目文件。 如果项目不需要将项目类型升级到新版本,则可以只返回 S_OK 标志。

  3. 如果项目需要将项目类型升级到新版本,则它必须确定是否通过调用 QueryEditFiles 方法和为 rgfQueryEdit 参数传入 tagVSQueryEditFlags 值来修改项目文件。 该项目接下来需要执行以下操作:

  4. 如果项目文件上的 QueryEditFiles 调用导致文件被签出、最新版本被检索,则卸载并重新加载项目。 一旦创建了项目的另一个实例,将会再次调用 UpgradeProject 方法。 在这第二次调用上,项目文件可以写入磁盘;建议项目使用 .OLD 扩展名按以前的格式保存项目文件的副本、进行必要的升级更改,并以新格式保存项目文件。 同样,如果升级过程的任何部分失败,该方法必须通过返回 VS_E_PROJECTMIGRATIONFAILED 指示失败。 这将导致项目无法在解决方案资源管理器中卸载。

    务必了解在环境中发生的整个过程,以应对调用 QueryEditFiles 方法(指定 ReportOnly 的值)将返回 QER_EditNotOKQER_ReadOnlyUnderScc 标志的情况。

  5. 用户尝试打开项目文件。

  6. 环境调用你的 CanCreateProject 实现。

  7. 如果 CanCreateProject 返回 true,则环境将调用你的 CanCreateProject 实现。

  8. 环境调用你的 Load 实现,以打开该文件并初始化项目对象,如 Project1。

  9. 环境调用你的 IVsProjectUpgrade::UpgradeProject 实现来确定是否需要升级项目文件。

  10. 调用 QueryEditFiles 并为 rgfQueryEdit 参数传入 QEF_ReportOnly 值。

  11. 环境将为 VSQueryEditResult 返回 QER_EditNotOK 且将在 VSQueryEditResultFlags 中设置 QER_ReadOnlyUnderScc 位。

  12. 你的 IVsProjectUpgrade 实现调用 IVsQueryEditQuerySave::QueryEditFilesQEF_ForceEdit_NoPromptingQEF_DisallowInMemoryEdits)。

此调用可能导致项目文件的新副本被签出、最新版本被检索,以及需要重新加载项目文件。 在这种情况下,将发生以下两种情况中的一种:

  • 如果你处理自己的项目重载,则环境将调用你的 ReloadItem (VSITEMID_ROOT) 实现。 当你收到此调用时,请重新加载项目的第一个实例 (Project1) 并继续升级项目文件。 如果你为 GetProperty (VSHPROPID_HandlesOwnReload) 返回 true,则环境知道你将处理自己的项目重载。

  • 如果你不处理自己的项目重载,则需为 GetProperty (VSHPROPID_HandlesOwnReload) 返回 false。 在这种情况下,在 QueryEditFiles(QEF_ForceEdit_NoPrompting, QEF_DisallowInMemoryEdits) 返回之前,该环境会创建项目的另一个新实例,例如 Project2,如下所示:

    1. 环境在你的第一个项目对象 Project1 上调用 Close,因此使此对象处于非活动状态。

    2. 环境调用你的 IVsProjectFactory::CreateProject 实现来创建项目的第二个实例 Project2。

    3. 环境调用你的 IPersistFileFormat::Load 实现,以打开该文件并初始化第二个项目对象 Project2。

    4. 环境第二次调用 IVsProjectUpgrade::UpgradeProject 以确定是否应升级项目对象。 但是,此调用在项目的第二个新实例 Project2 上进行。 这就是在解决方案中打开的项目。

      注意

      如果第一个项目实例 Project1 处于非活动状态,则你必须从对 UpgradeProject 实现的首次调用中返回 S_OK

    5. 调用 QueryEditFiles 并为 rgfQueryEdit 参数传入 QEF_ReportOnly 值。

    6. 环境将返回 QER_EditOK 且升级可以继续,因为项目文件可以进行编写。

如果不能升级,请从 IVsProjectUpgrade::UpgradeProject 返回 VS_E_PROJECTMIGRATIONFAILED。 如果不需要升级或你选择不升级,请将 IVsProjectUpgrade::UpgradeProject 调用视作不执行任何操作。 如果返回 VS_E_PROJECTMIGRATIONFAILED,则向项目的解决方案添加占位符节点。

升级项目项

如果在未实现的项目系统中添加或管理项,则可能需要参与项目升级过程。 Crystal Reports 是可添加到项目系统的示例项。

通常,项目项实现者想要利用已完全实例化且经过升级的项目,因为他们需要知道项目引用内容是什么,还可以根据哪些其他项目属性做出升级决策。

获取项目升级通知

  1. 在项目项的实现中设置 SolutionOrProjectUpgrading 标志(在 vsshell80.idl 中定义)。 这会导致当 Visual Studio shell 确定项目系统正在进行升级时自动加载项目项 VSPackage。

  2. 通过 AdviseSolutionEvents 方法为 IVsSolutionEventsProjectUpgrade 接口提供建议。

  3. 在项目系统实现完成其升级操作并创建新的升级项目后,将触发 IVsSolutionEventsProjectUpgrade 接口。 根据采用的方案,在调用 OnAfterOpenSolutionOnAfterOpenProjectOnAfterLoadProject 方法后触发 IVsSolutionEventsProjectUpgrade 接口。

升级项目项文件

  1. 必须仔细管理项目项实现中的文件备份过程。 这尤其适用于并行备份,其中 fUpgradeFlag 方法的 UpgradeProject 参数设置为 PUVFF_SXSBACKUP,其中已备份的文件放在指定为“.old”的文件旁边。 在升级项目时早于系统时间的备份文件可以指定为“过时”。 此外,除非你采取特定步骤来防止这种情况,否则可能会覆盖这些过时的文件。

  2. 在项目项收到项目升级的通知时,仍会显示 Visual Studio 转换向导。 因此,应使用 IVsUpgradeLogger 接口的方法为向导 UI 提供升级消息。