Distributing Components

   

Distributing Components

When considering distribution of your components, the file of most interest to you is the .bpl file. This is the package library that the IDE will use to make the components available at design time.

WARNING

Whenever you build your packages, be sure to check the Option source (Edit, Option, Source) of the package and remove any unnecessary library file entries in the <LIBRARIES> and <SPARELIBS> sections. If this is not done, you might find that you have many frustrated users looking for a .lib file that your package references, but that they don't have. However, because your package references the .lib file, they will not be able to use the package without it, even though it is not required for the correct operation of the package.


The files that must be distributed for each package to be useable are shown in Figure 4.6.

Figure 4.6. Files required for package distribution.

graphics/04fig06.jpg

As can be seen from Figure 4.6, in addition to the .bpl , .bpi , and .lib files previously mentioned, the header ( .h ) files for each unit appearing in the runtime-only package's Contains section must also be distributed. You should also distribute the header files of the units used in the design time-only package; the exception to this is the header file for the registration unit itself ”this is normally empty and is of no use. Typically, though, you would distribute the source for the registration unit so that the users of your components can see what changes your package will make to the IDE. Note that .rc files are shown as the resource files to store the Component Palette bitmaps for the components. There are alternatives to this: .res files or .dcr files can be used. This is discussed in the "Creating Component Palette Bitmaps" section later in this chapter. Consider also that if you do ship custom property and component editors, it should be possible for the user to derive new editors from them.

Remember from the "Understanding and Using Packages" section in Chapter 2, "C++Builder Projects and More on the IDE," that the .lib file for a package is effectively a collection of the package unit's .obj files. Distributing either the .lib file for the runtime-only package (that containing the source for the component(s)) or the .obj files for the component(s) will enable the components to be statically linked to the application, if the user so desires. The .lib file has the advantage that all the components' .obj files are contained in a single file, making file maintenance easier.

Where Distributed Files Should Be Placed

Where your distribution files are placed on the target computer can affect how easy they are to use. Using the default directories that C++Builder uses, shown in Figure 4.7, effectively guarantees that the user will not have to edit his project's direc-tory settings. Most people prefer that third-party components not be placed in to these directories. However, an appreciation of the structure that C++Builder uses is useful.

Figure 4.7. Default package directories.

graphics/04fig07.gif

Note that the naming convention shown in Figure 4.7 has been used for consistency. Also, source modules have not been shown because their placement is only of concern when compiling the package. The simplest alternative to using the default directories of Figure 4.7 is to place all the files belonging to a package into a single directory, with the exception of the runtime library .bpl files, which should be placed in Windows\System or an equivalent directory. This makes adding paths to a project's directory settings easier. For linking, the linker must be able to find the .bpi and .lib files of the runtime package ( assuming a runtime/design time pair), so the directory that contains these should be added to the global Library Path setting (Tools, Environment Options, Library). This can also be modified by editing the Registry key HKEY_CURRENT_USER\Software\Borland\C++Builder\6.0\Library\Search Path , where 6.0 is the version number of C++Builder (it could be 1.0, 3.0, or 5.0). Remember that if the directory (or directories) that contain your components is off the main C++Builder installation directory, you can use the $(BCB) string to represent the installation directory in any pathnames required.

Naming Packages and Package Units

The naming of packages and package units is of crucial importance when preparing components for distribution. Assuming that a runtime-only/design time-only package pair (or similar model) is adopted for packaging the components, we must name the runtime-only package (or packages) so that it can be distinguished from the design time-only registration package. Typically, an _R or _RTP is appended to runtime-only package names and _D or _DTP is appended to design time-only package names. An underscore is entirely optional.

Another consideration is that you should include a number that represents the version of the VCL that was used in the creation of the package. In the case of packages made for C++Builder 6, this would be 60 (the version number of the VCL in C++Builder 6). This makes it immediately obvious for which version of C++Builder the package is intended. For example, runtime-only packages could have _R60 appended to their names and design time-only packages could have _D60 appended to their names (again the underscore is optional). As long as this information is presented in some obvious way, the exact approach taken is immaterial. For example, the convention used for the VCL packages is to replace the V of VCL with a D (making DCL ) when the package is a design time-only package. The version number is appended to the end of the package name in both cases. For example, the package names for data access and data control components are VCLDB50.bpl and DCLDB50.bpl for the runtime-only and design time-only packages, respectively.

In addition to the naming of packages to reflect how the package should be used (design time or runtime), and the version of the VCL for which they are designed, the names must also be unique. It is not possible to install or use a package whose name clashes with an existing package in the same application.

The naming of units inside a package is equally as important as that of the package itself. Before we consider the possibilities, we must be aware that units exported from a package must be unique for any application that uses them (it is not possible to have units of the same name in more than one package if those packages are used simultaneously by the same application), and this includes the IDE.

Two cases must be considered . The first is the naming of units that would normally only be found in a design time-only package (units containing registration code and units containing property and component editors). The second is the naming of units normally found only in a runtime-only package (the units that contain the source for components).

When naming units in the design time-only package, you should include the package name in the unit name. This ensures that the unit name will be unique. For example, the unit containing the registration code could be called Registration_PackageName.cpp . Changing the word order, replacing Registration with Reg (and so on), and using underscores are all matters of personal choice; the essential thing is to keep the purpose of the unit clear and the name unique. Because the package must be unique, including this in the unit name helps ensure that it too will be unique. Therefore, for a design time-only package called NewComponentsD60 , the following are all suitable names for the registration unit of the package:

 Reg_NewComponentsD60  RegNewComponentsD60  Registration_NewComponentsD60  RegistrationNewComponentsD60 

Many more possibilities abound, but basically you simply need to adopt an approach and use it. The requirement for unit names to be unique precludes obvious choices for the names of units containing property and component editors. This means PropertyEditors.cpp and ComponentEditors.cpp are not good choices. The easiest way to ensure a unique name is as demonstrated earlier; append the package name to the unit name. However, there is an alternative to uniquely naming the units used to contain property and components editors. It is possible to use the #pragma package(smart_init,weak) directive in place of the normal #pragma package(smart_init) directive for these units. This results in these units being weakly packaged to the design time-only package. Effectively, the name of the unit becomes irrelevant because the unit itself is not added to the design time-only package; instead, the code from the unit is directly accessed when it is required. Everything will be fine as long as the property and component editor class names are unique.

The second case that should be considered when naming units is that of the units for the runtime-only package (or packages). If each unit is used to contain an individual component, it is sensible to use the component's name as the name of the unit, omitting the initial T . Because the component name must be unique (see the next section), following this convention will almost certainly guarantee that the unit name is unique. For units that contain several components, a sensible naming convention to follow is to choose a name that reflects the components contained within the package, and then append the package name to this name ”similar to the method presented for naming design time-only packages. If these guidelines are followed, the chances of name clash are small.

Naming Components

Choosing a suitable component name is very important. There are two considerations: First, the component name must be unique, and second, it must accurately represent the component's purpose.

Making sure the component name is unique is not as easy as it sounds. It is surprising how many components are made to accomplish the same task, such as encapsulating the serial port on a computer. There are only so many sensible variations of TComport that you can have. Because of this, developers serious about distributing their components normally assign a signature to the component name in the form of initials placed after the T and before the component name. Making such initials either all lowercase or all uppercase makes them easy to ignore. Some might find all- upper-case initials easier to ignore than all-lowercase letters because of the more symbolic nature of uppercase letters . The use of initials is similar to the approach taken when naming enumerations used by component properties. The choice of initials could be arbitrary or it could be the initials of your company. Assuming a company is called Components for Builder , the initials cfb could be used. Using our TComport example, the component's name becomes TcfbComport . It might not be pretty but, when the user has no access to the source of the component, it can be quite necessary. The likelihood of another component vendor using the same name and the same initials is slim. We mentioned user access to the source of a component. If the user has the full source for the component, the name of the component is not as important because the user can change it if he wants.

Choosing a name that adequately reflects the component's purpose can sometimes require careful thought. It is important to adhere to conventional names as often as possible and be aware of implied meanings for any names that you choose. For example, avoid choosing a name such as TCOM for a component that encapsulates the serial port because this obviously implies that the component offers functionality related to COM programming. This is a rather silly example, but it illustrates the point. If you are having difficulty choosing a suitable name for a component, it might be a symptom of poor design. Perhaps a rethink of the component is necessary.

Distributing Only a Design Time-Only Package

So far we have looked at distributing components as a set of packages ”a design time-only package with one or more runtime packages required by the design time-only package. In this section, we will look briefly at the distribution of components using a design time-only package model. If this is done, the user will be forced to statically link the object ( .obj ) files of your components directly to any application that uses them. This might appear to add complexity to the user's use of the components, but this is not the case. When a non-VCL component is added to a form in an application, the IDE does two things: It includes the header file of the unit that contains the component's definition, and it adds a #pragma link " unitname " statement to the form's implementation file (where unitname is the name of the unit that implements the component). This has the effect of requesting that the linker statically link the component to the application. The user need not write any additional code for this to be done. Assuming the linker can find the necessary .obj files, everything will be fine. Figure 4.8 shows the structure of a design time-only distribution. Files that must be distributed when using this technique are also shown.

Figure 4.8. The design time-only package model.

graphics/04fig08.jpg

One thing that is worth noting about this technique is that you can distribute a static library ( .lib ) file containing the object files of the components, rather than distribute the .obj files themselves . This can be achieved in one of two ways. You could create a runtime-only package containing only the components. When built, the .lib file will contain the necessary .obj files of the components. The remaining package files, the .bpl and .bpi files, are not required. This .lib file can be distributed instead of separate .obj files. Another possibility is to add the .obj files directly to the design time-only package using the USEOBJ macro. You can then distribute the design time-only package's .lib file. In either case, if the linker does not find the necessary .lib file, the project's Option source must be edited and the name of the static library added to the <SPARELIBS> line, as shown in the following:

 <SPARELIBS value="VCL50.lib  MyStaticLibrary.lib  "/> 

As long as this file is on one of the library file paths as specified in the project options, it will link successfully.

The thing to note about using this single design time-only package approach is that only one package needs to be maintained . When creating a package for different versions of C++Builder (the subject of the next section), this could possibly make life easier. The object files of the components can be generated simply by compiling the components in the different versions of C++Builder that are required, assuming of course that the code is structured so that it will compile on each version. You might also want to use this packaging model if you don't want to distribute a runtime library for dynamic linking (for whatever reason). On the whole, though, the runtime-only/design time-only package pair approach is superior and should be used in preference to this one.

Distributing Components for Different Versions of C++Builder

In cases where you want to distribute components as a component vendor, you will want to provide versions of your components that can be used on as many versions of C++Builder as possible (currently five versions exist: 1, 3, 4, 5, and 6). Of course, it might not be possible to do this in some cases because your components might rely on a feature or extend a feature that is only available in certain versions of the compiler. However, if we assume that it is possible to make the components available for more than one version of C++Builder (the majority of cases), more than one version of components must be produced. Distributing your components to different versions of C++Builder requires that you compile your code for each version to which you want to distribute. You must also create a package for components to be installed into versions 3, 4, 5, and 6 of the compiler (in the first version of C++Builder components are installed directly into CMPLIB32.CCL , and packages are not used). For this, the considerations presented previously are appropriate. The appropriate import libraries for each version of C++Builder are used and the package is built as before. This will be fine, assuming that the component source will compile on each version of the compiler. This is unlikely and, hence, this is the area that causes the most problems when trying to distribute to different versions. It is often not practical to maintain separate source listings for each version of the compiler for which the components are to be available, so an alternative approach is used. This alternative approach uses the same units for the components for each version of C++Builder. To allow for differences in compiler versions, the version of the compiler is detected and the preprocessor is used to select which code is to be used for which compiler. This and other topics relevant to the compiler version are listed in the following sections.

Detecting the Compiler Version at Compile Time

Each version of C++Builder defines a specific version number. By checking the value of this number, it is possible to selectively compile different sections of code. Listing 4.63 shows a method of setting a #defines for the different versions of C++Builder.

Listing 4.63 Setting #defines for C++Builder Versions
 #ifndef VERSION_DEFINES  #define VERSION_DEFINES     #if(__TURBOC__ == 0x550) // C++Builder 5     #define CPPBUILDER_VERSION_5     #endif     #if(__TURBOC__ == 0x540) // C++Builder 4     #define CPPBUILDER_VERSION_4     #endif     #if(__TURBOC__ == 0x530) // C++Builder 3     #define CPPBUILDER_VERSION_3     #endif     #if(__TURBOC__ == 0x520) // C++Builder 1     #define CPPBUILDER_VERSION_1     #endif  #endif 

Including the code shown above in the header file of your component's unit, or indirectly by placing it in another header file on its own and including that header file, allows code such as the following to be written:

 #ifdef CPPBUILDER_VERSION_5  // Register Property Filters as only version 5 supports this  #endif 

By using #ifdef / #endif and #ifndef / #endif , you can selectively remove sections of code for different versions of the C++Builder. In Chapter 3, it was advised that you avoid using the preprocessor unless absolutely necessary. This is one of those times when the preprocessor is your friend, and using it can save a lot of trouble.

Using the ValidCtrCheck() Function

This function is used to determine if any of the components that you want to install contain pure virtual functions. It detects this condition at compile time. Components containing pure virtual functions cannot be installed to the IDE. The function used for this is different in version 1 of C++Builder. The following ValidCtrCheck() function is used:

 static inline TComponentName *ValidCtrCheck()  {     return new TComponentName(NULL);  } 

For versions 3, 4, 5, and 6 of C++Builder, the following ValidCtrCheck() function is appropriate:

 static inline void ValidCtrCheck(TComponentName *)  {     new TComponentName(NULL);  } 
The Use of Packages and C++Builder Version 1

Version 1 of the compiler does not use packages, so the PACKAGE macro should not be used after the class keyword in the component's definition, and it should not appear before Register() in the registration function. Also, the #pragma package(smart_init) directive should not be found by the compiler in the component source files.

Because packages are not used in version 1 of the compiler, only the component's header and object files need to be distributed, optionally with a separate registration unit.

Using Set s in Components

The following discussion relates to the implementation of Set s in the different versions of C++Builder. Essentially, the use of Set s in versions 1 and 3 of the compiler is different than that in versions 4 up of the compiler. The following discussion explains this more fully.

In Malcolm Smith's MJFSecurity package for C++Builder 3 (available at http:\\www.mjfreelancing.com), the following code appears in the header file:

 #include <sysdefs.h>  enum TFailedShareRegKey { fsrNone, fsrInstalledDate,                            fsrRegUser, fsrRegOrgn,                            fsrRegCode, fsrRunCount,                            fsrUserDefined };  typedef Set<TFailedShareRegKey, fsrNone, fsrUserDefined> TFailedShareRegKeys;  typedef void __fastcall (__closure *TLoadErrorEvent) (TObject *Sender,                             TFailedShareRegKeys FailedKeys, bool &Terminate); 

In the implementation file, code similar to that shown in Listing 4.64 is used.

Listing 4.64 Source Code Illustrating the Use of a Set
 TFailedShareRegKeys FailedKeys;  FailedKeys << fsrNone;  // ... some sample code while reading the registry:  if(MyReg->ValueExists(KeyNames->InstalledDate))      FInstalledDate = MyReg->ReadDate(KeyNames->InstalledDate);  else  {     FailedKeys >> fsrNone;     FailedKeys << fsrInstalledDate;  }  if(MyReg->ValueExists(KeyNames->Username))      FRegisteredName = MyReg->ReadString(KeyNames->Username);  else  {     FailedKeys >> fsrNone;     FailedKeys << fsrRegUser;  }  // ... and many more  // ... and then later in the code that calls the event:  if(FOnLoadError)  {     bool Terminate = TerminateOnLoadError;     FOnLoadError(this, FailedKeys, Terminate);  } 

The code above should allow the component to construct a Set defining all possible causes for the application's failure to load. This Set is passed as a parameter to the OnLoadError event, where the user can examine the information and act accordingly .

This code will work fine in versions 4 and 5 of C++Builder, but not in version 1 and 3. In version 1 and 3, the following declaration is required:

 template class TFailedShareRegKeys; 

This explicit declaration forces the compiler to compile all methods of the Set class.

C++Builder 4 and 5 already contain an explicit declaration for Set in $(BCB)\Include\Vcl\Sysmac.h included indirectly through the line #include <system.hpp> . This is shown here:

 template<class T, unsigned char minEl, unsigned char maxEl>     class RTL_DELPHIRETURN Set; 

By wrapping the line

 template class TFailedShareRegKeys; 

in preprocessor directives it can be selectively compiled for versions 1 and 3 and ignored for versions 4 and 5.

Creating Component Palette Bitmaps

Throughout this discussion of packages, the Component Palette bitmaps have been referred to as .rc (resource script) files. The reason for this is that using resource script files (or a single resource script file with entries for all palette bitmaps ”the preferred method) enables greater flexibility over palette bitmap creation. Essentially, creating your Component Palette bitmaps using a more powerful graphics tool and manually adding them through a resource script file allows custom palettes to be used effectively. This can greatly enhance the appearance of your component bitmaps on the Component Palette.

Using Guidelines in the Design of Components for Distribution

Writing components for yourself or for in-house use is not the same as writing components for outside use by unknown parties. The main reason is that you have no idea how an outside party might want to use your component. To this end, you should design your component with the following considerations in mind:

  • Don't hide functionality from the user that he probably won't need. If making a feature available does not compromise the component design, make it available. There will always be someone who does need that functionality.

  • When adding events to components, be sure to fire events for each possible event that the user might want to handle. Failing to allow the user to respond to certain events because they have been omitted severely limits the usefulness of your component.

  • Don't force the user to rely on component linking as a method of achieving certain functionality by linking several of your components together. For example, if you write a component that allows input from a sound card to be captured and a component that displays this data, it makes sense to allow them to be linked together and the process controlled by the components. However, if that is the only way you can use the components, they can be virtually useless ”for example, the data that the sound card component captures should always be accessible for display or manipulation by any other means that a components user might require.

  • Try to keep the interface of your component intuitive, particularly at design time; this will be most users' first experience of your component. Where appropriate, make intelligent use of property and component editors. Sensible use of property filters (property Categories) can also make a component with many properties much easier to navigate.

  • Finally, you might want to create an abstract base class version of your component, such as the TCustom components in C++Builder. This might not always be appropriate, but it can prove very useful for users of your component.

Taking a Look at Other Distribution Issues

Other issues regarding distributing components include customizing the component's component editors so that a hyperlink is available to your Web site, your company logo is displayed, or other information is given (the component's version, for example).

Other things to consider are whether you want your component to be freeware or shareware. Do you want to include source or not? Should you pack your components so that they must be installed from an installation program that requires a license agreement to be accepted? And so on. These issues are beyond the scope of this section. However, it is important to be aware of them before you start to distribute your components to others.

A final consideration is that for all but the most trivial of components, you should always distribute accompanying documentation. You can do this in the form of help files, an actual electronic document, or even extensive instruction and comments in the header files of the components themselves. The general preference is for both help files and some printable electronic document. No matter how good a component is, it is virtually useless if no proper documentation is available for it. On a similar note, you should endeavor to ship example code and possibly a demo application(s) that shows your component being used.


   
Top


C++ Builder Developers Guide
C++Builder 5 Developers Guide
ISBN: 0672319721
EAN: 2147483647
Year: 2002
Pages: 253

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net