一个更好的类向导






4.44/5 (11投票s)
2004年10月14日
6分钟阅读

54327

472
如何解决“添加类”向导中烦人的路径问题。
引言
当你想使用通用类向导在 C++ 项目中添加一个类时,你经常会因为无法提供类文件创建的“默认”路径而感到恼火。大多数项目都使用分离的源文件,“cpp”和“h”文件不保存在与“sln”和“vcproj”文件相同的目录中。当你想要在一个目录中添加多个类时,你会浪费很多时间,因为你必须点击“.h”和“.cpp”的浏览按钮并选择一个目录。向导总是从存储项目配置文件所在的目录开始。每次你想添加一个类时,这两个浏览步骤都必须执行,而且 IDE 不会保存向导中使用的最后一个路径。对于下一个类,你必须再次浏览你的目录结构。如果你有很多目录和深层嵌套的文件夹,这很快就会变成一场噩梦。
Visual Studio .NET 向导
幸运的是,有一种简单的方法可以修复标准的向导。向导实际上由一个 JavaScript 文件组成,该文件与一个 HTML 文件和 Visual Studio .NET IDE 进行通信。这个文件是你将在 IDE 中看到的对话框的模板。HTML 还包含一些 JavaScript 函数来处理字段和按钮。你可以轻松地在向导中添加一些字段,并通过 JavaScript 方法扩展它。所有向导都存储在 Visual Studio .NET 文件夹中,你将在“Microsoft Visual Studio .NET/VC7/VCWizards”目录中找到它们。“添加类”向导可在“ClassWiz”文件夹中找到。这个文件夹包含名为“ATL”、“Generic”和“MFC”的子目录,我们将修改“Generic”文件夹。“Generic”文件夹包含三个子目录:“HTML”、“Images”和“Scripts”。你将在“default.htm” (/HTML/1033) 中找到对话框模板,相应的 JavaScript 是“default.js” (/sScripts/1033)。
HTML 模板
在浏览器中打开“default.htm”文件,你就会进入一个熟悉的界面。以下是你所能看到的(在 Firefox 中,背景将是紫色的)。
现在,如果你查看“default.htm”的源代码,你会发现第一部分定义了 HTML 表单,后面是一个 JavaScript 部分。“<HEAD>”部分位于文件开头,其中包含向导中使用的字段的定义。你可以添加一个类型为“SYMBOL”的元素。我们将添加一个“PATH”文本字段,默认值为一个空字符串。该字段将接收使用 GetDirectoryViaBrowseDlg
函数创建的对话框返回的路径。稍后将详细介绍。
<SYMBOL NAME="HEADER_FILE_VALID" TYPE=bool VALUE=false></SYMBOL>
<SYMBOL NAME="IMPL_FILE_VALID" TYPE=bool VALUE=false></SYMBOL>
<SYMBOL NAME="PATH" TYPE="text" VALUE=""></SYMBOL>
这些符号用于 JavaScript 访问字段值。下一步是在表单中添加字段。如果你有一些 HTML 知识,这很容易。表单布局基于一个表格,你会找到一些“TD”和“TR”标签。我们将把字段添加到基类字段的正下方。我们还必须在字段后面添加一个按钮,点击此按钮将打开浏览目录对话框,此行为通过 onClick
属性定义。每个控件都必须有一个唯一的 ID,你也可以定义访问字段的键盘快捷键(ACCESSKEY
属性)。名为 sideBtn666ThreeColumn
的文本字段会将它的值发送到我们上面定义的 PATH
符号。请注意,我们定义了一个名为 OnBrowsePath()
的新函数,当按下按钮时将调用它,我们必须实现这个函数。
<SPAN CLASS="itemText" TITLE="">
<LABEL FOR="BASE_CLASS_NAME" ID="BASE_CLASS_NAME_LABEL"
TITLE="Enter the name for the base class from
which the class is to be derived.">
<U>B</U>ase class:
</LABEL>
<BR>
<INPUT CLASS="sideBtnThreeColumn" ID="BASE_CLASS_NAME"
TYPE="text" ACCESSKEY="b"
TITLE="Enter the name for the base class from
which the class is to be derived."
TABINDEX="6">
</SPAN>
<!-- new code -->
<SPAN CLASS="itemText" TITLE="">
<P>
<LABEL FOR="PATH" ID="PATH_LABEL"
TITLE="Enter the path where the class files will be stored">
<U>P</U>ath</LABEL>
<BR>
<INPUT CLASS="sideBtn666ThreeColumn" ID="PATH" TYPE="text" ACCESSKEY="p"
TITLE="Enter the path where class will be stored" TABINDEX="6">
<BUTTON CLASS="buttonClass666Custom" ID="BrowsePathBtn" TYPE="BUTTON"
TITLE="Browse for the path." onClick="OnBrowsePath();"
TABINDEX="6">...</BUTTON>
</SPAN>
<!-- end of new code -->
我们还删除了“.h 文件”和“.cpp 文件”字段后面的两个按钮。我总是把“.h”和“.cpp”放在同一个目录中。如果你觉得这样烦人,你可以改变它,这是复制路径字段和相应代码的问题。
在浏览器中,现在应该看起来像这样
default.htm 的 JavaScript 部分
我们添加了 OnBrowsePath()
函数,它与 defaut.htm 中已有的文件浏览函数非常相似。它基本上调用 GetDirectoryViaBrowseDlg
函数,并将默认路径作为第二个参数。此路径将从 cookie 读取,我将在下一节中讨论这个问题。OnBrowsePath()
返回一个包含目录完整路径的字符串。此路径将附加到 .h 和 .cpp 文件(在 default.js 中)。
// new function added to the wizard, to allow path definition
function OnBrowsePath()
{
var strFile;
try
{
L_Title1_Text = "VS Wizards Select Path to store class files";
strPath = window.external.GetDirectoryViaBrowseDlg (L_Title1_Text,
cookieData.thePath);
}
catch(e)
{
if (e.number != OLE_E_PROMPTSAVECANCELLED)
{
var L_ErrMsg1_Text = "Error in OnBrowsePath()";
if (e.description.length != 0)
{
L_ErrMsg1_Text += ": ";
L_ErrMsg1_Text += e.description;
}
window.external.ReportError(L_ErrMsg1_Text);
}
return;
}
// store path in the textfield
PATH.value = strPath;
}
我没有添加任何代码来验证目录,我期望 GetDirectoryViaBrowseDlg
返回的目录是有效的。要创建一个更健壮的向导,你可以在 Validate(obj)
函数中出现的 switch
语句中添加一个 case。
为下次保存路径
我不是 JavaScript 专家,尝试了各种解决方案。MSDN 上有一个教程 (Scripting) Working with files,但它使用了 ActiveX 控件,并且每次调用向导时 Visual Studio .NET 都会打开一个授权窗口。出于安全原因,JavaScript 不能写入文件,你必须使用 ActiveX 控件或 applet 来 hack。一个更好、更干净的方法是使用 cookies。我找到了一些关于 cookies 的 JavaScript 代码,它直接取自 O'Reilly 的 JavaScript “权威指南”:cookies 代码。我只是把他们的代码复制粘贴到 default.htm 中。
我们首先定义一个全局变量 cookieData
,它在 InitDocument(document)
的开头实例化。我们为第一次使用向导提供了一个默认位置,cookie(“last_path
”)还需要一个关于其过期时间的指示。
// COOKIE CODE FROM OREILLY
// ............
//
// cookie for whole document
var cookieData;
function InitDocument(document)
{
setDirection();
CLASS_NAME.focus();
// create a cookie for 10 years, should be enough :)
cookieData = new Cookie(document, "last_path", 24*365*10);
// if cookie doesn't exist or is invalid, create it.
if (!cookieData.load() || !cookieData.name) {
cookieData.name = "last_path";
}
// store a default value for first session
if (cookieData.thePath==null)
{ cookieData.thePath="C:\\"; cookieData.store();}
if (window.external.FindSymbol("DOCUMENT_FIRST_LOAD"))
{
var L_WizardDialogTitle_Text = "Generic C++ Class Wizard";
window.external.AddSymbol("WIZARD_DIALOG_TITLE",
L_WizardDialogTitle_Text);
window.external.SetDefaults(document);
}
window.external.Load(document);
// store the cookie value into the textfield in the form
PATH.value = cookieData.thePath;
}
当我们完成时,我们不应该忘记将路径存储在 cookie 中。在 OnFinish(document)
中调用 cookieData
的 store()
方法就可以了。
function OnFinish(document)
{
if (!ValidateInput())
return;
cookieData.thePath = PATH.value;
cookieData.store();
// ....
}
default.js 文件
我们仍然需要修改充当 IDE 和模板之间桥梁的脚本,即“default.js”。需要在开头添加一个变量:var strPath = wizard.FindSymbol("PATH");
,它的作用是从模板获取 PATH
的值。现在很简单,我们只需要将路径附加到 .cpp 和 .h 文件。
var strProjectName = wizard.FindSymbol("PROJECT_NAME");
var strClassName = wizard.FindSymbol("CLASS_NAME");
var strHeader = wizard.FindSymbol("HEADER_FILE");
var strImpl = wizard.FindSymbol("IMPL_FILE");
var strBaseName = wizard.FindSymbol("BASE_CLASS_NAME");
var strPath = wizard.FindSymbol("PATH");
// ...
var newclass = oCM.AddClass(strClassName, strPath+"/"+strHeader,
vsCMAddPositionEnd, "", "", vsCMAccessDefault);
// ...
newclass.AddFunction(strClassName, vsCMFunctionConstructor, "",
vsCMAddPositionEnd, vsCMAccessPublic, strPath+"/"+strImpl);
var oDestructor = newclass.AddFunction("~"+strClassName,
vsCMFunctionDestructor, "", vsCMAddPositionEnd,
vsCMAccessPublic, strPath+"/"+strImpl);
安装
打开 zip 文件并将 default.js 和 default.htm 复制到正确的位置(我包含了完整的路径以确保无误,你可能需要将它们移动到正确的地方)。我建议你备份当前的向导,以防出现问题。你无需重启 Visual Studio .NET 即可设置新向导。如果你像在进行这些修复之前那样,在 C++ 项目中调用“添加类”向导,你应该会看到新版本。
结论
我在 Visual Studio .NET 2003 中开发和测试了该向导。我猜它在早期版本的 Visual Studio .NET 中也能工作,微软的原始版本是在 2001 年编写的。如果你能改进它,请随时在论坛上发布你的修改 :) 如前所述,我根本不是 JavaScript 专家,这是我第二次接触 JavaScript 代码,也是一个学习这个脚本语言的好方法(并发现我更喜欢 Python ;))。