Overview
In this chapter I introduce Borland C++Builder (BCB) and explain what it is
about. I also devote considerable time to explaining the purpose of this book
and the philosophy behind my approach to technical writing.
Technical subjects covered in this chapter include
- Creating a simple Multimedia RAD program that plays movies, WAV files, and
MIDI files.
- Shutting down the BCB RAD programming tools and writing raw Windows API
code instead.
- Creating components dynamically on the heap at runtime.
- Setting up event handlers (closures) dynamically at runtime.
- A brief introduction to using exceptions. This topic is covered in more
depth in Chapter 5, "Exceptions."
- A brief introduction to ANSI strings. This subject is covered in more
depth in Chapter 3, "C++Builder and the VCL."
- Using the online help.
- Greping through the include and source files that come with the product
and with this book.
This chapter includes sample programs or code snippets illustrating all of
these concepts. The sample programs for this chapter are found on the CD that
accompanies this book in the directory called Chap01. The same pattern
is followed for all other chapters. For instance, the code for Chapter 2, "Basic
Facts About C++Builder," is in a subdirectory on the CD called Chap02.
Getting in the Mood
Programming is part of an esoteric world where logic is sacred. Even if you
understand exactly why a program works, there is still a magical element
involved. Things appear and disappear. Objects materialize, and then
dematerialize. They do so according to strictly defined logical rules; but
still, there is the fact that things appear and disappear right before our eyes.
To be a good programmer, you have to be a wizard. You have to study arcane
material, sit up over it until your eyes are bleary, and ponder its meaning,
seeking to understand its mysteries. Many people never understand the subtleties
of programming. They don't ever penetrate to the inner mysteries of this
challenging field.
But think of the joy you feel when you finally figure it out! The profound
satisfaction of actually cracking the code, mastering the spells, and seeing
through to the inner mystery! The arcane minutiae of programming is part of a
subtle, intricate world that can be mastered only by a few dedicated souls who
are willing to work hard to get at the inner truth of an algorithm, of an object
hierarchy, of a coding technique.
Some products seem to be effective at capturing the essence of the beautiful,
mysterious logic that underlies the world of programming. C++ has always had
free entry into this realm. C++Builder, however, raises the ante in the C++
world by allowing you to create programs with a powerful set of tools that
gracefully augment your programming skills.
BCB is one of the first serious compilers that allows you to pick up objects
called components with the mouse and move them around so that you can change the
logic of your program visually, rather than solely with code. The core of this
technology is component programming--not large, bloated, difficult to create
components but small, sleek, easy-to-build components that run at lightning
speed, components that appear and disappear before your eyes at the click of a
mouse.
Programming is intellectually exciting. At times, it's even--dreaded
word--fun! C++Builder puts the excitement back in C++ programming. If you like
to write fast programs that are easy and fun to use, this is the right tool for
you. Best of all, C++Builder gives you full access to all the advanced features
of C++, including templates, name spaces, operator overloading, and the entire
Windows API, including cutting-edge APIs such as DirectX, OLE Automation, and
ActiveX.
Most of the time, BCB programming is surprisingly easy. On occasion, it's
very challenging. It is, however, always interesting and exciting. Let other
programmers plod along with boring compilers made by some huge soulless
conglomerate full of middle managers who middle-manage their products into one
giant, boring access violation. There is something different about BCB. Like its
cousin Delphi, it has something of the true spark of the real programmer's art
in its sleek lines, in its fast compilation, and in its subtle and artful use of
the C++ language.
The Audience for This Book
Throughout this book, I walk a subtle line between extremes. Sometimes the
text has pulled me in the direction of system programming, and at other times I
have relied on the RAD tools that make BCB so easy to use. At times, I have
wanted to find the fastest way to perform a particular task and at others I have
wanted to find the clearest, simplest way to perform a task. Almost a third of
the book concentrates on database tasks, but I also dig deeply into OOP,
component creation, and esoteric Windows APIs such as DirectX.
C++ is the language of choice for programmers obsessed with speed, who long
to optimize their programs down to the last clock cycle, and who love to plumb
the most intricate depths of the computer. Some C++ programmers feel physical
pain when they have to give up clock cycles that could be optimized out given
sufficient time. In short, C++ is a language designed for creating operating
systems and compilers.
RAD tools, on the other hand, are designed for programmers who have a job to
do and want to get it done quickly. These people want a safety net so they don't
crash and burn! They are willing to give up clock cycles in return for usable
code.
In short, RAD programmers are intent on getting a job done quickly and
safely, whereas C++ programmers are traditionally intent on creating the
smallest, fastest programs possible.
This book, and BCB as a whole, is about the meeting of these two diverse
camps. I am very much aware that many C++ programmers won't like the "smell" of
RAD, and that many RAD programmers will be appalled by the ornate subtleties of
C++.
However, I believe that there is a place where these two groups can meet, and
furthermore, I think C++ can provide the high productivity tools that RAD
programmers expect, along with the high performance, system-loving, optimized
intricacies that true aficionados of C++ demand.
In short, this book is for contemporary programmers who practice their art on
the cutting edge of modern programming techniques. That does not mean that this
book is about the most technical aspects of C++ and Windows, nor does it mean
that this book is about a dangerous, new form of programming that wastes clock
cycles indiscriminately. Instead, this book is about techniques that allow
systems programmers to get their work done quickly, while allowing RAD
programmers to speed up and enhance their programs.
I should perhaps add that a large portion of this book is dedicated to
client/server database programmers. Nearly 80 percent of the applications made
today involve databases, and this tool will undoubtedly be used very heavily by
client/server developers. I go into considerable lengths to talk about the
advanced database features found in BCB; I cover SQL, stored procedures,
triggers, filters, lookups, and numerous other database techniques.
BCB Versus VB
There is one thing that ought to be made clear right at the start. The
programs you write with BCB are comparable in terms of size and performance with
the programs you create with OWL or MFC. It would be a mistake to assume that
BCB has any of the limitations you find in VB or PowerBuilder, or even in
Optima. Anything you can do in MSVC or in BC5 you can also do in BCB, and you
can do it with the same, or an increased, degree of subtlety and artfulness.
Both BCB and VB are RAD tools. But that is where the comparison between the
two products must end. VB is a nice product, but it is not a serious programming
tool. BCB is a very serious programming tool. It is a real C++ compiler that
comes with all the bells and whistles.
The presence of RAD tools can lead you to believe that BCB is somehow
crippled in terms of performance or capability. However, that is an erroneous
conclusion. If you take the time to explore the product in depth, you will find
that it lacks nothing in terms of power or capability.
The RAD tools in this package add no real overhead to your programs that you
would not find in either OWL or MFC. The VCL is comparable to OWL and MFC in
every way, except for the fact that it is much easier to use and much more
elegantly designed.
The word component can also conjure up images of slow, buggy,
hard-to-understand ActiveX controls. BCB components are much faster, much
smaller, and much easier to make than ActiveX controls. OLE is a powerful
technology--and one that I use quite frequently--but it lacks the subtlety,
speed, and elegance of the VCL code that underlies BCB.
A Cautious Approach to Programming
Having gone to some lengths to emphasize the technical depth of BCB, I want
to turn around and discuss the relatively conservative approach I take to the
art of writing programs.
I have been writing code long enough to have grown suspicious of techniques
that are too fancy, too subtle, and too hard to parse, execute, and maintain. As
a result, I have adopted the style of programming championed by people who want
to write safe, easy-to-maintain programs.
I tend to promote a conservative programming style--and indeed, almost all
the good programmers I know use these same techniques, even when writing code
that is designed for high performance applications.
A certain degree of caution is necessary if you want to write robust code.
When in doubt, I always err on the side of caution.
Does this mean I want you to write slow, bloated code? No, of course not! My
goal is to walk that fine line between writing code that is such a high wire act
that it can't be maintained, and writing code that is so high-level, so
abstracted, that its performance becomes an abomination.
BCB is about the place you can get the maximum in terms of safety, without
giving up significant power in terms of speed and flexibility. It's about
walking the line between two extremes.
On Using C++
When creating the sample applications for this book, I tried to choose code
that walks the middle line between being too cautious and too daring. I tried to
take the best ideas from the C++ language and combine them with the benefits of
RAD.
I want to get far enough into C++ to leverage its power, without going so far
that I spend whole chapters parsing the subtleties of some obscure syntactical
corner of the language. I also want to use many high-level, RAD-based tools, but
I don't want to rely on them so completely that they overshadow the power of the
C++ language.
The goal is to find the middle ground, the artful line that yields the best
programs. If I am in doubt, I will err on the side of the RAD programmers who
have a job to do. The primary reason for this decision is simply that there are
already many great books out there on the intricacies of C++ and on the
subtleties of the Windows API. There is no need for another book on those
subjects. Instead, I want to show what
C++Builder brings to the table.
When exploring BCB, however, I will always keep at least one eye on the
system programmer. I know what you want, I believe in your cause, and I want to
show you how BCB can help you complete even the subtlest jobs more quickly than
traditional environments such as BC5 or MSVC. My promise is that the executables
you produce with BCB will be at least as small, and at least as fast as the
executables you produce with MFC or OWL. And, if you want, you can cut out BCB's
object-oriented tools and produce tiny executables that match anything that you
can do with BC5 or MSVC.
I am not trying to create a companion volume to a classic hard-core tome such
as the Zen of Assembly Language, More Effective C++, Undocumented Windows, the
ARM, or Inside Windows. Books like that have their place, of course, but that is
not the kind of programming I want to write about.
Clearly, I am trying to set practical, reasonable goals for this book.
However, I don't mean to imply that this is a plodding, methodical book that
will never take flight into any interesting subjects. On the contrary, I want to
show how you can do fancy, flashy, exciting things with a computer, without
having to parse the lowest-level bits in the operating system. If you want to
plumb to the lowest depths of the operating system, I will take you right up to
the edge, show you how to get started, and then wish you Godspeed. You can use
BCB to do some great system programming, but I will leave the specifics of how
to proceed to other authors, or to a second book of my own on the subject.
This book contains lots of exciting code on subjects such as multimedia,
games, and Internet programming. I concentrate on very high-performance tools
such as DirectX and on cutting-edge technologies such as OLE. Unlike other books
on these subjects, however, my goal is to show how you can integrate these
things into your projects even if you are on a tight schedule and even if you
would not normally be inclined to do the kind of spelunking that those names
imply.
In my opinion, the kind of programming described in this book is the essence
of cutting-edge computer technology (at the time of this writing). The best
programmers today use whatever tools they can find to allow them to quickly
produce high-performance programs. Plumbing the depths is fun, but it loses some
of its appeal when the Internet calls, or when you need to produce an inventory
program quickly, or when you want to spice up an application so that your users
actually enjoy sitting down to work with one of your creations.
My point is quite simply that today many of the best programmers are
specializing, not in plumbing the depths of the operating system, but in
producing real-world applications quickly. This is an advanced programming book
that assumes a good deal of experience on the part of the reader. However, I
want your experience to be not deep and narrow, but broad and expansive.
Humility, Crooked Timber, and the Practical Programmer
In the book Code Complete (published by Microsoft Press), Steve McConnell
quotes an award-winning paper by Edsger Dijkstra called the "The Humble
Programmer." I regard this work as one of the guiding lights of this book.
I would much rather write a humble program that works well than be involved
in a flashy, ego-ridden project that is never finished, or that ships two years
late. The key to getting things done is to show a little humility.
In particular, if you work under the assumption that any one programmer is
perfect, you are doomed to failure. Computers are reliable; programmers make
mistakes.
Computers, on the other hand, look remarkably dense when compared to the
creativity a good programmer can wield. Machines get a zero on the creativity
scale, whereas programmers can be very creative. The key is not to try to make
people like computers, but to find the best way to leverage the virtues of both
programmers and computers.
If you write code that assumes the programmer is perfect, sooner or later
that code will fail. Don't mix up the programmer and the computer. The computer
is the one that doesn't make mistakes; the programmer is the one that comes up
with ideas.
I write code that assumes I not only can make mistakes, but that I will make
mistakes. I write code that shies away from the extremely low-level code that
crimps my creative side, and which invites bugs.
The code I like to write assumes that I tend to make errors, and that I
should be free to exercise a degree of creativity. Code that is too technical,
too cutting-edge, or too clever is code that is prone to bugs and late,
sleepless nights.
The right kind of code gets the job done quickly enough to leave programmers
still fresh and alert, so that they can exercise creativity in coming up with
the best solutions.
Quite often in this book, I will recommend techniques that fly in the face of
the advice you undoubtedly get from that hotshot programmer who works down the
hall. My problem is not that I fail to appreciate the importance of performance
or producing small, fast programs. Rather, I worship at a different shrine, the
one that finds the middle ground between code that is too subtle and code that
is too abstract, too high-level.
This book is dressed in jeans or cords, good practical shoes, and a tough,
but attractive, plaid work shirt. Programmers who like to dress in patent
leather shoes and $2,000 suits might make fun of some of my techniques. What I
like about the clothes that this book wears is that they are tough, well-suited
to a wide variety of conditions, and they look great on a wide range of people.
I don't write for someone who wants to be the best programmer in a group.
Instead, I am interested in people who want to make things. I want to get from
conception to a finished product, and I don't care if all of the techniques I
use aren't the fanciest available. I don't, quite frankly, care all that much
about the schedule my manager wants to live by; rather, my goal is to get the
job done before I become utterly sick of it. I like to make things. I want to
finish the project.
Immanuel Kant is one writer who aptly captured the spirit by which most
programmers should live: "Out of timber so crooked as that from which man is
made nothing entirely straight can be carved." In other words, don't expect your
programs to be perfect, and don't waste time trying to achieve perfection. Aim a
little lower, instead. Rather than perfect, shoot for: "It works." Or, at best:
"This program is remarkably bug-free!"
Even better, aim for programs that are creative, fun to use, and useful. The
strongest suit a programmer can take to a task is creativity. Even the best
programmers look like bunglers when compared to the reliability of a computer.
The best programmers also make mistakes with a frightening regularity. I try
to accept that fact, accept my limitations, and then find ways to program that
are safe! If I have the humility to admit I am not perfect, I can start making
programs that work and that get turned in on time!
Once again, I don't really care about my manager's schedule; I care about my
schedule. I want to start a project, bring it to fruition, and then move on to
the next thing that interests me! I don't want to be stuck working on the same
task for years on end!
API Code Versus RAD Code
At this stage, it might be helpful to give a few specific examples of the
difference between RAD programming with BCB and traditional Windows programming.
My primary point here is to show that BCB can do it all. If you want to write
API code, BCB will let you write it. If you want to write OWL code, BCB will let
you do that, too. If--heaven forbid--you should even be foolish enough to want
to write MFC code (perish the thought!), you can also do that with BCB.
In this section, you will see two sample programs. The first is a traditional
RAD application written in BCB, and the second is a standard Windows API
program. I will spend a few moments talking about each program and then will use
these examples to illustrate just what it is about RAD that I find appealing, as
well as explain
something about the parts of RAD development that I will focus on in this
book.
A Very Short Introduction to the VCL
Reading this text over, I find that I am throwing a number of acronyms
around. One that really begs for a short explanation is VCL.
The VCL is the Visual Component Library. It is to BCB what OWL is to BC5, and
what MFC is to MSVC. (This is called acronym immersion therapy.) In other words,
it is the object- oriented library, or framework, that underlies BCB. The
difference between VCL and OWL is that the VCL is based on components,
properties, and events, while OWL and MFC have none of these features. In
particular, events support something
called the delegation model, which is an alternative to simple inheritance.
The VCL fully supports all standard OOP concepts such as inheritance,
polymorphism, and encapsulation. What it brings to the party are components,
properties, and events. (Events are also known as closures.) One of the goals of
this book is to explain components, properties, and events in the clearest
possible terms, and to state why I feel this is the proper programming model for
this stage of computer programming development.
Perhaps most importantly, the VCL is written in Object Pascal. In fact, it is
literally the same code base used by Delphi. Later in this chapter, I will
explain a bit more about BCB's relationship to Delphi, and I will explore the
subject in detail in the next chapter. For now, you need to know only that the
VCL would not be any faster or smaller were it written in C++. Object Pascal has
some stark differences from C++, but speed and size are not among them. As
explained previously, Object Pascal is a real, compiled, object-oriented
language that fully supports true inheritance, polymorphism, and encapsulation.
All Object Pascal code that works in Delphi works automatically in C++Builder.
That's all I will say on this subject at this point, though I will spend
considerable time defining the VCL and its related programming models more
carefully in later chapters. In particular, Chapter 2 and Chapter 3 go into
considerable depth on the subject of VCL, how it works, and why it exists.
On Using the Visual Tools
Before I get started with a specific programming example, I want to take a
moment to discuss the technique I use when writing about BCB programs. Except
for a few places in the first chapters, I will generally skip over detailed
descriptions of the visual programming tools.
In this text, I will usually not explain the process of setting up a standard
component at all. For instance, if text on a TButton component says OK,
Exit, or Close, I will not say Set the Caption field of the TButton
component to Exit, or Close. Instead, I will assume that you can figure that
much out just by looking at the figure that accompanies my description of the
program.
As you gain more experience with C++Builder, you will quickly learn how to
work with most of the properties associated with components. As a result, I
usually do not bother to write detailed explanations about setting up a
component, such as that you need to set its Align property to
alClient, or its Stretch property to True. I assume that
you can see that much just from glancing at the figure. Of course, I assume
there will be times when you will want to run the programs on disk to see
exactly how I have achieved a particular affect.
My point here is that you should be able to glean information of this kind
from the manuals that ship with the product or from the online help. You could
also turn to a beginning level C++Builder book, such as Teach Yourself
C++Builder in 21 Days (published by Sams). In the current text, however, I will
try to skip over that kind of beginning material, in order to best give you what
you expected when you bought an intermediate- to advanced-level book.
I am aware, however, that BCB is a new product and that some introductory
material is needed. I will try to keep it to a minimum. In particular, almost
all the introductory material occurs in this chapter and the next. After that,
I'll assume you know how to use the environment.
A Simple RAD Multimedia Program
The code for the Multimedia RAD program is shown in Listing 1.1. The entire
program is found on the CD that accompanies this book. An explanation of the
program follows these listings.
Listing 1.1. The header file for the Multimedia RAD
program.
//------------------------------
#ifndef MainH
#define MainH
//------------------------------
#include <vcl\Classes.hpp>
#include <vcl\Controls.hpp>
#include <vcl\StdCtrls.hpp>
#include <vcl\Forms.hpp>
#include <vcl\ExtCtrls.hpp>
#include <vcl\MPlayer.hpp>
#include <vcl\Menus.hpp>
#include <vcl\Dialogs.hpp>
//----------------------------------
class TForm1 : public TForm
{
__published: // IDE-managed Components
TPanel *Panel1;
TImage *Image1;
TMediaPlayer *MediaPlayer1;
TMainMenu *MainMenu1;
TMenuItem *File1;
TMenuItem *Load1;
TMenuItem *Play1;
TMenuItem *N1;
TMenuItem *Exit1;
TOpenDialog *OpenDialog1;
TMenuItem *Options1;
TMenuItem *ChangeBackground1;
void __fastcall Load1Click(TObject *Sender);
void __fastcall Play1Click(TObject *Sender);
void __fastcall Exit1Click(TObject *Sender);
void __fastcall ChangeBackground1Click(TObject *Sender);
private: // User declarations
public: // User declarations
virtual __fastcall TForm1(TComponent* Owner);
};
//----------------------------------
extern TForm1 *Form1;
//----------------------------------
#endif
Listing 1.2. The main module for the Multimedia RAD
program.
///////////////////////////////////////
// File: Main.cpp
// Project: Muli-media RAD
// Copyright (c) 1997 by Charlie Calvert
#include <vcl\vcl.h>
#pragma hdrstop
#include "Main.h"
#pragma resource "*.dfm"
TForm1 *Form1;
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
void __fastcall TForm1::Load1Click(TObject *Sender)
{
if (OpenDialog1->Execute())
{
MediaPlayer1->FileName = OpenDialog1->FileName;
MediaPlayer1->Open();
}
}
void __fastcall TForm1::Play1Click(TObject *Sender)
{
try
{
MediaPlayer1->Play();
}
catch(EMCIDeviceError &E)
{
AnsiString S("\rUse the File | Open menu item to select an AVI file.");
ShowMessage("Bummer: " + E.Message + ". " + S);
}
}
void __fastcall TForm1::Exit1Click(TObject *Sender)
{
Close();
}
void __fastcall TForm1::ChangeBackground1Click(TObject *Sender)
{
AnsiString RootDir(ParamStr(0));
AnsiString SaveDir = OpenDialog1->InitialDir;
AnsiString SaveFilter = OpenDialog1->Filter;
OpenDialog1->InitialDir = ExtractFilePath(RootDir);
OpenDialog1->Filter = "Picture | *.bmp";
if (OpenDialog1->Execute())
{
Image1->Picture->LoadFromFile(OpenDialog1->FileName);
Image1->Stretch = True;
}
OpenDialog1->InitialDir = SaveDir;
OpenDialog1->Filter = SaveFilter;
}
This program pops up in a window that has a picture of a Mayan temple as a
background, as shown in Figure 1.1. From the menu, you can pop up an open file
common dialog that will let you select and play either a movie file, WAV file,
or MIDI file. You can also browse to select new backgrounds for the main form.
FIGURE 1.1. The main screen for the
Multimedia Adventure program.
The RAD Tasks for Creating the Multimedia Program
To create the program, bring up BCB and select New Application from the File
menu. Drop down the following components on the main form:
TPanel *Panel1;
TImage *Image1;
TMediaPlayer *MediaPlayer1;
TOpenDialog *OpenDialog1;
-
NOTE: : In some RAD programming
books, code is presented that shows the exact location of the objects placed
on a form. For instance, here are some selections from the text representation
of the form for the Multimedia program:
object Form1: TForm1
Left = 244
Top = 147
Width = 493
Height = 388
Caption = `Form1'
Menu = MainMenu1
object Image1: TImage
Left = 0
Top = 41
Width = 485
Height = 301
Align = alClient
Picture.Data = { Lots of numbers omitted here }
end
object Panel1: TPanel
Left = 0
Top = 0
Width = 485
Height = 41
Align = alTop
TabOrder = 0
object MediaPlayer1: TMediaPlayer
Left = 192
Top = 5
Width = 253
Height = 30
TabOrder = 0
end
end
// Menu would appear here
object OpenDialog1: TOpenDialog
FileEditStyle = fsEdit
Filter = `Movies, Sound, Midi|*.avi;*.wav;*.mid'
InitialDir = `c:\'
Left = 48
Top = 48
end
end
You can convert your forms into this kind of text by right-clicking them
and selecting View as Text from a popup menu. Conversely, you can translate
text into visual forms by right-clicking them and selecting View as Form from
a popup menu. You can also convert programs back and forth from text to binary
files by running a command-line utility called Convert.exe. Finally,
you can type the preceding code into a text editor, copy it to the Clipboard,
and then paste it onto a BCB form or panel. After the paste operation, you
will not see text, but live visual components.
At any rate, I have opted not to use the kind of textual description of a form
shown above, primarily because the form itself is available on disk, and
secondarily, because a picture of a form, like that shown in Figure 1.2,
provides more information than a textual description. I do, however, provide
both binary and textual copies of the forms on the CD that accompanies this
book.
A Brief Note on Creating Menus
You can add a menu to this program by dropping down a TMenu
component, double- clicking it, and filing it out as follows:
TMenuItem *File1; // Popup menu
TMenuItem *Load1; // menu item
TMenuItem *Play1; // menu item
TMenuItem *N1; // (separator made by entering a dash in the Caption field)
TMenuItem *Exit1; // menu item
TMenuItem *Options1; // Popup menu
TMenuItem *ChangeBackground1; // menu item
The text for each of these menu items is the same as the name of the menu
item itself, except that the 1 is removed from the end of the name.
FIGURE 1.2. The Menu Designer as it looks during the construction of the
menu for the Multimedia RAD program.
-
NOTE: I would not normally cover
this kind of material in this book, but it might be helpful to hear one
description of how to use the C++Builder visual tools in hyper-mode. If you
grasp the subtleties of this technique, you will find that you do not need to
manually switch back and forth between a form and the Object Inspector.
Instead, the environment will move you back and forth automatically between
the two tools.
To get started working in hyper-mode, make sure the Menu Designer is closed,
because this process works best if you start from scratch. Now bring up the
Menu Designer by double-clicking the TMenu object you dropped on the
form. Immediately after double-clicking the item, start typing into the Menu
Designer dialog. Don't bring up the Object Inspector. Instead, type directly
on the Menu Designer, and watch as the Object Inspector comes up of its own
accord. When you want to switch back to the Menu Designer, just press Enter.
For instance, focus the Menu Designer on the first blank menu item and type
the word File. Press Enter. Type Open. Press Enter. Type
Close. Press Enter. To move over to the next column, press the right
arrow key. Type Edit, press Enter, and so on.
You can also select items on the Menu Designer and edit them inside the Object
Inspector. However, it is easier to use the hyper-mode method. For more
details, see the online help or an introductory book on BCB programming.
You should also go to the Object Inspector for the TImage component
and set its Picture property to the bitmap you want to have as the
background for the main form. The TImage component should have its
Align property
set to alClient, and its Stretch property set to True.
You should set the Filter property for the TOpenDialog
component so it looks like the screen shot shown in Figure 1.3. In particular,
the text for the property would look like this, if you were filling it out in
code rather than via the Object Inspector:
OpenDialog1->Filter = "Movies, Sound, Midi |
*.avi;*.wav;*.mid";
FIGURE 1.3. The Property editor for the
Filter property of the TOpenDialog component.
Loading a Multimedia File
The following function is called when the user selects the Load menu item
from the File menu:
void __fastcall TForm1::Load1Click(TObject *Sender)
{
if (OpenDialog1->Execute())
{
MediaPlayer1->FileName = OpenDialog1->FileName;
MediaPlayer1->Open();
}
}
This code first opens a common dialog to allow the user to select a filename.
If the user clicks the OK button in this dialog, the selected filename is
assigned to the multimedia component and the component is opened. The act of
opening the component "turns on the lights" on the multimedia control itself. In
other words, after you open the component, it moves from a grayed-out, inactive
state to a colorful, active state.
The VCL and Memory Allocation
Notice that the objects you are working with here are all created on the
heap. There is no such thing as a static instance of a VCL component, or indeed,
of a VCL object. All VCL objects are created on the heap--that is, you always
create a pointer to a VCL object and you don't ever create a static instance of
a VCL component. In fact, you can't create a static instance of a VCL object.
The compiler won't let you. VCL objects exist on the heap by definition, as will
be explained further in the next chapter.
-
NOTE: My mixing of the words
component and object in the previous paragraph is intentional. All VCL
components are nothing more than VCL objects with a small amount of overhead
added to them so they can appear on the Component Palette. All VCL components
are also VCL objects, but not all VCL objects are components. This subject
will be explained in depth in the chapters on creating your own components.
You will also find that I use the words class and object interchangeably.
Contrary to popular belief, this is not an error. Needless to say, I
understand that quite often people use the word class to refer to a
declaration, and they use the word object to refer to an instance of a class.
However, this rather fine distinction becomes a bit precious in real life, so
I tend to use the words interchangeably unless I have a specific need to make
a distinction between the two concepts. When that is the case, I will make it
abundantly clear that you need to make a distinction between a class
declaration and an object instance.
Notice also that you are not actually responsible for allocating or
deallocating memory for VCL components. If you want to, you can explicitly
create a component in code. For instance, here is how to dynamically create a
TButton control:
MyButton = new TButton(this);
MyButton->Parent = this;
MyButton->Caption = "Dynamic Button";
MyButton->Width = 250;
MyButton->Show();
This type of code is explored in depth later in this chapter, but if you want
to see it in action right away, you can run the DynamicButton program found on
the CD-ROM that accompanies this book, in the Chap01 directory. A
screen shot of that program appears in Figure 1.4.
FIGURE 1.4. The DynamicButton program
dynamically allocates a button at runtime.
It is also not usually your concern to deallocate the memory associated with
a component. BCB has no garbage collection facility. The constructor for
TButton shown above assigns this as the owner of the control. That
means that the main form for the program is responsible for disposing
MyButton, because it has been designated as the owner of the button. (The
this pointer is a bit of syntactical hand-waving that allows an object
to refer to itself. In this case, Form1 owns TButton, and the
identifier this is a way of referring to Form1.) The code in
TForm that deallocates MyButton is built into the VCL. You
never have to think about it. I will, however, discuss how it works in several
parts of this book.
The scheme described here sounds a lot like garbage collection. In fact, if
you drop a component on a form from the Component Palette and then run the
program and terminate it normally, you will never have to worry about either
allocating or deallocating memory for components. This is not a true
garbage-collection scheme because you have the option of not passing in a parent
to an object when you create it and you can decide to deallocate an object at
any time if you want. In other words, many of the memory management chores are
taken care of for you, but they do not have to be handled by the system if you
would rather do them yourself. This freedom is what I love about BCB, but it
also brings responsibility with it.
A true garbage collection scheme would never allow you to make mistakes
allocating
or deallocating memory. The VCL does not go that far. You can definitely make
mistakes with memory management if you are not careful. On the other hand, it
should be clear to you that BCB programmers are relieved of many onerous memory
management chores. In fact, if you do things right, you rarely have to think
about memory management. Part of the job of this book will be to explain how to
ensure that you never have to worry about memory management. I will, however,
also show you how to take matters into your own hands, if you wish.
A First Glance at Exception Handling
The simple act of opening the TMediaPlay component is not the same
as causing the component to play a movie or song. If the users want to play a
file that has been loaded into the component, they can push the green button on
the TMediaPlayer control. Alternatively, they can select the Play menu
item from the File menu:
void __fastcall TForm1::Play1Click(TObject *Sender)
{
try
{
MediaPlayer1->Play();
}
catch(EMCIDeviceError &E)
{
AnsiString S("\rUse the File | Open menu item to select an AVI file.");
ShowMessage("Bummer: " + E.Message + ". " + S);
}
}
As you can see, this code includes a try..catch block that shoulders
some exception-handling chores. If you wanted, you could safely leave the
explicit try..catch block out of this code and the VCL would still
automatically raise an exception if the user has picked the Play option before
loading a file. In that case, the VCL will automatically create an exception
that tells the user
No MCI Device Open.
For an error built into a programmer's library, the simple strings popped up
by the VCL are usually very complete and comprehensible error messages. However,
you will probably want to improve on it before showing it to the users of one of
your own programs. The code above accomplishes this task by catching the
exception, modifying its output string, and then using custom code to display it
for the user.
Exceptions in BCB are exactly like exceptions in normal C++ programs, only
now your entire program is automatically wrapped in a try..catch block
and you also have a very rich set of exception classes at your disposal courtesy
of the VCL. In particular, you can see that the Play1Click method
catches an exception class called EMCIDeviceError. All VCL exception
classes begin with the letter E, so this class might be more readily
comprehended as the MCIDeviceError exception class, used to raise
exceptions that occur when using the TMediaPlayer control.
-
NOTE: The TMediaPlayer
control is a wrapper around the now somewhat old-fashioned Windows Media
Control Interface, or MCI--hence the name MCIDeviceError. As you will
see, BCB can also use DirectX, DirectMovie, and other advanced multimedia APIs
made by companies such as Apple or Intel. In fact, BCB can use any API that
works in Windows, but this particular example happens to use the MCI, which is
more than adequate for the task at hand.
As will be explained in depth in Chapter 4, "Events," the following code
gives you access to a variable named E, of type EMCIDeviceError:
catch(EMCIDeviceError &E)
As you will see in Chapter 3, you can use E to call a number of
methods of the EMCIDeviceError class. One of these methods is called
Message, and it returns a human-readable string that can be displayed
to the user. I include this string in the text I show to the user, but I add
other information including a potential response to the error.
A Brief Introduction to AnsiStrings
One pair of lines that have surely caught your eye by this time are the ones
that use a new, BCB-specific class called AnsiString:
AnsiString S("\rUse the File | Open menu item to select
an AVI file.");
ShowMessage("Bummer: " + E.Message + ". " + S);
The AnsiString class is explained in depth in Chapter 3. For now,
all I will say is that it provides a type of string that is fully compatible
with the Object Pascal strings used by the VCL. In particular, the
AnsiString class overrides the + operator to support concatenating strings.
Underneath it is a simple string class that most likely calls strcat in
one of its methods. The use of operator overloading and several other techniques
makes it look and act like a Pascal string.
Though it is tempting to use either plain NULL-terminated strings,
one of the string classes from the STL, or one from some other library,
you will find that I use AnsiStrings almost exclusively in the code
that accompanies this book. The primary reason for this is their compatibility
with the VCL. However, I am also drawn to their safety and ease of use.
Two-Way Tools: Changing Properties at Runtime
The code in the ChangeBackgroundClick method shows how you can
manipulate properties either in code, or via the Object Inspector:
void __fastcall TForm1::ChangeBackground1Click(TObject
*Sender)
{
AnsiString RootDir(ParamStr(0));
AnsiString SaveDir = OpenDialog1->InitialDir;
AsiString SaveFilter = OpenDialog1->Filter;
OpenDialog1->InitialDir = ExtractFilePath(RootDir);
OpenDialog1->Filter = "Picture | *.bmp";
if (OpenDialog1->Execute())
{
Image1->Picture->LoadFromFile(OpenDialog1->FileName);
Image1->Stretch = True;
}
OpenDialog1->InitialDir = SaveDir;
OpenDialog1->Filter = SaveFilter;
}
This code changes the InitialDir and Filter properties of
the TOpenDialog object at runtime. At first, you might suspect that the
only way to manipulate a BCB component is through the Object Inspector. However,
everything that you can do visually with BCB also can be done in code.
Of course, I could have saved time writing some code here by placing two
TOpenDialog controls on the form. However, I do things this way so that
- You can see how BCB's two-way tools work. You can do things two ways: You
can write code, or you can do things visually.
- It is also a potentially more efficient use of memory not to have two
copies of the object on the form. I would, of course, have to run more tests
to be sure that this technique is really saving memory, but the point is that
BCB gives you the flexibility to do things as you think best.
The ChangeBackground1Click method first saves the current state of
the Filter and InitialDir properties. Then it changes them to
values that support the loading of a new bitmap for the background of the form.
In particular, the VCL functions called ParamStr and
ExtractFilePath are used to get the initial name of the program as it was
passed to the executable by Windows. (There is no need to parse argv;
that task is already done for you by ParamStr.) The ExtractFilePath
function strips off the executable name, leaving only
the path to the directory where your program was launched. The code then
assumes that some bitmaps suitable for a background are available in that
directory, which is the case with the code that ships with this book.
The VCL and the Windows API
Notice the LoadFromFile method used to initialize the Picture
property of the TImage component:
if (OpenDialog1->Execute())
{
Image1->Picture->LoadFromFile(OpenDialog1->FileName);
Image1->Stretch = True;
}
LoadFromFile entirely hides the act of loading a bitmap into memory,
getting a handle to it, and passing it to the TImage component that can
display it to the user.
This capability of components to hide the intricacies of the Windows API from
the user is one of the VCL's key strengths. It would be a mistake, however, to
view components as black boxes. They are no more black boxes than in any other
object you would find in OWL, MFC, or any other OOP library. The whole source to
the VCL ships with most versions of BCB and is always available from Borland.
-
NOTE: The fact that the VCL is
in Pascal should not be a great hindrance to most BCB programmers. If you
examine the actual source files, you will find that they consist primarily of
calls to the Windows API and of calls to other portions of the VCL. Because
BCB programmers work with the VCL all day, they should have little trouble
reading calls to the VCL that happen to be wrapped in begin..end
blocks rather than curly braces. Furthermore, a Windows API call in Object
Pascal looks almost exactly like a Windows API call in C++. There is no
significant difference in their appearance or performance, as long as you are
not confused by minor differences such as the appearance of a :=
operator where you expect to see a simple = operator. In Chapter 2,
"Basic Facts About C++Builder," I will show how you can step into the Pascal
code of the VCL with the debugger.
Even more important than the presence of the source to the VCL is the fact
that you can use BCB to create your own components. If you see yourself as
primarily as a system hacker who wants to be close to the Windows API, you can
get as close as you want while creating your own components. In fact, BCB allows
you to use the Windows API at any time, and in any way you want.
Needless to say, good VCL programmers use components whenever possible,
because they are so robust. If you need to drop to the Windows API, it is often
a good idea to wrap up the resulting code in a component and then share it with
or other programmers--or, if you prefer, sell it to other programmers.
Tools like Visual Basic or PowerBuilder gave people the mistaken impression
that RAD was innately slow and perhaps designed for programmers who didn't
really know how to write "real" code. Delphi put the lie to that misconception,
but it had to struggle uphill against ignorant prejudices concerning Object
Pascal. BCB is not as fully encumbered by that weight as Delphi, and it will
show everyone who cares to listen that the fastest way to build small, tight,
robust OOP-based programs
is through RAD.
Writing RAW Windows API Code with BCB
Having ended the last section on a rather provocative note, it's perhaps time
to show the more technical side of BCB programming. The WinAPICode program shown
below is written entirely using raw Windows API calls. There are no objects in
this program; instead, everything is done in a manner similar to the one Petzold
used back when Windows programming was just getting started.
The code shown here does not follow Petzold exactly, in that I use
Windowsx and STRICT to help make the code more readable, more
maintainable, and more portable. The basic approach, however, is tied very
closely to the Windows API. For instance, I have a real WndProc and
message loop, and I make calls to old standbys such as CreateWindow,
ShowWindow, UpdateWindow, and RegisterClass. The code
itself is shown in Listing 1.3.
Listing 1.3. Standard Windows API program that compiles
unchanged in BCB.
///////////////////////////////////////
// File: WinAPICode.cpp
// Project: WinAPICode.cpp
// Copyright (c) 1997 by Charlie Calvert
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <windowsx.h>
#pragma warning (disable: 4068)
#pragma warning (disable: 4100)
static char szAppName[] = "Window1";
static HWND MainWindow;
LRESULT CALLBACK WndProc(HWND hWindow, UINT Message,
WPARAM wParam, LPARAM lParam);
BOOL Register(HINSTANCE hInst);
HWND Create(HINSTANCE hInst, int nCmdShow);
// ===================================
// INITIALIZATION
// ===================================
//////////////////////////////////////
// The WinMain function is the program entry point.
// Register the Window, Create it, enter the Message Loop.
// If either step fails, exit without creating the window
//////////////////////////////////////
#pragma argsused
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInstance,
LPSTR lpszCmdParam, int nCmdShow)
{
MSG Msg;
if (!hPrevInstance)
if (!Register(hInst))
return FALSE;
MainWindow = Create(hInst, nCmdShow);
if (!MainWindow)
return FALSE;
while (GetMessage(&Msg, NULL, 0, 0))
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return Msg.wParam;
}
//////////////////////////////////////
// Register the window
//////////////////////////////////////
BOOL Register(HINSTANCE hInst)
{
/* You can use WNDCLASSEX and RegisterClassEx with WIN32 */
WNDCLASS WndClass;
WndClass.style = CS_HREDRAW | CS_VREDRAW;
WndClass.lpfnWndProc = WndProc;
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = 0;
WndClass.hInstance = hInst;
WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
WndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
WndClass.lpszMenuName = NULL;
WndClass.lpszClassName = szAppName;
return (RegisterClass(&WndClass) != 0);
}
//////////////////////////////////////
// Create the window
//////////////////////////////////////
#include <wtypes.h>
__RPC_FAR Sam()
{
return 0;
}
HWND Create(HINSTANCE hInstance, int nCmdShow)
{
HWND hWindow = CreateWindowEx(0, szAppName, szAppName,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL);
if (hWindow == NULL)
return hWindow;
ShowWindow(hWindow, nCmdShow);
UpdateWindow(hWindow);
return hWindow;
}
// =====================================
// IMPLEMENTATION
// =====================================
#define Window1_DefProc DefWindowProc
void Window1_OnDestroy(HWND hwnd);
//////////////////////////////////////
// The window proc is where messages get processed
//////////////////////////////////////
LRESULT CALLBACK WndProc(HWND hWindow, UINT Message,
WPARAM wParam, LPARAM lParam)
{
switch(Message)
{
HANDLE_MSG(hWindow, WM_DESTROY, Window1_OnDestroy);
default:
return Window1_DefProc(hWindow, Message, wParam, lParam);
}
}
//////////////////////////////////////
// Handle WM_DESTROY message
//////////////////////////////////////
#pragma argsused
void Window1_OnDestroy(HWND hwnd)
{
PostQuitMessage(0);
}
This program looks like, and indeed is, an old-fashioned Windows program from
back before the days of the object frameworks such as OWL or the VCL. It will,
however, compile unchanged in BCB. In fact, I did not have to change any of the
code or any of BCB's compiler settings in order to create this program.
To get started producing this code from scratch, open up BCB and start a new
project. Go to View | Project Manager and remove Unit1 from the
project. Now save the file to a directory created specifically for this program.
-
TIP: I believe you should always
create unique directories for each program you create. If you do not take this
step, you will never be able to keep the files from this program sorted from
files used by other programs you create. To not put a BCB project in its own
directory is to go face to face with the forces of chaos.
Go to the View menu again and choose the Project Source menu item. Strip out
all the code created for you by BCB and replace it with the code shown above.
You are taking control of this project and don't need any help from the BCB or
its rudimentary code generation processes.
In the next chapter I will explain in some depth why BCB should not be
considered a code generator, a CASE tool, or anything of the kind. I do,
however, feel that BCB is primarily an IDE-oriented tool. In this one case, it
would not matter much whether you wrote the code inside the IDE or from the
command line. However, BCB code is meant to be written, and your programs are
meant to be designed, inside the IDE.
This is not the place to get into a lengthy explanation of how the WinAPICode
program works. If you are truly curious, it is taken nearly verbatim from one of
the early chapters of my book called Teach Yourself Windows 95 Programming,
published by Sams Publishing. That book covers raw Windows API programming,
which is an invaluable skill, even for BCB RAD programmers. However, unraveling
the secrets of the Windows API is not the goal of this current book, so I will
merely point out a few key passages from the program.
The following code shows the message loop for the WinAPICode program:
while (GetMessage(&Msg, NULL, 0, 0))
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
This is the engine that drives the program, but it is unlike any of the code
you
see in a standard BCB program. It is not, however, any different from the
message loop that appears inside the VCL. I will show you how to step into the
code that contains that loop in Chapter 2.
At first, the following code also looks completely foreign to the BCB
paradigm:
LRESULT CALLBACK WndProc(HWND hWindow, UINT Message,
WPARAM wParam, LPARAM lParam)
{
switch(Message)
{
HANDLE_MSG(hWindow, WM_DESTROY, Window1_OnDestroy);
default:
return Window1_DefProc(hWindow, Message, wParam, lParam);
}
}
It would, however, be a mistake to assume the VCL knows nothing about Windows
procedures. In fact, you can add code to your BCB programs that gets called
every time the WndProc for your program or any of its forms gets
called. Once again, I am in danger of wandering too far afield, but if you open
up Forms.hpp from the include/VCL directory, you will find the
following declaration:
virtual void __fastcall WndProc(Messages::TMessage
&Message);
This call is one of the methods of TForm. As you can see, it is
declared
virtual, so you can override it in any of your own programs if you want. By
doing so, you place yourself directly inside the window procedure for your form.
This is an extremely powerful technique, but is one that most programmers will
never need to utilize. However, it is good to know that it is available if you
need it. I give an example of how to override this method in Chapter 4.
-
NOTE: The TMessage
structure that is passed to the WndProc method of TForm
contains all the fields that are passed to a standard Windows procedure:
struct TMessage
{
unsigned int Msg;
union
{
struct
{
unsigned short WParamLo;
unsigned short WParamHi;
unsigned short LParamLo;
unsigned short LParamHi;
unsigned short ResultLo;
unsigned short ResultHi;
};
struct
{
long WParam;
long LParam;
long Result;
};
};
};
All the information is there, if you need it. However, my point is that
there are few cases in BCB programming in which it is necessary to get down to
this level. Once again, this subject will be taken up later in greater depth.
In particular, I discuss TMessage and related structures in Chapter
4.
In this section of the chapter, I have shown that BCB allows you to get down
to the raw Windows API level if you want. There is, of course, nothing involving
the Windows API you cannot do in BCB, just as there was nothing involving the
Windows API that you could not do in either BC5 or Delphi. It would be silly and
fruitless to try to hunt for exceptions. Callbacks, pointers to pointers, COM,
whatever it is you want to do; BCB is up to the challenge. My point in this
section is simply to show the great technical depth of this product.
The DynamicButton Program
The DynamicButton program, shown in Listing 1.4, demonstrates how to use code
to do all the same tasks you would normally do with the visual tools. Needless
to say, I am not showing you this program as an example of how to program VCL
projects. Instead, I want to broaden the common base of understanding regarding
the way the VCL works. The goal is to demystify the visual tools so that you can
see that they do nothing magical or special but only execute code in the
background automatically, so that you are spared the laborious task of writing
the same lines of code over and over again.
Listing 1.4. The header for the DynamicButton program.
///////////////////////////////////////
// File: Main.cpp
// Project: DynamicButton
// Copyright 1997 by Charlie Calvert
#ifndef MainH
#define MainH
#include <vcl\Classes.hpp>
#include <vcl\Controls.hpp>
#include <vcl\StdCtrls.hpp>
#include <vcl\Forms.hpp>
class TForm1 : public TForm
{
__published: // IDE-managed Components
TButton *CreateDynamicButtonBtn;
void __fastcall CreateDynamicButtonBtnClick(TObject *Sender);
void __fastcall DynamicClick(TObject *Sender);
private: // User declarations
TButton *MyButton;
public: // User declarations
virtual __fastcall TForm1(TComponent* Owner);
};
//----------------------------------
extern TForm1 *Form1;
//----------------------------------
#endif
Listing 1.5. The Main source file for the
DynamicButton program.
///////////////////////////////////////
// File: Main.cpp
// Project: DynamicButton
// Copyright 1997 by Charlie Calvert
#include <vcl\vcl.h>
#pragma hdrstop
#include "Main.h"
#pragma resource "*.dfm"
TForm1 *Form1;
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
void __fastcall TForm1::DynamicClick(TObject *Sender)
{
AnsiString S("As you can see, I have an event associated with me. "
"Also, you can see the other button on the form has been "
"grayed out. Charlie explains all this in Chapter 1 of "
"the book. ");
ShowMessage(S);
}
void __fastcall TForm1::CreateDynamicButtonBtnClick(TObject *Sender)
{
MyButton = new TButton(this);
MyButton->Parent = this;
MyButton->Caption = "Push me: I'm a Dynamic Button";
MyButton->Width = 250;
MyButton->Show();
MyButton->OnClick = DynamicClick;
CreateDynamicButtonBtn->Enabled = False;
}
Here is code that creates a button dynamically:
MyButton = new TButton(this);
MyButton->Parent = this;
MyButton->Caption = "Push me: I'm a Dynamic Button";
MyButton->Width = 250;
MyButton->Show();
MyButton->OnClick = DynamicClick;
CreateDynamicButtonBtn->Enabled = False;
The first two lines of code duplicate what happens when you drop a component
on a form. First, the button is assigned an owner that will take care of memory
allocations. Next, the button is assigned a parent, so that Windows will know
what surface to draw the button on.
The last sentence contains a very important point. The VCL never does
anything more than call standard Windows API functions. It's not as if these
objects completely take over Windows and do things mysteriously in some dense
black box that cannot be understood. Instead, they wrap simple Windows API calls
or wrap entire Windows API classes, such as the button class. Underneath, you
just have the same old button that you see in any standard Windows program. The
job of the VCL is simply to make it easy for you to use these standard buttons.
The code then sets the Caption and Width for the component.
If you wanted, you could also have set the Left, Top, and
Height properties of the component.
To make the component visible, simply call its Show method. This
will ensure that the object is made visible to the user.
The next-to-last line of code in the method sets up an event handler:
MyButton->OnClick = DynamicClick;
This line of code is comparable to double-clicking a button at design time to
set up an OnClick event handler. As you will see in Chapter 4 the whole
delegation model is based on the simple task of assigning a method pointer to a
method.
Right now it is not terribly important that you understand how events work.
Instead, I just want to show you that you do the same things in code that you do
with visual tools. In particular, you can create components dynamically, assign
values to their properties, and set up events. This is what RAD is all about;
the VCL environment makes it easy for you to do these things with visual tools,
and the code shown here demonstrates how to do the same task in code.
The only key piece of the RAD puzzle not demonstrated by this program is the
art of making components. But that truly is a more complicated subject, and one
that I will broach only at the appropriate time.
In the last few sections of this chapter, you have seen a number of programs
that demonstrate key features of BCB. I have shown you these programs not so
that you can understand them in their entirety but as an introduction to the VCL
and to the material covered in this book.
During the course of Chapter 2, the core technical content of this book will
be
introduced bit by bit. By the time you reach Chapter 3, the story will be in
full swing, and it will not let up until the last page. The remainder of this
chapter, however, provides overviews of important matters of general interest.
On the Structure and Contents of This Book
I will now go on to give an overview of the contents of this book, as well as
highlight certain ideas and programming techniques that permeate most of the
rest of the text. Few of the ideas presented in the rest of this chapter are of
earth-shattering importance, and you are free to skip them if you are in a
hurry. I include them here only to help clarify my purposes in this book and to
create a common foundation for the ideas on which the rest of this text will
build.
Program Builders Versus Component Builders
An important distinction can be made between people who do low-level work and
people who build entire programs. For the moment, let me call the people who do
low-level work component builders or tool vendors, and those who do high-level
work program builders or program designers.
Suppose you are a program builder and you develop a need to convert files of
type PCX to type GIF. There are two things you can do:
- 1. You could find a book on file formats and start writing the code
for converting PCX files to GIF files.
2. You could search the Net, bookstores, and vendors, such as
Programmer's Paradise (www.pparadise.com),
for components, objects, or libraries that will do this for you.
Here is the moment of truth. What do you want to be: a component builder or a
program builder? Most programmers do a little of both, but there comes a time
when you have to decide which camp you will call home. You might think that you
live in both worlds, but the odds are against it!
Stop and think for a moment about the PCX-to-GIF creation tool. Here are some
of the factors you need to take into account:
- 1. You can't just assume there is only one version of the PCX and
GIF file format. Both standards have been around for a long time, and there
are undoubtedly several versions of each format.
2. You can't just work with 16-bit color files; you need to be prepared
for 8-bit, 16-bit, and 24-bit color--at a minimum! And don't forget that new
formats might come out, so you might have to update your code!
3. You have to create code that is reusable and maintainable. You can't
just throw this code together. What happens if you need to add a new format to
your program? What happens if you need to move from 32-bit Windows to 16-bit
Windows? What if you wanted to start supporting OLE automation? You have to
structure your code so it can be maintained.
4. Think long and hard about bugs. How clean is your code going to be?
What about corrupt PCX files? How about testing on a wide range of systems?
What about all the different size bitmaps people might want to make. What if
you find that your code works fine under most circumstances, but all of a
sudden a user from Germany complains that he wants 1152x864 size bitmaps and
that your code is messing up the last 20 rows of pixels. You tested 1024x768,
800x600, and 640x480 bitmaps, but you never thought about 1152x864 bitmaps. In
fact, you didn't even know that this was a standard screen size supported by
Windows 95 and used by lots of people all over the world! Where are you going
to find a computer that can even show screens at that resolution?
Take all the points listed above into consideration. How long is it going to
take you to create code that is at least reasonably bug-free and safe? One week?
Well, that's probably optimistic. Maybe two weeks? Well, possibly, if you are
very good, and if you add in maybe one week more for bug fixes. But let's be
realistic. For many programmers, a job of that size might take a month, or even
two. That's just to release the beta. Then you enter into another month or two
of testing, bug fixing, and updating.
Now, how long is it going to take you to find a component that does the same
thing? Maybe you could get one in an hour on the Internet. It might even be
free, and it might even come with source. Maybe you will have bad luck--and it
might take five hours to find the code, you have to pay some shareware low-level
stud $50 for it, and all you get are the binaries. (Bad sign. You want that
source code!) Or maybe, worst-case scenario, you can't find the code out there
anywhere on the Net, and you have to go to Programmer's Paradise
(1-800-445-7899,
www.pparadise.com) and actually buy the code for some big-time $150
to $200 expenditure!
Think: $150 now, plus three hours to learn the product, means I could have
this portion of the code written by tomorrow at noon. Or, maybe I should do it
myself and spend four weeks on the project. Four weeks of work is 4x40 hours a
week; that's 160 hours. Now suppose the following:
- 1. I'm a hotshot consultant being paid $100 an hour for my work.
160 hours times $100 is $16,000.
2. I'm a struggling corporate type being paid $25 an hour. 160 hours
times $25 is $4,000.
3. I'm a hotshot housewife hacker who has three shareware products on
the market. My husband is complaining about being a computer widower, but he
kind of likes the extra $15,000 I brought in last year with my products. A
month or two of weekends and weeknights hacking the PCX format might finally
force a real domestic crisis! After all, there would be no time for dinners
out and no time for movies or concerts. Just that darn GIF format! Or, maybe I
should just bite the bullet, buy the software tools for $150, and maybe skip
having dinner and a movie out this one week. Which is worse, another shoot-out
at the domestic corral or maybe that one painful $150 expenditure?
My point here is that being a low-level programmer is not necessarily the
intelligent thing to do any longer. It used to be that all the smart people
learned to hack the low-level code, and their effort made them very successful.
Now, however, the smart people find ways to avoid writing the low-level code!
Of course, if you work day in and day out with components, you need to know
how to build them as well as use them. This book will teach you enough about
components to show how to create them when you need them. I will cover all the
key areas, including the major sticky points that trip up beginners. However, I
will not focus on the subject to the exclusion of other, perhaps more important,
themes.
The Case for the Low-Level Programmer
The majority of readers of this book will fit into the program builder,
component user category. Of course, there are others who will want to take the
opposite tack. These are the programmers who want to make programming tools for
a living. In particular, if you think about the facts I've outlined above, it
must be obvious that there is a market for people who build programming tools.
It happens that I spend a good deal of my professional life talking to
programmers who build tools for a living. I've been working at Developer
Relations at Borland for several years now. The primary job of Developer
Relations is to work with independent software vendors (ISVs) who build tools
that can be used with Borland's programming tools.
During my years at Developer Relations, I have met many people who build
programming tools for a living. Some of them work alone, some of them work for
small companies, and some of them work at large companies and build tools on the
side as a moonlighter. Many of them work part-time as writers, part-time as
consultants, and part-time as tool vendors.
I understand that there is a thriving market for tool vendors. In fact, I
want to say as loudly as possible that if you want to build tools for a living,
that is certainly a reasonable goal. It is not necessarily an easy way to make a
living and not necessarily a very romantic way to make a living, but it can be
done.
The trick, however, is not to get caught in some never-never land between
being a program developer and a component or tool developer. Don't start to
build a program and then end up spending months building the tools you want to
work with! In that way lies madness!
This book shows quite a bit about how to build components, and how to add
component editors and property editors to your tools. However, if you want to
work full time on components, you should use this book as a starting point and
then go on to read Delphi Component Design, (Addison Wesley, by Danny Thorpe)
and Delphi Components, (Coriolis Group, by Ray Kanopka). These guys work in
Object Pascal, but they take the VCL down to the bare metal. Of course, I've
read those books, so many of the
hot techniques these authors cover will be included here. But as a rule, this
book is about program building, not about tool building!
Though I cover components in considerable depth, this book is aimed primarily
at program builders. Whether you create shareware, work for a corporation, or
work as a consultant, the goal of this book is to show you how to use components
to create programs.
I will definitely take time to talk about the Web, about what's on the Web,
and about how to find things on the Web. I will talk about companies like
Programmer's Paradise. I will show you what is available, and I will supply many
tools on the CD that comes with this book.
Types of Technical Books
There are three types of technical writing commonly used today:
Reference: These texts contain lists of information. For instance, a common
reference book might contain a list of functions in one or more APIs. The
reference would state the name of the function, where it can be found, its
purpose, and its parameters.
Tutorial: These texts step you through a process. For instance, a tutorial
might explain how to create a form by walking you through the process one step
at a time. Here is some sample text from an imaginary tutorial: "First create
a new form. Now drop a button on the form and use the align dialog to center
it at the bottom of the window."
Discursive (explanatory): A discursive, or explanatory, text attempts to
explain a concept in plain English. These texts work with concepts and
theories and frequently attempt to take complex subjects and explain them in
clear, easy-to-understand language.
This book uses all three of the techniques described above, but my primary
emphasis is on the third. The decision to focus on this third technique is in
part a natural consequence of my own talents and inclinations. However, I have
also spent considerable time thinking about how to write technical books, and I
have concluded that the discursive technique is the best one to use when
explaining complex material such as a compiler.
The problems I have with reference books include the following:
- They are intensely, even painfully, boring to read.
- They present material on paper, even though reference materials are
perhaps best kept in online help files. In other words, I think reference
texts are slowly being moved from paper to binary format, because binary
formats allow the users to search quickly for the specific information they
need.
- There is no question that some reference books are invaluable; but as a
rule, this technique is not useful when trying to teach someone a new language
or product. Reference books don't teach people anything; they are simply a
useful tool that can aid a programmer.
For some reason, tutorials are a favorite technique among managers. They
appear to be a shortcut to a particular end, and like downsizing, they seem to
promise easy profits with little work. Indeed, like layoffs, one can gain a lot
from a tutorial in a short period of time. However, tutorials are plagued by the
following problem:
- They are brutally boring, though not quite as mind deadening as a
reference. You usually know where a tutorial is headed, so there is little
sense of surprise or anticipation.
Tutorials force you to constantly move back and forth between a text and your
computer. This is a very uncomfortable thing to do. You read a sentence, try
something on your computer, and then try to find your place in the text again,
only to be immediately torn away and forced to find your train of thought
onscreen. I find this an extremely unpleasant and distracting process that I
have never managed to pursue for more than an hour at a time. Usually, I find
some excuse to escape from a tutorial after about 10 or 15 minutes.
Discursive, or explanatory text, has the following advantages:
- It teaches you the theory behind an idea. After you understand the theory
or the concepts behind an idea, you can perform the entire action on your own,
unaided. A tutorial might step you through 30 moves to show how a single
concept works. Conversely, if you grasp a single concept correctly, you can
usually intuit the entire 30-step process needed to bring it to fruition.
- You can read a discursive text straight through without interruption. You
need not read a few sentences, go to your computer, and then come back to your
text. You can concentrate first on the text and then on the computer, which is
more pleasant and palatable.
- Discursive text has some intellectual tension inherent in it, which can,
at least on occasion, make it enjoyable to read. For instance, a discursive
text can pose a problem and then take you on a small, intellectual excursion
while you learn the solution to it. When a matter is posed in this way, the
reader has his curiosity peaked and then gets a chance to, as it were, solve
the problem with the writer of the book. This creates intellectual tension,
excites curiosity, and gets the reader's mind working. In other words, a
discursive text can be useful because it is interesting to read, whereas
references and tutorials tend to gather dust simply because they are so
dreadfully boring.
Once again, I want to emphasize that I use all three techniques in this book.
There are many miniature tutorials in this text, and on occasion I will allow my
prose to degenerate into a reference if I feel it is helpful. As a rule,
however, I try to make this text as interesting as possible and provide you with
a book that is at least somewhat compelling to read.
In particular, I am much taken by the power of computer languages and by the
intellectual excitement inherent in the act of programming. It's fun to program,
and writing code is one of the most intellectually engaging tasks I know. If a
subject is this interesting, the text written about it should also capture some
of that excitement. The Internet, GDI, DirectX, OLE, and other topics are
innately interesting, and I write about them with as much energy and enthusiasm
as they engender in the best programmers.
Programming Theory
In keeping with the discursive theory of writing, this book tries to
encourage the development of general solutions to technical problems. In short,
when solving problems, I often find solutions that work in one instance, but
that aren't applicable to general instances. My goal in this book is to look
beyond single instances to find the common threads that run through a particular
type of programming problem.
To grossly oversimplify the matter, consider the following function:
int AddTen(int Value)
{
return Value + 10;
}
This function adds 10 to whatever number you pass into it.
Now consider the following function:
int Add(int Value1, int Value2)
{
Result = Value1 + Value2;
}
This function adds two numbers together.
The first function presented above solves one specific problem. The second
function solves a general class of problem.
When reduced to an example as simple as the one shown above, it would seem
like
programmers would always opt for general solutions rather than specific ones.
The second function requires two parameters rather than one, but it obviously
has considerably more power. As a result, most programmers would choose it over
a function like AddTen, which has less flexibility and less power.
Of course, in the real world, there is a much greater temptation to select
specific, rather than general, solutions. Some problems are hard to find general
solutions for, and as a result, programmers tend to find simple solutions that
solve the immediate problem, rather than looking for a more general solution.
However, the key point here is that general solutions can be reused, whereas
specific solutions are generally good for only one try. It is therefore usually
worthwhile spending extra time trying to find general solutions whenever
possible. It is also often worth using up one or two clock cycles on a general
solution rather than constantly writing custom solutions
tailored to speed up individual tasks. In other words, I often prefer a
general solution that takes up 50 clock cycles to a hand-crafted solution that
takes up 40 clock cycles.
Throughout this book, I often take the time to find general solutions to
problems. Sometimes this will make it appear that I am going the long way around
when doing something. For instance, the first example above is briefer, more
concise. It takes only one parameter rather than two. Isn't it therefore better,
more highly optimized, than the second example? Well, in a word, no. Sometimes
it's better to write a little more code in order to save room in the long run.
One method with two parameters is better than 50 methods with one parameter,
even if at first it appears to be the long way around.
About Comments
I tend to avoid using comments in my programs. However, I will occasionally
jot notes at the top of a module or right before a potentially complicated or
confusing procedure.
I am adamant about not including comments inside a block of code or inside a
class declaration, except under the most extreme and unusual circumstances. Code
is hard enough to read as it is, but adding comments to the middle of a block of
code leads to the worst kind of cognitive dissonance.
As a rule, I limit comments to single block before the implementation of each
method, or a longer section at the top of a module. This way, you can view the
code without having to simultaneously wrestle with reading comments. When you
need the comments, you can scroll up to find them.
RAD Versus the Command Line
Some long-term C programmers find themselves shying away from BCB's
components, experts, and other visual programming tools. Indeed, there was a
time when it made sense for some programmers to eschew fancy environments and to
stick to a "command-line ethic" that involved working mostly with simple text
editors and the Make utility. After all, there was a time when the IDE merely
helped to simplify the effort involved in learning certain tasks. During that
phase, the IDE did not necessarily speed up or improve development; it just made
things easier. BCB, however, actually brings tools to the table that outperform
anything you can do from the command line.
If you have decided to work in BCB, you have, in a sense, crossed the
Rubicon. There is no point in going back to the old command-line way of
thinking. BCB is a rapid application development tool, and the correct attitude
is to find ways to increase your ability to utilize the IDE, and even to come up
with suggestions for how the Borland team can improve the IDE. In fact, you can
even create your own experts that will improve the IDE itself.
One of the burdens of this book is to convince even the most recalcitrant
command-line programmer that RAD programming, when done right, is every bit as
technical and every bit as macho as the old "all I need is Brief and a makefile"
way of viewing programming.
-
NOTE: Brief is an old text-based
editor that has always been very popular in the C programming world.
Makefiles, of course, are still used in Builder. However, there is rarely a
reason why BCB programmers should ever need to write or edit a makefile
themselves. BCB uses the make syntax primarily because it is a convenient,
text-based way to manage projects. Some members of the BCB team prefer
text-based project management to the kind of binary file currently used by
Microsoft, and previously used by the Borland C++ products.
The point I'm making in this section is not that programmers should give up
the old, hard-core systems view of programming. All of that is very much a part
of good RAD environments such as BCB. Everything that you know about the Windows
API, assembly language, and the art of memory management applies just as much to
BCB as it did to BC5 or MSVC. Good systems programmers are as badly needed in
BCB as they were in BC5.
What, then, is the difference between programming BCB and programming BC5 or
VC++? One thing that has changed is the number of people who can play the game.
In BC5, only a relatively small set of programmers could make the grade. With
BCB, more people can manage to acquire true competency in the product.
BCB programmers are divided into two groups. There are the expert system
programmers who spend most of their time designing objects and creating
components. The second group of programmers are the consumers of these
components. It often takes a great deal of skill to create the components, and
considerably less skill to use them.
Of course, in many cases, the same programmer will be both a creator and a
consumer of components. This means some of the time you will have your systems
programmer hat on, and some of the time you will just be a RAD programmer who
produces programs in an incredibly short period of time.
The beauty of BCB is that it allows you to wear both hats. When you need to
be
productive, you can use BCB to put programs together faster and with more
ease than any Visual Basic programmer. At other times, you can get back into
that command-line ethic and dig into the Windows API, into the system's code.
My personal belief, however, is that 95 percent of the time, the command-line
ethic should just be a metaphorical view of the world. You shouldn't literally
program at the command line with Brief and a series of makefiles. BCB is
bringing that world to a close. But you want to hang onto that tough "I'm a real
programmer" attitude. That's something that's very much a part of the BCB
programming world.
One final word, just to avoid any misunderstandings: You will find that at
times I do program from the command line, and I will discuss techniques for
doing so with BCB. In other words, I am an advocate of hard-core Windows API
system programming. I have spent much of my C/C++ programming career working
from the command line with Brief and a series of makefiles. I believe in that
heritage, but I think it is important to recognize the sea of change that BCB is
introducing into the programming world. BCB is a RAD tool based inside an IDE,
not at the command line.
Of course, part of the fun of the programming world is that it allows people
to maintain a sense of identity. I'm sure there will be some successful
programmers who never come over to the RAD way of thinking. I'm not saying
"Beware, your career is toast unless you get this new visual programming
paradigm!" That's not my point at all. However, there is something pretty
important going on here, and like Dylan's Mr. Jones, you'll probably be better
off if you figure out what it is.
On Builder's Object Pascal Origins
BCB is an unusual product in that it is a C++ tool built on top of an Object
Pascal object framework called the Visual Component Library, or VCL. In fact,
all of the VCL, and almost all of the IDE, is written not in C++, but in Object
Pascal.
The reason for this unusual structure is simply that BCB is built on top of
the architecture for the successful Object Pascal-based Delphi product. Delphi
solved many of the difficult architectural problems inherent in creating a
high-performance, compiled, object-oriented language that could be elegantly
hosted in a RAD development environment. It was natural to build BCB on top of
this framework, even though it is C++-based and Delphi is Object Pascal-based.
I am well aware of the fact that, for many people, BCB's Object Pascal
heritage will be considered a rather serious drawback. For many programmers, a
reliance on Object Pascal immediately implies that the product must be both slow
and somehow crippled. This is a natural position for a C++ programmer to take,
and for those who have never been exposed to Object Pascal, it is perhaps the
only possible conclusion to reach.
It happens, however, that I have two great loves in the programming world:
C++ and Object Pascal. This puts me in a somewhat unusual position, which is
often rather awkward. However, it does give me good credentials for writing this
book, and hopefully it qualifies me to at least attempt an explanation of the
true nature of the relationship between BCB and Delphi, and between C++ and
Object Pascal.
As a big fan of both Object Pascal and C++, I want to take a few moments to
clear up some commonly held opinions that I regard as erroneous. Some of the
most important of these misconceptions I can simply list in a few short
sentences:
- There are many people who think that C++ is better than Object Pascal
because it is faster. This is simply untrue. There is no significant
difference in speed between the two languages. For instance, the 32-bit
version of Delphi uses the same compiler as Borland C++ 5.0 and as BCB itself.
Both languages produce the same code; they just express the ideas behind the
code somewhat differently. The equality of speed between the two products was
equally true in Borland's Windows and DOS implementations of C++ and Object
Pascal. This does not mean that you won't find particular cases in which one
product is faster than another, but the overall trend is toward two products
that have the same performance level.
- It is also untrue that C++ produces smaller, tighter code than Object
Pascal. In fact, the advantage in this particular issue probably resides with
Object Pascal.
- There are many people who think there are limitations inherent in Object
Pascal that make it incapable of performing certain functions. This is also
completely untrue. For instance, many people simply assume that you can't
express the idea of a pointer to a pointer in Object Pascal. To those who know
Object Pascal, the very idea of this objection is ridiculous. Of course, you
can express that concept or any other fundamental programming concept in
Object Pascal. It's a general purpose programming language just like C++. It's
not an innately limited language like Visual Basic.
- Some people think that the Pascal language stopped growing in 1970. Object
Pascal is not your father's Pascal any more than C++ is the same language that
Kernighan and Ritchie invented those many long years ago, back when the
earth's crust was still cooling. In fact, Object Pascal is a much more dynamic
language than C++, and it has changed to adopt to the latest programming
developments in a way that C++ cannot hope to change, due to the nature of its
committee.
Okay, so some of the objections to BCB's reliance on Object Pascal can be
eliminated. It does not have an effect on the speed or size of the executables
you produce. In fact, if the whole product were written in C++, the code
produced by BCB would not be any smaller or faster. Nor does the reliance on
Object Pascal mean that BCB is innately incapable of performing certain
functions. You can do anything in Object Pascal that you can do in C++, so the
VCL is every bit as flexible and powerful as a C++ framework.
Does this then mean that there are no drawbacks to BCB's Object Pascal
heritage? Well, I would have to stop short of making quite so bold a claim.
There are some problems, some of which are more than minor annoyances, that
result from BCB's heritage.
The first and most significant problem is that the VCL is indeed written in
Object Pascal, so you have no C++ source that you can peruse to find out how
your object framework is put together. This does not mean, however, that there
is no source for you to look at. The Object Pascal source ships with the
product, and if you have any understanding of Object Pascal, you should be able
to read it to see what BCB is doing in the particular cases when you really need
to know. You can also link this code into your project and step through it if
you desire, as shown in Chapter 2.
You will also find that that VCL sometimes expresses concepts in a manner
somewhat foreign to C++ programmers. This can be a bit like listening to a very
intelligent foreigner speak English. What they are saying makes sense and might
even be rather eloquent; but at times their choice of words, or their word
order, or just something about their accent, betrays the fact that they are not
native-born English speakers. Ninety percent of the time, this fact might not
even be noticeable, but occasionally it will surface.
Xenophobes always tend to assume that the awkwardness they perceive in a
foreigner's speech is due to some inherent limitation in that person's culture.
From my experience, as someone who knows both C++ and Object Pascal, I find that
this type of analysis of the two languages is flawed. For instance, there are
some things Object Pascal does better than C++, just as there are some things
C++ does better than Object Pascal. And it is only natural that some people will
prefer one language to the other, just as someone might prefer French to
Spanish, or vice versa. But the truth is that there might be some things that
sound wonderful in French that sound rather prosaic in Spanish, but that doesn't
mean French is better than Spanish. The issue, of course, is that you can almost
always reverse the tables by finding something that sounds wonderful in Spanish,
but doesn't sound so good in French.
In short, it's best not to judge these matters too quickly. C++ and Object
Pascal are both extremely sophisticated languages, and the marriage between them
found in BCB may prove to be a much better match than some suppose. In fact, the
ability to pull the best from both languages may prove to be an advantage for
the product, rather than a drawback!
To close this section, I should perhaps add that I am not an advocate of
either language over the other language. I like both languages equally. C++ has
a richness and syntactical fluidity that can make Object Pascal appear quite
naked, and, conversely, Object Pascal has a simplicity and elegance that can
make C++ appear rather florid and overdone.
Ultimately, I believe the only crime is to be a language bigot who assumes
his language is better than the other guy's language due to a classic case of
contempt prior to investigation. Almost all the programmers I know who are truly
fluent in the latest incarnations of both languages tend to agree with the
conclusions I am reaching here. The hardcore Object Pascal and C++ bigots that I
have met are usually only truly conversant in one language or the other. And
strangely enough, it's always the language that these people know well that they
consider best!
Creating Programs Quickly
In the last days of DOS, back in the early '90s, I remember a stage where
many of the people I knew were creating great utilities that we could all use.
In fact, many of the programs that I ran day to day were utilities that I or my
friends had created in C or in Object Pascal.
The introduction of Windows soon put an end to that outpouring of great
utilities. Windows was more fun that DOS, and more interesting, but it usually
took a good deal of work to create a program that was the least bit useful.
Now, after five or six years of work, Windows is finally at the stage where
programmers can again feel as though they are productive when using common
programming tools. Using C++Builder, you can put together all kinds of useful
applications in a short period of time.
Part of the fun of programming is the ability to create your own programs for
your own needs. It's much more fun to use one of your own programs than it is to
use a program written by someone else.
C++Builder is one of the set of exciting new tools that puts the fun back in
programming. Using this tool, you can create your own applications in days or
hours, rather than in months or years. This makes our managers in our assorted
corporations happy, but more importantly, it helps us put the fun back in our
own jobs.
Most programmers are in the business because they got bitten by the
excitement of this profession at some point in their careers. Most of us have
been able to stay with the fun for a long time. Some programming experiences,
however, can be too much like drudgery and too little like a good time.
C++Builder helps make our careers enjoyable. That's a very strong statement,
when you come to think about it, but I believe it to be true. The great thing
about RAD is not what it does for corporations, but what it does for you, the
programmer!
Use the Source, Luke!
Some versions of BCB ship with not only the invaluable header files found in
the include directory, but also the original Pascal source to the VCL.
You should
become familiar with both "sources" of information.
The key include files for BCB are in the ../BCB/include/VCL
directory, where the first part of the path references the place where you
installed BCB. You should become familiar with all of these files, but as you
will see in the next two chapters, two of the most important are the ones called
sysdefs.h and dstrings.h.
My Favorite Program: Grep
BCB ships with a command-line utility program called Grep.exe. This
humble 73,000-byte program is one of the most useful tools in my programming
arsenal. I use it to scan over tens of thousands of lines of source code for
examples that I can use.
For instance, suppose I have just finished reading an article on exceptions
and I want to add them to my program. Perhaps one of the first lines of code I
write looks like this:
catch(runtime_error)
Unfortunately, when the compiler reaches this line of code, it pops up a
message about runtime_error being an unknown identifier. Having been
down this road a number of times before, I know immediately what is wrong: I
need to include a header file in my project that declares runtime_error.
The question, of course, is which file is it that I need to include?
If I'm lucky, I can put my cursor over the word, press F1, and be taken to an
online example that will show me how to use the code and which files to include
with my project. However, it's quite possible that
- The online help is broken.
- The online help is incomplete.
- The online help is working and complete in this instance, but this
particular reference was apparently written in under 30 seconds by a harried
individual who at least shows signs of being under the influence of drugs that
might increase a person's production, but at the expense of his or her
fundamental reasoning powers.
- The code I'm looking for is from a proprietary library that has no help
file.
- I wrote the function or identifier I'm searching on myself, and therefore
there is no online help on it.
- This is a beta version of a product, and as a result the documentation is
not yet complete.
Of course, the first three options are out of the question for a Borland
product--say what?--but the last three occur from time to time. When you are
stuck in this kind of situation, Grep is one of the best ways out.
What I normally do is go down to the root include directory for BCB, and type
grep -id "runtime_error" *.h*
This command causes Grep to search over the include directory, and all
subdirectories beneath it, for the string "runtime_error", without
regard to case, as it appears in files that have an extension that begins with
an H. In particular, the -I switch says search without concern
for case, and the -d switch says search in subdirectories. I sometimes
add the -l switch, which tells Grep to just return the names of files
that include the string and not to quote any occurrences of the string:
grep -lid "runtime_error" *.h*
Here is the result of running the first command:
File STDEXCEP.H:
class RWSTDExport runtime_error : public exception
runtime_error (const string& what_arg): exception(what_arg) {;}
class RWSTDExport range_error : public runtime_error
range_error (const string& what_arg): runtime_error(what_arg) {;}
class RWSTDExport overflow_error : public runtime_error
overflow_error (const string& what_arg): runtime_error(what_arg) {;}
File STDMUTEX.H:
runtime_error,
runtime_error,
runtime_error,
runtime_error,
Here is the result from running the second command:
File STDEXCEP.H:
File STDMUTEX.H:
Needless to say, there are many times when I find it useful to Grep across
the directories for the sample programs that ship with a product, across the
directories for the Microsoft SDK, or across the sample directories in the CD
for a book. For instance, if I wanted to see not the declaration for
runtime_error but the way it is used in a program, I would Grep across
sample directories, looking for *.c* rather than *.h*.
When I am writing a book like this or when I am programming a new API with
which I am not familiar, I might use Grep as often as 10 times a day. There are,
of course, many Windows-based versions of Grep, and I'm sure there are even some
available that will integrate directly into the BCB IDE. However, I rarely use
any of these tools for long. For one reason or another, I find it simplest just
to use the plain old, humble, command-line version of grep.
If you grep across directories and get many lines in return, use the
more command to browse through one page of information at a time:
grep -id "WinINet" *.cpp | more
If you want, you can also send the output to a text file:
grep -id "Windows" *.htm > WebResults.txt
Hardware Requirements for Using BCB
BCB needs at least 20MB of memory, and really comes into its own if you have
32MB or more. Trying to use it on Windows NT with less that 32MB of memory is
probably too frustrating.
You should have a Pentium 120MHz or higher computer, with at least 100MB of
disk space free on the drives where you store BCB and your projects. That is
100MB free after the installation, not before!
If at all possible, you should run at a screen resolution of 1024x768 pixels.
800x600 is probably acceptable in most circumstances, though it is far from
ideal. You can also run at 640x480, but you will spend a lot of time shuffling
Windows around. (I should know, because I use this resolution a lot when I am on
the road. Its appeal wears thin over time.)
In this day and age, you really should try to get not just a good machine,
but an ideal machine. At the time of this writing, the ideal machine is a 200MHz
Pentium Pro with 64MB of memory and a 4GB SCSI drive. The machines I wrote this
book on were 120 and 133MHz Pentiums with 32MB of memory. One was a laptop with
1.2GB of hard drive space, and my home machine has about 3GB of hard drive
space. Throughout the whole process, I was shameless enough to long for yet more
power, more memory, and more hard drive space. If Ecclesiastes were writing
today, I'm sure he would have added: "Of the longing for more MIPS, there is no
end. This also is vanity and a striving after wind."
Getting Updates for C++Builder Unleashed
If you want to find updates for the code, you can visit my Web site at
users.aol.com/charliecal. If AOL stays afloat, and if its rates
remain reasonable, it will remain my online headquarters indefinitely. A backup
site is
www.supersonic.com/calvert.
I am afraid that I reserve the right not to answer questions sent directly to
my e-mail address. However, if you cannot find my Web site or if you have a bug
to report that is not addressed on my Web site, you can send me e-mail and I
will attempt to get some form of answer back to you. If you just want to say
hello, to pass the time of day, or to rag on me regarding some portion of this
book, of course you should feel free to write, and I will try to respond if at
all possible.
Every effort is made to assure that the code in this book works correctly.
However, if you see something in the book that does not look exactly right,
check the CD to see if it is corrected in the electronic version. If that still
doesn't look right, check my Web site. If all else fails, send me mail.
Be sure to find the readme file on the CD and to examine it carefully. You
should find both a text-based and an HTML-based version of the readme file.
You can write me at
ccalvert@wpo.borland.com,
76711.533@compuserve.com,
charliecal@aol.com, or
71601.1224@compuserve.com. If you want to send a binary file to me via
the Internet, write to
ccalvert@corp.borland.com. I tend not to answer technical questions
sent to me by mail, but I can give you updates on the location of my Web site
and about where to search on the site for answers to your question.
My Web site contains links to places of interest on the Web, including other
major BCB or Delphi Web sites and to vendors of BCB components. If you have
never visited a really hot, privately maintained, product-specific Web site, you
are missing out on one of the more valuable and interesting aspects of
contemporary programming. Programmers who can somehow carve some free time out
of their schedules often create fantastic Web sites with lots of free
components, tech tips, and news of general interest. I personally don't have
time for that kind of endeavor, but I try to list links to the most important of
these sites on my Web pages.
Adapting to the Tides of Change
To sum up this introductory chapter, it might be helpful to simply focus on
the subject of change. Clearly this book is very different from most C++
programming books. Instead of concentrating on the C++ language or on the
Windows API, I am focusing on components, databases, games, and on the Web.
Furthermore, I am saying that I am doing this because I believe this is the
best, even the mainstream, way to build programs in these waning years of the
twentieth century. If I am right about all this, that means that the programming
industry is changing.
There will probably be a time when the waves of rapid change that have been
washing over the computer industry will at last subside. But for now, and for
the foreseeable future, we can look forward to change and then even more change.
C++Builder is part of one of the big waves of change that have come washing
up on our shores of late. This is a RAD-based tool with powerful component and
database
capabilities. This is a more powerful tool than what we had before, but it is
also a new tool for most users, and that means we have lots to learn.
During these times of change, the only way to survive is simply to adapt to
the idea of living in a constantly shifting landscape. In fact, it's not a
landscape at all, but a shifting sea of technology that looks different
depending on the flow of the tide, the force of the wind, and the hour of the
day.
Those who don't take the risk inherent in changing tools will find that
others get the jump on them. With C++Builder, most developers can turn out
programs at least twice as fast, and often three or four times as fast, as they
could with Borland C++ or Microsoft C++. Furthermore, there will be no price to
pay in terms of the size or performance of your final program. This product
represents a huge change in the way we think about programming.
The fundamental idea we need to grasp is that components are now the core
around which our programs are built. At one point it was procedures and
functions; then it was objects, and now it is components. Undoubtedly, the
future will bring yet more changes, but this is what we need to learn now.
If there is any doubt, I should perhaps add that I believe C++Builder, and
its sister tool, Delphi, are the best programming tools on the market today.
They aren't just better than Borland C++, Microsoft Visual C++, or Visual Basic,
they are three or four times better. Four or five years from now, I expect that
tools like Borland C++ and Visual C++ will play the same role in the market that
Assembler plays today. Visual Basic will probably still be widely used, but it
is unlikely that it will ever catch up with the flexibility and performance of
C++Builder or Delphi.
In short, I am an extremely dedicated adherent of C++Builder. I am completely
serious in saying that I think this is the best way to program C++ in today's
world. Furthermore, I have made every effort to ensure that this book captures
what is best in this product and shows you how it can be used to create the
best, cutting-edge development tools that simultaneously impress users and solve
a wide range of problems.
Summary
In this chapter you learned a few facts about BCB, and quite a bit about my
approach to writing this book. There will be a few more general-interest
comments in the next chapter, but most of the material in the remaining portions
of this book focuses on hardcore programming issues. This chapter is the place
where I set the mood and the tone for the book. The rest of the material focuses
in on the core task of writing code!
Before closing, I should perhaps point out that over the last few years, I
have spent many long hours using C++Builder's sister product, Delphi. My long
experience with Delphi means that I have, literally, more than three and one
half years of experience using a product that is very similar to C++Builder.
Just how similar the two products really are will become clear over the course
of the next three chapters.
I believe that my Delphi-based experience helps me focus on what is best in
C++Builder. The opinions I have about this remarkable product have not been
reached hastily. They have been forged through years of experience.
I have now spent about six months with BCB, and I can say that it is a
product that lives up to the Delphi name, and that surpasses that product in
many ways. This is a great tool, which has really changed the way I think about
writing code.
There is a lot of material to study in this book and lots of hard work ahead.
But don't be so rushed that you never take the time to see what's exciting and
artful about this product. Programming is a lot of work, but it is also a lot of
fun. Right now C++Builder exists on the cusp of what's best and most exciting in
the programming world. Learn how to use the product, but also take a little time
to admire the sheer elegance of its execution. I have found that it always pays
off to take the time to see beneath the surface of a product, to see not only
how it works, but why it works!
|