为 Visual Studio .NET 创建项目项代码模板






4.86/5 (41投票s)
2005 年 3 月 15 日
17分钟阅读

192701

2477
本文描述了 Visual Studio .NET 2003(和 2005)中的新项目项代码模板的工作方式,并介绍了一个可以轻松编写新模板的组件。
- 下载 VS.NET 2003 的源代码文件 - 38.7 Kb
- 下载 VS.NET 2003 的二进制文件 - 486 Kb
- 下载 VS.NET 2005 的源代码文件 - 43.6 Kb
- 下载 VS.NET 2005 的二进制文件 - 604 Kb
目录
引言
本文描述了在 Visual Studio .NET 中添加新项目项时使用的代码模板的工作方式。CodeProject 上还有几篇触及此主题的文章(其中几篇列在底部“参考文献”部分),本文旨在更详细地描述其工作原理。
此外,还介绍了一个名为 Krevera Template Manager 的组件,它可以(相当)轻松地创建自定义模板,并对其功能进行了描述。该组件随附了用于 C#、VB 和 C++ 的强类型集合和强类型字典的模板。如果需要,可以扩展此组件以创建更强大的模板。所有代码示例均用 C# 编写。
该组件有两个版本,一个用于 Visual Studio .NET 2003,另一个用于 Visual Studio .NET 2005。后者已在 Beta 2 上进行测试,但可能也适用于更高版本。
背景
Visual Studio 中有多种模板和向导,其中最值得注意的是项目模板和项目项模板。项目模板包含不同类型项目所需的基本文件,而项目项模板是单个文件模板(如果项有代码隐藏文件,则为多个文件),可以包含在项目中。为准确起见,实际上并非所有项目项都需要实际的模板文件,因为它们也可以完全即时创建,但通常为每个项目项设置一个模板文件,然后在包含到项目中时对其进行微调是最容易的。
本文讨论的是项目项模板,项目模板请读者参考其他资料。
当向项目中添加新项目项时,会显示“添加新项”对话框
我们将自定义模板添加到此对话框中。
它是如何工作的?
上面对话框中的项由 Visual Studio 通过读取当前项目类型的项目项目录中的配置文件来生成。例如,如果当前项目是 Visual Basic 项目,则此目录中的项
C:\Program Files\Microsoft Visual Studio .NET 2003\Vb7\VBProjectItems
会被分析(当然,这取决于我的系统,确切位置取决于安装位置)。
项目项目录中有两种重要的文件类型
- .vsdir 文件 - 列出要显示的项,并提供对话框中显示的所有属性,如名称、描述、图标、默认项文件名等。对于每个项,还会命名其对应的 .vsz 文件。
- .vsz 文件 - 每个项目项都有一个 .vsz 文件,用于告诉 Visual Studio 如何创建该类型的项。更准确地说,它命名了一个 Visual Studio 可以用来创建该项的 COM 组件,以及该组件应接收的参数(例如,模板文件名)。
这两种文件类型和一个创建实例的 COM 组件就是创建项目项所需的一切。这是所有连接在一起的总体图景
在介绍我们创建灵活项目项模板的解决方案(如您可能猜到的,它是上述 COM 组件的实现)之前,将更详细地描述这些文件的格式。
请注意,通过将我们的 .vsdir 和 .vsz 文件存储在子目录中,我们可以创建项目项类别。在上图中,我们有例如“本地项目项”、“UI”、“代码”等子目录。
项目项目录
在详细介绍文件格式之前,了解文件的位置可能很有用。一如既往,当然有多种方法。最简单的方法可能是使用资源管理器导航到 Visual Studio 的安装目录,并查找不同 .NET 语言的子目录。例如,VB7 显然包含 Visual Basic 的相关内容。在该目录中,查找名称中包含“projectitem”的目录。很快,我们就会找到包含文件的 VBProjectItems 目录。查找其他项目类型的项目项目录同样容易。
但是,如果我们想以编程方式进行,我们会使用一种更可靠的方法,即从注册表中读取,位于此键下
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\7.1\
Projects\{[PROJECT_TYPE_GUID]}
我们感兴趣的值称为 ItemTemplatesDir
,它包含正确目录的绝对路径。唯一的复杂之处在于我们必须知道项目类型的 GUID。对于 Visual Basic 和 C#,该 GUID 在 Visual Studio Automation 接口中以这些常量形式提供(需要对 VSLangProj.dll 的引用)
VSLangProj.PrjKind.prjKindCSharpProject
VSLangProj.PrjKind.prjKindVBProject
不幸的是,VSLangProj
没有为所有项目类型定义常量,因此,例如,对于 C++,我们必须硬编码。幸运的是,使用 regedit 找到正确的 GUID 非常容易。导航到上述键,然后在每个项目子键中查找 DefaultProjectExtension
值(对于 C++ 是 vcproj)。找到后,我们就可以确定我们处于正确的项目类型中。使用此方法,我们发现 C++ 项目的 GUID 是 {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}。坏消息是,我们不能绝对确定 Microsoft 不会在下一个 Visual Studio 版本中更改它,但到时候再说……
这是一段检索 C# 项目项目录的代码
const string sVisualStudioVersion = "7.1";
RegistryKey rk;
// C#
rk = Registry.LocalMachine.OpenSubKey("SOFTWARE")
.OpenSubKey("Microsoft")
.OpenSubKey("VisualStudio")
.OpenSubKey(sVisualStudioVersion)
.OpenSubKey("Projects")
.OpenSubKey(VSLangProj.PrjKind.prjKindCSharpProject);
_sCSProjItemPath = (string)rk.GetValue(@"ItemTemplatesDir");
注意:对于 Visual Studio .NET 2005,我们将 sVisualStudioVersion
设置为 "8.0"
。
.vsdir 文件格式
.vsdir 文件列出了一组项及其属性。同一个目录中可以有多个 .vsdir 文件。
MSDN 中描述了文件格式(使用索引搜索查找“.VSDir files”),但这里是简要总结
- 文件包含每行一个项。
- 每行是按以下顺序排列的 | 分隔的属性列表
RelPathName
- 项的 .vsz 文件的相对路径。{clsidPackage}
- 可选的组件 GUID,包含本地化资源(例如字符串)。LocalizedName
- 要在“添加新项”对话框中显示的项目的名称。SortPriority
- 决定“添加新项”对话框中项相对顺序的数字。数字较小的项排在数字较大的项之前。Description
- 项目的描述,显示在“添加新项”对话框的状态字段中。DLLPath
或{clsidPackage}
- 包含要为该项使用的图标资源的 DLL 的路径或 GUID。IconResourceId
- 第 6 列中组件包含的图标的资源 ID。Flags
- 要使用的标志的组合值。SuggestedBaseName
- 要为该项使用的基文件名。请注意,“添加新项”对话框会在该名称的文件扩展名之前插入一个数字。例如,基文件名“Class.cs”将向用户建议为“Class1.cs”,或者如果项目已包含此类项,“Class2.cs”。
在我看来最有趣的是粗体标记的字段。我通常将所有其他字段设置为 0,除了
SortPriority
,设置为 1 是一个合适的数字。如果要添加图标,将图标文件(扩展名为 .ico)复制到与 .vsz 文件相同的目录中,并将其命名与 .vsz 文件相同(当然,除了扩展名),这比创建资源组件更容易。
以下是一个 .vsdir 文件可能样子的示例(为便于阅读,行已换行)
Strongly Typed Collection.vsz|0|Typed Collection|1|
Sub-class of CollectionBase with elements of a given type|
0|0|0|TypedCollection.cs
Strongly Typed Dictionary.vsz|0|Typed Dictionary|1|
Sub-class of DictionaryBase with keys and values of the selected types|
0|0|0|TypedDictionary.cs
如果我们将该文件存储在子目录“Krevera Template Manager”中,则以下项将显示在“添加新项”对话框中(图像中给出了一些列号,以显示 .vsdir 文件中的列字符串是如何使用的)
当用户选择一个项并单击“打开”按钮时,Visual Studio 会打开该项的 .vsz 文件,因此现在是时候描述它的格式了。
.vsz 文件格式
.vsz 文件的唯一目的是告诉 Visual Studio 调用哪个组件来创建项目项以及它所需的参数。文件格式非常简单,这是一个示例(即 C# 的标准“Class”项目项)
VSWIZARD 7.0
Wizard=VsWizard.VsWizardEngine.7.1
Param="WIZARD_NAME = CSharpAddClassWiz"
Param="WIZARD_UI = FALSE"
Param="PROJECT_TYPE = CSPROJ"
描述
- 第一行应始终为“VSWIZARD 7.0”,以便 Visual Studio 知道预期的版本。
- 第二行告诉 Visual Studio,应使用哪个 COM 组件来创建项目项。此处必须给出组件的 ProgID 或 CLSID。
- 从第三行开始,我们可以根据需要向组件传递一些额外的参数。在上面的示例中,项模板的创建者希望传递三个参数。请注意,引号中的所有内容都将原样传递给组件。Visual Studio 不知道“
WIZARD_NAME = CSharpAddClassWiz
”的含义,这取决于组件来决定。
关于 .vsz 文件格式,确实没有太多要说的了,所以我们继续描述实际创建项目项的逻辑。
COM 组件
如前所述,实际的项目项是由 .vsz 文件指向的组件创建的。该组件必须满足几个要求
- 它必须是 COM 组件。要使用 .NET 组件,我们必须为其注册 COM 互操作。
- COM 类必须实现
EnvDTE.IDTWizard
接口(该接口定义在 EnvDTE.dll 库中,顺便说一句,该库还包含 Visual Studio 大部分自动化接口)。该接口只有一个方法,Execute
,其原型如下void Execute( object Application, int hwndOwner, object[] ContextParams, object[] CustomParams, ref wizardResult retval );
Execute
函数的参数如下
Application
- 一个DTE
对象(Visual Studio 自动化模型的顶层对象),引用正在运行的 Visual Studio 实例。hwndOwner
- 用于显示我们想要显示的对话框的父窗口。ContextParams
- 关于当前项目属性的列表。确切列表取决于向导的调用方式,但对于新项目项,列表如下WizardType
- 向导的类型,对于我们关心的项目项,它总是等于Constants.vsWizardAddItem
。ProjectName
- 当前项目的名称。ProjectItems
- 当前项目的ProjectItem
对象集合。完成后,我们将在该集合中创建一个新项。LocalDirectory
- 以ManagerPortal 文件形式存储当前项目的本地目录。ItemName
- 新项的请求名称(此名称来自“添加新项”对话框)。InstallationDirectory
- Visual Studio 的安装目录。Silent
-bool
值,如果为true
,则表示组件不应显示用户界面。
CustomParams
- .vsz 文件中提供的额外参数列表。retval
- 一个输出参数,Execute
方法用它来告知 Visual Studio 是否成功创建了新项目项。
就是这样。当要创建项目项时,Visual Studio 会创建一个类的实例并调用其 Execute
方法,在该方法中,该类创建新的项目项并将其插入到 ProjectItems
集合中。创建项目项通常涉及复制现有的模板文件,并根据“添加新项”对话框中指定的名称、当前项目的默认命名空间和其他信息对其进行修改。
Microsoft 以这种方式创建了数量相当多的项目项模板。实际的模板文件不与 .vsdir 和 .vsz 文件存储在一起,而是分开存储。完全可以调整它们,使用 Code Project 文章“Customizing Visual Studio's Code Generation Templates”中描述的方法,但可能进行的修改仅限于布局更改、标准文件头等。无法添加动态内容(当前日期、用户名等),因为用于调整项目模板文件的组件仅限于执行 Visual Studio 开发人员认为可行的操作。另外,据我所知,没有关于标准组件执行的变量替换的文档。另一个问题是,我们无法构建新逻辑,例如向用户询问信息。
因此,为了允许创建更复杂的模板,我们必须创建一个新的 COM IDTWizard
组件,而这正是现在要介绍的。
Krevera 模板管理器
Krevera Template Manager 本质上是一个用于处理创建新项目项请求的 COM 组件。它用 C# 编写,完整的源代码可在本文顶部下载。还有一个二进制下载,其中包含组件的安装程序以及一些用于在 C#、VB 和 C++ 中创建强类型集合和字典的示例模板。
模板是普通文本文件,其中包含变量,Krevera Template Manager 在创建项目项时会替换这些变量。变量以 XML 格式编写,包括以下内容(type
属性此处仅为清晰起见,在这种情况下可以忽略,因为其默认值为“standard
”)
<variable type="standard" name= "[variablename]"/>
可以使用 13 种不同的变量
变量名 | 描述 | 示例值 |
fullfilename |
新项目项的绝对路径。 | C:\Documents and Settings\Emil\My Documents\Visual Studio Projects\Template Manager Test\CS_Test\FoobarCollection.cs |
curdate |
当前日期。 | 2005-03-14 |
directory |
创建新项目项所在目录的绝对路径。 | C:\Documents and Settings\Emil\My Documents\Visual Studio Projects\Template Manager Test\CS_Test |
domainname |
计算机连接到的域的名称。 | FRODO |
domainusername |
域限定的用户名。 | FRODO\Emil |
文件名 |
新项目项的文件名。 | FoobarCollection.cs |
filenamebase |
不含扩展名的文件名。这通常用作新项的名称。例如,名为 MyClass.cs 的新文件中的类可以假定为名为 MyClass 。 |
FoobarCollection |
fullusername |
当前用户的全名。为此,计算机必须连接到域。 | Emil Åström |
installdir |
Krevera Template Manager 的安装目录。在 .vsz 文件中引用相对于安装目录的模板文件时很有用。 | C:\Program Files\Krevera Software\Krevera Template Manager\ |
machinename |
计算机的名称。 | FRODO |
命名空间 |
当前项目的默认命名空间。 | CS_Test |
projectname |
当前项目的名称。 | CS_Test |
用户名 |
当前用户的用户名,不包括域名称。 | Emil |
我们还可以通过将 type
属性设置为“environment”并在 name
属性中给出环境变量的名称来访问环境变量
<variable type="environment" name="TEMP"/>
最后,我们还可以使用自定义变量,这些变量的值通过在类似向导的界面中向用户询问来检索。以下是一个示例
<wizard>
<page>
<title>Select data type</title>
<description>Select data type for which to create
a strongly typed collection</description>
<variable name="datatype"/>
</page>
</wizard>
创建包含上述向导片段的项目项时,会显示以下对话框
当用户输入自定义变量(在示例中名为“datatype
”)的期望值后,我们可以在模板中使用它,方法是使用 type
属性设置为“custom
”的变量
<variable type="custom" name="datatype"/>
为确保模板的变量声明不会与模板文本中的代码语法冲突,所有变量都必须用 [%
和 %]
包围。
总而言之,现在我们展示一个真实模板的顶部部分,即随代码分发的创建强类型集合的模板
[%
<wizard>
<page>
<title>Select data type</title>
<description>Select data type for which to create a
strongly typed collection</description>
<variable name="datatype"/>
</page>
</wizard>
%]using System;
using System.Collections;
namespace [%<variable name="namespace"/>%] {
public class [%<variable type="standard"
name="filenamebase"/>%] : CollectionBase
{
public [%<variable type="custom"
name="datatype"/>%] this[ int index ] {
get
{
return( ([%<variable type="custom"
name="datatype"/>%]) List[index] );
}
set
{
List[index] = value;
}
}
...
如果我们将此类型的项目项插入到默认命名空间为 CS_Test
的项目中,将项命名为 FoobarCollection.cs,并回答向导问题 Foobar
,我们将得到包含以下内容的如下文件
using System;
using System.Collections;
namespace CS_Test
{
public class FoobarCollection : CollectionBase
{
public Foobar this[ int index ]
{
get
{
return( (Foobar) List[index] );
}
set
{
List[index] = value;
}
}
...
希望大家都同意,与手动创建相比,这是一种非常简单的创建强类型集合的方法。
模板的其他常见用途是强制执行有关文件头的组织范围规则。使用此组件,可以轻松地自动将文件名、创建日期、用户名等包含在文件头中。如果需要,创建新变量也很容易,只需修改代码中的 Wizard.EvaluateVariable
函数即可实现新变量的评估。
实现概述
此处不详细描述代码;直接阅读代码及其注释可能更容易。但是,有一个代码工作原理的概述可能会有所帮助,所以我们开始吧
- 该组件是用 C# 编写的 .NET 组件,启用了 COM 互操作(项目属性“Register for COM Interop”设置为“
true
”)。为了使其正常工作,我们必须为该组件关联一个强名称(一个 .snk 文件),该文件可以从命令行生成sn -k TemplateManager.snk
密钥文件还必须在 AssemblyInfo.cs 中引用(有关详细信息,请参阅该文件)。
更新:这不适用于 Visual Studio .NET 2005 版本。我们仍然需要设置Register for COM Interop,但不必担心强名称等。
- 我们的 .vsz 文件必须将
Krevera.TemplateManager.Wizard
指定为要使用的组件,以及指定要使用的模板文件的强制参数TEMPLATE_FILE
。这是一个示例(为便于阅读,已换行)VSWIZARD 7.0 Wizard=Krevera.TemplateManager.Wizard Param="TEMPLATE_FILE = [%<variable name="installdir"/>%] Templates\CSharp\Project Items\StronglyTypedCollection.cs"
请注意,如果需要,我们可以在参数中使用变量。
- 使用以下代码将模板文件复制到项目中(以及
ProjectItems
集合)ProjectItem newfile = _projitems.AddFromTemplate( _sTemplateFile, _sNewItemName);
- 包含的文件将被打开,并且所有
[% ... %]
块的内容都将使用 XML 架构进行验证。然后,如果块是变量,它将被替换为变量的值。如果块是向导片段,则会显示该块中的所有页面,用户提供的值将存储在具有自定义变量值的字典中。 - 源代码中还包含一个安装程序项目。该项目基本简单,除了它使用所谓的自定义操作将实际的 .vsdir 和 .vsz 文件复制到 Visual Studio 子目录中的正确项目项位置。执行复制操作的函数位于组件项目中,名为
TemplateManagerInstaller.OnAfterInstall
。如果项目项不应放置在“Krevera Template Manager”子目录中(这是默认设置),则应修改此处。
安装
运行安装程序(如果在安装期间 Visual Studio 打开,则重启 Visual Studio)或手动执行
- 构建组件项目。这应该也会注册 COM 互操作组件。
- 确保存在注册表项 HKEY_LOCAL_MACHINE\SOFTWARE\Krevera Software\Template Manager(对于 Visual Studio .NET 2005 版本,附加“ VS2005”),其中有一个名为
InstallDir
的值,该值给出 Krevera Template Manager 的安装目录位置(用于查找模板文件)。值的示例是 C:\Program Files\Krevera Software\Krevera Template Manager\。 - 将 .vsdir 和 .vsz 文件复制到 Visual Studio 的项目项目录中。
这些步骤足以使用模板。
许可证
您可以使用此软件的任何方式,但不能用于营利。您可以自由地私下或在组织中使用它(原始或修改状态),但不得将其包含在商业产品中等。至少在获得作者(即我!)的明确批准之前,不得这样做!
参考文献
图书
- Inside Microsoft Visual Studio .NET,作者 Brian Johnson、Craig Skibo 和 Marc Young。
这是一本内容广泛的书,但不幸的是,内容看起来不太有条理,而且存在一些错误。尽管如此,它并没有太多竞争对手,所以对于有兴趣为 Visual Studio 创建宏和外接程序的读者来说,它仍然值得推荐。
其他 Code Project 文章
- 自定义Visual Studio的代码生成模板.
- Visual Studio .NET“添加新项”自定义模板安装程序.
- 将自定义 Visual Studio 代码生成模板添加到“添加新项”对话框框.
- 如何为 VS.NET 构建自己的 ASP.NET 模板.
历史
- 2005-07-24
- 错误修复:在项目中创建的子文件夹中的文件未正确打开。
- 对文章文本进行了一些小的更改,最值得注意的是修正了
LocalDirectory
的描述,之前错误地声称该值为新项的路径,而实际上它是项目文件夹的路径(如果项在子文件夹中创建,则不相同)。 - 添加了适用于 Visual Studio .NET 2005 Beta 2(可能还有更高版本)的模板管理器版本。感谢 Marcin Celej 指出所需修改的简单性。
注释
- 编译时有 3 个警告,但运行正常。
- 示例模板与以前相同(强类型集合和字典),尽管现在有了泛型,它们的作用要小得多。它们仍然能说明模板机制,所以我保持原样。
- 2005-04-24
修复了一些错误(布局和语法)。
- 2005-03-15
首次发布版本。