Did you know that it is so easy to extend Delphi 2009 IDE?
Delphi 2009 provides a dedicated Open Tools API interface that let you interact and extend the functionality of the Delphi and C++Builder 2009 IDE. "Eric’s Open Tools API FAQ and Resource" is probably the best place to learn about Open Tools API. Eric Berry is well known in Delphi community as author of GExperts extension to all versions of Delphi and also as expert on Tools API itself.
In this post I would like to provide step-by-step instructions how to create a simple menu wizard. The wizard will add an item under Delphi "Help" menu and will just display a "Hello World" message. Nothing fancy, but just a good starting point for adding some more useful functionality.
Create new Delphi "Package" project
- In Delphi 2009 "File" menu select "Other" and in the "New Items" dialog select a "Package" project from "Delphi Projects" category.
- Click on "Save All" from "File" menu and give the project "HelloWorldMenuWizard" name.
- Display "Project Options" dialog clicking on "Options" from "Project" menu and provide a description for a package project in "Description" tab.
This description will show up in the list of the installed packages.
- In the "Usage Options" select "Designtime only" as our wizard is only used inside the IDE.
Anything that is installed in the Delphi IDE lives in a package. You can think about a package as a special kind of DLL library. OpenTools API wizards and VCL components are installed into the IDE via packages. By convention a package that is designed to be installed in the IDE contains "Register" global procedure that is called by the IDE when installing a package. For IDE wizards in the body of "Register" routine you should call "RegisterPackageWizard" procedure defined in the "ToolsAPI" unit and pass a reference to a wizard instance that implements "IOTAWizard" interface.
Add an empty unit to the project
After creating a new package project you may think that nothing has happened as there is no default main form in a package project. In order to add code to our wizard we need to add an empty unit from "New Items" dialog.
- In Delphi 2009 "File" menu select "Other" and in the "New Items" dialog select "Unit" from "Delphi Files" category.
- Click on "Save All" and give the new unit a name. For example "uMain".
- Declare "Register" procedure with no parameters in the "interface" section of the unit and provide empty implementation for it. This procedure will be called by the IDE during registering the package.
In the body of the "Register" procedure we need to create an instance of our "Hello World" wizard and pass it to a call to "RegisterPackageWizard" procedure defined in the "ToolsAPI" unit. If you add "ToolsAPI" to the "uses" clause of the unit and try to compile your package project, you will get a compiler error "File not found: ToolsAPI.dcu’".
That is the tricky part. In which library package this unit is defined? It is in "designide.bpl" that is part of Delphi installation. You need to add "designide" to required packages of our package project. We do not have source code for all units in "designide" package, as Delphi is not an open source project, but luckily "ToolsAPI.pas" file is provided in the "source\ToolsAPI" subfolder of Delphi 2009 installation directory.
Add "designide" package to "Required" packages of package project
- Select "Add to Project" from "Project" menu. This will display "Add" dialog. Select "Requires" tab and click on the "Browse" button. Locate "designide.dcp" from the "lib" folder from the Delphi 2009 installation directory.
Now the Project Manager should look like this:
Now we are ready to add some code to the "uMain" unit to define a class that will act as our menu wizard.
It is more effective to do it in the "implementation" section of our unit. In this way the only functionality exposed to the external world from our wizard is "Register" procedure. The "RegisterPackageWizard" procedure expects "IOTAWizard" parameter, so this is the first interface that our wizard class needs to implement. The "IOTAWizard" wizard interface derives from "IOTANotifier" interface. To avoid implementing all methods defined in both interfaces we need to select an appriopriate class to derive from. "TNotifierObject" is a convinient base class as it already contains empty stub implementations for "IOTANotifier" methods. In the definition of our class we need to explicitly list all interfaces our wizard implements as the IDE will use reflection to find out capabilities of our wizard.
Write code, compile and install
The full source code of our wizard is listed below:
The "GetIDString" method returns a unique name for the wizard that is used internally by Delphi. The "GetName" method returns human-readable name of the wizard. The "GetState" function returns "TWizardState", which a set of two possible enumerated values: "wsEnabled" and "wsChecked". The "Execute" method is called when wizard needs to be executed and the "GetMenuText" returns a string to be displayed in Delphi.
- Replace the body of the "uMain" unit with the above code. If you do not want to type it in, you can download the whole project from here.
- Compile wizard project. One of way of doing this is to right-click in the Project Manager on the project and select "Compile".
- Install wizard in the IDE. Again right-click on the project node in the Project Manager and select "Install" from the context menu.
If successful you should see confirmation message about installing our wizard. Note that the resulting bpl file is located in different directory as project file.
On XP this would be: "C:\Documents and Settings\All Users\Documents\RAD Studio\6.0\Bpl\".
Our simple "Hello World" Open Tools API menu wizard has been installed and now you should be able to find a new item under Delphi "Help" menu.
If you click on the new item you should see the following message dialog displayed from the wizard.
If you want to verify that the wizard is installed or you want to uninstall it, you can do this from Component" menu "Installed Packages".
These are real basics. Next time I’m going to implement something more useful:-)