在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;
                }
            }
        }
        
排序函数还不完美,但对来说已经足够了。