创建自定义向导以生成带有默认窗体的托管 C++.NET 应用程序






4.69/5 (20投票s)
2003年2月1日
5分钟阅读

183140

1249
创建自定义向导以生成带有默认窗体的托管 C++.NET 应用程序
引言
当我开始涉足 C++.NET 时,很快就发现它在 .NET 平台中似乎被遗忘了,与 VB 和 C# 相比。许多功能被省略或根本没有文档。这包括 MSDN 中缺乏 C++ 特定的文档/示例。
所有新的 .NET 应用程序都使用 Forms 而不是对话框。不幸的是,在 C++.NET 中,没有像 C# 和 VB.NET 那样可用的 Form 设计器。不仅如此,还没有一个向导可以生成一个基本的 Form 应用程序,其中包含所有代码,可以开始工作。你得到的所有东西就是一个空的控制台应用程序。经过一番研究,我找到了几种方法可以做到这一点,并移除那个讨厌的控制台应用程序。为了使事情变得更容易,我决定创建一个自定义向导,它可以为你生成所有基础代码。注意:在我研究自定义向导的过程中,我 actually 找到了一个 MSDN 示例来生成一个窗体应用程序 :( 唉,但是它非常基础,不允许更改应用程序名称,也没有预定义的编译器/链接器设置。因此,我继续下去,并决定解释如何制作这个向导。它非常基础……
- 加载 Visual Studio.NET
- 点击“文件”、“新建项目”。然后选择“Visual C++ 项目”树项。接着从“模板”窗口中选择“自定义向导”,并将“FormApp”作为你的项目标题。你可以自己决定在哪里创建项目文件。
- 对于友好名称,请输入“Managed C++ Form Application”,同时选择“No GUI”。
- 所有基础文件都将为你创建,但它们只会包含一个ReadMe.txt模板。
你应该做的第一件事是熟悉项目中的文件
- Template Files\ReadMe.txt ---> 你创建的这个向导将放入你项目中的唯一模板文件。
- Script Files\Default.js ---> 项目的核心,其中包含复制/重命名/编辑文件模板的代码。
- Miscellaneous Files\Templates.inf ---> 仅包含一个文件名列表,这些文件名将被复制。
- Miscellaneous Files\ ---> 以及我们不关心的各种其他文件,包括图标等。
你需要做的第一件事是将所有默认项目模板文件添加到模板文件部分。转到你创建向导项目的文件夹,然后进入Templates\1033子文件夹。你会注意到Readme.txt文件。现在,开始创建 6 个空白文本文件。将它们命名为
- FormAppForm.h
- StdAfx.h
- AssemblyInfo.cpp
- FormApp.cpp
- FormAppForm.cpp
- StdAfx.cpp
现在回到 Visual Studio,右键单击Template Files树项,然后选择“添加”|“添加现有项”。导航到你的项目\Templates\1033文件夹,并将所有 6 个文件添加到你的项目中。
现在编辑Template.inf文件,并在新行上将每个文件名添加到该文件中。
现在是实际代码部分。我只会简要解释这一部分,因为文件中包含的大部分 C++ 标准且非常基础。我们必须编辑上面包含的每个文件,并添加适当的 C++ 代码。在FormApp.cpp中,你可以添加
// This is the main project file for VC++ application project // generated using an Application Wizard. #include "stdafx.h" #include "[!output PROJECT_NAME]Form.h" #using <mscorlib.dll> #include <tchar.h> using namespace System; using namespace System::Diagnostics; // Main entry point into application int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPTSTR lpszCmdLine, int nCmdShow ) { Application::Run(new C[!output PROJECT_NAME]Form()); return 0; }
你会注意到上面几行包含“[!output PROJECT_NAME]”。这是因为当我们实际使用这个向导时,Visual Studio 会将这段代码替换为我们选择的项目名称。这在 MSDN 示例中有所欠缺。
FormAppForm.h将包含
#pragma once #using <mscorlib.dll> #using <System.dll> #using <System.Drawing.dll> #using <System.Windows.Forms.dll> using namespace System; using namespace System::Windows::Forms; __gc class C[!output PROJECT_NAME]Form : public Form { public: C[!output PROJECT_NAME]Form(void); ~C[!output PROJECT_NAME]Form(void); protected: Button* m_pbtnOK; Button* m_pbtnCANCEL; Label* m_stHeader; System::ComponentModel::Container* m_pComponents; void InitializeComponent(void); void Dispose(bool disposing); void OnOkClick( Object* source, EventArgs* e); void OnCancelClick( Object* source, EventArgs* e); };
FormAppForm.cpp将包含
#include "StdAfx.h" #include "[!output PROJECT_NAME]Form.h" #include <stdlib.h> // NULL support #using <mscorlib.dll> C[!output PROJECT_NAME]Form::C[!output PROJECT_NAME]Form(void) : m_pbtnOK(NULL) , m_pbtnCANCEL(NULL) , m_stHeader(NULL) , m_pComponents(NULL) { m_pComponents = new System::ComponentModel::Container(); //Initialize the Form InitializeComponent(); } C[!output PROJECT_NAME]Form::~C[!output PROJECT_NAME]Form(void) { } void C[!output PROJECT_NAME]Form::InitializeComponent(void) { m_pbtnOK = new Button(); m_pbtnCANCEL = new Button(); m_stHeader = new Label(); SuspendLayout(); // Initialize all control properties // // m_pbtnOK // m_pbtnOK->Location = System::Drawing::Point(248, 112); m_pbtnOK->Name = "m_pbtnOK"; m_pbtnOK->Size = System::Drawing::Size(88, 24); m_pbtnOK->TabIndex = 0; m_pbtnOK->Text = "OK"; m_pbtnOK->add_Click( new System::EventHandler( this,
&C[!output PROJECT_NAME]Form::OnOkClick ) ); // // m_pbtnDone // m_pbtnCANCEL->Location = System::Drawing::Point(344, 112); m_pbtnCANCEL->Name = "m_pbtnCANCEL"; m_pbtnCANCEL->Size = System::Drawing::Size(88, 24); m_pbtnCANCEL->TabIndex = 1; m_pbtnCANCEL->Text = "Done"; m_pbtnCANCEL->add_Click( new System::EventHandler( this,
&C[!output PROJECT_NAME]Form::OnCancelClick ) ); // // m_stHeader // m_stHeader->Text = "TODO: Place your controls here."; m_stHeader->Size = System::Drawing::Size(200, 200); m_stHeader->Location = System::Drawing::Point (20, 20); // // MainDlg // AutoScaleBaseSize = System::Drawing::Size(5, 13); ClientSize = System::Drawing::Size(440, 141); FormBorderStyle = System::Windows::Forms::FormBorderStyle::FixedDialog; Name = "[!output PROJECT_NAME]Form"; Text = "[!output PROJECT_NAME] Dialog"; Controls->Add( m_pbtnOK ); Controls->Add( m_pbtnCANCEL ); Controls->Add( m_stHeader ); ResumeLayout(false); } void C[!output PROJECT_NAME]Form::Dispose(bool disposing) { // Form is being destroyed. Do any necessary clean-up here. Form::Dispose(disposing); } void C[!output PROJECT_NAME]Form::OnOkClick( Object* source, EventArgs* e ) { Close(); } void C[!output PROJECT_NAME]Form::OnCancelClick( Object* source,
EventArgs* e ) { Close(); }
StdAfx.h将包含
// stdafx.h : include file for standard system include files, // or project specific include files that are used frequently, but // are changed infrequently // #include <windows.h> // Required for windows
最后,我们必须编辑Default.jsAddFilters(proj)
函数,以便将我们的文件名放置在我们生成的项目中适当的树文件夹中。为此,请将函数更改为(注意:HEADER_FILTER 等在FormApp.vsz中定义)
function AddFilters(proj) { try { // Add the folders to your project var strSrcFilter1 = wizard.FindSymbol('SOURCE_FILTER'); var strSrcFilter2 = wizard.FindSymbol('HEADER_FILTER'); var strSrcFilter3 = wizard.FindSymbol('RESOURCE_FILTER'); var group1 = proj.Object.AddFilter('Source Files'); var group2 = proj.Object.AddFilter('Header Files'); var group3 = proj.Object.AddFilter('Resource Files'); group1.Filter = strSrcFilter1; group2.Filter = strSrcFilter2; group3.Filter = strSrcFilter3; } catch(e) { throw e; } }
我们生成的代码的另一个重要更改是告诉向导重命名一些文件以匹配适当的项目名称。这在 MSDN 示例中也缺失了。为了实现这一点,请编辑Default.js中的GetTargetName(strName, strProjectName)
函数。
function GetTargetName(strName, strProjectName) { try { // TODO: set the name of the rendered file based on the template
// filename var strTarget = strName; if (strName == 'readme.txt') strTarget = 'ReadMe.txt'; if (strName == 'formapp.cpp') strTarget = strProjectName + ".cpp"; if (strName == 'formappform.h') strTarget = strProjectName + "Form.h"; if (strName == 'formappform.cpp') strTarget = strProjectName + "Form.cpp"; if (strName == 'stdafx.h') strTarget = 'StdAfx.h'; if (strName == 'stdafx.cpp') strTarget = 'StdAfx.cpp'; if (strName == 'assemblyinfo.cpp') strTarget = "AssemblyInfo.cpp"; return strTarget; } catch(e) { throw e; } }
严格来说,现在我们的向导应该可以工作了,但是如果你使用它生成一个项目,你会发现没有设置编译器选项。你会在 .NET 代码上收到错误,因为选项中缺少 /CLR。此外,没有指定调试信息,这意味着我们的 Release 模式和 Debug 模式之间没有区别。为了纠正这个问题,再次从Default.js中编辑AddConfig(proj, strProjectName)
函数。
function AddConfig(proj, strProjectName) { try { var config = proj.Object.Configurations('Debug'); config.IntermediateDirectory = 'Debug'; config.OutputDirectory = 'Debug'; var CLTool = config.Tools('VCCLCompilerTool'); CLTool.CompileAsManaged = managedAssembly; CLTool.DebugInformationFormat = debugEnabled; var LinkTool = config.Tools('VCLinkerTool'); LinkTool.ProgramDatabaseFile = "$(outdir)/" +
strProjectName + ".pdb"; LinkTool.GenerateDebugInformation = true; LinkTool.LinkIncremental = linkIncrementalYes; LinkTool.OutputFile = "$(outdir)/" + strProjectName + ".exe"; config = proj.Object.Configurations('Release'); config.IntermediateDirectory = 'Release'; config.OutputDirectory = 'Release'; var CLTool = config.Tools('VCCLCompilerTool'); CLTool.CompileAsManaged = managedAssembly; var LinkTool = config.Tools('VCLinkerTool'); // TODO: Add linker settings } catch(e) { throw e; } }
这就是几乎所有内容了。保存你的项目。编译它不会有任何结果,你不需要编译任何东西,它只会产生找不到各种包含文件的错误。当你的自定义向导被创建时,Visual Studio 会将FormApp.ico、FormApp.vsdir和FormApp.vsz复制到C:\program files\Visual Studio.Net\VC7\vcprojects\或你安装 VS.NET 的位置。在尝试使用你的自定义向导之前,我建议你先将这三个文件从你的项目文件夹复制到 VS.NET 文件夹,以确保它使用的是最新版本。
用法
为了使用上面的 zip 文件,你必须
- 解压文件,将内容复制到你的项目文件夹。
- 编辑FormApp.vsz并编辑
Param="ABSOLUTE_PATH"
,将其更新为你项目文件夹的路径。 - 将FormApp.vsz、FormApp.ico和FormApp.vsdir复制到C:\program files\Visual Studio.Net\VC7\vcprojects\或你安装 VS.NET 的任何位置。
要使用你的新自定义向导,请加载一个新的 VS.NET 实例,点击“文件”菜单,然后是“新建项目”。选择“Visual C++”树项,然后是“Managed C++ Form Application”。瞧,你的新项目将连同默认窗体一起生成,准备好进行编辑。
我知道这对于你们中的许多人来说并不算是什么突破性的创新,但它解释了一些事情,即使你不太感兴趣,这个新向导也可以在你希望创建新的托管 C++ 对话框应用程序时节省大约 10 分钟的时间。