When I first delved into C++Builder, as an OWL programmer I was a little dismayed to find I had to learn a new class library. Still, enough similarity existed between OWL and VCL that I could find my way around.
I noticed right away that VCL, like OWL, has classes called TRect, TPoint, and TSize. "Well, I know what to do with those!" I thought. But it didn't take long to realize that the VCL classes were like the OWL classes in name only--they were just wrappers around the Windows RECT, POINT, and SIZE structures, respectively. Bummer! I like those OWL classes!
Take TRect, for instance. It includes lots of overloaded constructors to let you easily create a TRect object. In addition, the overloaded operators and functions make manipulating rectangles easy with the OWL version of TRect. If only I could use the OWL TRect and other great OWL helper classes in my VCL applications.
In this article, we'll show how to incorporate some of the OWL helper classes in your C++Builder applications. In order to use the classes discussed in this article, you'll need Borland C++ 5.02. While you might be able to use classes from other versions of OWL, this article assumes that you have the C++ 5.02 update. We'll also discuss a couple of OWL classes that you probably don't know exist. Let's begin by looking at some specific classes you might want to use.
Several other classes in the Classlib/Winsys family may be of use--TProfile (an INI file-manipulation class), TRegistry, TString, and others. However, VCL provides good classes for these needs, so you don't have to bother with the Winsys classes that deal with INI files, the registry, string manipulation, and so on.
Assuming you want to use some of the Winsys classes in your C++Builder applications, how do you go about it? There are several ways:
Add the appropriate Winsys source
files directly to your application.
| Link the classes statically using
BIDSF.LIB.
| Link the classes dynamically using
BIDSF.DLL and the BIDSFI.LIB import library.
| |
The method you use depends largely on your needs, although in most cases, I think you'll prefer the first method. Let's take a look at each of these options and how to implement them in a C++Builder application.
We'll get back to namespaces in a
minute, but first, let's take a look at how to add the
source files to your project. Implementing this method is
a four-step process:
The easiest way to add the files is to use C++Builder's Add File To Project command. To access this feature, simply click the Add File To Project button on the C++Builder speed bar. Doing so opens the file-selection common dialog box. Navigate to your \BC5\SOURCE\CLASSLIB directory and select the GEOMETRY.CPP, UIMETRIC.CPP, and SYSTEM.CPP files. To add the files all at once, hold down the [Ctrl] key while clicking on each of the filenames. Then, click the Open button, which closes the dialog box and adds the source files to your project.
Next, choose Project... from the C++Builder Options menu. In the resulting Project Options dialog box, click on the Directories/Conditionals tab. At the end of the Include Path field, add the path to the INCLUDE directory in which you've installed Borland C++ 5.02. In our case, we added the text
;c:\bc5\includeas shown in Figure A. Be sure to use a semicolon to separate the path you type from the other paths, and take care not to remove the paths already listed. This step is necessary because when you compile the application, the Winsys files will need to include other Winsys headers that C++Builder must be able to locate.
Figure A: Enter the path to the INCLUDE directory where you installed BC++ 5.02.
As long as you're in the Project Options dialog box, let's take care of step 4 in this process by adding a symbol to the Conditional Defines field. Type the following in that field:
BI_NAMESPACEThis entry will tell the compiler to use the ClassLib namespace when it compiles the Winsys files. (For more information about namespaces, see "The Magic of Namespaces" below.) Click OK to close the dialog box.
For the final step (besides writing code), you need to include the headers for the files. For example, the include directive for the GEOMETRY header looks like this:
#include <winsys\geometry.h>Since you've already added the base path (C:\BC5\INCLUDE, for example) to the project's include path, you don't need to type the entire path to the Winsys directory when you include the Winsys headers. This point is important: You must place the include directive for the Winsys classes above any VCL includes. Otherwise, the compiler will confuse classes with common names (the VCL TRect versus the Winsys TRect, for example). Take a peek at Listing A to see where we placed the includes for the Winsys files.
At this point, you're ready to type some code and compile. You can begin using the Winsys classes just as you would use VCL classes--with one important distinction. For more information on that distinction, see "The Magic of Namespaces."
Statically linking Winsys classes
You may prefer to link to the pre-built static libraries that come with Borland C++ 5.02 rather than adding the source files directly to your project. Doing so might be desirable if you want to use a number of the Winsys classes in one of your applications. You might find it easier just to add one library file to your project than to add a dozen or more source files. Again, you're dealing with a multi-step process:
In step 1, you'll again use Add File To Project to add the static library file. In this case, add \BC5\LIB\BIDSF.LIB to your project--the Winsys classes reside in this static library file.
Steps 2 and 3 are identical to their counterparts discussed in the previous section. Notice that you don't add the Conditional Define for BI_NAMESPACE in this case. The static library is built without namespaces enabled, so adding that define would have no effect.
This method has a minor drawback. Since the ClassLib namespace hasn't been defined, you can't use it to differentiate between the two versions of TRect that the compiler must deal with. But, because the Winsys version of TRect was compiled without a namespace, it's in the global namespace by default.
As a result, you can modify an example shown in "The Magic of Namespaces" and use code like this:
using ::TRect; TRect rect;
The double colon by itself signifies the global namespace. This method gives you a little less control over namespace pollution, but it's viable nonetheless. Once you've taken the steps we've outlined, you're ready to begin using the Winsys classes in your applications.
VCL itself can't be used in a DLL--and
for that reason, all C++Builder applications are
statically linked. However, that fact doesn't stop you
from using the Winsys classes in a DLL. The DLL has
already been built for you, so all you have to do is put
it to use. Using the Winsys classes in a DLL requires the
following steps:
This list might look like the one from the previous section, but if you look closely you'll see that the library file is BIDSFI.LIB, not BIDSF.LIB.BIDSFI.LIB is the import library file for the BIDS52F.DLL, which you'll use for the Winsys classes.
To add the import library file, use C++Builder's Add File To Project feature to add the BIDSFI.LIB file (located in the \BC5\LIB directory) to your C++Builder project. Steps 2 and 3 are the same as in the previous section.
Whenever you add an import library file to a C++Builder project, the associated DLL will automatically load when your application starts. You'll need to be sure that BIDS52F.DLL is either in the application's directory or somewhere on the path--you can probably find BIDS52F.DLL in your \BC5\BIN directory. As with the previous methods, once you've set up the project to use the DLL, you can start using the Winsys classes as you would any VCL classes.
As you look over the code in Listing A, notice the FormCreate() function. This function uses the TSystem class to determine which operating system the user is running. It then displays the operating system information in a Label component.
The Button1Click() function uses Winsys TRect objects to create and manipulate two rectangles. The first rectangle changes the size and position of the application's window. The second rectangle displays a string on the form using the Windows API function DrawText(). The code determines the X position of the displayed text by using TUIMetric to obtain the height of a standard menu. The code finds the Y position by using TUIMetric to obtain the width of a vertical scroll bar.
When you use the Winsys TRect with API functions rather than the VCL TRect, the fourth parameter of the DrawText() function requires a Windows RECT structure pointer. For reasons that are beyond my understanding, the VCL TRect class doesn't have an operator to convert a TRect pointer to a RECT pointer. However, the Winsys version of TRect does have such a conversion operator. If you used a VCL TRect, then the DrawText() function would require a cast, and you'd have to write it like this:
DrawText(Canvas->Handle, "HelloThere!", -1, (RECT*)&rect, DT_SINGLELINE);
Because the Winsys TRect class has a RECT* conversion operator, you can write the DrawText() function without a cast, like so:
DrawText(Canvas->Handle, "Hello There!", -1, &rect, DT_SINGLELINE);
While this difference probably isn't enough to compel me to use the Winsys TRect on a regular basis, it's an added benefit to using the Winsys TRect over using the VCL TRect. The same benefit ratio is true of the TPoint and TSize classes. For more information on Winsys classes, check out "Undocumented Winsys"
Listing AMAINFORM.CPP
//--------------------------------------------------- #include <winsys\geometry.h> #include <winsys\uimetric.h> #include <winsys\system.h> #include <vcl.h> #pragma hdrstop #include "MainForm.h"
//--------------------------------------------------- #pragma resource "*.dfm" TForm1 *Form1;
//---------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
// Create a string.
String s = "Operating System: ";// If we're running Win95 append "Win95" to the end // of the string.
if (TSystem::IsWin95())
s += "Win95";
// Otherwise must be NT so add "Windows NT" to
// the end of the string. Also add the NT version
// number and the build number to the string.
else {
s += "Windows NT ";
s += String((int)TSystem::GetMajorVersion());
s += ", Build No. " +
String((int)TSystem::GetBuildNumber());
} // Display the string in the label.
Label1->Caption = s;
}
//---------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
// Tell the compiler we want the global version
// of TRect and not the VCL version.
using ClassLib::TRect;// Create a TRect object. TRect wndRect;
// Get the window rect using API GetWindowRect. GetWindowRect(Handle, &wndRect);
// Inflate the rect by 100 pixels all around. wndRect.Inflate(100, 100);
// Move the window to the new size and location.
MoveWindow(Handle, wndRect.Top(), wndRect.Left(),
wndRect.Width(), wndRect.Height(), true);// Make x the height of a menu. int x = TUIMetric::CyMenu;
// Make y the width of a vertical scrollbar. int y = TUIMetric::CxVScroll;
// Create a TRect object using x and y as the top // and left coordinates. TRect rect(x, y, 200, 40);
// Use it in the API DrawText call. DrawText(Canvas->Handle, "Hello There!", -1, &rect, DT_SINGLELINE); } //---------------------------------------------------
Kent Reisdorph is a editor of the C++Builder Developer's Journal as well as director of systems and services at TurboPower Software Company, and a member of TeamB, Borland's volunteer online support group. He's the author of Teach Yourself C++Builder in 21 Days and Teach Yourself C++Builder in 14 Days. You can contact Kent at editor@bridgespublishing.com.