[ ERCB Home |
New |
Feature |
Brief |
DDJ |
Letters |
Links
]
Custom Controls and Windows Programming
Review by Michael Floyd
Copyright (C) Dr. Dobb's Journal, June, 1994
When it comes to the popularity of custom and visual controls, there's
no mystery. As a custom-control developer, you can create a control that
is used in the Visual Basic (VB) environment just as any standard control
is used. When loaded, the control appears on the tool bar. In Visual Basic,
you can edit the control's properties and event code just as with standard
controls--extending, in effect, the VB environment.
Think of custom controls as data encapsulation on a grand scale. A custom
control is an object contained within a .VBX file--it has a predefined set
of features and a clearly defined interface to that feature set. Yet developing
custom controls adds a level of complexity and isn't without drawbacks.
Although a .VBX is a DLL, developing a custom control is far more involved
because a custom control is self contained--it must encapsulate the machinery
required to operate in both the application space and the visual-design
environment. From the programmer's perspective, there are compatibility
issues between versions of the VB API. And you must determine whether your
controls need to be visual, nonvisual (for use only in an application),
or both. Windows Programming Power with Custom Controls by Paul Cilwa
and Jeff Duntemann offers insight into these problems and provides useful
tools for the custom-control developer. Windows Programming Power with
Custom Controls takes a workbench approach to developing custom controls.
First, it walks you through the development of a generic control, then guides
you through new concepts while creating more interesting and useful controls.
Tools for Development
If you plan on creating the visual versions of the controls in the book,
you'll need to get either the Visual Basic 3.0 Professional Edition, which
includes the Control Development Kit (CDK), or the Visual Control Pack.
The CDK, a C-based SDK, contains files needed for writing VBXs, as well
as the visual layer required by the controls. You'll also need a C compiler
and linker capable of generating a Windows DLL. Duntemann and Cilwa suggest
Microsoft C 6.x, Borland or Turbo C++, or Symantec C++.
Because the authors use Borland C/C++, you'll need to make some changes
to the modules to get these controls to compile under other development
environments. An obvious example is the author's use of Borland's #pragma
argsused to eliminate certain warning messages when compiling. This doesn't
appear in the book's listings, but is in the source code supplied on the
accompanying disk. The Microsoft equivalent is #pragma warning. When I loaded
the .RC files into AppStudio, the error "undefined keyword or key name
VOS__WINDOWS16" appeared. This is a constant specified with the VERSIONINFO
statement. Including VER.H in the resource-definition file solved the problem.
Since the Borland compiler doesn't directly support the creation of .VBX
files, you must define your project as a .DLL and rename the output file
using a .VBX extension. The authors provide a utility DLL2VBX to aid in
this. Interestingly, Borland C++ 4.0 allows you to rename the file's extension
from within the IDE. I tried this, but the compiler bombed out on the build
with the cryptic error message: "IDE error." I solved this problem
by restoring the file's extension to .DLL.
A Bare-Bones Control
The book's first project creates a "skeleton" control that implements
the basic functionality of a custom control. The skeleton is used in later
chapters as a basis for creating more-interesting controls. Upon finishing
this chapter, you'll know how a generic custom control performs its job.
You'll also have a functional custom control that you can use as a custom-control
template in your own development. The skeleton control is minimal by design,
so you may want to enhance this template for real work. In keeping with
a modular design, the skeleton control is broken into several components.
INTERNAL.H is a private #include file that supplies the constants and prototypes
used by the skeleton control. SKELETON.H contains the #defines for message
IDs and data structures used by a control. Because the skeleton control
doesn't define any messages, this file is initially empty. The SKELETON
header file is renamed and used in subsequent chapters.
MAIN.C is where the Windows message handler resides. In this respect, this
module looks like a typical Windows application. However, there are some
differences in how a custom control handles certain messages. For instance,
a control can respond to both Windows messages and messages generated by
visual development environments like VB. The difference is that VB messages
refer to properties and events. The discussion here glosses over this point,
but is still better than a blow-by-blow description of the source code.
For example, the authors provide useful tidbits, such as how to use static
near to simulate function nesting (a feature supported by languages such
as Borland Pascal). Duntemann and Cilwa also point out that DLLs require
the large memory model. This is, in fact, a requirement of C++. However,
you can still make intrasegment calls near.
VISUAL.C, an optional module that supports controls in a visual environment,
is a fine introduction to the inner workings of the VB environment. This
module zeros in on properties, methods, and events. Properties help define
how a visual control will appear to the environment. The discussion of properties
primarily refers to custom properties. It's worth noting that VB also supplies
a library of standard properties and events which help to define a uniform
interface to visual controls so that you don't have to reinvent the wheel.
When you can, use them. To implement a standard property, simply include
the appropriate constant in the property table.
It's interesting that the authors decide to set up properties at compile
time. Typically when a control is loaded, it's registered, and the MODEL
control data structure is initialized. Standard programming practice is
to store a visual control's property-name strings in a resource pool to
be loaded when the control is initialized. This added overhead can slow
things down. Setting up properties at compile time can significantly improve
performance.
One topic the authors skim over is the MODEL structure, merely mentioning
that MODEL is a sort of master structure that points to all other structures
in the control. This is partially true. However, MODEL contains information
about the control's general behavior, window styles, class names and styles,
and so on. Understanding this is key to the behavior of your control. Figure
1 recreates the MODEL structure as defined in VISUAL.C. VB_VERSION is an
unsigned int defined in VBAPI.H, and specifies the version of VB being used.
Next, two flags are listed. The MODEL_fFocusOk flag allows the control to
receive the focus at run time, and MODEL_fArrows allows the arrow keys on
the keyboard to be used within the control. Normally, arrow keys are used
to move between controls. PCTLPROC specifies the address of the control
procedure. CS_VREDRAW, CS_HREDRAW, and WS_BORDER are window styles. The
rest of the structure is as advertised. The Control Development Guide documents
the MODEL data structure. Other modules include DIALOG.C (to provide support
for dialog boxes), PAINT.C (paint routines for displaying a control), and
HELP.C (for online help).
Other Controls
Subsequent chapters begin the process of creating something real. First,
a panel control is created. The panel control is a customization of the
skeleton control and shows how you can give a three-dimensional look to
controls by adding sunken and raised bevels. The control also supports flood
filling within the beveled control. Because the panel control is similar
to that in VB, the authors reasoned that it was not necessary to support
the visual environment hooks. Therefore, the VISUAL module is not included
for this control and it can only be used within a standard Windows app.
I found this surprising since the authors hint elsewhere in the book that
other visual environments will soon support visual controls.
On the upside, I was pleasantly surprised by the inclusion of a Pascal unit
that allows the panel control to be used in Borland Pascal for Windows applications.
Other controls created in the book include a virtual list-box control which
shows how you can extend list boxes to contain up to 32 Kbytes; a database
Browser control that allows you to browse records in a database; a page-list
control that allows the control user to select a document page from a list
of page icons; a text-file viewer; and a text-file editor based on the file-viewer
control. Throughout these sections, a number of useful embellishments are
provided, including the use of offset text to highlight key points and the
use of text boxes to highlight some rather interesting asides.
One text box, for example, explains how Borland's Resource Workshop extends
the CTLINFO data structure, which defines the class name and version number
for a custom control. The CTLINFO structure also contains an array of CTLTYPE
structures, each of which lists commonly used combinations of control styles
(called "variants") with a short description and information about
the suggested size. Borland redefines this structure as RWCTLINFO in CUSTCNTL.H.
The authors have created their own, called CONTROLINFO. These and other
tidbits are sprinkled throughout the book.
Likewise, every good book has a hidden gem. In Windows Programming Power,
this jewel is the .INI file manager control, called "IniData."
This VB-only control allows users to view, process, and manage Windows .INI
files--a task not easily accomplished in VB. One unique feature of this
control allows you to encrypt key values to prevent users from reading or
modifying an application's .INI file. The algorithm uses simple substitution,
where each plaintext character is replaced by a ciphertext character. Most
substitution algorithms are easy to break because they merely shift to the
right a specified number of characters to obtain the encrypted text. Such
algorithms do nothing to hide the frequency of characters. Instead, this
algorithm uses a substitution table to supply the encrypted text characters.
The algorithm is not sophisticated. It converts all letters to upper case
and only works with alphabetic characters; nonalphabetic characters are
passed through. However, the purpose here is to prevent a user from accidentally
blowing away a sensitive section of the .INI file.
Missing in Action
The Microsoft approach to developing custom controls is through Visual C++
and the Microsoft Foundation Class (MFC) Library. MFC 2.5 emulates portions
of VB to allow an MFC application to use .VBXs. If you're looking for help
in this area, look elsewhere. This topic is missing, but not missed. The
authors' SDK-style approach to developing custom controls precludes any
discussion of the MFC approach. And with OLE Custom Controls on the horizon,
future support of .VBXs in MFC is uncertain.
The three levels of support for custom controls coincide with the three
versions of VB. Each version adds support for new features, and older versions
can be viewed as a subset of subsequent versions. Visual C++ only supports
VB 1.0-compliant controls. Naturally, you may have to support multiple versions.
It's possible to develop a single custom control that behaves correctly
in each host environment. This is detailed in the Control Development Guide.
Unfortunately, the book does not cover this.
Creating custom-control wrappers is another topic you might find useful.
A wrapper is basically a custom control that provides a high-level interface
to DLL functions. Many developers who have created DLLs have the potential
to convert these to custom controls. Even if the DLL has no UI components,
you can create an invisible control which displays its bitmap at design
time, but is hidden at run time.
Conclusion
Windows Programming Power with Custom Controls is not a replacement
for the CDK documentation. In fact, you'll want to keep the Custom Control
Reference and the Control Development Guide nearby. Windows Programming
Power does provide a generic custom control which can serve as a starting
point for any custom control, and it gives you useful controls that can
be used as-is, or embellished for your specific needs. The authors have
designed this book so that the learning process is incremental--each new
control introduces a new concept while leveraging concepts built in previous
chapters. Within this process, Duntemann and Cilwa add their own unique
insights into the design and development of custom controls. In the end,
you'll walk away with a better understanding of custom controls and some
new and useful tools.
Windows Programming Power with Custom Controls
Paul Cilwa and Jeff Duntemann
Coriolis Group Books, 1994, 480 pp.
$39.95
ISBN 1-883577-00-4
Figure 1: The MODEL data structure.
MODEL Model =
{
VB_VERSION,
MODEL_fFocusOk MODEL_fArrows,
(PCTLPROC) CtlProc,
CS_VREDRAW CS_HREDRAW,
WS_BORDER,
sizeof (VBDATA),
8000,
NULL,
NULL,
NULL,
Properties,
Events,
IPROPINFO_STD_NAME,
IPEVENTINFO_STD_CLICK,
-1
};
Electronic Review of Computer Books
Created 5/1/96 / Last modified 6/4/96 / webmaster@ercb.com