By Regina Black,2014-03-02 08:43
11 views 0

How To Write Delphi Wizards

    发信人: strayli (stray), 信区: Delphi

     : How To Write Delphi Wizards(1)

    发信站: BBS 水木清华站 (Thu Nov 5 21:59:25 1998) WWW-POST

     How To Write Delphi Wizards



     Delphi and C++Builder are truly open development environments, in that they

    have interfaces to enable us to integrate our own tools and experts within

    their IDE. This article will focus on writing and integrating Wizards (previously called Experts) with Delphi. The resulting (32-bits) Wizards will

    be compatible with Delphi 2.0x, Delphi 3 and C++Builder. Delphi has four kinds of Wizards: Project Experts, Form Experts, Standard

    Experts and (32-

    bits only) AddIn Experts. The first two can be found in the Repository, Standard Experts can be found under the Help menu (like the

    Database Form Expert), while AddIn Experts have to provide their own menu-

    interface with the Delphi IDE (typicaly anywhere in the menu except for

    the Help Menu, which seems to be reserved for Standard Experts only). Project and Form Experts can be activated whenever you create a new Project

    or Form (just like Project and Form Templates). Standard and AddIn Experts

    are the other kind of Wizards that generally do not create a new project or

    form, but provide some kind of information, or only create a new file



    If you've ever tried an Wizard, you know what power and ease they can


    to you. The Project Expert develops an entire project for you based on your

    preferences (like for example the Application Wizard). The Fspecific


    Experts develop custom forms that are added to your current project.


    Database Form Expert, for example, generates a form that displays data from

    an external database. These example Wizards are not just external tools that

    can be started from Delphi, they actually communicate with Delphi and

     are an

    integrated part of the development environment. While this is not so strange

    for the existing Delphi Experts (after all, they were developed and a

     dded by

    the same team that developed Delphi in the first place, and we all know

    Delphi's IDE is written in Delphi), it sounds intriguing at least to know

    that we, too, can write a Delphi Wizard that is able to communicate with

    Delphi in the same way. Could we actually write an Wizard that also opens

    files in the IDE, that can be used to start a new project from scratch? Yes, all this is possible, and more, as we will see


    1. TIExpert Interface

    The major reason why everybody thinks writing custom Wizards is difficult, is

    because they are not documented. Not in the manuals or on-line Help, that is

    (they are documented in my book The Revolutionary Guide to Delphi 2 and in my

    column in The Delphi Magazine). If you take a look at the documentation and

    source code on your harddisk, you'll find some important files and even two

    example Wizards that are installed automatically by Delphi itself. The

    important example files can be found in the DOC, SOURCE\VCL or SOURCE\TOOLSAPI subdirectories, and the main files are EXPTINTF.PAS, TOOLINTF.PAS, VIRTINTF.PAS and SHAREMEM.PAS. The first one shows how to

    derive and register our own Wizard, while the second one shows how to

     use the

    tool-services of Delphi to make the integration with the IDE complete. In order to start working on a custom wizard, we have to take a look at the

    abstract base class definition TIExpert in EXPTINTF.PAS, which is as follows

    for the 32-bits versions of Delphi:


     TExpertStyle = (esStandard, esForm, esProject, esAddIn);

     TExpertState = set of (esEnabled, esChecked);

     TIExpert = class(TInterface)


     { Expert UI strings }

     function GetIDString: string; virtual; stdcall; abstract;

    function GetName: string; virtual; stdcall; abstract;

     function GetAuthor: string; virtual; stdcall; abstract;

     function GetStyle: TExpertStyle; virtual; stdcall; abstract;

     function GetMenuText: string; virtual; stdcall; abstract;

     function GetState: TExpertState; virtual; stdcall; abstract;

     function GetGlyph: HICON; virtual; stdcall; abstract;

     function GetComment: string; virtual; stdcall; abstract;

     function GetPage: string; virtual; stdcall; abstract;

     { Launch the Expert }

     procedure Execute; virtual; stdcall; abstract;


    2. TGenericExpert: Hello, World!

    If we want to derive our own Wizard, say TGenericExpert, we have to derive it

    from the abstract base class TIExpert, which has seven or nine abstract

    member functions (GetStyle, GetName, GetComment, GetGlyph, GetState, GetIDString and GetMenuText, and for the 32-

    bits versions of Delphi also

    GetAuthor and GetPage) and one member procedure Execute. Since TIExpert is an

    abstract base class, we need to override every function we need for any

    particular Wizard.

    unit Generic;



     Windows, ExptIntf;


     TGenericExpert = class(TIExpert)


     { Expert Style }

     function GetStyle: TExpertStyle; override;

     { Expert Strings }

     function GetIDString: string; override;

     function GetName: string; override;

     function GetAuthor: string; override;

     function GetMenuText: string; override;

     function GetState: TExpertState; override;

     function GetGlyph: HICON; override;

     function GetComment: string; override;

     function GetPage: string; override;

     { Expert Action }

     procedure Execute; override;


     procedure Register;




    { The implementation details of TGenericExpert will follow in the text }

     procedure Register;



     end {Register};


    Let's have a closer look at our generic Wizard from this listing. Since

    TIExpert is an abstract base class, we need to override every function we

    need for our TGenericExpert. First of all, we need to specify the style of

    the Wizard with the GetStyle method that can return one of three (or four)

    possible values: esStandard to tell the IDE to treat the interface to


    Wizard as a menu item on the Help menu, esForm to tell the IDE to treat this

    Wizard interface in a fashion similar to form templates, or esProject

     to tell

    the IDE to treat this interface in a fashion similar to project templates.

    For 32-

    bits Delphi Wizards only, we can also return esAddIn here, to indicate

    that this is a special klind of Wizard that handles all its own interfaceing

    to the IDE through the TIToolServices interface. For our TGenericExpe

rt, a

    Standard type Wizard that only shows a MessageDlg to say hello to the


    we can use the esStandard style.

     function TGenericExpert.GetStyle: TExpertStyle;


     Result := esStandard

     end {GetStyle};

    The GetIDString should be unique to all Wizards that could be installed. By

    format of the string is: CompanyName.ExpertFunction, convention, the


    Borland.Expert or DrBob.GenericExpert.

     function TGenericExpert.GetIDString: String;


     Result := 'DrBob.TGenericExpert'

     end {GetIDString};

    After we've set the style of the Wizard, all we need to do is fill the other

    options accordingly. The GetName must return a unique descriptive name

    identifying this Wizard, like 'Generic Wizard'.

     function TGenericExpert.GetName: String;


     Result := 'Generic Wizard'

     end {GetName};

    If the style is esForm or esProject, then - for 32-

    bits versions of Delphi

    only -

     we need to return a valid name for the Author. In this case, the style

    is esStandard, so we can return an empty string instead. For an esForm or

    esProject style Wizard the name would be displayed in the Object Repository

    of the 32-bits versions of Delphi.

    {$IFDEF WIN32}

     function TGenericExpert.GetAuthor: String;


     Result := 'Bob Swart (aka Dr.Bob)' { although not needed for esStandard


     end {GetAuthor};


    If style is esForm or esProject then GetGlyph should return a handle to a

    bitmap (for Delphi 1) or icon (for Delphi 2.0x and 3) to be displayed

     in the

    form or project list boxes or dialogs. This bitmap should have a size


    60x40 pixels in 16 colours. The icon should be 32x32 in 16 colours. Again,

    since the style is just esStandard for our TGenericExpert, we can return 0

     can even combine the 16- and 32-here. We

    bit version of GetGlyph here (0 is a

    valid value to indicate that an icon or bitmap is empty). Note that if we

     a 0 when a bitmap or icon is needed, Delphi will use the defaureturn



    {$IFDEF WIN32}

     function TGenericExpert.GetGlyph: HICON;


     function TGenericExpert.GetGlyph: HBITMAP;



     Result := 0 { not needed for esStandard }

     end {GetGlyph};

    If style is esForm or esProject then GetComment should return a 1 or 2 line

    sentence describing the function of this Wizard. Since the style is esStandard, we can return an empty string.

     function TGenericExpert.GetComment: String;


     Result := '' { not needed for esStandard }

     end {GetComment};

    If style is esForm or esProject then - only for 32-

    bits versions of Delphi -

    using GetPage we can specify the name of the page in the Object Repository

    where to place our Wizard. If we don't specify a name here, then the Wizard

    just gets added to the Default Form or Project page. Since we're writing an

    esStandard Expert, we don't need to supply a page name, so we can return an

    empty string again.


     function TGenericExpert.GetPage: String;


     Result := '' { not needed for esStandard }

     end {GetPage};


    If style is esStandard then GetMenuText should return the actual text


    display for the menu item, like 'Generic Wizard'. Since this function


     time the parent menu is pulled-called each

    down, it is even possible to

    provide context sensitive text.

     function TGenericExpert.GetMenuText: String;


     Result := '&Generic Wizard...'

     end {GetMenuText};

    If the style is esStandard then GetState returning esChecked will cause the

    menu to display a checkmark. This function is called each time the Wizard is

    shown in a menu or listbox in order to determine how it should be displayed.

    We just leave it esEnabled for now.

     function TGenericExpert.GetState: TExpertState;


     Result := [esEnabled]

     end {GetState};

    Finally, the Execute method is called whenever this Wizard is invoked

     via the

    menu, form gallery dialog, or project gallery dialog. Note that Execute is

    never called for an esAddIn style Wizard (this kind of Wizard will handle all

    its own interfacing to the IDE through the upcoming TIToolServices interface). The style will determine how the Wizard was invoked. In this

    case, we just call a MessageDlg in the Execute method to indicate that the

    Wizard is actually alive.

     procedure TGenericExpert.Execute;


     MessageDlg('Hello Nashville!', mtInformation, [mbOk], 0)

     end {Execute};

    To install our first Wizard, all we need to do is act like it's a new component: For Delphi 1.0, pick Options | Install, for Delphi 2.0x and 3

    select Component | Install, and add it to the list of installed components.

    Delphi 1 and 2 simply add the Wizard the the Component Library, but Delphi 3

    needs to add it to a package - the DCLUSR30 package by default: After we click onthe OK-

    button to add the generic unit with our GenericExpert

    to the DCLUSR30 package, we need to confirm that Delphi needs to rebuild the


    After the package is rebuilt and installed into the Delphi 3 IDE agai

     n, we

    can inspect the Package Editor and see that the generic unit is now part of

    it. This simple example already illustrates that packages are not lim

     ited to

    components, but can contain Wizards as well.

    When Delphi is done with compiling and linking COMPLIB.DCL or DCLUSR30.DPL,

    you can find our first new Wizard in the Help menu:

    Just select the "Generic Wizard" and it will show the world that it's


    As we can see, only the Execute method contains any significant code,


    this will remain so for all Wizards to come. In order to avoid that we have

    to print a long listing in this paper for an Wizard where only one method is

    relevant, I'll propose the following technique: let's use a table to define

    the nine methods, and only specify the Execute method in detail. Our TGenericExpert would then become the following:


    GetStyle: esStandard

    GetIDString: DrBob.TGenericExpert

    GetName: Generic Wizard

    GetAuthor (win32): Bob Swart (aka Dr.Bob)

    GetMenuText: &Generic Wizard...

    GetState: [esEnabled]

    GetGlyph: 0

GetPage (win32):


    With only the Execute method outlined in detail (see previous listing). We

    will use this notation in the rest of this session.

    3. TSysInfoExpert

    Instead of just popping up a MessageDlg, we can show any form we'd like. In

    fact, this is just were the fun starts. Generally, we can consider our Wizard

     of two parts: the Wizard engine and the form interface. Weto consist

    've just

    seen how to write the Wizard engine, and we all know how to write form

    let's put these two together and write our first interfaces, so


    trivial information Wizard. The information that I want the Wizard to present can be found in the SysUtils unit, and consists

     of the

    country specific informatin regarding currency and date/time formatting

    constants. In the on-

    line help we can find which constants are defined in

    SysUtils, but we can't see their value. This is unfortunate, since most of

    the time Delphi is of course up-and-

    running while we're developing, so

    SysUtils is active as well (remember: Delphi is written in Delphi!) and

    should know about these values.

    So, using the Dialog Expert, we can create a Multipage dialog, using a

    TabbedNotebook, and give the three pages the names "Currency", "Date"


    "Time". Next, we must drop a label on each of the pages, set autosize


    each label to false, and make them about as big as the entire notebook (so

    multiple lines can be viewed). The source code for the form merely consists

    of putting the right values on the right places when the form is created (in

    the OnCreate handler), so nothing complex at all for the interface side of

    the SysInfo Wizard. The engine of TSysInfoExpert is as follows: TSysInfoExpert

    GetStyle: esStandard

    GetIDString: DrBob.TSysInfoExpert

    GetName: SysInfo Wizard

    GetAuthor (win32): Bob Swart (aka Dr.Bob)

    GetMenuText: &SysInfo Wizard...

    GetState: [esEnabled]

    GetGlyph: 0

    GetPage (win32):


     method of the SysInfo Wizard is almost as easy, since allThe Execute

     we need

    to do is to create, show and free the form with the desired information.

    That's it. The source code for the Execute procedure is as follows:

     procedure TSysInfoExpert.Execute;


     with TSysInfoForm.Create(nil) do





     end {Execute};

    And presto! Our first "useful" Wizard, showing information at design time

    that isn't available any other way:

    This is only the first of many examples where we will see an Wizard engine

    that will show an interface form to show (or get) information to the user.

    One source of information to provide (or actions to apply) can be obtained

    from the so-called toolservices interface Delphi offers us in the TIToolServices class.

    4. ToolServices

    We've seen some generic but in fact pretty much useless Wizard so far. In

    order to write truly more useful Wizards, we need to do something special

    inside the Execute method, like show a (more) interesting form in which a lot

    of things can happen, a bit like we introduced with the TSysInfoExpert.

Report this document

For any questions or suggestions please email