Installing .NET Components into C#Builder using ToolsAPI - by Alain "Lino" Tadros

By: Lino Tadros

Abstract: This article will explain the preferred method of installing .NET components into the C#Builder IDE using the Open Tools API.

Untitled Document We were in the middle of shipping the first component suite by ComponentScience Inc, a subsidiary of Falafel Software Inc., called Elements(Ex) where we needed to integrate the components right into the C#Builder IDE upon install. Because of its name, we figured that the interface called “IOTAComponentInstallService“ would be important to us.

Because of a lack of documentation, we decided to open the assembly “Borland.Studio.ToolsAPI” ourselves and snoop around.
We used Reflector, available from http://www.aisto.com/roeder/dotnet/


As you can see the methods of the Interface seemed very interesting, especially the “Add” method which takes a ToolBoxItem and a Category.

We also wanted to use the IOTASplashScreenService and IOTAAboutBoxService as described in Erik Berry's excellent article on the BDN.

If you read Erik’s article already you know by now that you need to implement a static void function called “IDERegister” that will be called by the C#Builder IDE upon starting if a correct path of your assembly is found in the registry under KnownIDEAssemblies.

Great! So we decided to implement the IOTAComponentInstalService inside of the IDERegister as well, besides the other 2 interfaces for the Splash Screen and About Box.
This, by the way, proved to be the wrong route, because we needed the Splash Screen and About box code to be called every time the IDE starts but that is not necessary for installing the components onto the ToolBox.

So after talking to Allen Bauer and Corbin Dunn on the IDE architecture team at Borland and getting some feedback from Ray Konopka, (King of Component Design), Sean Winstead, ComponentScience’s Senior Architect, decided to create 2 design time packages. One for the SplashScreen and AboutBox that always gets called from inside of the IDERegister, in the main assembly for the components, and another design time assembly that runs only once after installation so that the C#Builder IDE creates a category and installs all components from Elements(Ex) into it.

It was the best way to accomplish the task knowing that we will need to integrate into Visual Studio using the VSIP and also Delphi 8 when it becomes available. It is cleaner and more maintainable that way.

Ok, now let’s tackle the challenges we had to overcome for installing these components onto the ToolBox:

First, the “Add” method of the IOTAComponentInstallService had a parameter of type ToolBoxItem which needed to be passed in for each component we wanted to “Add” defining the ToolBoxItem object.

Well, this is where “reflection” comes in handy.
While writing components in .NET you will find a special attribute specifying whether the component is supposed to be a ToolBoxItem component.

[ToolboxItem(true),
ToolboxBitmap(typeof(Bitmaps.Utilities), "CsLabel.bmp")]
public class CSLabel : ExBaseControl 
{
    
}

The first attribute for the component above specifies that it is a ToolBoxItem component.
So if we mark all our components with this attribute, we can easily load the assembly when C#Builder starts and use reflection to request all components with that attribute set to true and start creating an array holding all references to these ToolBoxItems, like in the following code:

private ArrayList GenerateToolboxItems() 
{
  // Generate one item per component in each product assembly.
  ArrayList items = new ArrayList();
  // _assemblies is a member of the class containing all the names of the
  // assemblies we need to check for components.
  foreach (Assembly assembly in _assemblies) {
  // Use reflection to get the controls and components in this assembly
  // having a ToolboxItem attribute.
  Type[] types = assembly.GetExportedTypes();
  foreach (Type type in types) 
  {
    object[] attribs =   type.GetCustomAttributes(typeof(ToolboxItemAttribute), false);
          if (attribs.Length > 0) 
    {
      ToolboxItemAttribute toolboxItemAttrib = (ToolboxItemAttribute) attribs[0];
            if (toolboxItemAttrib.ToolboxItemType != null) 
{
              ToolboxItem item = new ToolboxItem();
              item.AssemblyName = type.Assembly.GetName();
              item.DisplayName = type.Name;
              item.TypeName = type.FullName;
              object[] bitmapAttribs = type.GetCustomAttributes(typeof(ToolboxBitmapAttribute), false);
              if (bitmapAttribs.Length == 1) {
                ToolboxBitmapAttribute bitmapAttrib = (ToolboxBitmapAttribute) bitmapAttribs[0];
                item.Bitmap = (Bitmap) bitmapAttrib.GetImage(null);
              }
              items.Add(item);
            }
          }
        }
      }
      return items;
    }

So as you can see from the previous code, the return of the GenerateToolBoxItems function is an ArrayList of items that contain the assembly name, display name, type name and bitmap of each component.

Now that we have that ArrayList, we are much closer to calling the Add method on the IOTAComponentInstallService for each item in the array.


foreach (ToolboxItem item in GenerateToolBoxItems())
  componentInstallService.Add(item, Elements(Ex));

Pretty cool! Huh?


Ok, so we did that! Still 2 problems:
First, how can we limit the execution of the code for the integration into the toolbox to once instead of having to fire this code everytime the IDE starts?
What if the user changes the name of the category in C#Builder after the first run, he or she will end up having 2 categories of the same components? Not cool!

Thanks to Corbin Dunn, I was told to make the value of the entry to the design time assembly under HK_CurrentUserSoftwareBorlandBDS1.0Known IDE Assemblies equal to “RunOnce”, then the IDE will delete the entry from the registry after the first run. It worked perfect! Thanks Corbin!

The final problem was what happened when we added the registry entry to C#Builder, started C#Builder and found the Elements(Ex) category with all of our components installed (it was a happy moment ?) but when we shut down the IDE and brought it back up again, no more Elements(Ex) category or components ? (Not a happy moment)
This is when Corbin helped one more time to let us know that calling “SaveState” on the IOTAComponentInstallService causes the IDE to stream all of its ToolBox to an XML file under C:Documents and Settings<UserName>Application DataBorlandBDS1.0ApplicationSettings.xml and the IDE calls “LoadState” every time it loads to loads the ToolBox state from that file.

// Tell the component install service to save its state so that
// our changes are persistent.
   componentInstallService.SaveState();

Hope this information will save you some time in the future implementing similar integration routine..
Till next article, have fun!

Falafel Software is all about making the most of software development technology in order to complete the project on time and on budget with best possible user experience. Falafel Software offers a comprehensive suite of software development solutions ranging from strategy to design to implementation that businesses need in order to realize high returns on their investment.

 

Copyright ) 2003 Alain Tadros, Falafel Software Inc.
ALL RIGHTS RESERVED. NO PART OF THIS DOCUMENT CAN BE COPIED IN ANY FORM WITHOUT THE EXPRESS, WRITTEN CONSENT OF THE AUTHOR.


Server Response from: ETNASC04