在Visual Studio中开发扩展时,经常需要创建新的类或修改现有的类。这个过程可以通过使用Visual Studio的向导功能来简化。本文将介绍如何自定义类向导,以及如何使用JScript和CodeModel进行操作。
在开发过程中,每当需要在命名空间中添加一个类时,都需要手动选择“新建项目”->“新建类”,然后在文件的开始和结束处手动添加命名空间。这个过程非常繁琐,因此决定编辑原始向导,以便在其中包含命名空间字段(以及用于选择基类的类似选择器)。
在CodeProject上,已经写过关于向导的文章,创建向导文件的过程非常简单。但是,使用向导时遇到的问题不在于了解文件格式,而在于如何使用脚本。MSDN上的文档非常分散,即使需要编写JScript文件,大多数MSDN示例都是用VB或C#编写的。由于几乎不使用JScript(除了在向导中),所以找到需要的内容以及如何使用它们非常无聊。
将从基础开始;示例中的字符串数组等都是用Java语法编写的(显然)。可以在MSDN页面上找到所有可以在它们上使用的功能(以及其他功能)。在左侧的目录中,还有两个有用的部分(JScript函数和JScript对象)。链接的部分包括所有对象方法。也可以在那里找到JScript语法。
EnvDTE命名空间是一个通用的命名空间,CodeModel接口用于在Visual Studio内部访问代码模型,而更高级的VCCodeModel接口用于在Visual Studio 8内部访问代码模型。
如果使用VCCodeModel功能,将能够更方便地访问CodeDOM。例如,可以执行以下操作:
function OnFinish(selProj, selObj) {
// 获取项目的全局命名空间集合。
var nss = selProj.CodeModel.Namespaces;
// 获取第一个全局命名空间的命名空间集合。
var nss2 = nss.Item(1).Namespaces;
// 获取第一个子命名空间中的第一个类。
var cl1 = nss2.Classes.Item(1);
}
在Visual Studio的新版本(8)中,DOM提供了更用户友好的方式来访问代码模型。在以前的版本中,需要检索CodeModels集合,然后检查每个CodeModel的类型。需要注意的是,CodeModel是按照正常的DOM组织的,每个节点都有子节点,并且有一个主要的节点集合,通过它可以访问子节点,如上面的示例所示,selProj.CodeModel.Namespaces不是一个包含所有命名空间的通用列表,而只是全局命名空间,每个命名空间都包含子节点(即包含在命名空间内的CodeModels),这些子节点可以是类、接口,也可以是其他命名空间。
在每个向导中,可能已经知道,有一个实现接口的HTML文件和.js JScript文件(两者都命名为default.ext)。可以通过在htm文件中调用全局变量'window'的'window.external'和在js文件中的'wizard'来访问向导。
由于没有保存在编辑向导时找到的其他有用资源或链接,所以不能提供更多的帮助,因为不记得它们了。但是,如果好奇,下面是用来搜索的代码,以及一个命名空间和类的列表,即使它不是一个很好的代码(正如所说的,根本不了解JScript)。也因为写得非常快,想着,“哦,请做工作脚本”,可能会发现一些行对目的是有用的(或者至少是有趣的)。
function InitDocument(document) {
if (window.external.ProjectObject) {
var proj = window.external.ProjectObject;
var codeModel = proj.CodeModel;
var nssArr, nsArr = new Array();
GetNamespaces(codeModel.Namespaces, nsArr, "");
nssArr = nsArr.sort();
for (var i = 0; i < nssArr.length; i++) {
var oOption = document.createElement("OPTION");
oOption.value = nssArr[i];
oOption.text = nssArr[i];
NAMESPACE.add(oOption);
}
var oOption = document.createElement("OPTION");
oOption.value = "";
oOption.text = "";
NAMESPACE.add(oOption);
var cllArr, clArr = new Array();
GetClasses(codeModel.Classes, clArr, "");
GetNamespaceClasses(codeModel.Namespaces, clArr, "");
clArr = clArr.sort();
cllArr = clArr.sort(SortClasses);
for (var i = 0; i < cllArr.length; i++) {
var oOption = document.createElement("OPTION");
oOption.value = cllArr[i];
oOption.text = cllArr[i];
BASE_CLASSES.add(oOption);
}
var oOption = document.createElement("OPTION");
oOption.value = "";
oOption.text = "";
BASE_CLASSES.add(oOption);
}
}
function GetNamespaces(nss, nsArr, name) {
for (var i = 1; i <= nss.Count; i++) {
var ns = nss.Item(i);
var l = 0;
var nname = ns.Name;
if (name != "") {
nname = name + "::" + nname;
}
for (l = 0; l < nsArr.length; l++) {
if (nsArr[l] == nname) {
break;
}
}
if (l == nsArr.length) {
nsArr[l] = nname;
}
GetNamespaces(ns.Namespaces, nsArr, nname);
}
}
function GetClasses(cls, clArr, name) {
for (var i = 1; i <= cls.Count; i++) {
var cl = cls.Item(i);
var l = 0;
var nname = cl.Name;
if (name != "") {
nname = name + "::" + nname;
}
for (l = 0; l < clArr.length; l++) {
if (clArr[l] == nname) {
break;
}
}
if (l == clArr.length) {
clArr[l] = nname;
}
GetClasses(cl.Classes, clArr, nname);
}
}
function GetNamespaceClasses(nss, clArr, name) {
for (var i = 1; i <= nss.Count; i++) {
var ns = nss.Item(i);
var l = 0;
var nname = ns.Name;
if (name != "") {
nname = name + "::" + nname;
}
GetClasses(ns.Classes, clArr, nname);
GetNamespaceClasses(ns.Namespaces, clArr, nname);
}
}
function SortClasses(arr1, arr2) {
var arr = arr1.split("::");
var arrs = arr2.split("::");
if (arr.length > arrs.length) {
return 1;
} else if (arrs.length > arr.length) {
return -1;
} else {
var i;
for (i = 0; i < arr.length - 1; i++) {
if (arrs[i] != arr[i]) {
break;
}
}
if (i == (arrs.length - 1)) {
var l = 0;
while (arr[i][l] == arrs[i][l] && l < arr[i].length && l < arrs[i].length) {
l++;
}
if (l < arr[i].length || l < arrs[i].length) {
if (arr[i][l] > arrs[i][l]) {
return 1;
} else {
return -1;
}
} else {
return 0;
}
} else {
return 0;
}
}
}
排序函数还不完美,但对来说已经足够了。