Erik’s Open Tools API FAQ
- What is the Open Tools API?
- Where is the Open Tools API documented?
- Where can I get help with my Open Tools API questions?
- What is the “old” OTA and what is the “new” OTA? Which one should I use?
- Where can I get a simple wizard/expert to customize?
- Can I create wizards in C++Builder?
- Can I install a Delphi-created wizard into C++Builder?
- Should I ever call Release on an interface obtained from the IDE?
- How can I add published properties to a TForm descendent?
- How do I obtain the current project (IOTAProject) interface?
- How do I obtain the current project group (IOTAProjectGroup) interface?
- How can I obtain the IOTAProjectResource interface for a given project?
- How can I get a list of all installed components?
- Is there any OTA support for creating type libraries or controlling the type library editor?
- Is there any OTA support for parsing a source file or obtaining Code Insight information?
- Should I compile my wizard as a DLL or a Package?
- Why does my wizard have to dynamically link to the VCL/DesignIde packages?
- How do I get a hold of the designer for a DataModule?
- How do a I get a reference to an IOTAXxxx interface?
- How can I debug a DLL wizard?
- How can I debug a package wizard?
- How do I implement an IDE notifier (IOTAIDENotifier)?
- How do I implement an editor notifier (IOTAEditorNotifier)?
- How do I implement a form notifier (IOTAFormNotifier)?
- How can I get notified when a source file has changed?
- How can I get notified when a form designer or component is selected?
- How can I add a menu item to the IDE’s main menu?
- How can I add a shortcut to my main menu item?
- How can I paint the palette bitmap for a specific component?
- How do I implement a module creator (IOTAModuleCreator/IOTAFormWizard)?
- How do I implement a project creator (IOTAProjectCreator)?
- How can I save/load values to a project’s desktop settings (.dsk) file?
- How can I add menu items to the code editor’s popup menu?
- How does the native OTA deal with UNICODE strings in the editor?
- How can I iterate over all units/forms in a project?
- How can I do custom painting or drawing (colors, lines, etc.) in the IDE code editor?
- How can I publish a property of type T[Custom]Form?
- How can I create a form that docks into the IDE like the Object Inspector?
- How can I obtain the currently active form editor (IOTAFormEditor)?
- How can I obtain the current form designer interface (IFormDesigner)?
- How can I get a form editor from a module interface?
- Is there a way to determine if the user is editing a form or working in the code editor?
- How can I obtain the Name property of a form?
- How can I create a method and then assign an event handler at design-time?
- How can I force the code editor to show a specific file tab?
- How can I determine the filename of the binary/exe/dll/bpl/ocx/etc. generated by a compile or build?
- Known bugs in the Delphi 2007 Open Tools API (most also apply to earlier releases):
- Known bugs in the BDS 2006 Open Tools API (most also apply to earlier releases):
- Known bugs in the Delphi 2005 Open Tools API:
- Known bugs in the Delphi 7 Open Tools API (most also apply to Delphi/BCB 6):
- Known bugs in the Delphi 6 Open Tools API (most also apply to Delphi/BCB 5):
- Known bugs in the C++Builder 5.01 Open Tools API:
- Known bugs in the Delphi 5.01 Open Tools API:
If you have additions or corrections, please contact me. But, please do not send me questions about how to use the Open Tools API – instead, use the resources listed below.
What is the Open Tools API?
The Open Tools API (OTA) is a set of interfaces that allow developers to add features to the BDS, Delphi, and C++Builder IDEs. These additions are called wizards or experts. Wizards can use the OTA interfaces to modify the IDE, obtain information about the IDE’s state, and receive notification of important events. To create wizards, you should first get an IDE version that includes the VCL source (Professional, Enterprise, Architect, etc.) since these versions also include the interface definitions in ToolsAPI.pas that will make your programming easier.
Where is the Open Tools API documented?
In the latest help update for Delphi 6, in C++Builder 6, and in Delphi 7 the OTA is fairly well documented. Open the *iota.hlp file and look at the index there for details. For writing .NET addins in C#Builder and Delphi for .NET, also see my article on the EDN and the two IDE integration packages provided by CodeGear/Borland. Sadly, most of the official OTA documentation was removed and is not present anymore in Delphi 8-2007.
Where can I get help with my Open Tools API questions?
A good place to learn about the Open Tools API is is the ToolsAPI.pas unit and the related files in that directory such as PaletteAPI.pas, StructureViewAPI.pas, and CodeTemplateAPI.pas. If you don’t have any of those files, try reinstalling the IDE including the source code option, or maybe you need to upgrade to a higher-end IDE edition that includes the source files (Professional, Enterprise, Architect, etc.). All of the OTA interfaces are defined in those files, and many of them have comments about their purpose and usage.
Newsgroup Search Engines:
There are several free web services that allow you to search for answers to previously asked questions in the Open Tools API newsgroup. I recommend you search at least one of these before posting, as it generally gives multiple answers to the most common questions. Try one of the following:
|Search Site||Newsgroups||Date Range||Search Features||Speed|
|Google Groups||All||May 1981 – Now||Good||Fast|
|Code News Fast||CodeGear/Borland||Oct. 1997 – Now||Moderate||Moderate|
The Open Tools API Newsgroup:
Embarcadero runs a discussion forum that has an Open Tools API group in it. Before posting, please check the newsgroup search engines above for answers to your questions. You can access the forum on the web at https://forums.embarcadero.com/ under Delphi, Open Tools API or using an NNTP newsgroup reader via these instructions.
Other Web Sites:
- Ray Lischner’s Open Tools Resources – Ray’s web site is a bit out-of-date and only covers the interfaces through Delphi 3/4, but might still be useful to some people.
What is the “old” OTA and what is the “new” OTA? Which one should I use?
The “old” Open Tools API (OTA) was the preferred method for addins to interface with the IDE in Delphi 3 and older and was class-based. The “new” OTA is present in Delphi/BCB 4 or later and is interface-based. This FAQ only covers the new OTA, which consists mainly of the ToolsAPI.pas unit. Starting with Delphi 8 or greater, you will find a few more files that define the OTA such as PaletteAPI.pas, StructureViewAPI.pas, CodeTemplateAPI.pas, FileHistoryAPI.pas, DesignerTypes.pas and PropInspAPI.pas. The older OTA is depreciated and should no longer be used except to maintain compatibility with older IDE versions. Support for the old OTA will be completely dropped in a future version of Delphi, and existing bugs in it are not being fixed. The old Open Tools API consists of the following units: ExptIntf, FileIntf, IStreams, ToolIntf, VcsIntf, VirtIntf.
Where can I get a simple wizard/expert to customize?
Here is the Pascal source for the simplest “Hello World!” wizard using the Open Tools API. Just compile and install this package (DPK) into the IDE, and try out the new menu item on the Help menu. This example was written and tested in Delphi XE but should work in several other recent IDE versions. Older IDE versions such as Delphi 5/6/7 and earlier will require changes to the package list in the DPK.
Can I create wizards in C++Builder?
Yes. The Open Tools API was originally designed with Delphi in mind, so wizards might be easier to create in Delphi, but C++Builder works fine to create native code IDE addins.
Can I install a Delphi-created wizard into C++Builder?
Yes, GExperts is one example of an expert written in Delphi that can be compiled and installed into C++Builder.
Should I ever call Release on an interface obtained from the IDE?
It is not necessary to call Release on an IDE interface obtained via the Open Tools API. The interfaces are reference counted for you, and the associated memory will be freed as soon as all interface references go out of scope. Note that you can force the IDE to release an interface by setting all references to nil.
How can I add published properties to a TForm descendent?
- Add published properties to a regular TForm
- Add the form to the Object Repository (Project menu)
- Add the form to an existing design time package (such as Borland User Components) or to a new design-time package.
- Add DsgnIntf/DesignIntf to the implementation uses clause of a unit in the package, and add a Register procedure as follows:
procedure Register; begin RegisterCustomModule(TMyForm, TCustomModule); end;
- Finally, inherit from your form in the repository inside a project and the new published properties will show up. Remember to not try to link to the new designtime package when building your deployable application.
There is also a much more complex method involving writing a module creation expert, a repository expert, and using CreateModuleEx and different streams, but is much more error-prone and for most people, has no advantages.
Note that the IDE will not allow you to add both published properties and components to a custom module at the same time. The workaround is to create a form with your custom properties in a package, and then have a descendent form in the repository which adds the components you want there by default.
How do I obtain the current project (IOTAProject) interface?
Starting with Delphi 7, you can use the function GetActiveProject. For previous releases, you can iterate through all of the modules to find the project group and then get that group’s active project:
function GetCurrentProject: IOTAProject; var ModServices: IOTAModuleServices; Module: IOTAModule; Project: IOTAProject; ProjectGroup: IOTAProjectGroup; i: Integer; begin Result := nil; ModServices := BorlandIDEServices as IOTAModuleServices; for i := 0 to ModServices.ModuleCount - 1 do begin Module := ModServices.Modules[i]; if Supports(Module, IOTAProjectGroup, ProjectGroup) then begin Result := ProjectGroup.ActiveProject; Exit; end else if Supports(Module, IOTAProject, Project) then begin // In the case of unbound packages, return the 1st if Result = nil then Result := Project; end; end; end;
How do I obtain the current project group (IOTAProjectGroup) interface?
Starting with Delphi 7, you can use the GetActiveProjectGroup function. For earlier releases, you can iterate through all of the modules to find the one that implements IOTAProjectGroup:
function GetCurrentProjectGroup: IOTAProjectGroup; var ModServices: IOTAModuleServices; ProjectGroup: IOTAProjectGroup; Module: IOTAModule; i: Integer; begin Result := nil; ModServices := BorlandIDEServices as IOTAModuleServices; for i := 0 to ModServices.ModuleCount - 1 do begin Module := ModServices.Modules[i]; if Supports(Module, IOTAProjectGroup, ProjectGroup) then begin Result := ProjectGroup; Break; end; end; end;
How can I obtain the IOTAProjectResource interface for a given project?
Note that there is a bug in Delphi 2005/2006 that may prevent this from working.
function GetProjectResource(Project: IOTAProject): IOTAProjectResource; var i: Integer; Editor: IOTAEditor; begin Result := nil; for i:= 0 to (Project.GetModuleFileCount - 1) do begin Editor := Project.GetModuleFileEditor(i); if Supports(Editor, IOTAProjectResource, Result) then Break; end; end;
How can I get a list of all installed components?
See IOTAPackageServices.GetComponentName in ToolsAPI.pas.
Is there any OTA support for creating type libraries or controlling the type library editor?
If you look at IOTATypeLibEditor and IOTATypeLibModule, you’ll see there might be internal plans to add some support, but it isn’t implemented yet. For now those interfaces can only be used to see if a file is a type library or not. Apparently, COM provides some services to create type libraries.
Is there any OTA support for parsing a source file or obtaining Code Insight information?
C#Builder, Delphi 8+, and BDS support obtaining the .NET CodeDom for C# and Delphi code using IOTACodeDomProvider, but older IDEs do not expose unit structure details or Code Insight information such as method parameter lists, symbol declaration locations, class members, and symbol table information. As a result, you will need to parse the source yourself or use an existing language parser such as those at Torry’s Delphi Pages (search for mwDelPar or mwPasPar, for examples). Starting with Delphi 7, you can implement your own code completion and parameter hints via the IOTACodeInsight* interfaces, but the source parsing is still up to you.
Should I compile my wizard as a DLL or a Package?
Packages are easier to load and unload without restarting the IDE (and hence easier to debug), but they can create unit naming conflicts in the IDE. Conflicts happen when the name a wizard’s unit matches the name of a unit in another loaded design-time package. In this case, both packages can not be loaded at the same time. The recommended workaround is to prefix all of your unit names with a “unique” prefix. GExperts, for example, uses “GX_” as the name prefix for its units.
Why does my wizard have to dynamically link to the VCL/DesignIde packages?
If you want access to a useful BorlandIDEServices global variable from ToolsAPI.pas, you must compile your wizard linking to the DesignIde package (in Deplhi 6+) or the VCL package (in Delphi 4/5). See the Packages tab in the Project Options dialog for help on compiling with packages.
How do I get a hold of the designer for a DataModule?
The owner of the datamodule at design-time is a TCustomForm which has a Designer property.
How do a I get a reference to an IOTAXxxx interface?
In general, just search ToolsAPI.pas for a method that returns the interface type you are looking for. But, sometimes things are a little trickier, and you have to use Supports or QueryInterface to find what you want:
How can I debug a DLL wizard?
- Exit your IDE
- Remove any registry entries which load the expert DLL into your IDE. Look in HKEY_CURRENT_USER\Software\Borland\Delphi\X.0\Experts.
- Start your IDE, and verify the expert is not loaded.
- Compile your expert DLL. In the project options, be sure to turn on debug information, stack frames, reference info, etc.
- Turn optimizations off.
- Re-register the DLL with the IDE by adding an entry to HKEY_CURRENT_USER\Software\Borland\Delphi\X.0\Experts.
- Select Run, Parameters from the IDE menu. Enter the IDE’s executable as the host application for your DLL.
- Run the host application (F9), and another copy of your IDE should appear with the expert loaded.
- You can now debug the DLL as it were a normal program (watches, breakpoints, inspections, tooltip evaluation, etc.).
- Note that package debugging does not work well in Delphi 4 and BCB 4. Both will lockup fairly often when debugging DLLs and packages. Later releases should work better.
How can I debug a package wizard?
- In the project options for your package, turn on debug information, stack frames, reference info, etc. Turn optimizations off.
- Uncheck your package in the Project Options Packages tab, if necessary.
- Build your package (don’t install it).
- Select Run, Parameters from the IDE menu. Enter the IDE’s executable as the host application for your package.
- Run the host application (F9), and another copy of your IDE should appear.
- In the second copy of the IDE, open up the Project Options and load your expert package into the IDE.
- You can now debug the package as it were a normal program (watches, breakpoints, inspections, tooltip evaluation, etc.).
- Note that package debugging does not work well in Delphi 4 and BCB 4. Both will lockup fairly often when debugging DLLs and packages. Later releases should work better.
How do I implement an IDE notifier (IOTAIDENotifier)?
Create an object that implements all of the IOTAIDENotifier and descendent interface methods. Then register the notifier using IOTAServices.AddNotifier and watch for notification callbacks from the IDE. Be sure to call IOTAServices.RemoveNotifier when you are done. Here is a Delphi 5/6 example IOTAIDENotifier.
How do I implement an editor notifier (IOTAEditorNotifier)?
Declare something similar to this:
TTestEditorNotifier = class(TNotifierObject, IOTAEditorNotifier) public procedure Destroyed; override; procedure ViewActivated(const View: IOTAEditView); procedure ViewNotification(const View: IOTAEditView; Operation: TOperation); end;
Override any of the other IOTANotifier methods you need and add the notifier using IOTASourceEditor.AddNotifier. In the Destroyed notification remove the notifier using IOTASourceEditor.RemoveNotifier and the index returned from AddNotifier. Here is a sample notifier package that works in BDS 2006.
How do I implement a form notifier (IOTAFormNotifier)?
Declare something like this:
TMyFormNotifier = class(TNotifierObject, IOTANotifier, IOTAFormNotifier) protected procedure FormActivated; procedure FormSaving; procedure ComponentRenamed(ComponentHandle: TOTAHandle; const OldName, NewName: string); end;
Implement all of the above methods, even if they are blank. Finally, add your notifier using IOTAModule.GetModuleFileEditor(0).AddNotifier. Note that in Delphi 5, form notifiers won’t actually fire notifications for BeforeSave or AfterSave (use a module notifier for this). It should fire the notifications in the declaration above, as well as Destroyed and Modified from IOTANotifier.
How can I get notified when a source file has changed?
Starting in Delphi 6, IOTAEditorNotifier works for this. In Delphi 4/5 IOTAEditorNotifier never fires, so instead, attach a module notifier using IOTAModule.AddNotifier and watch for IOTAModuleNotifier.Modified.
How can I get notified when a form designer or component is selected?
When a form is activated, IOTAFormNotifier.FormActivated fires. If you are willing to add a notifier to every open form, this event will provide you with direct notifications. If you would rather not install a notifier for every open form, try using IDesignNotification.SelectionChanged. You will need to register your IDesignNotification interface with the IDE by calling RegisterDesignNotification and check the active form in each SelectionChanged callback.
How can I add a menu item to the IDE’s main menu?
Use INTAServices.GetMainMenu to obtain a reference to the IDE’s TMainMenu component. Iterate through all of the top-level menu items and find the parent menu item you want to add a menu item to. Then, use MyMenuItem.Insert() to add the menu item. GExperts has an IDE menu item expert that shows all IDE main menu items and their names. To handle selection of your menu item and associate an icon with it, will need to add an action to the main IDE action list, add an image to the IDE’s image list, and set the action’s ImageIndex and OnExecute properties. See INTAServices40 for the related methods. You may also find this tutorial (broken link?) by Miha Remec useful.
How can I add a shortcut to my main menu item?
In Delphi 5+, you should register a shortcut for your menu item using the keybinding interfaces:
- Implement IOTAKeyboardBinding and make sure GetBindingType returns btPartial
- Add your binding to the IDE using IOTAKeyboardServices.AddKeyboardBinding
- In the IOTAKeyboardBinding.BindKeyboard callback, use the passed in IOTAKeyBindingServices reference to call BindingServices.AddKeyBinding for each menu item, as described below:
- Pass in a TKeyBindingProc type procedure callback method for when your shortcuts are pressed procedure Callback(const Context: IOTAKeyContext; KeyCode: TShortcut; var BindingResult: TKeyBindingResult);
- Pass in the menu item’s name as the last parameter (the ill named “HotKey” in Delphi 5). The call should look similar to this:
AddKeyBinding([ShortCut(Ord('G'), [ssCtrl])], Callback, nil, 0, '', 'MyMenuItem');
In Delphi 4, you will need to wait a few seconds after the IDE starts and set the menu item’s ShortCut property. A timer works well to create the delay. Here is a sample keybinding unit that should work in Delphi 6 – Delphi 2007 to get you started. You will need to customize it to handle your menu item, as described above.
How can I paint the palette bitmap for a specific component?
In Delphi 4/5 you need to use the LibIntf unit, which is unsupported and undocumented, but try:
In Delphi 6+ you can try to load the component bitmaps from the .bpl package resources manually (by name) or try using the ComponentDesigner unit similar to this:
var PaletteItem: IPaletteItem; PalettePaint: IPalettePaint; begin with ComponentDesigner.ActiveDesigner.Environment do PaletteItem := GetPaletteItem( TComponentClass(FindClass('TButton'))) as IPaletteItem; PalettePaint := PaletteItem as IPalettePaint; PalettePaint.Paint(ACanvas, X, Y);
How do I implement a module creator (IOTAModuleCreator/IOTAFormWizard)?
Descend from TInterfacedObject and implement all of the methods in IOTACreator and IOTAModuleCreator as follows:
TGxModuleCreator = class(TInterfacedObject, IOTACreator, IOTAModuleCreator)
Here is sample code for a Delphi 6-2007 module creator that resides in the File, New, Other repository screen by implementing IOTARepositoryWizard and the other required interfaces.
How do I implement a project creator (IOTAProjectCreator)?
Descend from TInterfacedObject and implement all of the methods in IOTACreator and IOTAProjectCreator as follows:
TGxProjectCreator = class(TInterfacedObject, IOTACreator, IOTAProjectCreator)
Here is sample code for a project creator that installs itself into the Help menu.
How can I save/load values to a project’s desktop settings (.dsk) file?
In the FileNotification method of an installed IOTAIDENotifier, check for the ofnProjectDesktopLoad and ofnProjectDesktopSave NotifyCode values. When you see one of those, you can save/load values from the file indicated by the FileName parameter using a class such as TIniFile.
How can I add menu items to the code editor’s popup menu?
IOTAEditView.GetEditWindow.Form.FindComponent(‘EditorLocalMenu’) will return the editor’s TPopupMenu component that you can add to. Your added menu items will work best if you add them to the end of the popup menu and may not work at all if you associate an action with them. Note that you might want an IOTAEditorNotifier to determine when to add your new menu items to new editor windows.
How does the native OTA deal with UNICODE strings in the editor?
Starting with Delphi 8, the OTA will usually return UNICODE editor content as UTF-8 encoded strings and expect UTF-8 strings when writing into the editor. This means that for many common low-ASCII characters (the most common in the Latin alphabet), the encoding will not differ form the ASCII encoding, but for some other characters, such a German umlaut, the UTF-8 encoding will result in 2-bytes in the string for this character. You can convert from UTF-8 returned strings to ANSI using Utf8ToAnsi and convert from ANSI strings to UTF-8 for insertion into the editor using AnsiToUtf8.
How can I iterate over all units/forms in a project?
IOTAProject.ModuleCount can be used to iterate over all modules, and IOTAProject.GetModule returns a reference to IOTAModuleInfo which gives you the FileName, FormName, etc.
How can I do custom painting or drawing (colors, lines, etc.) in the IDE code editor?
There isn’t any OTA support for custom drawing in the editor, beyond writing your own syntax highlighter (see IOTAHighlighter). All of the attempts to hack custom drawing into the code editor involve low level techniques such as windows hooks, runtime VMT patching, package export patching, etc. You have to manually calculate the position of characters onscreen, so you need to know a lot about how the editor draws characters, what portions of the code are folded, how the IDE painting changes with certain fonts and with italics involved, etc. All of these methods are complex, cause extra flickering in the editor, are difficult to debug, can cause IDE instability, and can also slow the editor down enough that it becomes less usable on slower machines.
How can I publish a property of type T[Custom]Form?
It doesn’t work very well, but you can try to publish the property normally. The problem is that only currently created forms will be shown in the property editor, and storing an internal reference to one of those forms will often cause AVs when the target form is later closed. As a workaround, you can create a custom property editor that uses IOTAProject as above to get the class names of all forms in the project and insert them into the dropdown list for the property editor. Then store the form reference internally as a class name string, and use something like GetClass and RegisterClass to map a class name to a class type. With the class type, you can create the form at runtime. If you are sure your target form will always exist at runtime, another option to map from classes to instances is to search the Screen.Forms array.
How can I create a form that docks into the IDE like the Object Inspector?
You need to descend from TDockableForm in the DesignIDE package’s DockForm unit and perform some magic to register your dockable form with the IDE. Here is a docking form example should work in Delphi 7 – 2007 and shows how to declare a basic docking form. Compile the included package, install it into the IDE, and then look at the new menu item in the Help menu. Also, see Allen Bauer’s article on the CodeGear web site for an overview and a Delphi 5 example.
How can I obtain the currently active form editor (IOTAFormEditor)?
function GetActiveFormEditor: IOTAFormEditor; var Module: IOTAModule; Editor: IOTAEditor; i: Integer; begin Result := nil; Module := (BorlandIDEServices as IOTAModuleServices).CurrentModule; if Module nil then begin for i := 0 to Module.GetModuleFileCount - 1 do begin Editor := Module.GetModuleFileEditor(i); if Supports(Editor, IOTAFormEditor, Result) then Break; end; end; end;
How can I obtain the current form designer interface (IFormDesigner)?
function GetActiveFormDesigner: IFormDesigner; var FormEditor: IOTAFormEditor; begin Result := nil; FormEditor := GetActiveFormEditor; if FormEditor nil then Result := (FormEditor as INTAFormEditor).FormDesigner; end;
How can I get a form editor from a module interface?
You should iterate through all of the ModuleFileEditors, to see which one implements IOTAFormEditor:
function GetFormEditorFromModule(Module: IOTAModule): IOTAFormEditor; var i: Integer; Editor: IOTAEditor; begin Result := nil; if Module = nil then Exit; for i := 0 to Module.GetModuleFileCount - 1 do begin Editor := Module.GetModuleFileEditor(i); if Supports(Editor, IOTAFormEditor, Result) then Break; end; end;
Is there a way to determine if the user is editing a form or working in the code editor?
Starting with Delphi 6, there is IOTAModule.GetCurrentEditor for this purpose. If you find these methods don’t work for .h files in C++Builder try IOTAEditorServices.TopBuffer.FileName. You can’t do this using only the new Open Tools API in Delphi 5, but you can use the old API to get this information from ToolServices.GetCurrentFile.
How can I tell when a module was removed from a project?
The old OTA had an fnRemovedFromProject notification flag, but this doesn’t exist in the new OTA. As a workaround, you can watch for changes to the project file using an IOTAEditorNotifier and the iterate over all modules to see which one might have been deleted, if any.
How can I obtain the Name property of a form?
If the form is part of a loaded project, use IOTAModuleInfo.GetFormName. If the form is not part of a loaded project, you can try IOTAFormEditor.GetRootComponent.GetIComponent.Name, but it isn’t always reliable (it can cause AVs of the form is not visible). A more reliable method might be to force the form visible before calling GetRootComponent or you could manually scan the associated DFM stream for the form’s Name property.
How can I create a method and then assign an event handler at design-time?
You need to use I[Form]Designer.CreateMethod from the DsgnIntf/DesignIntf unit and then SetMethodProp from the TypInfo unit. Here is a simple Delphi 5/6 example that uses both of these functions to create and assign an OnClick handler for the current form.
How can I force the code editor to show a specific file tab?
You can either use IOTASourceEditor.Show or call IOTAActionServices.OpenFile and pass in the full path and filename of the file tab to activate.
How can I determine the filename of the binary/exe/dll/bpl/ocx/etc. generated by a compile or build?
- For Delphi 8 or greater, use IOTAProjectOptions.TargetName.
- For earlier releases, the method is a lot more complex to implement because it involves potentially scanning for the $E directive that specifies the executable file extension for the project, and then looking for the binary file on the path specified by the “OptputDir” project option, or the project directory if that option is blank (among many other possibilities and complexities). The best way to implement such a tool might be to start with the sample code in CodeGear CodeCentral sample ID 19823.
Known Open Tools API Bugs
Known bugs in the Delphi 2007 Open Tools API (most also apply to earlier releases):
- DLL experts can cause shutdown crashes under Vista and the Windows “This program has stopped working” dialog appears (QC 44578).
- If you try to delete a component from a form using IOTAComponent.Delete, and that component is inherited from an ancestor form, the following incorrect error appears “Cannot rename component Button1, the component was introduced on ancestor form.” and then the IDE gives an infinite sequence of AVs when closing the module or trying to shut down the IDE (RAID 203770).
- The indexes used for values returned by IOTAComponent.GetPropName(i) for VCL.NET do not match the indexes needed by by IOTAComponent.SetProp(i, Value), so the Index versions of GetPropValue and SetProp are unusable, since there is no way to know the right index to pass in (RAID 204184).
- After changing the name of a created project using IOTAProject.FileName, the Project Manager does not properly update the full path/file name displayed for the project (RAID 205616).
- The IOTAProjectMenuCreatorNotifier method does not fire for the project group node (RAID 213512).
- IDesigner.GetProjectModules never fires the passed callback method (QC 20589).
- You must implement IOTARepositoryWizard80 for all of the repository wizards (File|New dialog items) or they will not show up in the IDE (QC 20898).
- SplashScreenServices.AddProductBitmap does nothing with only a single personality loaded (use SplashScreenServices.AddPluginBitmap instead) (QC 42320).
Known bugs in the BDS 2006 Open Tools API (most also apply to earlier releases):
- Calling IOTAActionServices.OpenProject on any .dpr file results in the error “Invalid at the top level of the document.”. You have to use IOTAActionServices.OpenFile, even though this is a project (RAID 203690).
- IOTAProjectResource is no longer implemented by any of the project’s editors (QC 15657)
- RemoveMenuCreatorNotifier does not actually remove the INTAProjectMenuCreatorNotifier from the project manager (QC 38098).
- Adding a project to the existing project group fails unless the project is a package (QC 38281).
Known bugs in the Delphi 2005 Open Tools API:
- If you register a custom module using RegisterCustomModule, you will be unable to use the embedded designer with that form. The designer will always float, and you must use F12 to focus the form (fixed in BDS 2006).
Known bugs in the Delphi 7 Open Tools API (most also apply to Delphi/BCB 6):
- IOTAComponent.GetParent always returns nil.
- Calling IOTAEditView.SetTempMsg makes the code editor’s Diagram tab disappear when clicking back in the source code editor.
- Several of the IOTAProjectOptions do not work such as IncludeVersionInfo and ModuleAttribs. Also, some useful options are missing such as BreakOnException. Some options such as LibraryPath are not persisted across sessions.
- The HowMany parameter of IOTAEditPosition.Delete is ignored. As a result, the method always deletes one character.
- IOTASourceEditor.SetSyntaxHighlighter is deprecated and can no longer be used
- Setting IOTAEditView.CursorPos doesn’t update the edit window’s cursor position in the status bar.
- The IDE does not remove instances of IOTACustomMessage from the message view before unloading an expert. This can result in crashes as the IDE calls back into an unloaded library. The workaround is to call ClearToolMessages before your expert unloads, if it added custom messages.
- IOTAToDoManager.ProjectChanged is never called.
- You can’t add a keyboard binding for keys like Ctrl+/ and Ctrl+K.
- IOTAResourceEntry.DataSize must be divisible by 4 (aligned to a 4-byte boundary), or you will get an RLINK32 error when compiling.
Known bugs in the Delphi 6 Open Tools API (most also apply to Delphi/BCB 5):
- TIModuleInterface.GetFormInterface is deprecated and always returns nil. You must use IOTAFormEditor instead.
- The Open Tools keybinding interfaces sometimes raise AVs when using IOTAKeyBoardServices.AddKeyboardBinding.
- IOTAEditView.PosToCharPos raises an AV in dfwedit.dll every time it is used.
- IOTAEditorServices.TopView raises an AV in the coride package if called with no files open.
Known bugs in the C++Builder 5.01 Open Tools API:
- Given a regular unit without an associated form, calling IOTAModule.GetModuleFileCount returns 2 but IOTAModule.GetModuleFileEditor called with index 1 results in an AV and index 2 returns the .H file.
- Setting the LibDir project option using IOTAProjectOptions.Values results in an AV.
Known bugs in the Delphi 5.01 Open Tools API:
- Calling IOTAModuleServices.OpenProject on a BPG file will crash the IDE. Instead, use IOTAModuleServices.OpenFile.
- When querying IOTAProjectGroup.FileName, you won’t get a full pathname. Instead query the IOTAModule that implements IOTAProjectGroup for its FileName, and it will contain a full path.
- The project options MajorVersion, MinorVersion, Release, and Build don’t update the project options dialog when set.
- You can not use the keybinding interfaces to bind actions to keystrokes such as Ctrl+Enter, Shift+Enter, and non-shifted alpha-numeric characters.
- When opening a BPG file, IOTAIDENotifier will send a blank filename parameter along with ofnFileOpened into the FileNotification method.
- The IDE AVs or produces an I/O Error when specifying a file name without a complete path when implementing IOTAProjectCreator.GetFileName.