Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Creating a custom wizard to generate managed C++.NET applications with a default form

0.00/5 (No votes)
1 May 2003 2  
Creating a custom wizard to generate managed C++.NET applications with a default form

Introduction

When i first started messing with C++.NET it soon became apparent that it had been slightly forgotten among the .NET platform, compared to VB and C#. Many features are left out or simply not documented. These include the lack of C++ specific documentation/samples within MSDN.

All new .NET applications use Forms instead of dialogs. Unfortunately within C++.NET there is no Form designer like there is for C# and VB.NET. Not only that, there is no wizard to generate a base Form application with all the code there ready to begin work. All you get is an empty console application. After some reasearch i found a few ways todo this and remove the nasty console application. To make it easier i decide to make a custom wizard that will do all the base code generating for you.

NOTE: During my research on custom wizards i actually found an MSDN example to generate a form app :( sigh, however it was extremely basic, didnt allow application name changes and also no pre-defined compiler/linker settings are created. Therefore i carried on and decided to explain how to make the wizard. Its extremely basic...

  1. Load Visual Studio.NET
  2. Click 'File', 'New Project'. Then select the 'Visual C++ Project' tree item. Follow this by selecting 'Custom Wizard' from the 'Templates' window and entering 'FormApp' as your project title. You can decide where you want to create the project files yourself.
  3. For the friendly name enter 'Managed C++ Form Application' and also select 'No GUI'.
  4. All the base files will be created for you, however they will only include a ReadMe.txt template.

The first thing you should do is get familiar with the files within the project:

  • Template Files\ReadMe.txt ---> Your one and only template file that will be put into your project generated by this wizard.
  • Script Files\Default.js ---> The core of the project, that contains the code to copy/rename/edit file templates.
  • Miscellaneous Files\Templates.inf ---> Simply contains a list of filenames which will be copied accross.
  • Miscellaneous Files\ ---> And various other files which we arent concerned with including icons etc.

The first thing todo is to add all the default project template files that we need to the Template Files section. Goto your folder where you created the wizard project and enter the Templates/1033 sub-folder. In here you will notice the Readme.txt file. Now, start by creating 6 blank text documents. Name these:

  • FormAppForm.h
  • StdAfx.h
  • AssemblyInfo.cpp
  • FormApp.cpp
  • FormAppForm.cpp
  • StdAfx.cpp

Now back in Visual Studio, right click on Template Files tree item, then select Add | Add Existing Items. Navigate to your project \Templates\1033 folder and add all 6 files to your project.

Now edit Template.inf file and add each of the filenames to that file on a new line.

Here comes the actual code part. I will only explain this section briefly, because much of the C++ contained within the files themselves is standard and very basic. We must edit each of the files that we included above, adding the appropriate C++. In FormApp.cpp you can add:

// 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; }

You will notice above that several of the lines contain '[!output PROJECT_NAME]'. This is because when we actually come to use this wizard, Visual Studio will replace that code section with whatever we choose to call our project. This is somewhat laking in the MSDN example.

FormAppForm.h will contain:

#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 will contain:

#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 will contain:

// 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

Finally we must edit the Default.js AddFilters(proj) function so that it places our filenames in their appropriate tree folder within our generated project. Todo this change the function to (NOTE: HEADER_FILTER etc are defined within 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;
    }
}

Another important change for our generated code is to tell the wizard to rename some of the files to the appropriate project name. This was also lacking within the MSDN example. To achieve this edit the function GetTargetName(strName, strProjectName) within Default.js:

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; } }

Technically now, our wizard would work, however if you used it to generate a project you would find that none of the compiler options are setup. You would get errors on the .NET code because /CLR is missing from the options. Also no debug information has been specified meaning there is no difference between our Release mode and our Debug mode. To correct this edit AddConfig(proj, strProjectName) once again from within Default.js:

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; } }

Thats just about all there is to it. Save your project. Compiling it wont achieve anything, you dont need to compile anything and it will only produce errors about not finding various includes. When your custom wizard was created Visual Studio will have copied FormApp.ico, FormApp.vsdir and FormApp.vsz to C:\program files\Visual Studio.Net\VC7\vcprojects\ or where you installed VS.NET to. Before trying to use your custom wizard i would advice that you first copy those three files from your project folder to the VS.NET folder to make sure its using the latest versions.

Usage

In order to use the above zipfile you must:

  1. Unzip the file, copying the contents to your project folder.
  2. Edit FormApp.vsz and edit the Param="ABSOLUTE_PATH" updating it to the path of your project folder.
  3. Copy FormApp.vsz, FormApp.ico and FormApp.vsdir to C:\program files\Visual Studio.Net\VC7\vcprojects\ or whereever you installed VS.NET.

To use your new custom wizard, Load a new instance of VS.NET, click 'File' menu, followed by New Project. Select the Visual C++ tree item, followed by 'Managed C++ Form Application'. Voila, your new project will be generated along with a default form ready for editing.

I know this isnt exactly ground breaking for many of you, but it explains a few things and even if you arent too interested, the new wizard can help save 10 minutes or so when you wish to create a new dialog application in managed C++.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here