Borland C++ 3.0 Programming
Borland C++ 3.0 Programming
atwee pe
RATER ONO aR
Includes Windows"
Programming —
WITHDRAWN
Borland®* C++ 3.0 Programming
Second Edition
Ben Ezzell
A
vv
12345678 9-MW-95949392
First Printing, March 1992
ISBN: 0-201-60866-9
Dedication
While the first computer was built shortly after World War II, for several
decades afterwards, computers remained rare devices, tended by a strangely
mixed crew of electronic technicians and mathematicians. Slowly, a new
occupation came into existance with various titles such as systems analyst and
programmer. But most practitioners of this new field did not begin as software
specialists. Indeed, most of us have come to this industry from fields so
diverse that a list of origins would read like an encyclopedia of occupations.
But this is always the case when a new occupation is born and there is no
established infrastructure to determine who may enter and how. And, with
this freedom, the best of the best come froma host of backgrounds with a host
of talents to create and breathe life into something for which no single occupa-
tion could serve as parent.
This has happened in the past as well.
In the mid-15th century, at a time in Europe when guilds controlled virtu-
ally every occupation and skills were taught only by rote, a thirty-year-old
goldsmith fell.afoul of his guild and other political forces in Mainz, Germany
and, as a result, began a series of experiments which changed the entire world.
In the end, while it was not the developer’s aim, his processes resulted in
destroying the power not only of the guilds but the entire political, social, and
religious structures of the time—not merely in Europe but throughout the
world.
The echos of this event are still being heard today. Loud and clear!
i11
iv BORLAND C++ 3.0 PROGRAMMING, Second Edition
Johann Gutenberg
1394—1468
The Father of Communications
Contents
Acknowledgments xi
Part 1: An Introduction to Programming for Windows 3.0 1
Chapter 1: Installing C/C++ for Windows 3.0 3
BC++ vs TGr+ 2)
QEMM.-386 Version 6.0 6
Environments and Terminology 9
Compiling Programs for Windows a
WinHello—An Introductory Windows Program 11
Message Programming or Event-Driven Programming 20
The WndProc Procedure Oph
Notations, Constants, and Variables Ds
Summary 515
Too often, when finishing a book like this (after proofreading, correcting, and
other far from minor but perniciously aggravating details), the author is often
only too happy to allow the entire subject to sink into a sea of oblivion. In
doing so, he or she may forget to say a heartfelt and well-deserved ‘Thank
You” to others who have also spent more than a little time and effort in the
preparation of the book.
Therefore, lest I forget, lam taking this opportunity to acknowledge several
individuals who have also expended sweat, blood, and (sometimes) tears in
putting this book together. These individuals include:
Chris Ohlsen at Borland International, who has provided technical review
as well as many valuable comments and to whom I am very much indebted.
Nan Borreson, also with Borland International, for expediting all manner
of details and for her constant and careful attention to even my stupidest
inquiries.
And, not least, Chris Williams, Amy Pedersen, and Andrew Williams with
Benchmark Productions for their patience.
To each of you—collectively and individually—thank you very sincerely!
Ben Ezzell
oq)
a wa Oern _s
(a, Say |
Svea’ 4) So ee ;
er oe cane
Aly Pa Deas —
~
23 Oa
eee oe ngaly Bae Cacti (soGnria
PeeWee Ss
semafanmnrgialoroictsbe
CIUAPTEU I:.< Reeeret Tis Testes
= pee Waa 15 i i 7
Sigs -_ : = a =) = = x 7 - -
_ =
—_ ¥ ay 7 ‘6 + = OS
An Introduction To
Programming For Windows 3.0
For many programmers, writing applications for Windows 3.0 will constitute
an abrupt departure from familiar and accustomed practices—a departure
which may even leave some wondering if they are actually writing in C, or
if they have been suddenly catapulted into some strange, almost-parallel
universe.
For others, particularly those who may have worked with earlier versions
of Windows, or with OS/2, these new practices may seem less strange, or may
even be welcome as old friends.
Whether those with previous Windows or OS/2 experience will have any
advantage over those who have never programmed ina multitasking environ-
ment, is, however, a moot point.
This assertion is made partially because of the changes and enhancements
incorporated in Windows 3.0, which will require even experienced Windows
programmersto learn new skills. More important, because the Borland C++
compiler and the Whitewater Resources Toolkit provide features and
capabilities which greatly shift the emphasis from Windows experience back
to general planning/programming skills, i.e., with Borland C++, how
experienced you are in the intracacies of Windows programming becomes less
important than your overall skills as a programmer.
All of which is not to say or suggest that you will be able to program
Windows without understanding Windows; just that Windows programming
1
2 BORLAND C++ 3.0 PROGRAMMING, Second Edition
will be easier for you to learn and understand while the tools (the compiler
and toolbox) take care of the dirty work for you.
The emphasis, however, will still be on understanding how Windows
programming works; which means, of course, understanding how Windows
works.
Therefore, in Part 1, I’ll begin by helping you to set up your BC++ complier
and the Toolkit for Windows. Later, I’ll introduce the basics of preparing
displays for Windows, show you how mouse operations are used, and discuss
the principles of programming in a message-controlled enviroment.
If you are ready, we will proceed.
Chapter 1
Installing Borland C/C++ 3.0 on your system hard disk is perhaps the simplest
task you will ever accomplish on your computer. Simply insert the installation
disk and type A:>INSTALL. Then follow the directions on your screen.
Before you begin, however, you should be aware that the complete 3.0
package, which includes the Borland C/C++ compilers, tools, applications,
and examples, requires about 26 MB of hard disk. These space requirements
can be reduced by selecting only specific memory models for installation
(from the Install menu) or by limiting installation of examples and/or peri-
pheral applications.
In addition to allowing you to tailor your installation, Borland’s Install
program also permits you to revise the drive, directory, and subdirectories
where the various programs and examples will be installed. But, in addition
to your BORLANDC directory (BC), there is one other item to which you
should pay special attention: the location of your Windows directory.
By default, the Install program assumes that Windows is installed as:
C:\ WINDOWS. Following installation, the program will attempt to provide a
Windows installation utility to complete the task of setting up Windows 3.0
by creating a C/C++ compiler group under Windows Program Manager. If,
during installation, you have correctly identified your Windows directory, the
next time Windows is loaded, the GROUPS utility will execute automatically
as shown in Figure 1-1.
4 BORLAND C++ 3.0 PROGRAMMING, Second Edition
The GROUPS utility executes quite briefly but does so politely, beginning,
as shown in Figure 1-1, with a dialog window that offers options to proceed
or abort. The background image, of course, is purely optional but, the
GROUPS icon appears in the lower left quadrant (second icon from right).
Cancel
Accepting the default (OK) option in the GROUPS dialog produces a new
task group under the Program Manager titled Borland C++, which contains
the eight applications shown in Figure 1-2.
As shown in Figure 1-2, the Borland C/C++ 3.0 package includes two
separate compilers identified as Turbo C++ and as Borland C++, as well as the
Borland Resource Workshop, Turbo Debugger for Windows, Turbo Profiler
for Windows, the Import Library utility, and Winsight (a Windows spy util-
ity). However, for the moment, the important items are the two compilers,
Borland C++ (BC) and Turbo C++ (TCW).
BC++ vs TC++
Borland C/C++ version 2.0 introduced a new compiler identified as BC,
replacing the familiar TC compiler. BC was to become the new professional
programmer’s compiler while the familiar TC version would appear as a
hobbyist’s version, released in a more economical package with fewer frills
and utilities.
The change in version 2.0 from TC to BC was more than merely presentational
because the BC compiler handled four types of applications: conventional
DOS programs, DOS overlays Windows 3.0 applications, and DLL (dynamic
link library) units. However, while BC 2.0 compiled Windows applications
and DLLs, the compiler did not execute under Windows except within a
Windows DOS shell. And, although BC 2.0 did compile and link within the
Windows DOS shell, link times in this environment were ludicrously slow.
On the other hand, using BC, you did not have to compile a Windows
application within the Windows environment with its slow link times. In fact,
most programmers preferred compiling under DOS, then switching to
Windows to test the executable code—despite the inconvenience of switching
from DOS to Windows to DOS again. The time saving in this approach was
significant.
For example, an application project requiring only 16.20 seconds to compile
and link under DOS (on a 33 Mhz 386 system) using BC, required 261.63
seconds to compile and link under a Windows DOS shell—some 16 times
slower than under Dos. But, using version 3.0’s TCW, the Windows com-
pile/link times are essentially the same as using BC under DOS.
Again in version 3.0, the BC compiler offers four compiler options, provid-
ing for both DOS and Windows programming. But, in version 3.0, the former
TC compiler reappears as TCW, providing a true Windows-compatible com-
piler. And, most important, TCW compiles and links Windows applications
and DLLs without the speed penalties experienced by the BC compiler.
6 BORLAND C++ 3.0 PROGRAMMING, Second Edition
One caution, however, before any of you who are dedicated Windows
fanatics decide to delete the BC version: TCW compiles only Windows and
Windows DLL applications and does not offer any capabilities for compiling
DOS applications.
Ergo: both the BC and TCW compilers are important, each for their
appropriate environments.
And, as a note for those few die-hards who still decline to use the integrated
development environment, Borland C/C++ is also supplied as a command-
line compiler. (Of course, BCC is also useful for your occasional, very large
program when memory becomes a critical consideration.)
There is also a solution. You may revise your CONFIG.SYS file to exclude
specific memory areas from use by QEMM-386’s memory management. As
installed, the first line in CONFIG.SYS should look something like this:
DEVICE=C:\QEMM\QEMM386.SYS RAM
In the case of a video conflict with Windows 3.0, Quarterdeck suggests the
following revision:
BREAK=ON
STACKS=0,0
BUFFERS=25 /X
FILES=30
LASTDRIVE=E
SHELL=C:\DOS\COMMAND.COM /P /E:256
DEVICE=C-\GQEMMNEOADAT «SY SaJRati7 GCaNDOS VANSI.SYS
INSTALL=C:\QEMM\LOADHI.COM /TSR /R:1 C:\DOS\FASTOPEN.EXE
Cu=5 OF-2 5)
DEVICE=C:\QEMM\LOADHI.SYS /R:1 D:\WINDOWS\SMARTDRV.SYS 256
Note that HIMEM.SYS (installed by Windows) has been removed, and the
memory sizes used by SMARTDRV.SYS have been reduced.
These are optional changes, however, and the important revisions are the
exclusion statements in the first line. Three areas in the first megabyte of
memory have been excluded as:
Table 1-1 shows a typical usage for the first meg of RAM as reported by
Quarterdeck’s MANIFEST, with additional notes on excluded memory areas.
DRDOS vs MSDOS
While the appearance of Microsoft DOS 5.0 (MSDOS 5.0) has answered some
complaints about conflicts between TSR and applications and complaints
about overcrowding in the base 640K of RAM, many programmers say that
MSDOS 5.0 has appeared too late with too little. They prefer to continue with
earlier DOS versions, supplementing these version with memory-manager
Chapter 1: Installing C/C++ for Windows 3.0 9
utilities such as QEMM, 386MAX, or Headroom. (Of course, these, too, can be
used quite successfully with MSDOS 5.0.)
Others, however, have chosen to sidestep the entire issue by moving away
from MSDOS to the new DRDOS 6.0 (Digital Research DOS), which offers, in
addition to integrated memory management, other important features still not
supplied by the much-touted MSDOS 5.0. (And, if you note a hint of dis-
appointment with MSDOS 5.0, let me simply say that I found it seriously
underwhelming.)
If you have been hesitating for any reason, please be assured that DRDOS
6.0 not only lives up to its promises but is also and most important completely
compatible with Windows, Borland C++, and virtually all other applications.
Thus far, however, I have found only two minor incompatibilities: the first
being—regretably—Sidekick 2.0. The second is Irwin’s EZTape software
packages (both the DOS and Windows versions.)
As a historical note for those whose experience in the computer industry is
relatively recent, Digital Research originated the very popular CP/M operat-
ing system a decade or so and, only by a quirk of fate, missed out on becoming
the original operating system standard.
As a last comment, no memory area exclusions are required under DRDOS.
TLink Resource
Conpi ler
Compiled Products
Resource
Compiler
For the moment, the Resource Workshop is used to create window icons,
bitmapped images, menus, resource and dialog script files, and application
hotkey assignments. And, while many, though not all, of these resources could
be created by writing ASCII text source files, WRT provides considerable
convenience and saves more than a little time in development as an interactive
utility for creating and testing resources.
The .RC resource file created by Resource Workshop is compiled to a .RES
file (roughly the equivalent of an .EXE file). The file is created by the .RC
resource compiler before the compiler is invoked a second time to combine
the .EXE and .RES files, thus producing an executable Windows application
program.
The .MAK file, which normally provides the instructions for compiling the
various sections, can be omitted entirely when using either the BC or TCW IDEs.
The IDE compiler can handle this entire process, including recompiling and
updating any necessary program segments, without separate Make instructions.
Aside from this introductory overview of the compiler process, this will be
the last time you need to be concerned with the multiple compiler steps;
unless, of course, you prefer to use the command-line compiler, BCC, instead
of the integrated compiler versions.
Still, there may be occasions when memory requirements imposed by very
large programs require using the command-line compiler. However, for most
purposes, the BC/TCW compilers are considerably more convenient and will
be more than adequate for all of the examples in this book.
main()
© Vprante “Hello, sworld™ 2s }
The calling parameters are actually supplied by the start-up code segment,
which is supplied by the C/C++ compiler. But, this definition can be con-
sidered standard for all Windows programs, using the PASCAL calling sequ-
ence and returning an integer termination message to the start-up code. The
type identifiers for the four calling parameters, together with notational
conventions, identifiers, and Windows-specific data types and data
structures, will be explained shortly. The more general structure of the pro-
gram will be discussed first.
The four calling parameters for the WinMain procedure begin with the
hInstance parameter, which is a unique identifier referred to as the "instance
handle" that uniquely identifies a specific program instance under Windows.
Unlike the conventional DOS environment, where only one program (TSRs
excepted) can be operating at any time, several separate instances of many
programs can be operating simultaneously under Windows and, therefore,
require unique identification supplied by the Windows system. This is com-
parable to a "task ID" or "process ID" commonly used in multitasking operat-
ing systems.
The second parameter, hPrevInstance (previous instance), is the identifier of
the most recent instance of an application that is currently running. Of course,
Windows 3.0 13
Customer: eared
R1-BSJ-703 9780201608663
Picker Notes:
M
'Wnd
class
if€ ! hPreviInstance )
{
we.style = CS_HREDRAW | CS_VREDRAW;
The style record field is assigned two "class style" identifiers, which are
OR’d bit-wise. The CS_ identifiers are defined in Windows.H as 16-bit con-
stants with one flag bit set in each. The CS_HREDRAW and CS_VREDRAW
flags indicate that window instances are to be completely redrawn anytime
the horizontal or vertical window size changes. When WinHello is resized, for
example, the display is redrawn with the message string recentered in the new
window display.
we.lpfnWndProc = WndProc;
The WndProc procedure handles all window messages that are sent to
instances of this window type. The type prefix Ipfn identifies this field as a
"long pointer to function." These prefix conventions are provided for the
benefit of programmers and do not affect the compiler, except, of course, that
these particular record fields are predefined.
Chapter 1: Installing C/C++ for Windows 3.0 15
The two record fields below are integer fields that are reserved for the
application’s use. However, because they are unused here, they are initialized
as 0:
wce.cpClsExtra 0;
wce.cbWndExtra 0;
The cb prefix stands for "count of bytes."
The hInstance field is simply the instance handle of the program that was
also one of the parameters passed to WinMain:
we.hInstance = hInstance;
The hIcon and hCursor fields accept values returned by the LoadIcon and
LoadCursor procedures, for the moment, use the default IDI_APPLICATION
icon (a white square with a black border) and the default IDC_ARROW cursor
(the slanted arrow cursor):
we.hIcon = LoadIcon( NULL, IDI_APPLICATION );
we.hCursor = LoadCursor( NULL, IDC_ARROW )>;.
Later, custom icon and mouse cursor images will be introduced, but
defaults are used here for simplicity.
The h prefix in the preceding three fields is simply shorthand for handle.
The hbrBackground field controls the background color and pattern for the client
area of each application instance:
we.hbrBackground=GetStockObject(WHITE_BRUSH);
The hbr prefix stands for "handle to brush" where brush is a graphics term
referring to the pattern of pixel colors used to fill an area. Standard or stock
brushes defined by Windows will be discussed later.
Since WinHello has no menu, the IpszMenuName field is assigned a null
value:
wce.lpszMenuName = NULL;.
IpszClassName, the last field in this structure, is the name of the windows
class. It is normally the same as the application name stored in the szAppName
variable:
we.lpszClassName = szAppName,;.
After the values are assigned, the RegisterClass function is called with a
pointer to the structure we:
16 BORLAND C++ 3.0 PROGRAMMING, Second Edition
RegisterClass( &we );
L
WS_OVERLAPPEDWINDOW.
The next four parameters are the initial X and Y axis window positions that
specify the position of the upper-left corner of the window, and the X and Y
axis window sizes. The CW_USEDEFAULT message instructs Windows to use
the default values for an overlapped window:
CW_USEDEFAULT,
CW_USEDEFAULT,
GWRUS E DERAUIE Te,
CW_USEDEFAULT,
the icon display area. Thus, with several sequential instances, each subsequent
window will be somewhat smaller, with the lower right corner of each
window at the same position on the screen.
The next two parameters are null in this case because this application window
is not associated with a parent window. If, on the other hand, this window were
a child window, the parent window handle would be used so that the child
window would appear on the surface of the parent. For an example of child
windows, call the Windows’ File Manager program and observe the layout of the
child window as you step down through a directory tree. Because this application
has no menu, a second null parameter is passed for the window menu handle:
NULL,
NULL,
hInstance,
The final parameter is another null value in this example but, in other cases,
would be a pointer to some type of data that might be used by the application
window or by subsequent processes:
NULL );
UpdateWindow( hWnd );
18 BORLAND C++ 3.0 PROGRAMMING, Second Edition
This is necessary because ShowWindow does not repaint the interior of the
window—only the frame, sidebars (if any), and captions. It is left to the
Update Window call to create the interior of the window display.
This completes the process of creating the window and updating the screen
display. The message "Hello, World!" is finally displayed. This, however, is
not the end of WinMain’s task processing.
In most cases, only the first parameter is actually used, and the remaining
three are passed as NULL or zero.
The initial parameter returns a pointer to the message structure to be
retrieved and, in most cases, will be passed on to the TranslateMessage and
DispatchMessage functions. If no message is available, GetMessage yields con-
trol to other applications until a message becomes available. The second
parameter is a window handle specifying the window whose messages are to
be retrieved. When passed as NULL, as in the example, GetMessage retrieves
messages for any window belonging to the application making the call.
Chapter 1: Installing C/C++ for Windows 3.0 19
However, the GetMessage function does not retrieve messages for windows
belonging to other applications.
The third and fourth parameters provide a filter capability, estricting the
message types that are returned. When wMsgFilterMin and wMsgFilterMax
are both zero, GetMessage returns all available messages (no filtering is per-
formed). Alternatively, the constants WM_KEYFIRST and WM_KEYLAST can
be used as filter values to retrieve all messages related to keyboard input, and
the constants WM MOUSEFIRST and WM MOUSELAST can be used to
retrieve all mouse-related messages.
The return value specifies the outcome of the function. It is nonzero if a
message other than WM_QUIT is retrieved and zero if the WM_QUIT mess-
age is retrieved. This return value is normally used to decide whether to
terminate the application’s main loop and exit the program.
In addition to yielding control to other applications when no messages are
available, the GetMessage and PeekMessage functions yield control when
WM_PAINT or WM_TIMER messages for other tasks are available. Also, the
message functions GetMessage, PeekMessage, and WaitMessage provide other
applications with their share of the CPU time to execute. If your application
does not call any of these functions for long periods of time, other applications
cannot run.
In the example program, however, as long as the message (msg) is not
WM_QUIT, the message value is passed first to Windows’ TranslateMessage
procedure for any keystroke handling that may be specific to this application,
and then to Windows’ DispatchMessage handler for further dispatching to the
next appropriate message handling procedure:
TranslateMessage( &msg );
DispatchMessage( &msg );
}
When the message processing loop terminates, the wParam from the mess-
age record is returned to the calling application, which, in this case, is
Windows itself.
return( msg.wParam );
20 BORLAND C++ 3.0 PROGRAMMING, Second Edition
The POINT data type is a second structure, also defined in Windows.H as:
As you will soon see, many or most of these event messages will normally
be handled by Windows’ default message handler. Even when a program
22 BORLAND C++ 3.0 PROGRAMMING, Second Edition
we.lpfnWndProc = WndProc;.
Given this address, Windows is able to call WndProc directly, passing event
messages to WndProc in the form of four parameters:
The four calling parameters begin with the window handle and identify the
window to which the message applies. In the case of WinHello, of course, there
is only one window involved; but this will not always be the case. The second
calling parameter is obviously the 16-bit value identifying the event message,
while the third and fourth parameters are the 16- and 32-bit message
parameters described previously. The time and pt (mouse) portions of the
message record are not passed to WndProc, but are used by Windows to
determine where the specific message should be addressed and to resolve any
conflicts that might occur over the order of events.
WndProc declares three local variables as:
HDC nidice-
Chapter 1: Installing C/C++ for Windows 3.0 23
PAINTSTRUCT. ‘psy
RECH hect-
The hdc variable is short for handle device context and provides a handle
to the output device—in this case, the CRT. The ps variable contains informa-
tion for painting the screen and will be discussed later. The rect variable is a
rectangular structure with four integer fields: left, top, right, and bottom.
Most messages that might be directed to the WinHello program can be
handled by default processing (that is, handled by Windows). This includes
resizing and repositioning the application window, reducing the application
to an icon, or closing the application window. There are still two messages for
which most applications must provide some specific handling: the
WM_PAINT and WM_DESTROY messages. Within the WndProc procedure, a
simple switch statement is used to process messages and provide the appropri-
ate responses to each:
switch( message )
sf
The first message that requires a response is the WM_PAINT message. This
message is issued anytime an application window is moved, resized, restored
from an icon, uncovered by a change in another application window, or
anything else that invalidates the client area of the present application.
When WinHello’s style field in the winclass structure was given the
CS_HREDRAW and CS_VREDRAW flags, Windows was directed to invalid-
ate the entire window anytime a size change occurred. This caused the
WM_PAINT message to be issued as an instruction to redraw the client
window. Also, when an application window is reduced to an icon, the client
area image information is not retained. In any graphic display environment,
saving even a single screen image requires an excessive amount of memory.
With multiple application windows, system memory could easily be
exhausted if attempts are made to save screen images. In like fashion, when
an application window is overlapped by another window, the overwritten
portion of the screen is not saved. Instead, in any of these cases, when the
application client window is revealed, moved, or resized, the WM_ PAINT
message is sent to the application as an instruction to update the contents of
the invalid client window.
24 BORLAND C++ 3.0 PROGRAMMING, Second Edition
case WM_PAINT:
hdc = BeginPaint( hWnd, &ps );
GetClientRect( hWnd, &rect );
DrawText is called beginning with the hdc variable which provides access
to the display, then the string (text) to be drawn, followed by a third para-
meter, —1, which indicates that the string is null-terminated. The final para-
meter is a combination of flags defined in Windows.H which, in this case,
instructs that the string should be written as a single line of text, centered
vertically and horizontally.
The EndPaint procedure is called last to release the hdc and validate the
restored client area, thus completing the response to the WM_PAINT message.
The final return(0) instruction simply ends this case statement and returns
processing to Windows. In other cases, instead of a return, a break may be used.
Chapter 1: Installing C/C++ for Windows 3.0 25
case WM_DESTROY:
PostQuitMessage( O );
ReturnniG 20m ps
}
This last provision should also be considered standard for all WndProc
message handler procedures.
Structure Meaning
MSG message structure
PAINTSTRUCT paint structure
ati point structure (mouse position)
RECT rectangle structure
WNDCLASS window class structure
Since the link instructions are too long to fit on screen, the command-line
wraps to a second line.
The TLINK command-line is composed of link options followed by five
groups of filenames (object files, execute files, map files, library files and
definition files) with each group separated by a comma. Beginning with the
link options, the /Tw command indicates that the link target is a Windows
application, the /v instruction includes debugging information, and the /c
instruction forces case recognition in public and external symbols.
The first group of filenames are the .OBJ files to be linked. COWS is the
initialization module for small memory models and should always be the first
-OBJ. WinHello is the program module. The extension .OBJ is assumed in both
cases.
Chapter 1: Installing C/C++ for Windows 3.0 29
The second group of filenames has only one file, WinHello, which is the
filename desired for the executable file. The .EXE extension is assumed for
execution files, and .DLL for dynamic link libraries. (The .DEF file, introduced
later, would include a LIBRARY statement to create a .DLL output file.)
The third group, omitted from this example, is the output .MAP filename.
If no filename is specified, TLINK assumes the same name as the .EXE
specification but with the .MAP extension. MAP files are created only if
explicitly directed by a flag passed to TLINK.
The fourth group is a list of library files, beginning with the CWINS.LIB
small-memory model run-time library for Windows, CS.LIB as the regular
run-time library, and IMPORT.LIB providing access to the built-in Windows
functions. The .LIB extension is assumed for all library files.
The fifth and final group consists of the module definition file(s), Win-
Hello.DEF. The .DEF extension is assumed for all files.
After the application is linked to an .EXE file, the resource file must be
compiled and then combined with the executable program. No resource file
has been created for the WinHello program yet.
Creating resources using the Resource Workshop will be demonstrated
later in Part 2.
The resource compiler would be invoked as:
This will produce a .RES file with the -R option, instructing not to combine
the result with the corresponding .EXE file. The .RES file would be combined
with the .EXE file in a separate step by invoking the RC resource compiler a
second time as:
To load Windows and run the application, assuming that you are compiling
from DOS, simply type:
NAME WINHELLO
DESCRIPTION "Windows Hello, World Program"
ExX(Eiiey RE WINDOWS
STUB SeWiENSTUB
SEX Exe
For the Data segment, the MULTIPLE flag states that more than one copy
of the data segment can be created for use by multiple copies of the program.
In this case, the flag is definitely an affectation since the data is not altered by
Chapter 1: Installing C/C++ for Windows 3.0 31
HEAPSIZE 1024
STACKSIZE 8.192
EXPORTS WndProc
Since .MAK files contain instructions in the reverse order of their execution,
the first instruction specifies the WinHello., .DEF, .OBJ, and .RES files as
source files for linking the final .EXE file. Presently, however, the .RES file
should be omitted from actual practice. The second instruction, which is broken
into several lines, provides the link instructions that specify the sources and
directories for the command-line entry format as discussed previously.
32 BORLAND C++ 3.0 PROGRAMMING, Second Edition
The third instruction calls the resource compiler to combine the .RES and
.EXE files, as:
re winhello.res
But, before these instructions can be executed, the fourth instruction tells
MAKE how to create .OBJ files from .C source files (or from .CPP source files).
The options specified here are -W for a Windows application, -c calling for
compile-only, -ms for the small memory model, and -v for debugging:
PIC TOID))
bce -c -ms -W -v winhello.c
The final instruction, which is executed first, tells MAKE to create the
required .RES file(s) from the .RC file(s) of the same name:
mabaGee! ler.
nc) —re—i1¢G Ss\bc\i
ne Cude winine Wvonnc
The -r command, as shown above, calls for compiling the resources, but
without combining them with the executable file that has not yet been created.
The WinHello.MAK file is invoked as:
C:\BC >make -fwinhello.mak
Now select Project/ Add item and enter WinHello.* in the Name box to
display a list of all WinHello files shown in Figure 1-4. Select WinHello.C for
the application (later, when resources are added, the WinHello.RC resource
script file will also be selected) and close the Project dialog box when finished.
This is all that is necessary to create a Project file. But, what about the options
that were set using the command-line entry or using the .MAK file?
Particularly, what about selecting Windows applications and .DLLs?
Under the IDE (in TCW), instead of command-line options, application
selections are made through the Application Options dialog (figure 1-5) which
is called from the IDE menu as Options / Application. At the top of the dialog
box, principal current settings are displayed while, at the bottom, two buttons
select between Windows App and Windows DLL. For the present, simply
select Windows App (default).
Turbo C++
Options Window
1) winhello.res
inhello.c
Compiler »
Make...
Linker >
Librarian...
Directories...
These compiler options are discussed in detail in Borland C++ User’s Guide
but are mostly self-explanatory. However, familiarizing yourself with all of the
menu options and dialog boxes presented by the IDE would be time well spent.
Summary
While TCW provides its own installation utility that creates directories and
copies files to your system, installing the compiler as a Windows application
requires some special provisions in modifying the SETUP.INF file. But, the
C/C++ compiler can be used to compile Windows applications even if it is
run from DOS. Trade-offs between developing under DOS and under
Windows were also discussed.
Following installation, an initial Window application, WinHello.C, was
demonstrated with an explanation of the principal features and basic
requirements of Windows programming.
Three methods of compiling (using command-line instructions, using
.MAK files, and using the IDE) offered a choice of processes for developing
applications. In the future, however, when compiler operations are
mentioned, the IDE will be the default development process for both Windows
.EXE applications and .DLL files for the simple reason that the IDE is the most
convenient and the fastest environment for development.
In subsequent chapters, the primary topics will show how to develop
Windows applications, with compiler requirements occurring as a secondary
36 BORLAND C++ 3.0 PROGRAMMING, Second Edition
topic only when relevant to the applications developed. At the same time,
details on using the IDE Editor, performing file operations, and other periphe-
ral features will not be discussed here, but can be found in the Borland C++
Version 3.0 User’s Guide.
For now,the principal topics will to be developing applications for
Windows and the special requirements of programming for the Windows
environment, since there is little point in debugging applications until they
have been created.
| BSS e pals S=]oSeS SS SSS SSeSeoSeeeseseeseaesee
///
Ld WinHello.C Leh
// C++ Windows HELLO WORLD == Version 1 //
ji fees ees Sessa SoS SS] SS SSeS Sse SSeS Seeee/ //
Hinclude windows.h
switch( message )
{
case WM_PAINT:
hdc = BeginPaint( hWnd, &ps );
GetClientRect( hWnd, &rect );
Drawlext Ci hdc, sc Hel Uo World tit) Sere ctr.
DEL SINGLECINE | DEL2CENTER] | Di 2=VicENTER
EndPaint( hWnd, &ps );
rewewiriNe (@) 2.
case WM_DESTROY:
PostQuitMessage( O );
PeEuriMaG (@) HN;
}
return(€ DefWindowProc( hWnd, message, wParam, LParam ) );
}
int PASCAL WinMain€ HANDLE hInstance, HANDLE hPreviInstance,
LPSTR lpszCmdParam, int nCmdShow )
{
static char szAppNameL] = "WinHello";
HWND hWnd;
MSG MSG;
WNDCLASS WC;
Chapter 1: Installing C/C++ for Windows 3.0 37
if€ ! hPreviInstance )
{
we.style = CS_HREDRAW | CS_VREDRAW;
we.lpfnWndProc = WndProc;
WiGHECIDIG lasiE xatiina = 0;
we.cbWndExtra = 0;
wce.hInstance = hInstance;
we.hIcon ="Loadicon'G NULES“1DIZAPPLIEGAT ION») ;
wce.hCursor = LoadCurnsorG NULL, IDCJARROW
we.-hbrBackground = GetStockObject( WHITE_BRUSH );
we.lpszMenuName = NULL;
we.lpszClassName szAppName;
RegisterClass( &wec );
}
hWnd = CreateWindow(
szAppName, // window class name ifIf
"Hello, World - Windows Style", // window caption “//
WS_OVERLAPPEDWINDOW, // window style //
GCWRUSEDERAUIET Li Vieira x foxoOsiweiem ah
CW_USEDEFAULT, Ji Vibheial VY foesreien IfIf
CW_USEDEFAULT, KY “Wialiedell M4 Saee I df
CW_USEDEFAULT, VA sini wied YY Sieze Lap,
NUE // parent window handle Mfif
NULL, // window menu handle Hit
hInstance, // program instance handle //
NUE) // creation parameters I if
ShowWindow( hWnd, nCmdShow );
UpdateWindow( hWnd );
while€ GetMessage( &msg, NULL, O, O ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
return msg.wParam;
}
a eh nn em eae ah ESE
NAME WINHELLO
DESCRIPTION "Windows Hello, World Program"
EX caver WINDOWS
STUB “WINSTUBSEXE*
CODE PRELOAD MOVEABLE DISCARDABLE
38 BORLAND C++ 3.0 PROGRAMMING, Second Edition
PCr OIDy
bcc -c -ms -W -v winhello.c
Textual displays are integral to virtually all applications with very few, if any,
exceptions. Even graphic games generally employ some text even if only to
report scores.
In conventional, non-graphics environments, text output is relatively sim-
ple using the gotoxy function for position and the printf function (or a varia-
tion) to format and print the string information.
Even in a graphics environment, the text output isn’t much more com-
plicated, particularly if you create a graphic counterpart to the printf function.
In both cases, your application "owns" the display and simply writes the
desired output, then forgets it. See Chapter 8, Graphics Programming in Turbo
C++, for examples of gprintf and associated functions.
In the Windows environment, however, there are other factors to consider
that will affect the design and execution of your program.
OY
40 BORLAND C++ 3.0 PROGRAMMING, Second Edition
is brought to the front (that is, receives the focus of operations), a Windows
application must be able to recreate an invalidated screen display.
In a text environment, many TSRs and applications using pop-up dialog
boxes or pull-down menus save a memory copy of the existing display on
activation and restore the display before exiting. In a text environment, this is
a relatively simple matter because less than 8K are necessary to save an entire
50 X 80 text screen since 2 bytes per cell (one character, one attribute) are
needed.
For a graphics display, however, the corresponding operation would
require nearly 300K for a 16 color, 640 X 480 screen without data compression,
which is not practical in most systems.
In spite of memory requirements, there are circumstances when Windows
may still attempt to save a portion of the display, such as when a dialog or
message box is created or when a menu is pulled down, restoring the applica-
tion image when the operation is finished. This may or may not, however, be
successful, and, if it is not, the application will need to recreate the screen
display.
There are two cases in which Windows does save overwritten display areas:
when the display is overlaid by a cursor image or when an icon is dragged
across a client area. In these circumstances, no screen update is required. But,
in all other cases, Windows notifies applications whose screen displays have
been invalidated when it is time to update the client window (and, of course,
updates the application’s frame at the same time).
is processed by the application’s WinProc function and creates the initial client
window display. Or, in other circumstances, an application may call the
InvalidateRect or InvalidateRgn function which explicitly generates
WM_PAINT messages with information describing the area needing to be
updated.
While sending a message to Windows asking for a message to be sent back
to the application in order to accomplish a specific task may sound rather
round-about as well as contrary to normal programming customs, in actual
practice it works quite smoothly as it provides Windows with the opportunity
to intercede as necessary. This smooths potential conflicts that might other-
wise occur in a multitasking environment. The actual program structures
required are also quite simple.
To accommodate the requirement of being ready and able to rewrite (paint)
the client window area on demand, an application must take a different
approach to writing the screen display. Instead of the write-the-output-and-
forget-it approach, Windows applications must be structured to accumulate
the output information, and to write-on-demand and rewrite-on-demand as
well.
While the first three fields of this structure are available for use by
applications, the remaining fields are used internally by Windows only. The
hdc field is simply a handle to the device context, Windows redundancy, since
this value is also returned by the BeginPaint function that would be called
before any screen update operations commence. The fErase field is a flag value.
FALSE indicates that Windows needs to erase the background of an
invalidated rectangle and TRUE indicates that the background area has been
42 BORLAND C++ 3.0 PROGRAMMING, Second Edition
erased. For our current purposes, however, it is the rcPaint field which is most
important. This field consists of a RECT structure which is also defined in
Windows.H as:
In this example, the BeginPaint function returns both the device context
handle (hdc) and the PAINTSTRUCT structure (ps) which was passed as a
variable parameter. This is the form that will commonly be used in response
to WM_PAINT messages. A BeginPaint function is always matched with a
corresponding EndPaint function call to release the hdc, as:
So, why the two different forms? Because there are two principal differences
between the two formats. First, a call to BeginPaint returns an invalidated
(clipping) rectangle that was set by Windows in response to some overlying
display, or as a result of an application call to the InvalidateRect function.
However, unless specific provisions are made, such as calling InvalidateRect,
the clipping rectangle may restrict drawing operations undesirably. A call to
GetDC returns a clipping rectangle equal to the entire client window area;
imposing no restrictions whatsoever on subsequent drawing operations.
Second, while EndPaint validates any invalid rectangles, ReleaseDC does not
reset existing invalidations and inadvertently clears information that might
be needed later to insure correct restoration of an obscured area.
Note: Even though GetDC does permit drawing operations over the entire
client window area, it does not mean that drawing operations will overwrite
other Window applications that overlay the client window. Windows will
restrict the actual screen operations to the visible portion of the client window
even though the clipping rectangle extends over the entire client window.
Device Context Handles are only one part of creating a Windows display
and, for a graphics-based text display, there are a few other considerations.
This is also an example of using the GetDC function instead of the BeginPaint
function. In this case, no screen paint operations are executed; only a retrieval
of device context information. The TEXTMETRICS data structure is defined
in Windows.H and appears in full in Appendix A.
This information is used in determining how text will be displayed. Figure 2-1
illustrates seven of the 20 values available in the TEXTMETRIC data structure;
five of which are concerned with font vertical characteristics.
tmExterna lLeading
| tmAscent
FinMaxChardidthi
OTpAueChariidthe.
In some cases it may help to have a third value for the average width of
uppercase (capital) letters which can be approximated, for most fonts, as 150%
of tmAveChar Width.
Since this font information will not change during program execution
unless the application changes fonts, the simplest method of determining text
spacing is to get the text metric information when the application is initiated.
And, the best place for initialization code is in response to the WM_CREATE
message, the first message the WinProc procedure receives. The PrinText.C
program shows how this information is retrieved:
case WM_CREATE:
hdc = GetDCC hwnd );
Giettel exauMextinatec SiGuehidice, mart mame
ReleaseDC( hwnd, hdc );
cxChr = tm.tmAveCharWidth;
GxXiCiaipe — ac XiGhiipe 82.0 /aeror-
cyChr = tm.tmHeight + tm.tmExternalLeading;
Rest Ulin GuOmop-
This provides three values for determining text size, though only cxChr and
cyChr will actually be needed. However, a variation of the PrinText program,
which displays strings of uppercase characters, will need the cxCap variable
later.
Chapter 2: Transporting Text Applications to Windows 49
Window Limits
Before taking the application’s output further, a few comments on the
organization of an application window and on window coordinates will
clarify explanations that follow. If, of course, you have worked with graphics
and graphics windows in the past, what follows may well be old news but, for
those who have not, here’s how a graphics window is organized.
Figure 2-2 shows an application window overlying one of Windows
"wallpaper" displays. Within the application’s client window, the window
coordinates begin at 0,0 in the upper left corner of the window and increases
down and to the right. These coordinates are window-relative, not screen
relative, and the origin remains at the 0,0 point regardless of the place on the
screen at which this particular application appears. The application does not
know, and does not need to know, where on the overall screen the client
window is located, or even where its own frame window is located. Granted,
the absolute screen position includes information that Windows has, but it is
not information that the application needs nor should it have any reason to
access it.
How are screen paint operations carried out if the application doesn’t know
where its client window is located in the "real" world? The answer is quite
simple. Properly behaved Windows applications do not write directly to the
screen. Instead, they call Windows functions with directions for screen output.
Windows then adjusts for the absolute screen position and clips the output to
the active or exposed client window limits. Thus, if an application’s window
is overlaid by another application or by its own dialog box or menu, but the
screen display is still updating, as in the Clock program example, its Windows
task is to prevent the update operations from painting the overlying
window(s). At the same time, the application itself proceeds as though its
entire client window were active and draws its images without worrying
about possible conflicts.
There are other advantages in operating within a windowed graphic
environment. Graphic operations may also operate at coordinates outside the
window limits but may paint only within the currently valid display area. This
is no small advantage as will be demonstrated by the PainText.C program,
discussed shortly.
50 BORLAND C++ 3.0 PROGRAMMING, Second Edition
Caption Bar
Client Window
Invalidated Area
case WM_PAINT:
hdc = BeginPaint( hwnd, &ps );
value ps.rcPaint.top is the top limit of the invalidated rectangle area while
ps.rcPaint.bottom is the bottom limit. These are client window relative values.
What the application needs for cBeginPaint and cEndPaint are line numbers
referring to the text to be displayed:
In PainText, a vertical scrollbar has been enabled because the display is too
long for a single screen display and the scrollbar position, cyScrlPos, is also a
factor in the equation.
Note: The cyScrlPos (vertical) variable is not measured in pixels but in text
lines, and the cxScrlPos (horizontal) variable is also not measured in pixels but
in character widths using the average character width read from the tm font
metrics.
In order to forestall errors in calculation, the two macros, min and max,
insure that cBeginPaint is never less than 0 and cEndPaint cannot be greater
than NumLines. Once beginning and ending text lines have been calculated,
set text alignment before writing to the screen. Since TA_LEFT and TA_TOP,
meaning that the left-top of the characters will be aligned at the output
coordinates, are the default text alignment values, calling SetTextAlign may be
unnecessary but is still good practice:
The x-axis /y-axis screen (client window) positions are calculated for each
line output. Normally, for left-aligned text, the x position would be equal to
cxChr, supplying a one-character width inset from the client window frame
and only the y-axis position would need calculation. But, there are two factors
relevant here. First, each successive line is stepped to the right to provide a
display that is deliberately wider than any normal display terminal can handle.
52 BORLAND C++ 3.0 PROGRAMMING, Second Edition
The first parameter is the usual device context handle while the second and
third parameters are integer arguments (signed) that specify the window
relative coordinates at which output (paint operations) begins. The fourth
parameter, LPSTR, is defined in Windows.H as a FAR pointer to char. It is a
pointer to the buffer space at which the output string is located. However,
unlike conventional C, an ASCIIZ (null-terminated) string is not enough and
thus a fifth parameter, TextOut, is required which specifies the number of
characters in the string.
In one respect, this is similar to the Pascal string convention in which the
string length is stored as the first (ot) byte of the character array instead of
depending upon a null-terminator character. Here, however, the string length
is not available automatically and must be calculated by calling a C function
using the string as an argument.
Rather than entering all strings in a string table (a topic which will be
introduced later) so that they can be referenced first by strlen to find their
length and then passed to TextOut for display, the sprintf and wsprintf
functions can substitute within the TextOut function call, thus:
Scrollbars
Scrollbars provide an effective, intuitive device for adjusting window views.
They are not limited to graphic displays and are increasingly common in
text-based displays as you may have noticed when using the Borland C/C++
IDE. Perhaps the only drawback to scrollbars are that they cannot be operated
without a mouse; at least, not conveniently. However, a computer mouse is
no longer an exception and few, if any, Windows users will not have a system
mouse. Therefore, there is certainly no reason not to use scrollbars in your
applications; and every reason, including programming convenience, for
using them.
Scrollbars are not completely automatic, however. Some provisions
within the application are necessary in order to respond to scrollbar
messages. Figure 2-3 illustrates a window application with vertical and
horizontal scrollbars and diagrams displaying the seven scrollbar messages
that will be returned by mouse clicks at various positions on the scrollbar.
Both scrollbars return SB_LINEUP messages when the mouse is clicked
(button down) on the top or left endpads, and SB_LINEDOWN messages from
the bottom or right endpads.
Also, if the mouse button is held down while the mouse cursor remains on
the thumbpad, a series of SB_LINEUP or SB_LINEDOWN messages are
generated, providing continuous scrolling by line (or character, horizontally).
In either case, an SB_ ENDSCROLL message is returned when the mouse
button is released; but, normally, these SB_ENDSCROLL messages are simply
ignored.
SB_PAGEUP messages are generated when the mouse is clicked (button
down) anywhere above or to the left of the current thumbpad position and
SB_PAGEDOWN when the mouse is anywhere below or to the right of that
position. As with the endpads, as long as the mouse button is held down and
the mouse cursor remains on the scrollbar, a series of SB PAGEUP or
SB_PAGEDOWN messages are generated, providing continuous scrolling by
page. In either case, a SB_ENDSCROLL message is returned when the mouse
button is released.
The scrollbar thumbpad presents a different response set. It returns a series
of SB_THUMBTRACK messages when the mouse button is held down and
during which movement occurs; but, only when the mouse button is sub-
sequently released and the mouse cursor is still on the scrollbar does it
Chapter 2: Transporting Text Applications to Windows 55
|p SB_LINEUP (press)
F=3| SB_ENDSCROLL (release)
oe
SB_PAGEUP
af
elles _—_ (press)
B_ENDSCROLL (release)
SB_PAGEDOWN (press)
SB_ENDSCROLL (release)
SB_LINEDOWN (press)
"SB_ENDSCROLL (release)
both vertical and horizontal scrollbars are enabled. But, since both respond to
their secondary messages in essentially the same fashion, the WM_VSCROLL
message response will illustrate both:
case WM_VSCROLL:
switch( wPARAM )
{
case SB_PAGEUP:
CYSChUstple=. main —15° =cyWins -eyehr.):
break;
case SB_PAGEDOWN:
cyScrlStp = max(¢ ie cyWirn / cyCiir
break;
While the line and page scroll messages are relatively simple, responding
to the thumbpad messages requires the information above the thumbpad
position provided in the low word of the Lparam value:
case SB_THUMBTRACK:
cyScrlStp = LOWORD( LPARAM ) - cyScrlPos;
Chapter 2: Transporting Text Applications to Windows 57
break;
ClalSCie oS mH UIM BO SetulelOINb=
cyScrlStp = LOWORD( LPARAM ) - cySecrlPos;
break;
case SB_THUMBTRACK:
if €" TrackContinuous) break?
case SB_THUMBPOSITION:
ChYsS
1G alShG Dae — i OLWIO)RIDIG Praia Ma sec 29 CU Pioisy.
break;
If no scrollbar movement was selected, that is, the thumbpad ends exactly
where it was when the process began, a cyScr!Stp value of zero causes the if
test to fail and no subsequent actions are necessary. The test statement also
imposes conditions on any step value set to insure that movements remain
within the valid scrollbar ranges.
Note: Depending on compiler warning settings, this code statement may
prompt the warning message “Possibly incorrect assignment in ...’’. This
happens because the compiler normally expects to see a statement in the form
a == bas the test condition within an if statement. But, in this case, this is not
an error, simply a valid but alternative programming form.
After checking the movement value, it is still the application’s responsibil-
ity to first set a new value for cyScrlPos; second, scroll the window; and, third,
adjust the scrollpad position:
ChY¥sS
iC hr OS ie-+—= aC y SIcin ort pie
SicipolGGWainidioiwiGah windy-a 0) ee—cly Chirac y,Sicmls tion
NUE NIUE =
setsScrollPos€ hwnd, SBOVERT, cyScrlPos ..l RUE.)
While the first step is simple, the second step requires calling the Scroll Window
function with parameters instructing Windows how to scroll the client
window and how much movement desired.
In this example, the second parameter is zero, specifying no horizontal
movement. The third parameter specifies the vertical movement. A positive
value for cyScrlStp (scrolling down) becomes a negative argument to scroll the
present window display upwards. Negative values scroll left or up and
positive values scroll right or down.
Chapter 2: Transporting Text Applications to Windows 59
The final two parameters, each passed as NULL, are pointers to RECT data
structures.
The first pointer is to a RECT data structure that sets a portion of the client
area to be scrolled. If NULL, the entire client area is scrolled.
The second pointer indicates a RECT data structure containing data that
specifies the clipping rectangle to be scrolled. Only bits inside this rectangle
are scrolled. If NULL, the entire window is scrolled.
Note: Additional details on ScrollWindow operations as well as other
Windows functions can be called within the Borland C++ IDE by pressing
Ctrl-F1. See the C++ User’s Guide for additional details on using the help
features or, within the Help screen, press F1 again for Help on Help.
The last task is to call UpdateWindow which instructs Windows to return a
WM_PAINT message to the application, causing the window to be updated:
UpdateWindow( hwnd );
}
MecuuTnic Om Drs
Window Sizing/Resizing
While PrinText has been written deliberately to extend beyond the normal
window limits, thus necessitating the use of both vertical and horizontal
scrollbars, provisions are still required in order to respond to window resizing
operations.
The WM_SIZE message is sent to an application anytime the window frame
and, therefore, the client window are resized. The WM_SIZE message preced-
ing the first WM_PAINT message is also sent when the application is created
to paint the initial display. In WinHello.C in Chapter 1, the WM_SIZE message
60 BORLAND C++ 3.0 PROGRAMMING, Second Edition
was not handled by the application but was left for default handling by
Windows. Here, however, the application has become somewhat more
sophisticated and several operations are necessary. The first operations are to
retrieve new cyWin and cxWin values which are available as the high and low
word values in Lparam:
case WM_SIZE:
cyWin = HIWORD(C LParam );
cxWin = LOWORD( LParam );
Once the new window size is available, the scrollbar needs to be reset to
match. This is done by calculating a new cyscrlmax to correspond to the
number of lines that can be displayed in the resized window:
Since the scrollbar range does not change during execution of this program,
the SetScrollRange function could have been called in the WM CREATE
response. Alternately, in any application in which the vertical or horizontal
range does change, there may be more appropriate circumstances when this
task should be executed. The remaining task always needs execution in
response to a resize operation: calling SetScrollPos to update the thumbpad
position:
While resizing an application does not change the position within the
display, resizing does change the scale of the scrollbar display. Happily,
however, Windows is responsible for the actual position calculations within
the scrollbar image. The only information that needs to be passed, except the
application handle (hwnd) and the scrollbar identifier (SB_VERT or
SB_HORZ), is the current position, cyScrlPos. The final boolean parameter,
when true, simply instructs Windows to redraw the image as well as update
the scrollbar’s coordinates. These same instructions are repeated for the
horizontal scrollbar as well.
Scrolling Errors
The PainText example program was created primarily to demonstrate how to
write text to a virtual display larger than the application window and to
demonstrate the scrollbar controls and scroll operations that provide a means
of moving the active window to view any part of the virtual display. PainText
also demonstrates a rather unusual error which few applications will ever
encounter, but which is still worth being aware of.
Figure 2-4 shows an enlarged snapshot of a corner of PainText’s client
window with a box drawn around several characters which are, in this exam-
ple, illegible. In the same illustration, a shaded bar at the bottom of the
window shows the area that was redrawn in response to the last WM_PAINT
message which followed a one-line vertical scroll operation. The error that
appears in the last three lines in Figure 2-4 results from the display line being
drawn in two halves and the reported information having changed in the
interlude between the two paint operations. In each of the three error lines
shown, the top half of the line was drawn with the y-axis origin reported
corresponding to the last line on the screen. However, only the top half of the
line falls within the client window’s display area. After the screen is scrolled
up, the bottom half of the line is drawn, but is now attempting to report a
different y-axis origin.
Okay, this is hardly a typical application; but it does demonstrate a poten-
tial error that can occur when the written information changes between paint
operations. This same discrepancy can also be observed during some horizon-
tal scroll operations.
If it is necessary for an application to guard against this type of error, of
course, the InvalidateRect or InvalidateRgn functions could be called during
62 BORLAND C++ 3.0 PROGRAMMING, Second Edition
scrollbar message handling to insure that critical areas are always included in
the subsequent paint operations. But, in reality, this is not likely to be a
frequent concern.
at ¥:50 } W:273
datx:57 2,Y:2?9 |
d at X:64 / Y:229 |
ted at X:71 4 i299
Summary
Thus far, text output to an application’s client window has been demonstrated
as have scrollbar operations used to control the client window position as a
view into a larger virtual display. The present demonstration, however, has
limited operations to using the default Windows font and the default display
mapping mode, a topic to be introduced later.
Elementary mouse operations have also been demonstrated, if only
indirectly; and this is another topic that will receive further discussion later.
Because the keyboard is still the primary user interface, the next topic will
be keyboard operations.
The following is the complete source code for the PainText.C program, and
the PainText.DEF definition file. A partial program listing, PainTxt2.C, is also
included to show the changes necessary to incorporate the SysMets.H file,
which will report the system metrics information for your computer.
Chapter 2: Transporting Text Applications to Windows 63
/ / SSSSSSSeS=SSSSsoSsseeeasas// //
Hey PainText.C fet,
LP ViCrr WindowsarPaint stext: //
/ / SSSS3S S22 ]3SSSssSeosesesss
/ //
#Hinclude <windows.h>
switch( message )
{
case WM_CREATE:
hdc = GetDCC hwnd );
GetTextMetrics( hdc, &tm );
cxChr = tm.tmAveCharWidth;
CxiGalpae= mca Cities c/a
cyChr = tm.tmHeight + tm.tmExternalLeading;
ReleaseDC( hwnd, hdc );
PevuUPDme @ DF
case WM_SIZE:
cyWin= HIWORD(C LParam );
cxWin= LOWORD( LParam );
lepsesetuo vertical. scrollbar s//
eysertMaxesomaxGeO,> NumEines.t+e2veucyWane/ecyChr )7
cyScrlPos = milimiGanG y, SiGe PO)Ss mEGsysS
Cite Mal Xam as
SetScrollRange( hwnd, SB_VERT, 0O,
Cy Sion Max EeAlaSiEnes-
SetScrollPos( HiWiNiCs ro Bas Vie Ralie,
GY:SICmUROS ak UlEmmr
jf setup horizonval scrollbar 47
cxscrumMax = max O07, Numliness+s608-—aexWin,/ cxChr );
cxSertl Pose vmingecxScrliPos> cxSerl Max 27
SetScrollRange( hwnd, SB_HORZ, O,
64 BORLAND C++ 3.0 PROGRAMMING, Second Edition
case WM_PAINT:
hdc = BeginPaint( hwnd, &ps );
cBe ginPaint = max(C O,
eyScrilPos -* ps. reraint.tOp / cyYChr J.
cEndPaint = min(€ NumLines,
cyScrlPos.+ DS. rcPaince. Dotcom 7) cyChhr 7);
SoupeaxceAkign® nelé, Wh\slbery W) WA_volP Ys.
for € 7 S&S CHOCMRatiNe se a <= GEMGRAtTinicg wdsesr 2
{
~ 2 exehre = «€ 1 So EGxSerlPos & 1 DE
wy SB ever = © 1 = CvSecrlPos = 1 Vs
MmenatiOurt Gahidicy mex any aes 4D Utah ell mmWISiPieiiinitate GumSi 215 Untaieliaee
PT aieS eeleSee CHiMeie4 Camb Cuiniqiealtits pilealy,c Cmcaitmm Xase/4 Cluny mmnVare/1Cuue
tox 7 nek eee;
}
End Paint( hwnd, &ps );
ret Winn Ga Ome
case WM_VSCROLL:
Swi tch(€ wParam )
{
CalsiCeo par
AG E.UIP
CyschliStp. = minG =) —cywinscyCnr.)
break;
ciaisie ob ENEUIP
Oy S Cust pies =r.
break;
case SB_THUMBTRACK: ii
cyScrlStp = LOWORD( LParam ) - cyScrlPos; //
break; Hei}
case SB_THUMBPOSITION:
cyScrlStp = LOWORD( LParam ) - cyScrlPos;
break;
case SB_LINEDOWN:
Cy Cn Sit pas — lel
break;
case SB_PAGEDOWN:
cyScrlStp = max(€ 1, cyWin/cyChr );
break;
Chapter 2: Transporting Text Applications to Windows 65
default: CYScrustp b= U0 -
}
if€ cyScrlStp = max( -cyScrlPos,
MAINE GEC YASICIMALGKt
Die
cyScrlMax - cyScrlPos ) ) )
{
CyoCcrUPos t= —cysecr ls tp;
ScrollWindow( hwnd, O, -cyChr * cyScrlStp,
NULLEs NULLeD >
setocrotlUPosGthwnd, SBEVERT, cyScrlPos, TRUE);
UpdateWindow( hwnd );
}
ELC UNG OME
case WM_HSCROLL:
Switch€ wParam )
if
case SB_PAGEUP:
CxocrUstp = minG -—1,7 —cxWin/cxchr 2:
break;
case SB_LINEUP:
exSormuUStio = “=>
break;
ff SSSBoSesoosSSSsoesosesesSsessqoesSsssoseESeeSScessecess/ //
case SB_THUMBPOSITION:
cxScrlStp = LOWORD( LParam ) - cxScrlPos;
break;
case SB_LINEDOWN:
CodSicinSit p= ames
break;
case SB_PAGEDOWN:
COCSICMU SiG Die =nemia1 Gan ll-meC XOWan
ac XiG Niemene
break;
‘default: CXSICmU Ste pEe= anOr
}
if€ cxScerlStp = max€ -cxScrlPos,
MiiniGunc xcs leSstipy,
arSepliten 2 GSSerilipPOs 2) 22
{
cxScrlPRos += exScrlsitp;
ScrollWindow( hwnd, <-cxChr * cxSerlStp, O,
NUECES NULL >>
66 BORLAND C++ 3.0 PROGRAMMING, Second Edition
case WM_DESTROY:
PostQuitMessage( O );
PeCuirnG © Ds
}
return( DefWindowProc( hwnd, message, wParam, lParam ) >
return msg.wParam;
}
NAME PainText
DESCRIPTION "Windows Paint Text Program"
EXE ie WINDOWS
AB Swit iaWeLN'S TUB en eX Ee
CODE PRELOAD MOVEABLE DISCARDABLE
DATA PRELOAD MOVEABLE MULTIPLE
HEAPSIZE 1024
STACKSIZE 8192
EXPORTS WndProc
651-32071200 E71NCMPSRV Boulder CIS (9600)..... DOD MOM ZS ent.
O0 OMEN
CM PSII
Bioiyicielnas PUD auatiscam iD iemeneaeae 444 205
#include <windows.h>
#include "sysmets.h" // add include statement
switch( message )
{
case WM_CREATE:
Sr uct
AD IAE nindex;
char *szLabel;
cial es Siz Diersic
pl Dity ae ae Shes INeatilait
Crs) el =
{
SiMe XS GREEN, be ME CIXeS Clues EINings
E SICinejeneaWill Git Nines pa xXeulesuare
SMEG SIGE EINE, SiMe
Caves 1G RoEi EsNigee
sooreem height, ine paxeiee,
SM_CXVSCROLL, weo)/MEC XWVSIGIRO |Sleuaee
sa C atau SiCumO) mmaliccOsW mW Clit hues
SiMe GyYanis CRO. HSL CVI SEROILIE™ -
“Horz scroll arrow height.
SM_CYCAPTION, SrOlMeCayaCAl
Pale ksOiN ieee
Chapter 2: Transporting Text Applications to Windows 69
While applications of all types, not just those that execute under Windows,
are becoming increasingly mouse interactive, the keyboard is still the user’s
primary input device; either in the form of the standard 89-key or the
enhanced 101/102-key keyboards. Even in a graphic environment, there are
few applications that do not depend, at least in part, on a text output display.
Therefore, even under Windows, keyboard input and text output remain
central to all types of application programming and are the principal topics
in this chapter. I’ll begin first with the handling of input from the keyboard.
Keyboard Drivers
The earliest PCs were strictly mono-lingual, supporting only the English
alphabet, thus imposing chauvinistic constraints on all non-English speakers.
But now computers are international in nature and, in recognition, Windows
3.0 includes several keyboard drivers and .DLL libraries to support internatio-
nal (principally European) keyboard configurations.
The Windows SETUP program is responsible for choosing the appropriate
KEYBOARD.DRV driver. When Windows is started, SETUP saves the original
09h interrupt vector (the hardware keyboard interrupt address), redirecting
the interrupt vector to routines supplied by the driver. When a key is pressed
on the keyboard, an Interrupt 09h is generated that suspends execution of the
current program and passes control to the Interrupt 09h handler.
Teas
72 BORLAND C++ 3.0 PROGRAMMING, Second Edition
an accent or diacritic to a letter, but are called "dead" because they do not, by
themselves, generate characters. And, for most applications, these messages
can be ignored.
This leaves the WM_CHAR message as the single keyboard message that
most applications will need to process because this is where the character
code, the keyboard letter, is actually found.
But, I said that there were a total of eight pieces of information contained
in the keyboard message and the character code is only one of these. So what
else is available?
In DOS, in addition to character and scan codes, it is also possible to retrieve
keyboard shift-state information which contains the status of the left and right
Shift keys, the Ctrl and Alt key states, and the CapsLock and NumLock shift
states as well as the normally ignored Scroll Lock and Insert status. For
enhanced keyboards, the right and left Ctrl and Alt keys are identified
separately and the key positions (as well as toggle state) of the ScrollLock,
NumLock, CapsLock, and SysReq keys are reported. Key up and key down
events, however, are not reported as separate events under DOS. And, while
similar information is available under Windows (see GetKeyState), a different
type of information accompanies each keyboard event message, contained in
the wParam and |Param variables.
AFAELDACABHANS
Flag Bits Scan Code Repeat Count
6-bits 8-bits 16-bits
Figure 3-2 shows a series of key event messages captured by the KeyCodes.C
program. The program has added an additional half-line space after each
WM_KEYUP or WM_SYSKEYUP message to provide a degree of grouping
to the keyboard event messages reported. But, as you may observe, the
_KEYDOWN, _CHAR and _KEYUP messages for a specific key do not always
appear in sequential order.
For example, the "t" character WM_KEYUP message appears after the "h"
WM_KEYDOWN and WM_CHAR messages, not because of any defect in
Windows, but simply because, as a speedy touch-typist, I had not yet released
the "t" at the time I was pressing the "h" key. A similar mixed ordering appears
76 BORLAND C++ 3.0 PROGRAMMING, Second Edition
WM_KEYDOWN 56h
WM_KEYUP 56h
WM_KEYDOWN 48h
WM_KEYUP 48h
WM_KEYDOWN 14h
WM_CHAR
WM_KEYDOWN
WM_CHAR
WM_KEYUP
WM_KEYUP
WM_KEYDOWN
WM_CHAR
WM_KEYDOWN
WM_CHAR
WM_KEYUP
WM_KEYUP
WM_KEYDOWN
WM_CHAR Spacebar
WM_KEYUP
WM_KEYDOWN ere to
WM_CHAR {screen |
WM_KEYUP x capture |
rogra m |
A|od
od
olld
do
od
el —-——-§
ES
8eH
Ree
Oo
Pee
+e
for the "i" and '"s" characters for the same reasons. Also, notice the last line in
the illustration where the WM_KEYDOWN message appears without a
corresponding WM_KEYUP message. This occurred in the example program
because the keydown event was received by the demo program, but the key
pressed was the F11 key, which was used as a hot key to summon the screen
capture program used to produce this snapshot and, naturally, transferred the
keyboard focus to the new program (see Keyboard Focus, below).
Since the KeyCodes application did hold the keyboard focus at the time the
F11 key was pressed, it did receive the WM_KEYDOWN message. But, after
reporting this fact, the key event message was passed back to Windows using
the DefWindowProc function. Since Windows then called the capture utility,
Chapter 3: Keyboard, Caret, and Scrollbars 77
transferring the focus, the KeyCodes application did not receive the
WM_KEYUFP release event message.
Notice also that the Up Arrow and Down Arrow key events, identified by
comments added to the illustration, do not generate WM_CHAR messages;
only key down and key up event messages.
The listing for the KeyCodes demo program appears at the end of this
chapter and can be used to view most, but not all, keyboard event messages.
Virtual key definitions listed as not supported are not found on IBM/com-
patible keyboards but may be supported by some variant keyboards.
This latter type of query has been normally used under DOS for TSR
applications that install their own Interrupt 09h handlers; and, under
Windows, there are other methods of providing hot-key selections. The GetKey
State function can be used, for example, to query the state of the shift key(s):
This example returns TRUE if either shift key is down, or FALSE if both
shift keys are up. The GetKeyState( VK_SHIFT ) function, however, returns a
negative value if either shift key is down, and zero if both are up.
In other cases, as with the NumLock and CapsLock keys, GetKeyState
reports on the toggled state set by the key. Thus the query:
could wait forever since no change in status would ever be detected. But the
alternative
could viably execute as long as the message queue is queried within the loop.
Chapter 3: Keyboard, Caret, and Scrollbars 81
This while loop retrieves messages from the message queue, then passes
them for processing through the TranslateMessage function before dispatching
the translated messages for further action by the application’s WndProc pro-
cedures.
However,
it is the TranslateMessage function that converts keystroke
messages into character messages which are recognizable and usable by the
application. The keyboard, per se, generates only keystroke information
that is interpreted by the keyboard driver into WM_KEYDOWN,
WM_KEYUP, WM_SYSKEYDOWN, and WM_SYSKEYUP messages. It is
the translation process that generates the four character messages:
WM_CHAR and WM_DEADCHAR messages derived from WM_KEYDOWN
messages, and WM_SYSCHAR and WM_SYSDEADCHAR messages
derived from WM_SYSKEYDOWN messages.
This translation process includes processing shift-key information to gener-
ate upper- and lowercase characters, checking toggle states for CapsLock and
NumLock and, in the end, generating WM_CHAR or WM_DEADCHAR
messages. In the resulting WM CHAR messages, the /Param variables are
simply the same as the keystroke messages that generate the character mess-
age. But, the wParam variables are the important thing here and contain the
ASCII codes for the characters.
In Figure 3-2, the WM_KEYDOWN message shows a keycode of 54h (ASCII
"T"), but the generated WM_CHAR message has an ASCII code of 74h ("t"). In
one instance shown (the spacebar) the WM_KEYDOWN, and WM_CHAR
message codes are the same, 20h, but this is not always the rule even though
most keycodes do correspond to one of the ASCII codes generated by most
English-language keyboards. The scan codes, of course, report the physical
key pressed but these are keyboard dependent codes and also vary inter-
nationally.
In contrast, the up arrow key shows a keycode of 28h which would be an
ASCII ‘(’. The scan code of 50h does identify the up arrow key except that an
enhanced keyboard has two up arrow keys with two different scan codes. One
of these may be attempting to generate an ASCII "8" instead of a cursor
instruction.
Chapter 3: Keyboard, Caret, and Scrollbars 83
The point is that the WM_CHAR messages should be relied on for character
information and the VK_xxxx message parameters used for all function and
cursor key identification. Examples will be shown in the demo program,
Editor.C .
case WM_CREATE:
The fonts available under Windows will be discussed presently but the
OEM_FIXED_FONT, which is the familiar DOS system font, was selected for
this example in order to use a few of the extended ASCII character symbols
not available in other fonts. A second special provision is found in the
WM_PAINT response:
case WM_PAINT:
The two string definitions, szCapt and szUnLn, provide a column caption in
the client window but are written, effectively, on top of each other. The default
paint mode used by TextOut emulates a character-based display and paints
the character background. The result is that the second string displayed
overwrites (erases) the first. By calling SetBkMode with the instruction
84 BORLAND C++ 3.0 PROGRAMMING, Second Edition
case WM_KEYDOWN:
ShowKey( hwnd, 0, 0, WM_KEYDOWN, wParam, LParam );
return(Q);
case WM_KEYUP:
ShowKey( hwnd, O, 1, WM_KEYUP, wParam, lParam );
return(Q);
case WM_CHAR:
ShowKey( hwnd, 1, 0, WM_CHAR, wParam, lParam );
return(0);
case WM_DEADCHAR:
ShowKey(€ hwnd, 1, 0, WM_DEADCHAR, wParam, LParam );
Re curniGupr
case WM_SYSKEYDOWN:
ShowKey€ hwnd, 0, 0, WM_SYSKEYDOWN, wParam, lParam );
break;
case WM_SYSKEYUP:
ShowKey(€ hwnd, 0, 1, WM_SYSKEYUP, wParam, lParam );
break;
case WM_SYSCHAR:
ShowKey€ hwnd, 1, 0, WM_SYSCHAR, wParam, lParam Des
break;
case WM_SYSDEADCHAR:
ShowKey(€ hwnd, 1, 0, WM_SYSDEADCHAR,
Chapter 3: Keyboard, Caret, and Scrollbars 85
wParam, LParam );
break;
The four WM_SYSxxxx message cases each end with a break statement
rather than a return statement. This insures that these messages are passed,
after review, to WndDefaultProc for any necessary handling by Windows. The
two character messages and the WM_KEYDOWN and WM_KEYUP messages
end here, needing no further processing.
The flag parameters following hwnd identify, first, the format that ShowKey
will use to display the event message and, second, a provision for additional
line spacing following the display of WM_...KEYUP messages.
The subprocedure ShowKey is essentially self-explanatory, breaking the
[Param values down according to the field shown in Figure 3-1 and displaying
these in a formatted arrangement. Since the /Param values for the
WM_...CHAR messages are the same as the WM_...KEYDOWN messages that
cause the TranslateMessage function to generate the character messages, these
fields are only displayed for the WM_..KEYDOWN and WM_...KEYUP
messages, not for the character messages.
There is one other item worth noting:
esxat Olt: GuahiGicy mC OC Nie mnC YW aS be prmrS 2 BIUnti
the,
wsprintt C\szButf > oszFormatli typed,
CLPSTR) szMsg,
display a few words or a single line to full screens of unstructured text, there
are a few basics that apply to any type of text input. Also, when using
Windows a few differences exist as well. These differences are discussed
below.
Caret vs Cursor
In Windows, the word cursor is reserved for the mouse cursor: that is, the
bitmapped image representing the mouse position and, frequently, indicating
the type of action the mouse will control. But what about the old, familiar DOS
text cursor? That ubiquitous blinking underline character that has so reliably
guided our interactions for so many years?
Well, the text cursor, under Windows, is now known as the caret; perhaps
a poor choice of terms since caret is also the name of that funny little hat-shaped
character (--) that C uses as the bitwise XOR operator (and that other human
languages use as an accent as in the characters A, E, 1,O, U and a,e,i,0,u). Still,
under windows, the caret is the new text cursor and can be a horizontal line,
a character-sized block, a vertical line, or a bitmapped image.
An underline caret is, of course, equivalent to the standard DOS cursor,
while the box or caret has been used by a variety of editor/word processor
applications. The vertical line caret, however, has not been a familar element
in DOS except in the case of graphics-based programs such as paint or
typesetting programs. This latter caret (cursor) is preferred, however,
whenever proportional typefaces (fonts) are used because underline and box
carets can not vary their widths conveniently to match varying character
widths.
Caret Functions
Because the caret (text cursor) is a system resource, individual applications
can not create and destroy their own carets any more than they can create and
destroy the system mouse cursor. And, like the mouse cursor, there is only
one caret in the system.
Applications can, however, borrow the system caret as needed but can do
so only while they hold the system (input) focus. An application can modify
the caret, thus changing the caret type and, of course, modifying the caret
position.
Chapter 3: Keyboard, Caret, and Scrollbars 87
An application must first know when it has or loses the system focus. This is
established by receipt of WM_SETFOCUS and WM_KILLFOCUS messages.
These messages are always issued in pairs, that is,a WM_KILLFOCUS message
is never sent to an application unless it has already received a WM_SETFOCUS
message; and a WM_KILLFOCUS message is always sent to an application
before the system focus is removed.
Receipt of aWM_SETFOCUS or WM_KILLFOCUS message does not mean
that an application is being created or destroyed. It only means that the focus
is being shifted to or from the current application. A WM_CREATE message,
however, always follows a WM_SETFOCUS message and a WM_DESTROY
message precedes a WM_KILLFOCUS message. Also, an application always
receives an equal number of WM_SETFOCUS and WM_KILLFOCUS
messages during its active life.
Thus, to use the caret, an application calls CreateCaret in response to the
WM_SETFOCUS messages and calls DestroyCaret in response to
WM_KILLFOCUS messages:
GalsiecmW MeroEumOCUISr:
GreatieCainet Gahiwinid,.sle,ssc x Chin, = cy.C hit ss
Sie tiGalmestiOrs) Gu xiCral pert x Chinen yiCialne tae y/C inane
ShowCaret( hwnd );
Pee Wien (0) 5
When CreateCaret is called, the caret is always invisible and its position
within the client window is indeterminate. Therefore, SetCaretPos is needed
to set the caret position and ShowCaret is needed to make the caret visible.
case WM_KILLFOCUS:
HideCaret( hwnd );
DestroyCaret();
return( O );
ShowCaret can be called to reveal the caret; no matter which window owned
the caret.
The parameters for CreateCaret are:
case WM_KEYDOWN:
switch(€ wParam )
{
me g
the aid of hein country and this is a tes
of a simple Windows editor where the quic
brown fox can jump over the lazy red dog
reen
| positioning
In this case, the response for the indicated line begins by copying each
character from one position beyond the delete position to the end of the line
back one position, that is, deleting the current character by shifting the
remainder of the line left and adding a blank at the end of the line. This much
is fairly simple since no wrapping or other editor elaborations are attempted.
But, in addition to deleting a character from the buffer, the process also
executes an immediate screen update:
HideCaret( hwnd );
hdc = GetDCC hwnd );
SelectObject( hdc,
GetStockO0bject € aS¥S JEM FIXED
FONT Jo):
LextOut© hdc, xGaretecxChm, yCaret*cy chin,
SButten® “Caret, yGaret.).
Cx Bilin xan Chima
ShowCaret( hwnd );
ReleaseDC( hwnd, hdc );
break;
Chapter 3: Keyboard, Caret, and Scrollbars 91
Notice that the HideCaret function is called before the screen is updated and
the ShowCaret function is called afterwards to restore the caret. This is a
necessary process anytime a screen paint operation is executed. It is needed
to insure that the caret does not interfere with the paint operations. Similar
precautions are exercised when the mouse cursor is active.
Before the WM_KEYDOWN message handling exits, SetCaretPos is called
to update the caret position, even though most of the WM_KEYDOWN
messages have no effect on the caret position:
case WM_CHAR:
for(€ 1=0; 1<LOWORDC(CLParam); i++ )
{
And, within the loop, the wParam variable contains the character code:
switch(€ wParam )
{
The order in which the characters are handled is not particularly important,
but the ’\b’ or backspace character is a special case:
message, thus allowing the earlier handling to delete the character and update
the display. The tab key also uses the SendMessage function to generate a series
of WM_CHAKR space character messages:
The next two special character codes simply change the caret position in
this application:
Rather than duplicating the linefeed provisions for the carriage return case,
the carriage return case is not given a break statement. This allows it to fall
through for additional handling by the linefeed statement.
The ESCape key resets the entire text buffer which also happens in this
example anytime the screen is resized. It is provided here with a dialog box
to query the action before execution:
break;
in the buffer; then, as before, hides the caret, updates the screen and restores
the caret (text cursor).
As editors go, this example is an idiot editor and has no provisions for
anything except the simplest of input and display with elementary positioning
and revision capabilities. Still, this example does show how WM_KEYDOWN
and WM_CHAR messages can be handled, and demonstrates the basic caret
functions.
case WM_KEYDOWN:
switch( wParam )
{
caisies VKEEE Rin:
SendMessage( hwnd, WM_HSCROLL, SB_LINEUP, 0L );
break;
Case ViIKERNGiHil=
SendMessage( hwnd, WM_HSCROLL, SB_LINEDOWN, OL ye
break;
CaisiemaVi KaaUIR
SendMessage( hwnd, WM_VSCROLL, SB_LINEUP,
break;
case VK_DOWN:
SendMessage( hwnd, WM_VSCROLL, SB_LINEDOWN, OL );
break;
}
return(Q);
In this fashion, the four arrow keys generate scrollbar messages equivalent
to clicking on the arrow keys at the ends of the scrollbars. Or, for faster
scrolling, the PageUp, PageDown, Home, and End keys could be added to
generate SB_PAGEUP and SB_PAGEDOWN messages. The SendMessage func-
Chapter 3: Keyboard, Caret, and Scrollbars 95
tion is capable of sending almost any type of message, not just keystroke,
character, or scrollbar messages. These messages will be discussed further in
future examples.
Both character sets illustrated are fixed-width fonts and, for characters 20h..7Eh are
essentially the same. The Windows 3.0 fonts, however, lack the symbol characters
O1h..1Fh as well as the boxdrawing characters B3h..D8h but expand the available inter-
national characters and symbols. The specific characters in the extended ASCII set may
vary according to national language requirements.
Chapter 3: Keyboard, Caret, and Scrollbars 97
SYSTEM_FIXED_FONT
The quick brown fox jumped over the lazy red dog
SYSTEM_FONT
The quick brown fox jumped over the lazy red dog
ANSI_FIXED FONT
The quick brown fox jumped over the lazy red dog
ANSI YVAR_FONT
The quick brown fox jumped over the lazy red dog
DEVICE_DEFAULT_FONT
The quick brown fox jumped over the lazy red dog
OEM_FIXED_FONT
The quick brown fox jumped over the lazy red dog
light typeface approximating the Elite or 10-point (12 characters per inch)
typeface which is popular on many printers and typewriters. The
ANSI VAR _FONT is a clean, sans-serif font that supports high-density text
display with only a minimal loss of clarity. Finally, the
DEVICE_DEFAUL T_FONT approximates the 12-point Courier (10 characters
per inch) typeface which is standard on typewriters and older printers.
Ansa
ip pein: Gar Disse ass
These strings can also be used for single characters but do require typecast-
ing to insure that the high-order word of the parameter is null (0):
While this typecasting may seem rather extreme, converting a char value to
a byte value first, then to a long value, and finally to a long pointer to a string,
it does work. And, in this form, the AnsiLower and AnsiUpper functions return
32-bit (LPSTR) values that contain the converted character in the low byte of
the low word, that is, in the 8 least significant bits. If ch is a BYTE value, no
Chapter 3: Keyboard, Caret, and Scrollbars 99
explicit typecasting is required on the returned value. Or, for character strings
that are not null-terminated, the AnsiUpperBuff and AnsiLowerBuff functions
can be called as:
The second form also uses the Alt-Keypad combination, but begins with
Alt-0 (keypad zero) in the entry of a four digit ANSI code 0196. This also
generates a WM_CHAR message displaying the ANSI character "A". In this
form, the OemToAnsi function is not invoked for translation.
The shorter form uses the three-digit ASCII character codes and fits
established habits, while the longer form provides entry of international
characters that do not appear in the OEM character sets (such as ------------ ).
If you would like to experiment with the Alt-Keypad entry form using the
KeyCodes program to demonstrate these effects, change the
OEM_FIXED_FONT entries to SYSTEM FIXED _FONT. (This will invalidate
the checkmark and arrow symbols used but will show the ANSI characters.)
Alternatively, the Editor program can be used, without modification, to
show both forms of entry.
Note: In either form, accidental entry of more than three digits (four with
a leading 0) results in the last three digits being used as the character value.
Likewise, decimal values above 255 are truncated as byte values ( NNN
mod 256 ).
Summary
While Windows keyboard messages are initially a bit more complicated than
simply retrieving characters from the keyboard buffer, responding to
WM_KEYDOWN and WM_CHAR messages provides a much more conveni-
ent method of handling mixed cursor and character keyboard entry.
Also, while less elaborate than WYSIWYG typesetting word processors, the
option of changing from fixed-width fonts to proportionally-spaced fonts
provides a major improvement in text presentation.
Less immediate, but more important in the long run, the ANSI character set
provides greater flexibility in reaching many international markets, while
future drivers can be expected which will provide even wider access to
non-English-speaking users.
Chapter 3: Keyboard, Caret, and Scrollbars 101
#include <windows.h>
REGT rect:
WISIN
din tah, Gees Z.5 Ustatee szFormatLlLiTyped,
(LPSTR) szMsg, message constant
wParam, key code value
ChCode, character or space
GhiGnity, repeat char count
ChScan ) 4 keyboard scan code
EndPaint( hwnd, &ps );
ValidateRect( hwnd, NULL );
}
" ee
switch( message )
{
case WM_CREATE:
hdc = GetDCC(C hwnd );
SelectObject( hdc, GetStockObject(OEM_FIXED_FONT)
);
GetTextMetrics( hdc, &tm );
cxChr = tm.tmAveCharWidth;
cyChr = tm.tmHeight + tm.tmExternalLeading;
ReleaseDC( hwnd, hdc ;
PACER UO) = 2? Csclir es
RevGuin
i) Gu Om E
case WM_SIZE:
cxWin = LOWORD( lParam ); // window width Lik
cyWin = HIWORD( lParam ); // window height Uy)
Fect..pight ) += cxWins
rect.bottom = cyWin;
UpdateWindow( hwnd );
necurnG color
case WM_PAINT:
Chapter 3: Keyboard, Caret, and Scrollbars 103
case WM_KEYDOWN:
ShowKey( hwnd, "WM _KEYDOWN,"wParam, lLParam );,
return(Q);
case WM_KEYUP:
ShowKey( hwnd, eeWie VaU) Pater a Wika clilancl) Illes LParam );
return GOs
case WM_CHAR:
ShowKey( hwnd, "WM_CHAR",wParam, UParam.)
He wUuinniGO)y:
case WM_DEADCHAR:
ShowKey( hwnd, "WM _ DEADCHAR",wParam, lParam );
return(0);
case WM_SYSKEYDOWN:
ShowKey( hwnd, 0, "WM _SYSKEYDOWN",wParam, lParam );
break;
case WM_SYSKEYUP:
ShowKey( hwnd, "WM_SYSKEYUP",wParam, lParam );
break;
case WM_SYSCHAR:
ShowKey( hwnd, "WM SYSCHAR",wParam, lParam );
break;
104 BORLAND C++ 3.0 PROGRAMMING, Second Edition
case WM_SYSDEADCHAR:
ShowKey( hwnd, 1, 0, "WM_SYSDEADCHAR"wParam, lParam};
break;
case WM_DESTROY:
PostQuitMessage( O ); return( O );
}
return( DefWindowProc( hwnd, message, wParam, lParam )
);
}
Ti Lo MnP RAWVUiMGReeliMea 2
DispatchMessage( &msg );
3
return( msg.wParam );
}
NAME KEYCODES
DES GRi Pi LON "Windows KeyBoard Program"
BME VPLS WINDOWS
oT UB SVU SUINESS UpU3) 6 129,13 4
CODE PRELOAD MOVEABLE DISCARDABLE
DATA PRELOAD MOVEABLE MULTIPLE
HEARST Ze 1024
STACKSIZE 8192
EXPORTS WndProc
Hinclude <windows.h>
Pde
ta nes Buds elakt x4.) 4¥) > = Co pBur fen ity * cxBu tit 1+ 2x2)
switch(€ message )
{
case WM_CREATE:
hdc = GetDCC( hwnd );
Sie lec tOlbi je cit. Ganidicy,
106 BORLAND C++ 3.0 PROGRAMMING, Second Edition
GetStockObject(SYSTEM_FIXED_FONT) );
GetTextMetrics( hdc, &tm );
exChr = tm.tmAveCharWidth;
cyChr = tm.tmHeight;
ReleaseDC( hwnd, hdc );
PaewUriN€ W@W MW.
case WM_SIZE:
cyWin = HIWORD( lParam ); // window pixel height //
cxWin = LOWORD(C LParam ); // window pixel width Lh
GX Bilistatae mem aye Gael tC XsWalia CEXCihit
SMU S inept a, emliin “ Cavinie DMF
tuG folswarver Ws We 2
free( pBuffer ); // free any existing buffer el?
Taf GunGlaO)NIGD ec Stitt emily 5 Ulta Ol) 151) aan
( pBuffer = malloc( cxBuff*cyBuff ) ) == NULL
MessageBox( hwnd,
"Insufficient memory - reduce window size!",
Mel won IN@loxoreig
MB_ICONEXCLAMATION | MB_OK );
else
fori Gay =OFe yicichy/BiUbhehie meey, Ft ae
for GQ x=0"" x<cxButipe xet)) BUT
fer (x. yan:
xCaret = QO;
yiGametie=! OF
if€ hwnd == GetFocus() )
setCaretPos€ xCaret * cxChr, yCanetwsucy Gir ey:
case WM_SETFOCUS:
CreateCaret hwnd,4, -cxChr cy chran:
// gray caret (Ccursor) Ad)
SetCaretPos( exCarete= @cxChroeyCaret *i cyChr, ):
ShowCaret( hwnd );
PowwMirm¢ (0) Wwe
case WM_KILLFOCUS:
HideCaret( hwnd );
DestroyCaret();
recurnc au, >
case WM_KEYDOWN:
switch(€ wParam )
{
case VK_HOME:
xCaret.= O07 break;
case VK_END:
Chapter 3: Keyboard, Caret, and Scrollbars 107
case WM_CHAR:
forG 1=0=F1<LOWORD CUP aram:;: Mae )
if
switch( wParam )
{
cases es Nb // backspace Wf
ii taGuexdCialls ety an Ome
{
KiCiaimeste,
SendMessage( hwnd, WM_KEYDOWN,
VK@DECEHEST 1S =
a bDimelaiksy
clase: A\itite: // tab if
do SendMessage( hwnd, WM_CHAR, ' ", 1L );
ghiveG xCaret-e, yo .h= \0,t)t;
108 BORLAND C++ 3.0 PROGRAMMING, Second Edition
break;
Case? waNihes = // carriage return //
xCaret = 0; // tatlsrsehrough ta '\nt fe.
case, {Nines // line feed bh
if( ++yCaret == cyBuff ) yCaret = 0;
break;
cases a\exciB // escape //
if€( MessageBox( hwnd, "Reset text buffer?",
"Editor Query",
MB_ICONEXCLAMATION | MB_OKCANCEL |
MB_DEFBUTTON2 ) == IDOK )
{
fcOlln Gay = Ore ay icy BUhi thse ya
fom x=0 x <cxBiuUihfi xXch-ee,
BusstienG (x yio)i =e 2 to,
xCaret = 0; yCaret = OQ;
InvalidateRect( hwnd, NULL, FALSE );
}
break;
default: // all other characters LA
Buffer( xCaret, yCaret ) = (char) wParam;
HideCaret( hwnd );
hdc = GetDCC hwnd );
SelectObject( hdc,
GetStockObject(SYSTEM_FIXED_FONT)
case WM_PAINT:
hdc = BeginPaint( hwnd, &ps );
SelectObject( hdc,
Fe GetStockObject(SYSTEM_FIXED_FONT)
for(€ y=0; y<cyBuff; yt+ )
TextoOutC thde, *07 “y*cychr,
Chapter 3: Keyboard, Caret, and Scrollbars 109
&Buffer( O, yi DeacxButiso7
EndPaint( hwnd4 &ps );
PREUMNG W WE
case WM_DESTROY:
PostQuitMessage( O );
Recunn.G. Os:
}
return( DefWindowProc( hwnd, message, wParam, lParam )
5
ney hPrevinstance
{
wc.style CS_HREDRAW | CS_VREDRAW;
wc.LpfnWndProc WndProc;
we.cbClsExtra 0;
we.cbWndExtra 0;
we.hInstance hInstance;
we.hIcon KFoadicon© NULL, IDIZAPPLICATION »);
we.hCursor LoadCursor( NULL, IDC_ARROW );
we.hbrBackground GetStockObject( WHITE_BRUSH );
we.lpszMenuName NULL;
we.lpszClassName szAppName;
RegisterClass( &w c );
}
hwnd CreateWindow( szAppName,
"A Simple Windows Editor",
WS _OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULE NULL hinstance, NULL ~ rs
ShowWindow ( hwnd, n CmdShow );
UpdateWindow( hwnd ) tA.
while( GetMessage( &msg, NUE O50) 2)
{
110 BORLAND C++ 3.0 PROGRAMMING, Second Edition
TranslateMessage( &msg );
DispatchMessage( &msg );
}
return(€ msg.wParam );
}
PTS SSS SSS SSS SS SSS SSH SSS SSS SSS HHS SHS SHH;
NAME EDITOR
DESCRIPTION "Windows Editor Program"
SME WIFE WINDOWS
STUB SeWHDINISh
RU Bienes Xxems
CODE PRELOAD MOVEABLE DISCARDABLE
DATA PRELOAD MOVEABLE MULTIPLE
HEAPSIZE 1024
SHPALGIKeSileZa 8192
EXPORTS WndProc
Chapter 4
1
112 BORLAND C++ 3.0 PROGRAMMING, Second Edition
provided support for south-paws, permitting the left and right buttons to be
swapped as a system default rather than an application option.
There are also newer devices, such as track-ball pointers; but these emulate
two- or three-button mice without requiring special treatment. And, of course,
there are still newer devices, such as control gloves, which sense spacial
motion and, eventually, may not only type on truly "virtual" (or possibly
holographic) keyboards, but may eventually—as Brer Rabbit phrased it —
read writin’. These are, however, still in the experimental stage and, for the
present, can be safely ignored.
Restrictions aside, most applications will find it sufficient to recognize
two-buttons (right and left) while reserving the third (middle) button for
optional short-cuts.
Mouse Messages
Twenty-four mouse event messages are defined in Windows.H. Two of these
messages have values that duplicate other mouse messages and are,
apparently, provided for compatibility with earlier versions of Windows. The
other twelve are normally handled by Windows through the WndDefProc
function. The first nine of the remaining mouse event messages concern mouse
button events as shown in Table 4-1.
Double-Click Messages
WM_xxxxDOWN and WM_xxxxUP messages are generated automatically but
WM_xxxxDBLCK messages (double-click) are generated only if the class
definition of the client window includes the double-click enabling flag:
pixel but per unit of physical mouse motion, i.e., one message per mickey,
subject to the processing speed of the window procedure handling the move-
ment message.
Note: A mickey is not a joke. It is the basic unit of mouse movement and,
depending on the physical mouse, movement may be reported in increments
of 200 to 320 mickeys per inch.
With rapid mouse movement, WM_MOUSEMOVE messages may be
reported at various screen spacings as will be demonstrated by the Mouse1.C
program. With slower mouse movement, WM_MOUSEMOVE message may
be received at contiguous pixel positions but this cannot be guarenteed,
regardless of system (CPU) and program speeds. Also, WM_MOUSEMOVE
messages are issued only while the mouse cursor remains within the
application’s client window. The same is true of the mouse button messages.
The x and y coordinate values are the coordinates of the mouse cursor’s hot
spot relative to the client window area as offsets (positive values only) from
the upper-left corner of the window (0,0). The wParam variable contains the
state of the mouse button(s) as well as the Shift and Ctrl keys. The state of
these keys can be tested using the MK_xxxx bit masks defined in Windows.H
(MK stands for "mouse key" even though two of these are keyboard status
bits). Key status can be tested as:
switch( message )
{
case WM_LBUTTONDOWN:
fPlaninit= eh eablinktemcomel
MessageBeep(0Q);
return(Q);
case WM_MOUSEMOVE:
iC Le an fit...)
{
hdc = GetDCC( hwnd );
SetPixel( hdc, LOWORD( LParam ),
HIWORD(C LParam ), OL );
ReleaseDC( hwnd, hdc );
m
return(0);
if€ ! hPrevInstance )
{
The cbWndExtra field is a provision for two bytes of additional data which,
while not used in this example, will be needed in Mouse3 to hold flag values:
wce.hIcon NULL;
wce.hCursor NULL;
118 BORLAND C++ 3.0 PROGRAMMING, Second Edition
For the purposes of this demonstration the Cursor field must be declared
as NULL. In other circumstances no specification of any kind is required if
the default cursor will be used by the client window. Or, a specific cursor,
either predefined or custom, may be selected for this window class using
the LoadCursor function:
we.lpszClassName = szChildClass;
RegisterClass( &we );
}
The child window class is registered in the same fashion as the parent
window class. Note, however, that this is only the class declaration and does
not create any instances of the class.
In this example, ten child window instances will be used in a 2x5 array.
Since the child window instances belong to the main application window, they
are created in response to the WM_CREATE message in WndProc:
case WM_CREATE:
fol GG OF eaX< DX ee)
for€ y=O0; y<2; yr )
hwndChild(€CxJLCy] =
CreateWindow( szChildClass, NULL,
WS_CHILDWINDOW | WS_VISIBLE,
OF OF Oe OF eehiwinide
il 47 <e85 /icohtldawindow id //
GetWindowWord( hwnd, GWW_HINSTANCE ),
NUE:
return(Q);
The Create Window function is called for each of the child window instances,
following the same pattern as in WinMain for the application’s client window,
with three principal differences. First, each child window receives the hwnd
variable indicating the parent window. For the parent window, of course, this
value was NULL.
Chapter 4: The Mouse in Windows 119
Second, each child window requires a unique ID; a WORD value defined
by xl y<< 8. This parameter was declared as NULL again in the declaration of
the application client window. This child window ID will be used later in
Child WndProc , the example which will set the cursor image for each instance.
Even if it is not used directly within the application, it is still necessary.
Third, the child window instance parameter is supplied by calling the
Get WindowWord function while, for the application client window, this para-
meter was supplied by Windows as the hInstance parameter. In each case, a
unique instance handle is required and must be supplied either directly or
indirectly by Windows.:
There are other differences in the absence of a window title (declared as
NULL) as well as the window style flags used, but they may vary depending
upon the type of child window desired. Also, within WndProc, the WM_SIZE
message response has provisions for resizing the child windows which,
incidentally, are not defined to receive individual WM_SIZE messages:
case WM_SIZE:
cxWin = LOWORD( LParam ) / 5;
cyWin = HIWORDC LParam ) / 2;
for€ x=0; x<5; x++ )
FOR Wee sss svcpce >)
MoveWindow( hwndChildCxiJdLly]d,
x*cxWin, y*cyWin,
CEX(Wall
nema), Wein en RO ae
return(0);
When the client window is resized, the child windows are individually
repositioned by the Move Window function and at the same time are given new
size parameters to fit the resized client window. This completes the handling
provided by the client window for the child window instances. Handling is
also provided for individual child window instances within the ChildWndProc
process.
do not receive these messages. Instead, the mouse movement message is used
because it is reported only to the client or child window at which the mouse
is located. An individual child window receives this message if, and only if,
the mouse is in that window. To set a different cursor shape for each of these
ten child windows, the child window ID is needed to identify it. This is
retrieved by using the GetWindow Word function:
case WM_MOUSEMOVE:
switch( GetWindowWord( hwnd, GWW_ID ) )
{
case 0x0000:
The formula x|y << 8 was used to generate the child window ID values.
Here, it’s certainly easier to use constants, particularly since variables are not
accepted in case statements. The child window ID values generated were
0x0000..0x0004 and 0x0100..0x0104. The switch..case structure simply assigns
one of the predefined cursors using the SetCursor and LoadCursor functions:
Siete
Gul Sills Gam ©: GiUp:S)
Oj Gam NU)ey er UD) Gu XGXeXOXGXQ =
resource. Second, SetCursor executes only if the new cursor shape, indicated
by hCursor, is different from the current cursor. Otherwise, SetCursor simply
returns without action.
There is, however, one circumstance where both LoadCursor and SetCursor
execute repeatedly. Before the child window class was registered, the
we.hCursor field was defined as NULL. If this definition is commented out, or
if a specific cursor is defined for the child window class, a very different execution
of this example program may be observed. The cursor images for the child
windows would then be quite jerky as the new images continually reload and
reset to replace the default image. This undesired effect can be easily observed
by commenting out the we.hCursor assignment in the child window definitions
in Mouse2.C, and then recompiling the program. Or, you could create separate
versions with and without this provision and compare them directly.
Complexities are also possible here because the mouse cursor visibility state
is not a simple boolean flag but is an integer variable. Repeated calls to
ShowCursor(FALSE) decrement the variable while multiple calls to Show-
Cursor(TRUE) increment it. Thus, multiple sequential calls to Show-
Cursor(FALSE) will require multiple sequential calls to ShowCursor(TRUE) to
make the cursor visible again. And, in like fashion, multiple calls to Show-
Cursor(TRUE) require multiple calls to ShowCursor(FALSE) to hide the cursor
again.
Normally this does not happen and shouldn’t be a problem. But, if for any
reason, multiple calls of either type could occur, there is another way to insure
that the mouse cursor is turned on or off cleanly. To do this use the value
returned by ShowCursor which is the cursor visiblity setting:
These two algorithms insure that the mouse cursor is immediately visible
or hidden by repeating the show or hide instruction until the desired condition
is reported. However, when either form is called, the ShowCursor instruction
is always invoked at least once and, therefore, could still increment or decre-
ment the counter so that conventional processes would require multiple
ShowCursor calls.
each valid grid element is drawn as a white square with a black outline. Grid
elements whose fState flag is set have two diagonal lines added. Other than
the form and demonstrating mouse hits, Mouse3 does not introduce any new
program elements requiring explantion. This BcpiC will be discussed again
later in further demonstrations.
Non-Client-Window Messages
All mouse operations thus far have occured within the application’s client
window or within child windows belonging to the client window. However,
outside of the client window, a different series of "non-client-window" mouse
messages are generated. Normally these messages are ignored by applications
and are passed back to Windows for handling via the WndDefProc function.
Eleven of these messages are non-client-window mouse messages, beginning
with the nine button messages shown in Table 4-2.
124 BORLAND C++ 3.0 PROGRAMMING, Second Edition
Coordinate Conversion
Screen and client-window coordinates can be converted from one form to the
other using the ScreenToClient and ClientToScreen functions:
ScreenToClient(€ hwnd, LpPoint );
ClientToScreen(€ hwnd, lLpPoint );
Chapter 4: The Mouse in Windows 125
will effectively disable all mouse messages both within the current
application’s client area and outside of the client window. The mouse buttons
will simply work anywhere on the screen as long as this interception is in
effect. Use this with extreme caution, if at all.
to the topmost parent window of the window being activated. The [Param
variable then contains the hit-test area code in the low-order word and the
mouse message number in the high-order word. The mouse cursor
coordinates are not included in the message parameters.
Normally, the child window passes this message to the DefWindowProc
function which passes the message to the window’s parent window before any
further processing occurs. If the parent window returns TRUE, processing is
halted.
Summary
Mouse operations under Windows are, in general, more convenient than in
conventional graphics environments. Three example programs have
demonstrated mouse operations within the client window area, including hit
testing, changing mouse cursors, and mouse tracking.
Other, non-client area mouse messages have also been discussed but not
demonstrated because they should normally be handled by default message
processing. These are messages intended for other applications and are not
intended for local handling.
Because the mouse is integral to Windows operations, mouse operations
will continue to appear in later chapters.
The source listings for the Mouse1, Mouse2 and Mouse3 demo programs
follow.
// /[SasSsSsseseeoSseessesessese
////
LS Mouse1.C //
// Windows Mouse Tracking //
if// SS SSS SS 3SS26S See orsenesssss// //
#include <windows.h>
HDC hidics
PAINTSTRUCT ps;
Statre, BOOLIS# Paint =1 0;
Switch( message )
it
case WM _MOUSEMOVE:
Ju alPeiinie »
{
hdc = GetDCC hwnd );
SetPixel( hdc, LOWORD(C LParam ),
HIWORDC LParam ), OL );
ReleaseDC( hwnd, hdc );
} .
return(0O);
case WM_LBUTTONDOWN:
WPAN = Wieden © als
MessageBeep(Q);
return(Q);
case WM_PAINT:
hdc = BeginPaint( hwnd, &ps );
EndPaint( hwnd, &ps );
ne uulnniGopE
case WM_DESTROY:
PostQuitMessage( O );
esc Ui Ga Oe a=
}
return( DefWindowProc( hwnd, message, wParam, lParam ) );
}
we.lpfnWndProc = WndProc;
WIC ICID CllUS/EMGtulnra = 0;
we.cbWndExtra = (0)
we.hInstance hInstance;
we.hIcon Loadlcon(CeNUELs 1DISAPPLELCATIONID >
we.hCursor LoadCursor G NULLA TDCeARROWS)>:
we.hbrBackground = GetStockObject( WHITE_BRUSH );
we.lpszMenuName NULL;
we.lpszClassName = szAppName;
RegisterClass( &wc );
}
hwnd = CreateWindow( szAppName, "Mouse1: Tracking Demo",
WS_OVERLAPPEDWINDOW,
CWRUSEDEIRPAUIRIe a GWa USE DE RAUIEiie:
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, MULL, InMiinevaingee, MWILIL Xd-
ShowWindow( hwnd, nCmdShow );
UpdateWindow( hwnd );
while€ GetMessage( &msg, NULL, O, O ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
return(€ msg.wParam );
}
NAME MOUSE1
DESCRIPTION "Mouse1: Demonstrates Mouse Tracking"
EXE Vee WINDOWS
STUB SAWPEN
Sain) Bier ext Eee
CODE PRELOAD MOVEABLE DISCARDABLE
DATA PRELOAD MOVEABLE MULTIPLE
HEAR oMaZae 1024
SuIPALG KS ayZ4E 8192
EXPORTS WndProc
i (PSSSeSeSesssoesseoceses/ //
17 Mouse2.C aa |
// Windows Mouse Test //
/ / SSSS=SSSSeesSsSsssossss
//
#Hinclude <windows.h>
Chapter 4: The Mouse in Windows 129
switch( msg )
i
case WM_MOUSEMOVE:
switch(€ GetWindowWord( hwnd, GWW_ID ) )
{
case 0x0000:
SICLG-CUipSiOln GuLoad Cun son GuNU Sean TlDiCaiGROSISmmm r=
break;
case 0x0001:
SieltCUlipsS Ol Guo ald) CUS olny Gu NiU)
Le een lnD Cais EAM em e
break;
case Ox0002:
Siete GUllsrS Ol er Oral CG Ulin S Oly GaN UI[eee aD)Cann CO) Nine) m=
break;
case 0x0003:
Sie cicuns ome Load Cursor GaNnUE Eel DiGaUPARROWN ls)» =
break; |
case OxO0004:
Srert Gui sions sora GgiG Ul SiOn GaN IU)LaLa DiCun WIAtIo li)
break;
case 0x0100:
SiertyG UiSiOl Gam told iC CU SiOln GamN IU) leyemt D Came OnLeZs Ee) D>
break;
case 0x0101:
Sentilles Ola Gan OraiG GU SiON CaN |Seyes DIGamGrleZ EeWie a nme
break;
case 0x0102:
Ser Curnsorc woadcursome NULLS WDC os ZENWSIE: 2).
break;
case 0x0103:
SetcursorG LoadcCursorG NULL, TDC 'STZENS ~)> );
break;
case 0x0104:
Sercursorn« voadcursorG NULL, IDC _STZENESW >. D3
break;
130 BORLAND C++ 3.0 PROGRAMMING, Second Edition
return(Q);
case WM_LBUTTONDOWN:
Setcursor@!) LoadCursorCONULL, “IDC_ARROW ) ),;
return(Q);
case WM_RBUTTONDOWN:
ShowCursor(Q); // whilteC ShowCursorc0O) >= 09907 77
return(Q);
case WM_RBUTTONUP:
ShowCursor(1); Li white C ShowCursor€1) <20) >> I if
return(Q);
case WM_PAINT:
hdc = BeginPaint( hwnd, &ps );
GetClientRect( hwnd, &rect );
Rieicitianigme G@undicraOr a Or amtce citer G| nity mee CL DO; COmmn an
EndPaint( hwnd, &ps );
return(0);
}
return DefWindowProc( hwnd, msg, wParam, lLParam );
}
switch( message )
Ht
case WM_CREATE:
for€ x=0; x<5; x++ )
for€ y=0> y<2> y+ )
hwndChild[CxJLCy] =
CreateWindow( szChildClass, NULL,
WS_CHILDWINDOW | WS_VISIBLE,
OF On mOG a Uy an wn
me tl se es > /f oniwtd window id) //
GetWindowWord( hwnd, GWW_HINSTANCE ),
NULL );
Chapter 4: The Mouse in Windows 131
Mewwuin niGUpE-
case WM_SIZE:
cxWin = LOWORD( LParam ) / 5;
cyWin = HIWORD( LParam ) / 2;
fort x=O075x<5> x+47)
for(€ y=0; y<2; yt+t )
MoveWindow ( hwndChildCxJCyl,
x*cxWin, y*cyWin,
Cr tih, CYAN, willis 5
return(0);
case WM_LBUTTONDOWN:
MessageBeep(0Q);
return(Q);
case WM_DESTROY:
PostQuitMessage( O );
Pocwrme (a)
3
return( DefWindowProc( hwnd, message, wParam, lParam ) );
}
if( ! hPreviInstance )
i
we.style C S_HREDRAW | CS_VREDRAW;
we.lpfnWndProc = WndProc;
we.cbClsExtra = 0;
we.cbWndExtra = O>
we.hInstance = hInstance;
we.hIcon Ss (Lodcducom@ NULL, Ube /NPIPIICCEV Os) 2)5
we.hCursor =m NOald GU mSiOin Ga NUL eye LD CanARIROW MD:
we.hbrBackground = GetStockObject( LTGRAY_BRUSH );
we.lpszMenuName = NULL;
we.lpszClassName = szAppName;
RegisterClass( &wc ) Uf
132 BORLAND C++ 3.0 PROGRAMMING, Second Edition
Be SS to
NAME MOUSE2
DESCRIPTION "Mouse2: Demonstrates Child Windows and Cursors"
E XiEsrae WINDOWS
STUB WINS TUBSE XE
CODE PRELOAD MOVEABLE DISCARDABLE
DATA PRELOAD MOVEABLE MULTIPLE
HEAPSIZE 1024
STACKS: ZE 8192
EXPORTS WndProc
/ /SSSeSeSoSsSsoesessseoessesaess// //
// Mouse3.C fal,
// Windows Mouse Hit Test //
/ (Passes SaSeeesrsSSssooeesses
////
Hinclude <windows.h>
Switch( message )
{
case WM_CREATE:
if€ ! GetSystemMetrics( SM_MOUSEPRESENT ) )
{
MessageBox( hwnd,
"Mouse not found! This application can"
"not operate without a working mouse!",
wMOUSIe Hl scmme%S Cure
MBE CONST O\Paaiee MhBm0)|Ke oe
SendMessage( hwnd, WM_DESTROY, O, OL );
}
TORPG SEW ses7ip sap D)
for(€ y=0; y<7; yr )
et Vial Gad Exel sy d=":
fStateCxJLy] = O; }
TOR, S09 SAS sebae D
TONGA Guay — Ob mnyee ay -h-t ane)
TMWe\l elle s¢ Je 37 a fia lirdiixctpall Es yee
fValid£— x ICLy+5] fValid£Cx+51]0Ly+5] oil oO
‘:
nevuUulmniGuoy-
case WM_SIZE:
cxWin = LOWORDC LParam );
cyWin = HIWORDC LParam );
cxGrid = cyGrid = min( exWin, cyWin )./. 8;
EXOT i =e cxWwin — oC (cxKGrid) 7—). D7, 2s
cyOtie== ( cyWin |= (hicyGrid=*. /oepavnd zy
return(0);
case WM_LBUTTONDOWN:
Yea MOWORDC. UParam. o: = cxOtt ) 7) cxGrida;
y = GC oHLWORDC AParam 0) — ‘cy0Ttt JeV/acyGri ds;
ites (ok wy Kee ta alo) Dy) 2
{
fstatelxIlys 4= 71;
merc treneentat = xPos € x. 2;
134 BORLAND C++ 3.0 PROGRAMMING, Second Edition
rect.top yPosi(eiy 9%
merciteenh
ag nit xPos(x+1);
rect.bottom yPos(y+1);
InvalidateRect( hwnd, Srect, FALSE);
} else MessageBeep(0);
return(Q);
case WM_PAINT:
hdc = BeginPaint( hwnd, &ps );
foniG x=0 x7 ext te
Foun Gay = OF Sy <i ny tt
var qe GlEs< AES A}
ft
Rectangle( hdc, xPos (x) +1, yPos(y)+1,
xPos(x+1), VIPOISIGYal Des
if€ fStateLxidbyd )
{
MoveTo( NiGics, xPos( x ), yIPOsiG yn)
LineTo(¢ hidicy x PloisiGx1) yPos(y+1)
MovetTo( hidies. XPOS GaexXane ee yPos(yt1)
LineTo(¢ nidice, (POS Cxe4P 1)D 5 yPos( y )
} }
EndPaint( hwnd, &ps LE
return(Q);
case WM_DESTROY:
PostQuitMessage( O ); Rel GUlnniGu Ol s-
}
return ( DefWindowProc( hwnd, message, wParam, lParam ) ur
}
if€ ! hPrevinstance )
{
wce.style = CS_HREDRAW | CS_VREDRAW;
we.lpfnWndProc = WndProc;
we.cbClsExtra = (8)
wce.cbWndExtra = 0;
Chapter 4: The Mouse in Windows 135
t tA
NAME MOUSE3
DESICRE Paler ON "Mouse3: A Mouse Hit Test Program"
EXiEdey Re WINDOWS
STUB PARENES Ws} addi
CODE PRELOAD MOVEABLE DISCARDABLE
DATA PRELOAD MOVEABLE MULTIPLE
eA SelyZae 1024
STAIGKS EZE 8192
EXPORTS WndProc
@eodsncc hoe
a eh etter A
_-
=
_
a ; ; 4
a f i] 7
“ Se
¢s5 626-2:
= ¢& e orerts > St CF COO 2ary, %
een Od we Cet stl naire U alchbomw 729. beeing, ae
Tah hie - 2a 5 =< A resp eeevasy -
: eset
cers
ae oye ASA /
t 4 ve
Chapter 5
Windows Buttons
These predefined "child windows" are commonly referred to as buttons or
control buttons, but also include edit and list boxes, combo boxes, and
scrollbars. Of course, this last category has already appeared in earlier pro-
gram examples, but scrollbars do have applications other than scrolling
windows. :
Control buttons will be covered in this chapter, leaving the other buttons
to be discussed in subsequent chapters. If you have used Windows at all,
you've already encountered a number of these control elements and should
already be aware of their convenience for the user. These are also convenient
for the programmer because, in Windows, you do not need to worry about the
137,
138 BORLAND C++ 3.0 PROGRAMMING, Second Edition
mouse logic or about making the control features change state or "flash" when
clicked. Instead, all that is necessary is to define the desired controls and then
wait fora WM_COMMAND message to be returned. This message provides
notification that a control has been selected.
In the previous Mouse2 example, a child window class was defined and
registered. Then, individual instances of the window class were created by
calling CreateWindow and were positioned by calling Move Window. For instan-
ces of the predefined window classes used as control elements, however, the
process is much simpler. The classes already exist as "button", "combobox",
"edit", "listbox", "scrollbar", or "static", and the CreateWindow function, using
the predefined class, simply sets the size, position, and function of the child
window control, including, of course, the appropriate labels if desired. Dialog
boxes also use control button elements but, as will be shown in Part 2, these
function at a different level and are isolated from direct interaction with the
program. For now, child window controls will be demonstrated using direct
interactions, beginning with button controls.
Button Types
Windows provides eleven predefined button types in three principal groups:
push buttons, checkboxes, and radio buttons. A fourth type, called a group
box, does not respond to mouse or keyboard events but is used to group other
buttons. Two other special "types" will be covered later. The standard
Windows button types are illustrated in Figure 5-1.
Pushbuttons
Pushbutton controls are rectangular boxes that display a centered text label
and a light outline that simulate a raised button (3-D shading). When a button
is clicked, the outline vanishes to simulate a (physically) depressed button.
Pushbutton controls are used principally to initiate an immediate action
without retaining or displaying any type of status (on/off) information.
Radio Buttons
Radio buttons are small circular buttons with text (labels) that appear to the
right (see also BS_LEFTTEXT). Customarily, two or more radio buttons are
grouped together, representing mutually exclusive choices and permitting
only one button in a group to be checked at any time. Clicking a radio button
a second time does not change the button status.
The set condition is shown as a solid center (see Figure 5-1) and a default
or initial choice is normally shown set when the group is first displayed.
140 BORLAND C++ 3.0 PROGRAMMING, Second Edition
Check Boxes
Check boxes are small square or rectangular buttons with text appearing to
the right of the button. Checked state is shown by crossed diagonals.
Specials
Button Operations
The button operations demonstrated by Button1.C, shown in Figure 5-1, are
fairly restricted because these child window controls respond only to the
mouse. No provisions are made to permit movement from one control to
another using the Tab or cursor keys nor do any of these, including the default
pushbutton, respond to pressing the Enter key. Except for the
BS_AUTOCHECKBOX, BS_AUTO3STATE, and BS_AUTORADIOBUTTON
styles, none of these controls display any change of state aside from the
immediate selection "flash" when the mouse button is held down. And, less
obvious in the example program but still relevant, these child window con-
trols can obtain the input focus but do not, once acquired, release the input
focus to the parent window.
These buttons do, however, senda WM_COMMAND message to the parent
window when clicked with the mouse. And, in this demo, the WndProc
function displays the wParam and |Param values from the command message
at the bottom of the screen.
The Button1 example has been written with a gray background to make the
full button (child window) visible. In the pushbutton examples, it’s obvious
that the entire button area is an active hit area. The button or checkbox image
is not the sole target region for the checkbox and radiobutton controls. A
mouse click anywhere in the white child window region is accepted.
hwndGroup3 =
CreateWindow
( TDULtone,. Raaio Button Group’,
BStyle | BS_GROUPBOX,
Bilndents.— 2 *wex.Ghiriacy.chir,
SS Cx CN ra Dwi hyelek AOyGhr,. hWnd, 9.
CCLPCREATESTRUCT)LParam) -> hInstance,
CreateWindow
¢ LDU. VON, EtRadLO mbit £0:
BStyle | BS_RADIOBUTTON,
Bindents> BVStepe * 27 "BWidth, BHeighit,,
hwnd, 10, (CCLPCREATESTRUCT)LParam) -> hInstance,
CreateWindow
¢ PDUtLON: > AUTOR aG 10 ebuttOn”® ,
BStyle | BS_AUTORADIOBUTTON,
BindentS, BYVStep2e * 3, BWidth, ‘BHeight,
NWN ene, ee GIS PGRIE AviNe SiueUlG ial Pralbalml> a> sahplinisstiainicess
NULL
) ) Dis
This particular format is valid and is used in the Button1.C example. This
format has one drawback—the created control buttons do not belong to the
groupbox in which they’re displayed because the groupbox and the individual
buttons all have the same parent window specification (hwnd)—the applica-
tion client window. For this same reason, the coordinates for each of the
buttons are specified as offsets from the application client window origin, not
from the groupbox origin.
For the purposes of this demonstration, this is also an advantage because
the event messages generated by each button are reported directly back to the
application. But it is also a disadvantage because an array of radio buttons
declared in this form are not grouped by the groupbox outline enclosing them.
Instead, all such radiobuttons belong to the application client window, even
if they appear in different groupboxes. This means that clicking any one of the
radiobuttons would reset all others regardless of your intention in grouping
them. Button2.C illustrates another more practical means of grouping buttons.
It uses the groupbox element as the parent window:
hwndAPBLOJ =
CreateWindow
144 BORLAND C++ 3.0 PROGRAMMING, Second Edition
¢ UbiUtetO
Ni ny-eeeeh.
aC kOe GU) GiteOlnmeG fyOU Daa,
BStyle | BS_GROUPBOX,
4£3_* cxchr, cycnr, 4 © cxChr + BWidth,, Nom cy Cnr,
hwnd. 9> ninst, NULL;
This format creates four autoradio buttons that belong to the groupbox and
are, therefore, isolated from any other radio buttons.
There is, however, still a flaw in this particular format. These child window
buttons report not to the application client window but to the groupbox. The
groupbox itself does not report these events to its parent or handle them in
any fashion. Instead, in an actual application, the groupbox should be created
as a child window with a child window message function similar to the
ChildWndProc functions demonstrated in other examples. But, for the moment,
this format will be used in Button2.C to demonstrate other aspects of child
window buttons.
precisely what event has occurred. The values of these submessage codes are
shown in Table 5-1.
The WM_USER identifier is provided so that programs can define their own
messages. WM_USER has a value of 0x0400 which permits each window class
to have its own set of messages unique to that class. Also, predefined child
window controls can have their own messages defined in terms of WM_USER.
146 BORLAND C++ 3.0 PROGRAMMING, Second Edition
Note: All WM_msg values below 0x0400 are reserved. See Appendix A.
The BM_SETCHECK and BM_GETCHECK messages are sent by the parent
window to a child window control button to set or retrieve the check state of
checkboxes and radio buttons. In Button2.C, as shown in Figure 5-2, the
BM_SETSTATE message has been sent to three of the checkboxes and to three
of the radiobuttons in the group RadioButtons 1. Using the BM_SETCHECK
message to set more than one box is valid and appropriate for the checkboxes,
and would be used the initial state for a series of them.
C] Check #1 @ Radio #1
Check #2 @ Radio #2
Or, to clear the checkstate of a button, another message could be sent as:
The first parameter is the handle of the child window (control button), the
second is the message identifier, and the third parameter is a boolean value
explicitly setting or clearing a flag state. The fourth parameter is not used and
is passed as 0 (zero).
The single radio button in the second checked group was turned on by
clicking the mouse, not by aBM_SETCHECK message. And, in the first group,
clicking on any of these buttons will restore the normal state of one button on
and all others off.
The BM_SETSTATE message is slightly different than the BM.SETCHECK
message. The BM.SETCHECK message simulates the pushbutton "flash" that
occurs when a button is clicked by the mouse or, after receiving the focus, by
pressing the spacebar. In the checkboxes shown in Figure 5-2, the 2nd through
4th checkboxes have been sent BM_SETSTATE messages in the same fashion
as the BM_SETCHECK messages, as shown by the heavy outlines. The third
"set" message is slightly different because this message permits changing the
style of button on-the-fly, so to speak. Two examples of this message type have
been incorporated in Button2.C. It first changes the third pushbutton (left
group) from BS_PUSHBUTTON to BS_DEFPUSHBUTTON:
The BM_SETSTYLE message, unlike the other button messages, uses the
fourth parameter and passes a non-zero argument to request that the control
button be immediately redrawn. A zero argument does not redraw the control.
In this example, these messages were all sent during the WM_CREATE
response and, therefore, did not require a redraw instruction. In like fashion,
the third control element in the right-hand group (shown in Figure 5-2) was
originally created as an autoradio button, then revised with a BM_SETSTYLE
message:
Once this has been done, the control button behaves exactly like any other
checkbox control while remaining a member of the RadioButton 2 group.
148 BORLAND C++ 3.0 PROGRAMMING, Second Edition
Notice the negation operator (!) that precedes the second SendMessage
instruction to return the current state after inversion. More often, however,
the AUTOxxxx styles would be used to relieve the application of the need for
updating the button status (as in Button2.C). In these cases, only the get
message would be needed to inquire about the current status:
fStatus = SendMessage( hwnd..., BM_GETCHECK, O, OL );
Hwnd is a handle to the window of whatever type while the string specifica-
tion is a long or far pointer to an ASCIIZ string.
The current text label can also be retrieved from any window type using the
GetWindowText function:
The buffer must be large enough to hold the string returned while the
nMaxLen specification places a limit on the length copied. At the same time,
the Get WindowTextLength function can be used to determine the text length:
Examples in the first section of this book have been a mixture of conventional
C programming code with calls to Windows-specific functions to demonstrate
Windows programming. Chapter 5 introduced child window control buttons,
including pushbuttons, checkboxes, and radio buttons. These were created
using conventional C programming even though Windows-specific functions
accomplished many of the programming tasks. At the same time, Windows
handled more than a little of the actual processing, not by provisions within
the example programs.
In addition to control buttons and group boxes, other child window controls
such as edit boxes, list boxes, scrollbars, and icons as well as custom controls
were briefly mentioned. None of these have yet been illustrated by examples,
except for scrollbars used in a text window.
Collectively, all of these control elements, as well as bitmaps, keyboard
accelerators, menus, and strings, are known as "program resources"—a term
denoting that they are used by programs but, in Windows particularly, are
not necessarily provided directly within the source code. In some cases, as
with menus and keyboard accelerators, these resources cannot be created
within the application source code (whether C or Pascal). Instead, they are
created through resource scripts and become part of the finished application
when they are compiled by the resource compiler (RC) shown in Figure 1-3,
on page 10.
149
150 BORLAND C++ 3.0 PROGRAMMING, Second Edition
Resource Scripts
Having mentioned resource scripts—which Windows and OS/2 pro-
grammers will already be familiar with—a brief explanation is in order for
those programmers who are new to Windows.
A resource script is an ASCII text script used by the resource compiler in
preparing menus and other program resources that interface to Windows. The
source code, however, is not written in C, Pascal, or assembly language but in
a relatively simple high-level language as shown below:
SolitronMenu MENU
BEGIN
POPUP "&MENU"
BEGIN
MENUITEM "&Reset...\t*N", Sol_Menu_Reset
MENUITEM SEPARATOR
MENUMSE Mens Qui stress Nutley, Sol_Menu_Quit
END
Using Borland C++, however, you are not required to learn and write
resource scripts.
Resource Components
Resource files do not outwardly reveal their contents and a single .RES file
may contain many different resource elements as well as many different
resource types. In like fashion, while the Resource Workshop appears as a
single utility program, in actuality, eight principal different-but-related utility
functions are provided. Each of these functions will be discussed in detail later
in this section. These eight utility functions include:
Central Control Program—provides selection of resource file types,
directory access for loading and/or saving specific resource files, and
access to the appropriate workshop editors for each resource type. It
also permits saving individual resources as separate files or importing
saved resources from other sources.
Dialog Editor—creates new dialog boxes and/or edits existing dialog
boxes. Tools are also included for creating combo boxes and owner-
draw controls. Combo boxes are a combination of edit controls and a
list box. Owner-draw controls are customized control elements that
normally require maintenance by the application. Provisions are also
included for testing dialog box operations.
Bitmap, Cursor and Icon Editors—support and edit these several
Windows bitmap formats as well as maintain color tables for these
formats.
Menu Editor—supports hierarchic pop-up menus introduced in
Windows 3.0. Hierarchic menus permit creating multiple pop-up
menu levels for each menu item.
Accelerator Editor—creates and edits accelerator resources, which pro-
vide hot keys for commands.
String Editor—creates and edits string resources that are text strings
used by applications for messages or window captions. Because these
message strings appear in a resource format rather than embedded in
the execute program, revisions and/or foreign language translations
become practical without requiring recompilation of the source code.
The Resource Workshop utilities are not the end-all and be-all of applica-
tion programming, but they do relieve you of much of the work in writing the
Windows-compatible interface portion of any application. This is no small
portion of the overall task.
Chapter 6
153
154 BORLAND C++ 3.0 PROGRAMMING, Second Edition
of these elements within the source code would not only make the .EXE
program too large for execution, it would also be unwieldy.
editor’s facilities are changed somewhat according to the needs of the image
type being edited. Individual bitmapped resources can also be stored as
separate files prior to compilation, each containing a single bitmap image with
the type identified by the appropriate extensions.
Dialog Boxes
Dialog boxes are input windows that provide the ability to select program
options through graphic control elements, including push buttons,
checkboxes, radio buttons, list boxes, scrollbars, and entry fields. The dialog
box editor permits interactive construction of dialog screens, showing them
exactly as they will appear within the application. Tools are included for
drawing, positioning, aligning, sizing, and moving dialog box controls.
Menus
Menus provide lists of available program options that can be executed either
as immediate actions or by calling submenus with additional options. The
menu editor defines menu resources and allows construction and testing of
pull-down menus. Hot-key shortcuts for menu items are defined by the
Accelerator menu.
Strings
Strings are text strings that are displayed by an application in its menus,
dialog boxes, error messages, and so on. By defining strings as resources,
applications can easily be revised or customized for international use without
requiring source code revisions (or distribution). Also, defining strings as
resources makes it easier to improve and edit application messages. For
example, an ambiguous message can be revised without altering the
application’s source code, or a series of text messages in English can be easily
rewritten for the Norwegian or German markets.
fonts used by the application to display text, raw data resources (.RC data
files), and user-defined resources (included in the executable file).
Since the Resource Workshop can save resources directly as .RES or .EXE
files, Microsoft’s Resource Compiler is not required to compile resources or
to include compiled resources in the .EXE file. Thus, .DLG and .RC file types
are not required, nor is the Microsoft Resource Compiler.
One final file type can be copied, renamed, or deleted but cannot be created,
browsed, or edited using the Resource Workshop, as shown in Table 6-2
below.
Linking Resources
Normally, the Resource Workshop compiles resources directly to an .RES (bin-
ary) file and then links the .RES file into the .EXE file. But, when editing an
existing .EXE or .DLL file, the Resource Workshop does not create a .RES file, but
writes the resources directly to the runtime program. However, there are still
reasons for having the intermediate .RES file rather than compiling all resources
directly to the .EXE or .DLL files. The main reason is that a .RES file needs to be
compiled only once and isn’t subject to subsequent changes in the program code.
After this, when C compiles an application’s source code, the first step
involves updating any .OBJ files whose sources have changed. And, with this
step finished, the linker converts the .OBJ code to .EXE code, links the run-time
and Windows libraries, and, finally, links the .RES code to produce the
finished executable program. After an application has been compiled to run-
time, the application interface, that is, the resource elements, can be modified
directly through the Resource Workshop without recompiling the application.
Header Files
Windows resources, like the constants defined in Windows.H, are identified
by numbers. A series of dialog box resources, for example, might use the
identifiers 1000, 1001, 1002, 1003, and so on. Windows applications use these
same numbers to identify the individual control elements.
158 BORLAND C++ 3.0 PROGRAMMING, Second Edition
2.
a.
a.
4
2.
2.
2
ge.
e
eB
a
2.
4
2
2.
Z.
=.
2.
a.
Opening A Project
Editing an existing resource—whether for a new project in progress or a
revision of an existing resource—is quite simple. The Open Project option calls
a dialog box (see Figure 6-2), providing not only drive, path, and filename
selection but also a pull-down list of resource types (detailed in Table 6-1).
160 BORLAND C++ 3.0 PROGRAMMING, Second Edition
=
Z2
a.
a
a «(RC resource script
a |DLG resource script
s CUR cursor image
HA
iC
File Edit
2
Z‘2
2
2
Z Resource type 7
Sh
2 BITHAP
2Se
\ DIALOG
ACCELERATORS
=
g ie ENTRY_DIALOG
“2
2
2
BITMAP eee
2
22 CURSOR MAIN MENU
DIBLOG VENDOR._LIST
2
2
FONT =
2
2
ICON
2
2 ee co CARAT
2 Place resource in CLARITY’
g
g
Zz
a COMMENT
Z
CULET
The default element type names can be changed at any time using the
Resource / Rename menu option.
Two further options appear in the New resource dialog: the New Type button
and the destination edit list.
The New Type button is quite simple and presents a list of resource types,
permitting type assignment for the new resource.
The destination edit list (Place resource in:) displays the current (active or
default) resource project. Clicking on the arrow button at the right of the edit
box calls an edit list of current and previous resource projects or, optionally,
permits entry of a new resource project name.
Note: Any selection becomes the new active (default) resource project, replac-
ing the current resource project.
Resource memory options can only be set individually, not globally. Also,
the default settings (ON) cannot be altered. Changes to these settings should
be made only as explicitly required.
Chapter 6: Application Resources and Resource Files 165
Edit... ae 4 o i .. - : :: “f : 2 a BITMAP
Edit as text... oes GINI_LOGO
View... e
¥ Loadoncall v Discardable
Moveable v Pure
Resource Identifiers
Unfortunately, Borland’s Resource Workshop does contain one flaw: the lack
of any facilities for creating header files corresponding to resource identifiers
(and, correspondingly, failure to display identifier mneumonics while editing
resources).
The importance of resource identifier mneumonics is quite simple. While
application source code can reference resources using the same integer iden-
tifiers which are assigned in the Resource Workshop—and this is essentially
what is done after the application is compiled. These integer identifiers are
not convenient for the programmer. And, as I have stated in numerous pro-
gramming books, computer programs—including the Resource Workshop—
should be designed for the user’s convenience, not for the computer's!
166 BORLAND C++ 3.0 PROGRAMMING, Second Edition
But, because of this flaw, you, the programmer, are faced with two options:
first, using a Windows editor to create the necessary .H header file containing
mneumonic identifiers corresponding to the resource identifiers; or, second,
ignoring the Resource Workshop in favor of another resource compiler.
A variety of alternative resource compilers are available, including the
Whitewater Resource Toolkit (previously distributed with BC++ version 2.0
and now available separately from the Whitewater Group), Microsoft’s
Software Developer’s Kit (SDK) and WindowsMAKER from Blue Sky.
Cut Shift+Del
Copy Ctri+ins
Delete Del
Duplicate
Beginning at the top of the menu, the Undo option (Alt+Backspace) reverses
whatever your last action was. That is, if you added or deleted a resource
element, this action is undone; if you drew a line in a bitmap, the line is
removed; etc. The Redo option, in like fashion, reverses your last undo action.
The Cut (Shift-Del) and Copy (Ctrl-Ins) options copy selected resource
elements to a clipboard. In the first instance, removing the element(s) from the
resource and, in the second, simply duplicating the elements without affecting
the existing resource definition.
Chapter 6: Application Resources and Resource Files 167
The Paste (Shift+Ins) option remains inactive (grayed-out) until either the
Cut or Copy options have been used to copy to the clipboard.
The Delete (Del) option removes selected items without copying to the
clipboard.
The Duplicate option is used to create multiple copies of a resource element
within the resource instance. That is, use the Duplicate option. For example,
to create a row or column of buttons by duplicating an already defined button.
The Select all option selects all resource elements belong to the current
resource instance—this can be useful for duplicating, copying or deleting all
elements within a resource instance.
Selecting the By file display option displays both type and resource name
on single lines, identifying all resources belonging to a single resource file.
The Show items option simply expands the display of certain resource items
such as menus and dialogs—handy, for example, for identifying the contents
of a resource without actively editing the resource.
The Show unused types option can only be used when the display is
selected as By type and Show items is not selected. This option simply lists all
resource types regardless of whether the present resource contains any
defined instances of that type.
168 BORLAND C++ 3.0 PROGRAMMING, Second Edition
Summary
In most respects, the Resource Workshop needs little explanation and operates
in a clear and straight-forth manner, supplying a single utility capable of
creating, editing and compiling the full variety of Windows application
resource elements. The one exception cited, of course, is the absence of a
header editor function though this can be circumvented using an Windows
editor program.
As alternatives to the Resource Workshop, a variety of other resource editor
programs are available but, in future chapters, one or another of these editors
will be needed to create application resources for the example programs
presented.
Chapter 7
Under the Resource Workshop, the bitmaps, cursors, icons, and fonts are all
edited using essentially the same drawing program even though the oper-
ations and options for each type of graphic resource do differ slightly.
For bitmap and icon images, for example, colors are limited only by the
resolution of the video device. Thus, bitmap images may use palettes contain-
ing 2, 16, or 256 colors with no limitations (aside from memory) on the size of
the image. For icons, however, palettes can be 2, 8, 16, or 256 colors, but sizes
are limited to 32x16 or 32x32 pixels. Last, for cursors, both size and palette are
fixed with dimensions of 32x32 pixels and a palette of four ‘’colors” (black,
white, transparent, and inverted). On the other hand, fonts are limited to two
colors only (black and white) but can vary in size or may have no fixed size
(variable width).
Despite differences, however, the paint (editor) utility for all four image
types follows the general styles and patterns of other popular paint utilities.
Therefore, most of the operations provided by the editor will be familiar and
will require relatively little explanation.
169
170 BORLAND C++ 3.0 PROGRAMMING, Second Edition
a Eee =
ze
rher
Fieady Pen
Color Palettes
By default, if Windows 3.0 is installed on any system using an EGA or VGA video
card, the 16-color palette (Figure 7-1, upper left) will be valid and available for
bitmap and icon images. As shown in the illustration, the foreground color is
identified by the initials FG on the color bar while the current background color
is identified with the initials BG. A new foreground color can be selected by
clicking on the desired color bar using the mouse’s left button, while the
background color selection is made in similar fashion using the right button.
Chapter 7: Bitmaps, Cursors, Icons,and Fonts 171
Custom Colors
While both the 16-and 256-color palette options offer a default palette selec-
tion, provisions are also included to redefine any individual palette entry, as
shown in Figure 7-2 on the following page.
Individual palette colors are defined by three RGB values in the range 0
through 255. Each value establishes the relative intensity of the red, blue, or green
color-gun within the video monitor. For 16-color palettes, despite the fact that
the color panel shown accepts the full 0 through 255 range, any requested color
will be mapped to the nearest corresponding standard palette color, that is, it will
be mapped to one of the existing 16 colors. Thus, the end result, for 16-color
palettes, is that the color customization option is fairly ineffective. After all, no
real purpose is served by having two or more palette colors with identical hues.
172 BORLAND C++ 3.0 PROGRAMMING, Second Edition
|
tii
Edit-toreground color...
Edit background color...
WIN.INI file (in the Windows directory) contains a number of settings con-
trolling Windows startup configuration and, after installing an SVGA card
and driver, will contain one or two lines describing the video setup.
One simple way to switch between VGA and SVGA Windows display
modes is to create two files titled SYS_16.INI and SYS_256.INI. Then, depend-
ing on the mode desired, the SYSTEM.INI file is deleted, and the desired mode
file is copied as a replacement.
The exact specification requirements for these two initialization files will
differ depending on the video card and driver required. Below, are two
examples used with a Trident graphics processor SVGA card.
The conventional VGA SYSTEM.INI file (SYS_16.INI) contains the follow-
ing lines:
[boot.description]
display.drv=VGA
[boot.description]
display.drv=TRIDENT TVGA 640x480 256-color
Because my video card also supports 800x600 and 1024x768 video modes,
both with 256-color palettes, the following display configurations could also
have been specified:
Please remember, however, that your video card and drivers may require
different specifications. Therefore, check the documentation supplied with
your video card for the appropriate forms.
174. BORLAND C++ 3.0 PROGRAMMING, Second Edition
using the mouse. First hold the Shift key down, then click within the
selected area and drag the region to the location desired.
Last, to simply move the selected area, click with the mouse anywhere
within the selection region and drag to a new location before releasing the
mouse button.
ae The Scissors tool operates in the same fashion as the Arrow except
- that an irregular area rather than a rectangle can be selected. As
before, select a starting point, click the left mouse button and hold
down while outlining the desired region.
The Zoom tool can be used either to zoom an entire image up to 1600
percent in increments of 400 percent or to zoom a selected region of
an image.
To zoom in on the entire image, double-click on the Zoom tool
icon. To zoom out, execute the same operation but hold the Shift key down.
Of course, the View/Zoom in and Zoom out menu options can be used for the
same purpose (see Figure 7-3 below).
Alternately, after clicking on the Zoom tool, you can select a rectangular
area in the same way you would using the Arrow tool. When the mouse button
is released, the selected area will be zoomed in using the highest magnification
(up to 1600 percent in 400 percent steps), which does not cause the selected
region to exceed the size of the viewing area.
Z LEZ 7 \y
2
Split horizontal
Split vertical
176 BORLAND C++ 3.0 PROGRAMMING, Second Edition
The Erase tool is, in effect, a square paintbrush. While the left mouse
button is depressed, the Eraser draws using the background color
selection. With the right button, the foreground color is used.
Alternately, if you double-click on the icon (left mouse button
only) you will erase the entire image, using the currently selected background
color.
~ The Pencil tool is used to draw anywhere within the image, using
the foreground color while the left mouse button is pressed or, if the
right mouse button is down, the background color. The width of the
line drawn can be varied by clicking on the line sample at the bottom
of the toolbar.
Note: The line style selection does not affect the Pencil tool, which always
uses a Solid line (see Figure 7-4).
While the Pencil can be used for freeform drawing, the Line tool
aS provides a means of drawing straight lines. A line is initiated by
pressing either mouse button, anchoring one end of a line. The line
terminus is created when the mouse button is released. Both line
width and line style can be selected by clicking on the line sample at the bottom
of the toolbar (see Figure 7-4).
Note: The dashed line styles are valid only for thin lines and cannot be
applied to the thicker line styles.
. The Airbrush tool, like the Paintbrush, is used for freeform drawing.
Th Like the Paintbrush, the Airbrush can vary its coverage both in size
and shape. (Select Options/ Airbrush or click on the airbrush pattern
sample at the bottom of the toolbar (see Figure 7-4). Also, like the
Paintbrush, the Airbrush does use the selected background color but, unlike
Chapter 7: Bitmaps, Cursors, Icons, and Fonts 27
the Paintbrush, does not create a solid background. Instead, the Airbrush uses
the selected background color in the same pattern as the foreground spray.
The background color, however, may be completely or partially overwritten
by the foreground spray.
Figure 7-4: Pattern, Brush and Line style Options
Pattern...
Brush shape...
Airbrush shape...
Pen Style...
al The Bucket tool is used to fill large contiguous areas using either the
foreground or background colors. Areafill is constrained by any
continuous outline in any color different from the fill point or, of
course, by the borders of the image. Line, airbrush, and pattern style
selection do not affect fill operations.
The Text tool permits entering text directly into a bitmap, icon or cursor
| image. Text uses only the foreground drawing color. Clicking the right
mouse button turns off text entry. Note particularly that CR/LF
characters are not displayed and do not affect text positioning. The
options shown above in Figure 7-5 provide typeface, font, and style selections
while providing a sample display of the current selection. One useful hint: if
you find your presently selected typeface or size too large or too small, instead
of erasing the text entry, immediately select the Text/Font option and change
fonts and/or size. The current text display will be changed accordingly.
178 BORLAND C++ 3.0 PROGRAMMING, Second Edition
| Modem
Roman
Script
Symbol
System
ABCDEFGHWKLMNOPQRSTUVWXYZ
abcdefghiklmnoparstuywxy2
The quad-square at the bottom of the tool bar shows four current
ee style selections, beginning at the upper left with the Brush shape
ees] and continuing at the upper right with the Airbrush shape. The
current line style is shown at the lower left, and the current pattern
at the lower right. Each of these options can be selected for mod-
ification by clicking on the desired area (see preceding Figure 7-4 for shapes
and patterns) or by selecting Options from the menu bar.
Cursor Resources
Cursor resources (that is, mouse pointers) are a specialized form of bitmap
that provide three elements: a pointer image that nominally indicates the
general function and tracks the mouse position; a screen image that governs
the interaction between the pointer image and the screen; and a hotspot that
references the absolute screen position. In general, the cursor image is the
primary concern, with different types used to indicate selected functions such
as I-beams or vertical bars for text editing, a pencil for drawing, or a hand with
the index finger extended to push buttons.
When designing cursor images, only one palette is available regardless of
the color resolution supported by the display device. For cursor images, the
supported palette contains four “‘colors’’: black, white, transparent, and
inverted (see Figure 7-6).
The black and white colors used for cursors differ from conventional
bitmaps and icons only in that the background image is preserved and
restored as the mouse cursor moves. In every other respect, these are simply
two colors written directly to the display. The transparent ‘‘color’’ is equally
simple: the existing background is blank with no writing—a non-color, in effect.
But, the fourth ‘‘color’’—inverted—is a special case. All inverted pixels in
a mouse cursor cause the underlying pixel(s) to be inverted in color. In other
word, a black pixel becomes white, a red pixel becomes blue, and so on.
The cursor example in Figure 7-6 shows a hand that has been outlined in
black and filled with white. The area surrounding the hand is transparent.
Generally, this cursor outline will be visible against most backgrounds.
But, to enhance visibility, a shadow outline has been added inside the black
outline, using the “inverted” color selection. In this particular example, the
cursor needs little or nothing to ensure visibility, and using the inverted
“color’’ to ensure maximum contrast is simply gilding the lily.
180 BORLAND C++ 3.0 PROGRAMMING, Second Edition
Transparent
Horizontal [x)-
Vertical [p}:
Icon Resources
Icon resources are used to represent applications, system resources, or sub-
systems or controls within an application. Under Windows, icons are most
commonly displayed on screen or in Program Manager windows to represent
minimized or potential programs. In general, these are static images, although
182 BORLAND C++ 3.0 PROGRAMMING, Second Edition
some applications create their own dynamic icons such as the Clock program
distributed with Windows. Custom icons aside, however, conventional icons
are either 32x16 (CGA) or 32x32 bit images using 2, 8, 16, or 256-color palettes.
But, technical limitations aside, how you design an icon for your application
is, a matter of personal esthetics. This is not a course in commercial art design.
Remember, however, that your application is not going to stand or fall on the
basis of your icon unless, perhaps, you have a very unusual application.
Still, a brief caution is in order. While there is a great temptation to create
elaborate icons, too much detail is quickly lost when the icon is actually
displayed. In general, a simpler, less detailed icon will be easier to recognize.
Don’t forget that not all systems can support elaborate multicolor icons.
This doesn’t necessarily mean that you must design a separate icon for each
video because, on a monochrome system for example, Windows will dither all
colors except black and white. As a last ditch solution, a simple design drawn
largely or entirely as inverse and screen pixels will always be visible, as shown
in the two examples in Figure 7-7.
See8ae
z Bae2ee
eee
ee
appears on the left. Both icons were designed to stand out against any
background. They begin with a background drawn using the inverse brush,
while the letters use the screen brush with an outline in light blue. The
details of the two icons can be varied according to your own tastes, but both
icons will be needed by the example program. These icons should be
created in FileView.RES with the titles FILEVIEW and FILETYPE.
Custom Fonts
A fourth type of bitmapped resource is the custom font. Custom fonts are not,
however, frequently required resources and will not be covered in extensive
detail. Still, fonts are created with the same edit tools described for bitmaps,
icons, and cursors, but with a few differences.
The principal difference is that only two colors, black and white, are used
in font creation. Also, as a minor note, the pouring tool works only with black,
not with white—even if white is selected as the foreground color. Figure 7-8
shows the font editor being used to create a font of Celtic runes. The first nine
runes appear ina column along the right side of the dialog box and have been
superimposed on the image as a runic text line at the bottom of the dialog box.
7, Hide palette
Hide toolbox
4 Facename: Runeg
Header...
: : Font size... ‘
Mv
ADI
VNNTP?
Show or hide palette window Ue ans oiled
184 BORLAND C++ 3.0 PROGRAMMING, Second Edition
Size Character
|First
_ Height Last
Average width | Default
Maxuginm wiciih Break
While stock fonts normally have a character range from 0..255, custom fonts
may have any desired character range assigned. In the example, the range
begins with the character value 64—which is also the default character, the
equivalent of a space—and ends with the character 128. Assigning the break
character is optional.
Note: The values shown are ANSI decimal values—inconveniently, hex
values are not accepted.
Stock fonts also require size information and always have a fixed height (in
pixels, since these are bitmapped, rather than stroked fonts). The font width,
may be either fixed or variable. If variable, however, a maximum width is
assigned for the entire font and, from the Font menu, character widths are
assigned to individual characters.
Optionally, existing characters can be stretched (resized) to fit any width or
height changes.
Chapter 7: Bitmaps, Cursors, Icons,and Fonts 185
Ss
Z
=
Of the remaining fields, the version number refers to the Window version
and, by default, refers to version 2.0, which is compatible with all versions.
The Attributes fields describe font characteristics that will be used to
identify the types of characters included in the font (italics, underlined
186 BORLAND C++ 3.0 PROGRAMMING, Second Edition
Summary
Bitmapped images are integral to Windows even if the only bitmaps used are
a cursor and an icon. The graphic editors included in the Resource Workshop
make creating these resources a relatively simple task. The Resource Work-
shop can also be used to revise existing bitmaps, cursors, and icons in other
applications or to copy existing resources from one application to another.
Windows provides a number of predefined icon images such as MB_ICON-
HAND, MB_ICONQUESTION, and MB_ICONASTERISK, which are invoked
in quite a different manner, as shown in Chapter 14, Message Boxes.
Chapter 8
Dialog boxes provide a wealth of resources for applications. They allow the
presentation of everything from elaborate error message popups to list boxes,
selection buttons, and help windows—virtually anything you can to imagine.
Dialog boxes introduce a flexibility in program design that previously was
only manageable with elaborate coding and special provisions for saving
overlaid screen information. Of course, these same elements have been used
in conventional (DOS) programming, but not with the convenience afforded
under Windows.
The Dialog Box editor provides a matching convenience by offering a
visually interactive platform for designing and editing dialog box resources.
Using the Dialog Box editor is every bit as simple and very nearly as instinctive
as using a paint program. Tools are provided for creating and/or editing all
types of controls—from buttons and checkboxes to listboxes and edit fields.
The Dialog Box editor also provides several special capabilities in that
dialog box resources can be edited from and saved to resource files (.RES),
executable files (EXE), and dynamic link libraries (. DLL). Dialog box designs
can also be saved as dialog box resource scripts (.DLG) as well as opening
header files (aka include files) to create lists of mnemonic symbols correlated
with dialog box resources.
187
188 BORLAND C++ 3.0 PROGRAMMING, Second Edition
Resource Control
Control Types
The Control menu option calls the pull-down menu shown on the following
page in Figure 8-2. The option list shown displays all common dialog ele-
ments and allows rapid selection of the desired type while constructing a
dialog box. Of course, all of these control element types can also be selected
from the Tools menu. Note that the seven custom types appearing in the right
column of the Tools menu do not appear on the pull-down list.
190 BORLAND C++ 3.0 PROGRAMMING, Second Edition
Push button
Radio button
List box
Check box
Group box
Combo box
Edit text
Static text
Icon
Black frame
Black rectangle
Custom...
Figure 8-3 shows the dialog box style option list with the default settings
for a new dialog box. The relevant settings shown are popup window type,
caption frame style, and system menu and modal frame dialog styles. The
default caption is assigned as DIALOG_1 (further dialog box captions will be
incremented automatically), and the system font is used for the default text
display.
Chapter 8: The Dialog Box Editor 191
ae Se Horizontal scroll
a Popup | Dialog frame Minimize box
Child Border _ Maximize box
Ge
Modify control style "Modify, Absolute Grid «: 18 y:18 cx 142 oy: 92
Frame Styles
Frame styles determine the appearance of the dialog box frame and the
presence or absence of a caption bar. Frame styles are defined as:
Style Options
The Dialog style list shows a check list of 14 style options, described below,
which can be used in various combinations. These options govern both the
appearance of the dialog box and how users can interact with the dialog box.
Including Menus —
Normally menus are limited to application windows but, since all dialog boxes
are windows, menus can be added using the Menu option. To include a menu,
define the menu as a separate resource but include it in the current project.
Then, add the menu’s resource name or numeric ID in the Menu input box.
Note: The dialog editor will not display the defined menu but will display
a popup window titled ‘’Menu,” including a single item titled “Item”.
194. BORLAND C++ 3.0 PROGRAMMING, Second Edition
Courier Hold
Fences | aes
Helv :
iLfidereigve?
MS LineDraw
MT Extra i Spigckc nese?
Small Fonts
ABCDEFGHIJKLMNOPGQRS TUVWXYZ
abcdefghijkimnopqrstuvwxyz
Custom Classes
An option exists to assign a custom class to a dialog box, permitting custom
processing of dialog box messages in place of Windows’ default processing.
Warning: An understanding of custom classes and the CLASS statement is
required. This topic, however, is not covered in this volume. Use with caution.
The Group control tool is used to define radio buttons as a logical group.
Radio buttons can also be enclosed, appearing as visual groups, with or
without group captions, using the Group Box tools.
A second, custom radio-button type is supplied by the Resource Workshop.
Text Fields
Two text field types are provided: static text fields which are used for informa-
tion or labels, and edit fields which are used for user entries and response.
Edit fields or edit controls are used for text entry, but are sometimes
also used to display default text. While the Tools menu offers only
a single of edit control, several styles of edit field can be defined as
single- or multi-line edit fields with vertical and/or horizontal
scrolling (automatic) or with vertical and/or horizontal scrollbars.
Edit field options can be selected by calling Control/Style from the Workshop
menu bar. See Figure 8-5 on the following page.
Single-line edit fields are commonly used for brief entries but can
accommodate entries longer than the entry field box (the outlined area) by
scrolling horizontally. This does not require a horizontal scrollbar if the entry
display is set to automatically scroll in response to keyboard entries or in
response to the left and right arrow keys. An I-beam cursor is supplied
automatically when the edit field is active.
Chapter 8: The Dialog Box Editor 197
+ Case insensitive
- Upper case
Lower case
Control id
Attibutes | | Baseword
_¥ Tab stop _ Group Convert OEM
_ Disabled v Border ___ Keep selection
Se Sl
+ Left _ | Horizontal | Horizontal
__. Right | Vertical
Center
Multi-line edit fields operate in essentially the same fashion, except multi-
ple lines of text can be entered. Anytime an edit field is created, a default text
entry may optionally be supplied.
List boxes provide for the display of text lists, permitting selection
of one or more items from the list. List box entries are normally
supplied by the application, as, for example, a list of filenames.
When lists are too long for the allocated space, a built-in scrollbar
appears for vertical scrolling.
Custom list boxes can also be defined as owner-draw list boxes. These
boxes have the general characteristics of conventional list boxes but are
functionally supported by the application rather than by Windows.
To define a custom list box, begin by creating a standard list box, then, either
double-click on the list box or select the list box and choose Controls /Styles.
Next, from the Styles dialog box, select Owner Draw Fixed or Owner Draw
Variable. Fixed requires that all items in the list box be of equal height.
Variable permits items to vary in height. One advantage of a custom list box
is that bitmaps as well as text strings can be displayed. Refer to Microsoft's
Software Development Kit for more information on supporting owner-draw
list boxes.
Combo boxes combine the features of edit boxes and list boxes.
They permit selection from a list of items or use of an entry field.
While the toolbar supplies only one tool icon for combo boxes,
Chapter 8: The Dialog Box Editor 199
three styles of combo box are supported: simple, drop-down, and drop-down
list combo boxes. Combo box styles and other options are selected through the
Control/Styles menu. Alternatively, the Style dialog box can be called by
creating a simple combo box (the default style), then double-clicking on the
combo box.
Simple Combo Boxes display both the list box and edit area features. Each
behaves exactly like the corresponding control elements except that selection
can be made by either typing an entry in the edit box or by selecting an item
from the list box. Typed entries in simple and drop-down combo boxes are not
required to correspond to list box items.
Drop-Down Combo Boxes behave in the same fashion as the simple combo
boxes except that only the edit box portion is displayed initially, and the list
box appears only when the down arrow to the right of the list box is clicked.
The list box portion of the drop-down combo box is sized in the same
fashion as a simple combo box but, by not appearing initially, permits tighter
spacing of controls.
Drop-Down Combo List Boxes are similar to drop-down combo boxes but
differ in the behavior of their edit box areas. For a drop-down combo list box,
entries can be made in the edit box, but must correspond to items included in
the list box.
Custom Combo Boxes can also be created as owner-draw combo boxes and are
defined in the same fashion as custom list boxes.
Scrollbars are used to create stand alone scroll bars within the dialog
box. The edit controls, list boxes, and combo boxes have their own
integrated and attached scrollbars and do not require the creation of
separate scrollbars. Stand alone scrollbars are used in a variety of
ways and can be created in either vertical or horizontal varieties.
More than one vertical or horizontal scrollbar can be created and
can be used to control anything, not just window display
coordinates. For example, the bitmap editor in the Resource Workshop uses
200 BORLAND C++ 3.0 PROGRAMMING, Second Edition
three horizontal scrollbars to mix intensities of red, green, and blue to customize
a palette color. Four or more scrollbars might be used in an MIDI control
application to set tone, voice, fade, and reverberation. Anything else in an
application that needs scaler control could be set by using one or more scrollbars.
The Icon (top) and Bitmap (bottom) controls offer a bit of fancy
work for dressing up dialog boxes. The Icon control is essentially a
static control sized to display a standard icon image, but does not
react to the mouse or to tab selection.
The Bitmap control, on the other hand, not only allows display of a
larger image than the Icon control but, more important, is treated
as a button control. Thus, the Bitmap control operates in essentially the same
fashion as the standard or custom pushbuttons discussed previously. The
primary difference, of course, is that the Bitmap button is selectable, while the
Icon control is not.
The two Group Box controls are not dialog box control elements in
the same sense as buttons or scrollbars but, instead, are used to
group other controls both functionally and visually. Optionally, a
caption can be displayed in the upper left corner of the Group Box
frame.
The White Frame static control simply provides a thin outline, but
does not obscure anything behind it.The Black Rectangle is solid and obscures
anything lying behind it regardless of color selection.
The two shade controls shown actually comprise four separate con-
trols and are titled Horizontal Dip, Horizontal Bump, Vertical Dip,
and Vertical Bump (see Control/ Styles). Each of these controls is, in
effect, a simple control element for placing a vertical or horizontal
line within a dialog box. In application, these shade controls are most
useful for separating items or possibly grouping items together.
Chapter 8: The Dialog Box Editor 201
The first control tool is the Pick or arrow tool which is chosen to
cancel other tool selections, returning you to the default arrow
cursor. The Pick tool is used to size, place, and select other tools,
either from the toolbar or within the dialog box being edited.
When a new control element type is selected, however, the arrow cursor
will be replaced by a cursor representing the selection.
Rows
Columns
Row spacing
Column spacing
When the Tab Stop control is selected, any controls currently set as tabstops
will be identified by surrounding boxes.
To set or clear a tabstop, click on the control desired and the present tabstop
status will be toggled.
With this done, select the Set Groups tool. The mouse cursor will change to
a ‘G’ and all controls that are currently the first members of a group will be
surrounded by identifying boxes. Now, using the Set Groups tool, tag the first
item in each group desired.
Note: These may or may not be the same controls that were set for the
tabstops.
Remember, the numbers that show the control ordering appear only when
the Order tool is selected, but this ordering still governs how groups are
assigned. Thus, if you tag the center control (8) in place of the left center
control (4), the new group would consist only of controls 8 and 9 while controls
1 through 7 would replace the illustrated 1 through 3 group.
Last, select the Pick tool (to clear the Set Groups tool) and select Test to
check performance. At this point, the Tab key should switch you between
groups but the arrow keys should only circulate within a group.
Figure 8-8 shows a 3x5 array of buttons (left) and their ordering (right,
numbers), together with tab locations (heavy outlines) and first-of-group
controls (shading). Grey lines delineate the resulting groups. When this array
was originally created, the button order started at the upper-left and randown
by column, then right by row. This order, however, was not appropriate for
the desired groups.
ARs!
LAAN
OOOO
204 BORLAND C++ 3.0 PROGRAMMING, Second Edition
The Order tool allows changing the order of all controls or the order of
controls within a group. Several methods can be used to set the order,
but some are trickier than others—check your results carefully.
The simplest method is to begin by selecting the Order tool. This
selection will change the control display to show the present order of the
controls (see Figure 8-8, right side). Now, set the order through the controls,
clicking on each one in the desired order. When all controls are ordered, the
arrow cursor will be restored automatically.
Caution: Reselect the Order tool to check your results.
A second method is to select a group of controls by clicking on them with
the Pick tool, then clicking on the Order tool. There are two hazards in this
method: First, you need to be careful to ensure that you click on every control
in the group and do not rely on the outline provide, because the outline is
determined by corners and may enclose controls that were not selected;
second, even if every control within the rectangle was clicked correctly, the
resulting order may or may not be appropriate.
Aligning Controls
The left- and right-arrow alignment tools move controls horizont-
ally, aligning the controls along the left or right sides of the sizing
4 frame. In like fashion, the up- and down-arrow alignment tools
move controls vertically, aligning them along the top and bottom
sides of the sizing frame. Variations in horizontal and vertical size do not
affect alignment.
Spacing Controls
In addition to the alignment toolbar, the Align Controls dialog box (select
Align/Align from the menu bar) duplicates all of the preceding options but
adds two other capabilities: horizontally spacing a series of controls equally
or vertically spacing a series of controls equally.
To space a series of controls, you group the controls as before, then select
either of the ‘Space equally’ options from the Align controls menu option,
shown in Figure 8-9.
Note: Both horizontal and vertical spacing can be executed simultaneously.
Chapter 8: The Dialog Box Editor 207
Size ingControls
Provisions also exist for resizing controls either individually or as a group
(that is, making all selected controls the same size). The Size controls dialog
appears in Figure 8-10 and is reached by selecting Align/Size from the menu
bar.
The No change options (horizontal and vertical) are selected by default.
For a group (that is, more than one control element), four sizing options are
provided: resizing to match the smallest member of the group, the largest
member of the group, the sizing box, or the dialog box. Only one selection can
be made from each option set.
For an individual control (that is, a single control element), the enter value
options become available and permit entering both the upper-left corner
coordinates (X and Y) and the horizontal and vertical sizes (CX and CY).
Note: The corner coordinates position the control element relative to the
dialog box, not relative to the screen or to any sizing box.
Header Files
Unfortunately, using the Resource Workshop, header files (aka: include files)
require special handling and can only be opened for .RC resource scripts—not
for .RES resource files.
Hopefully, this will be corrected in future releases. But, for the present, one
alternative is to use the Windows Notebook editor to open a separate .H
header file to create mneumonic labels and assign corresponding numerical
values.
208 BORLAND C++ 3.0 PROGRAMMING, Second Edition
The dialog ID values appear (in gray) below each dialog element, but only
the pushbutton, with a value of one, returns a value when activated. The value
one corresponds to the message constant IDOK, which is defined in
Windows.H but will also later be duplicated in FileView.H.
Chapter 8: The Dialog Box Editor 209
ea
Select File Type
a7 = < 3 a)
The purpose of the File Type dialog is found in the fourteen auto-radio
buttons, each labeled with a different file extension (concluding with a
wildcard extension option). Most of the file extensions used are common to
Windows applications but were chosen here simply for an example of a radio
button list. The auto-radio button style has been used for all of these controls,
freeing the application from the responsibility of checking and unchecking the
button display each time a control is selected. In most cases, this will be the
preferred approach. The radio buttons are enclosed here in a group box, but
the group box does not control the grouping.
210 BORLAND C++ 3.0 PROGRAMMING, Second Edition
File Selection
Pesos EP
ecm tile Spee
ic aes) |Tt
Pi Salil sat
The icon box displays the FILEVIEW icon, while the Ok and Cancel buttons
are essentially stock controls returning IDOK and IDCANCEL messages. The
remaining button control returns the message IDM_TYPE, which in FileView.H
defines and which the application receives as a request to call the File Type
dialog box.
These values and constants also appear in the header listing FileView.H
(Chapter 12) but will be needed as control element IDs while creating the three
dialog boxes illustrated.
Summary
While dialog boxes are integral to Windows applications, and the Dialog Box
editor is a tremendous convenience, dialog boxes do not exist or operate ina
vacuum. As mentioned, the Dialog Box editor does not even directly support
dialog box menus, which must be created by the Menu editor described in the
next chapter.
Also in this chapter, custom dialog box controls and owner-draw styles
were mentioned, referring the reader to Microsoft’s Software Development
Kit for more information. While these custom features are both possible and
practical, they also require additional programming skills and provisions that
go beyond the scope of this book. Moreover, they are not needed or desired
by the majority of applications.
While interesting, custom controls not only expend an inordinate amount
of programming time, but also may distract from program execution or even
confuse the user. Use these controls with care.
Chapter 9
The Menu editor provides full facilities for creating and editing dialog box
menu resources. Menu resources can be edited from resource files (.RES),
executable programs (.EXE), and dynamic link libraries (.DLL). Menu resour-
ces can also be saved to a new .RES file or saved to a resource script file (.RC).
Like the Dialog Box editor, the Menu editor cannot open a header file (aka:
include file) to correlating symbolic constants with the menu resource unless
the .RES file is first converted to an .RC file. Instead, only numerical values
can be entered while the menu is being created, and corresponding mnemonic
(in an .H include) must be defined separately as described in Chapter 12.
215
214. BORLAND C++ 3.0 PROGRAMMING, Second Edition
‘Copy,’ ‘Paste,’ ‘Search,’ and ‘Clear clipboard.’ Two further items appear on
the main menu bar: ‘Print’ and ‘File.’ The ‘Search’ item calls a second pull-
down menu with two further options: ‘Find’ and ‘Replace.’
~MENU_1
nes text POPUP "sEdit”
eringeF MENUITEM “Ctut\t"U"
ee MENUITEM "&Copy\t*C"
_‘Item id MENUITEM "&Paste\t®P"
POPUP "$Search\t"S"
eee ee MENUITEM "&Find\t*F"
Lge sae gonoe to coe MENUITEM “Replace\t*R"
$333 ue? + No break __End Popup__
The menu script (lower-right window) is the main working area for creating
a menu and, within the script, individual items can be inserted (Ins, Ctrl-P or
Ctrl-?), edited (left window), or deleted (Del) from the script. Figure 9-2 shows
seven menu editor options, the first six creating new menu elements and the
seventh executing a check for duplicate entries.
Chapter 9: The Menu Editor 215
The first three options simply insert new menu elements at the posi-
tion(s) indicated in the menu script, setting the level of the menu element
as appropriate for the position. For example, if you begin by selecting the
‘__End Popup__’ line using the cursor, then press Ctrl-P, you will create
a new, submenu popup entry following the ‘Clear clipboard’ item.
The ‘New menu item’ or Ins key simply adds a new menu item element,
while the ‘New separator’ or Ctrl-S key adds a separator line as shown
immediately below the corresponding menu item.
The second trio of menu options are slightly more complex and could
be described as macro entries. Each of these three options creates an entire
main menu and pull-down submenu as shown in Figure 9-3. These three
are stock menus that are used frequently by many different applications
or, alternatively, may be used as a convenient basis for customization.
Edit Help
New Edit
Open... Cut Shift+Del
Save Copy ShifttIns |
Save 4S... Paste Ctrl+Ins Keyboard
Print... Commands
Page setup... Procedures
Printer setup... Using help
216 BORLAND C++ 3.0 PROGRAMMING, Second Edition
Editing A Menu
Editing an existing menu or a menu in work does have a few restrictions.
First, menu items cannot be changed from one type to another—that is,
a popup item cannot be changed to become a standard menu item or a
separator.
Second, the order of menu elements cannot be revised except by using
the delete/insert or cut/copy/paste operations from the Edit menu
option.
Third, deleting, cutting, or copying a menu element (popup) with
submenu elements affects the submenu structure as well as the target
element.
Other than this, the menu operations are relatively straightforward, and a
minimum of experimentation should familiarize you with how to create and
modify the structures of Windows application menus.
| Pop-up Item] Item2_ Ttem4 Item5 Item6 Item? ltem8 Item9 Item10
ltem11 Item12 Item13 Item14 Item15 Item16 Iteml? Item18 = Item19
| Item20 Item21 Item22 Item23 Item24_ Item25
Granted, there are actual limits on the number of elements and levels that
a menu can support, but the real limits are simply practical considerations. A
menu bar like the one shown is simply too complex and confusing. Similarly,
an excessive number of pull-down levels would either become totally confus-
ing, tediously frustrating, or impossible to display (or all three) long before
actual programming limits were reached.
Chapter 9: The Menu Editor 217
Your best rule of thumb for creating menus is simply to keep them simple
and to employ simple menus to call dialog boxes when complex options need
to be displayed.
Menu Breaks
The Break before box (to the right of the Menu Type block) offers a second set
of radio button controls. These, however, are selectable and assign a break
status to individual menu items. The default, of course, is the ‘No break’
status, which keeps the present item in the same column as the preceding
element(s).
The ‘Menu bar break’ option causes the selected item to break and begin a
new column, with a vertical bar separating the present from the previous
column. See Figure 9-5 in which the Cut and Copy elements are separated from
the Paste and Search elements by a vertical bar.
Chapter 9: The Menu Editor 219
The ‘Menu break’ option causes the selected item to break and begin a new
column but without the vertical separator bar. This is illustrated on the
following page in Figure 9-5 by the Clear clipboard menu element.
The ‘Help break’ option is used only with top-level menu items and moves
the selected item to the far right of the menu bar. Setting this option has no
effect on lower-level menu elements.
Clear clipboard *L
*S Find “FE
Replace *R
tem text —
'|&%Open...\t*O
lean
Ready
Summary
The Menu editor permits you to create, define, and test dialog menus. Acceler-
ator keys can also be shown as prompts in the menu item text, but must be
defined separately using the Accelerator editor discussed in Chapter 10.
Chapter 10
The Accelerator editor is used to create and edit accelerator resources, which
are hot keys for issuing application commands. While conventional (DOS)
programs often provide similar services and sometimes use TSR utilities to
translate individual keystrokes or key combinations into command sequences,
accelerator keys take a different form. They do not depend on processing by
the application under Windows.
All keyboard events are handled by Windows in the first instance, and only
keyboard messages are passed to the applications instead of raw key data.
Therefore, Windows acquires the accelerator key information from each active
application. It then responds to hot-key events by issuing the appropriate
command messages to the application just as if a corresponding control button
or other control feature had been activated.
However, it remains the programmer’s responsibility to define hot keys and
to prepare this information ina form acceptable for Windows to interpret. This
is the purpose of the Accelerator editor. The purpose of the accelerator editor
is to aid this programmer is defining keys and formatting information for
Windows.
221
222 BORLAND C++ 3.0 PROGRAMMING, Second Edition
as a message value, which is sent to the application when or if the key or key
combination is entered.
In the Accelerator editor, a list box at the right shows the hot keys in the
left column with the message value to the right. The accelerator table label
(name) appears at the top of the list as well as in the resource list (far right in
Figure 10-1).
To the left of the list box, the Command edit box field shows the message
value returned by the current selection and, immediately below, the Key edit
box shows the key identifier.
Accelerator keys may be defined: as either ASCII or virtual keys, and the
current selection is shown by the radio buttons below the Key field. The
difference between ASCII and virtual keys will be covered momentarily.
# § Ctrl-O, value 103, (defined as IDM_OPEN) calls the File Open dialog.
= Ctrl-T, value 104, (defined as IDM TYPE) calls the File Type dialog.
® Ctrl-X, value 101, (defined as IDM QUIT) calls the exit routine.
Chapter 10: The Accelerator Editor 225
CO e ae
[Add anewkeytotable
Summary
Accelerator key definitions are used by many applications and, depending on
the application, different accelerator key tables may be loaded at different
times for different purposes. In general, however, accelerator keys provide
simple short-cut alternatives to complex menus. Users of your applications
will greatly appreciate them. The routines for using the accelerator keys will
be demonstrated in the FileView.C program discussed in Chapter 13.
A al a terete)
=
a iden) tah
\. i.e (2 Gees TOA ee
a
-
| , = wee aaplenhaleaye
a ;
a 7
a al ara
; = : 7 7
:
_
Ss &
= % => ca ee ai a 7 '
=>
antl ore a: - sr
| ee be = = ae , * a WYO al ba oe moan th. s
ed nl a.ay
.
ee
asi Pay a2
4
: . 4 4 — aT =e"
i hid ok. ib : aaeaa »
+.
; . ;
a a ~~)
_ <--> ee
are Nyaa e
: . s ~ 7 = ae 4 : 4 - :
_
‘a.
.
an =ve
rr,
al
“7
= oa
rh
wee nt 7
; ips = & om by eT ae,
i ed
«
coe i 7
La enaeinet ae = _
oot. 5,
u BS ea eA | hp cece
; yihiag ©
a
7 Me my
i.
maa
he ei
[- a - = < 7 = tarts : ee
_ _ a — = = : 7 =
ay
i . 2 wy tig asi encantayst mA
| :
lindgviikiag f reiiiaslges .
; h ? ise) Aer ot ooh
| Wei hl De te>Frala 9m
=
’ 1. aft acer WITIS6 Vitor Te
{ e. arity G } ; Merrett ;
a — ! '
a ; ‘fe
Chapter 11
Defining Strings
String resources may consist of any string data, including error, status and
general system messages; window captions; and even brief explanations dis-
played by dialog windows. Button and control captions are not normally
included in the string resources because they can be edited directly through
the Dialog editor. Likewise, menu strings can be edited through the Menu
editor. Individual strings may contain up to 255 characters, while the max-
imum size of the string table is 64K bytes. Only one string table can be defined
for any specific application.
227
228 BORLAND C++ 3.0 PROGRAMMING, Second Edition
New item
Delete item
Thus, when an application requests string 10, Windows loads all strings in
the ID range zero through 15. Ergo, by grouping associated strings within
hexidecimal blocks, Windows is permitted to execute fewer loads and memory
overhead used during execution is reduced. Otherwise, there are no
restrictions on the values used to define string text.
: FileView Ay
|| FILEVIEW
Lic
Summary
While string resources are a bit less convenient than simply entering strings
within the application source code, they do have the advantage of providing easy
revision and/or conversion to other languages. Perhaps more important, this
also reduces the amount of string information that must be held in memory while an
application executes.
Chapter 12
While the Resource Workshop has nominal provisions for editing header (.H)
files, this feature is enabled only for use with .RC resource script files and not
with .RES compiled resource files—an oversight at the very least and, in many
programmers’ estimations, a serious flaw.
However, in the absence of integrated support and access to these header
files within the Resource Workshop, it is still possible—using the Windows
Notebook editor or other Windows editor programs—to open and maintain
an application header file while using the Resource Workshop.
For example, Figure 12-1 shows the Resource Workshop editing the FileView
stringtable while overlain by the Windows Notepad with the FileView.H
header file. Of course, the drawbacks of this approach are obvious: first,
switching between two applications and making what are, in effect, duplicate
entries is time consuming; second, errors that result from this inconvenience
will not be immediately visible but will be revealed when the application is
compiled.
As an alternative, the Resource Toolkit from the Whitewater Group—which
was distributed with Borland C++ version 2.0—is still compatible with
Borland’s C++ 3.0 even though the Resource Toolkit does lack the Font Editor
provided by the Resource Workshop. However, resource files (.RES or .RC,
etc.) created by either of these resource editor systems, or by other third party
editors, can be mutually read, edited, and recompiled.
24
232 BORLAND C++ 3.0 PROGRAMMING, Second Edition
On the other hand, you can also express your irritiation over this shortcom-
ing directly to Borland International where—one may assume—correction of
this oversight will be forthcoming in the near future.
The header file shown will be needed later on by the FileView application
in Chapter 13.
Chapter 12: Header Files With The Resource Workshop 233
‘/
Pf.
SS
Chapter 13
In Chapters 6 through 12, the various Resource Workshop editors and creating
header files were discussed. Each chapter ended with one or more sample
resources used by the FileView application. It is now time to put all of these
fragments together by creating the application itself.
The FileView program opens a file of any type, displaying the file contents
in hexadecimal format with the file offset address display at the left and an
ASCII string display at the right. The main purpose of this application is to
demonstrate the several resources created by the Resource Workshop editors
and to show how
they are called on by Figure 13-1: The FileView Application and Dialog Window
an application.
Figure 13-1 shows FileView - DABC\EXE\FILEVIEW. OBJ
a composite of the
FileView application
d:-\bc\exe
and of the several about2.exe [*] | Fiiet -
TYPE
included. Complete
4h 49 52 2E
source code listings 6OD8
OGEG6
46 49 4C 45
61 60 66 8C
GOFG 68 E7 65 86 buttons.exe
for both FileView.C, 6166 66 EE 61 86 charset.exe
6116 87 88 6C cursors.exe
and FileView.DEF, 6126
16130
88
77
66
6E
06
64%
66
OA
ES 61 BC 68
editor.exe
6148 65 86 6B E7
as well as FileView.H 66 1D 66 21 00 31 86 3D 68 42 08 45 66 58 66 SC
235
236 BORLAND C++ 3.0 PROGRAMMING, Second Edition
appear at the end of this chapter. The fourth component, FileView.RES, must
be created using the Resource Workshop following the directions in previous
chapters.
The constant IDS_NAME identifies the desired string resource. The string
is copied from the stringtable to the global variable szAppName with the final
argument, 10, specifying the size of the data transferred.
In other circumstances, multiple strings might be loaded as needed, during
dialog initialization. The only menu resource created belongs to the main
application window, but requires an assignment instead of a Load statement.
Chapter 13: Putting It All Together 237
This specific instruction, however, only provides the icon for the applica-
tion itself; itis used when the application is reduced to icon size. Alternatively,
the various icons used in dialog boxes are not affected by this instruction. They
execute their own icon loads without requiring provisions in the source code
for icon handling.
Notice that each of the resources loaded has the same name as the applica-
tion. If desired, you could use other "names." When multiple resources are
used, more than one name is obviously essential. At the same time, these
names are often supplied simply as numeric identifiers, but a simple string
suggesting the resource identity is usually easier to work with. The remaining
instance initialization is essentially the same as that shown in previous
examples.
Initializing Settings
Following the instance initialization, provisions are also made to set a default
file type and to copy the default type string to szFileExt:
These two provisions, however, do not set either dialog box. Only global
variables are affected at this point, and additional specific provisions are
required when the affected dialog boxes are called.
to different menus or dialogs. Simply loading the accelerator is only half the
trick because the TranslateAccelerator function is necessary to provide the
actual processing:
while( GetMessage( &msg, NULL, O, O ) )
{
if( !TranslateAccelerator( hWndMain, hAccel, &msg ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
ae
Most of this loop should be familiar enough from previous examples but
now has the additional provision of translating accelerator key messages into
application messages. Simply put, this function intercepts any key events that
are defined as accelerator keys and, in turn, issues a new message in the form
provided by the Accelerator table.
This initiates the dialog by making an instance of the dialog and returning
a handle (IpProc). It then calls the Dialog Box function to display and operate
the dialog box. In this case, only one response is expected, an IDOK message,
which will also exit the dialog. Therefore, no further handling is necessary.
For the second dialog box, in response to IDM_TYPE, a local subprocedure
is called:
CallFileTypeDlg( hInst, hwnd );
Initiating Dialogs
The FileTypeDlgProc and FileOpenDlgProc functions provide a variety of
services, but, for the moment, the two responses to the WM_INIT~DIALOG
messages are the principal topic. In FileTypeDlgProc, when the dialog box
receives the WM_INIT~DIALOG message, only one initialization provision is
required—setting the initial state of the grouped radio buttons:
The CheckRadioButton function is called with the dialog box handle (Dlg),
the first and last (range) radio button IDs to set and, finally, an ID value
indicating which member of the group should presently be turned on (set).
Since the radio button controls in this dialog have been declared as auto-
radio buttons, no further provisions, within the application source code, will
be made for changing their state. However, this initial setting is necessary, as
is a later provision to determine which has been set (see the WM_COMMAND
message response in FileTypeDlgProc).
The FileOpenDlgProc has a slightly more extensive response to the
WM_INITDIALOG message because there are three elements in the File Open
dialog that require initialization: two edit boxes and a list box. The first task
sends a message to the filename edit box, IDD_FNAME:
The DlgDirList function is called with the handle for the dialog box (hDlg),
a drive/path/file specification (szFileSpec), the list box and path (edit) box
identifiers, and a file attribute flag (wFileAttr). When called, DlgDirList fills
the list box with a list of files that match the specification and fill in the current
drive/directory in the edit box.
Subdirectories are shown enclosed in square brackets ([subdir]) while
drives are shown in the format [-x-] with x replaced by the drive letter. The
file attribute flag, wFileAttr, limits the types of file entries displayed as shown
in Table 13-1 below.
Attribute Meaning
0x0000 Read /write data files with no additional attributes
0x0001 Read-only files
0x0002 Hidden files
0x0004 System files
0x0010 Subdirectories
0x0020 Archives
0x2000 LB_DIR flag (note 1)
0x4000 Drives
0x8000 Exclusive (note 2)
Chapter 13: Putting It All Together 241
The LBS_SORT style for the list box will sort the entries. LB_INSERTSTR-
ING can be used to insert items in a specific order with the third parameter
(zero in the preceding example) specifying the list position.
In the case of the FileOpenDlgProc, the DigDirList and the SetDlgItemText
functions are called in essentially the same context in response to other item
selection messages. The majority of the dialog box interactions, however, are
carried out without intervention by the application, including scrolling the
file list and inverting (high-lighting) selections.
Summary
In this chapter, the resources created, using the Resource Workshop editors
in previous chapters, are actually brought into play in the FileView applica-
tion. Many of the resources created, such as the menu and the icons, required
little or no intervention within the application source code, while others were
only partially autonomous in their actions. The complete listing for FileView
appears on the following pages.
242 BORLAND C++ 3.0 PROGRAMMING, Second Edition
ifif FileView.C Lf
// demonstrates Resource Workshop interactions //
/ /aaSzEBEBSStasSesasssssesessesssesesssssssssssa=//
#include <windows.h>
#include <dir.h>
#Hinclude <IS) tCOree
n>
Hinclude "fileview.h"
Py HSSSSeSSeeneases Aiviimieq
et prototypes ===========S====//
Dali cmdShow );
===
= = end dectarat tons == a]=]]]==Se=s—s—s5/)/
/ /PSSeslosSsS]sS]3
SSS 3 Ss] SSeS] SSeS] SSSsooe pass —Seea// //
Itif CloseFV: used to clean up application instance Wf
/ /SSS=S=SSe SSS] SSS 5 S65 S SSS SS SS SS SSS SS SS SS Se SS a See a
void CloseFV() { }
ae
// FormatLine: formats 8/16 bytes per Line for Hf
ifif display, «begining wetieti'Ge rote
se tye =/7/
// hex values grouped by fours and ASCII //
iif text at end. 1
HfIf returns: pointer to szBuff Hfif
ee ee
Wine a les
unsigned char chr, szTemp1C801], szTemp2(801];
*szBuff = O;
i= sje 0F
Sri? Sevens WBOrr* “, [EmoOgws Ww // line offset
StGeGiOiy, GaeSiZ15 Uma SrZane mOn mes Hi e\ole) oO lew
if2O1
ha Gee) <M) Gus elinyeiid DAES Pilla Yo ame tt ae)
i
chr = *lpMem++;
olste Gant Conset-u |i) m/e hm)
SorimerG sven. W“4O2" ™, Glnie Dye L/S Wie ere he pcmann
e sees) Ditsalimiten GunS Zale) mph, mee) X quae CH pa = ioe ChGMha OLtameeOlUlts
S Ginc alts 215 Ultnty- mS Zale mn Daler [ac Cie hielx<aa.t Ob Unit
iC .eaehrle=.0x%20) &s chr <= O0x/— >6|
C chr >= OxAO && chr <= OxFE ) 0) szTemp2EiJjJ = chr;
esieles zempi2 ie — eee =
}
szTemp2Lild = OQ; WH @ele) tow b
ore Ge =a SH Dasipilary ary ++
{
lt CA Gjitipe eats te cat:C Ss2Butts,. 55 ae) y // pad hex
else cstreat.G sz putt. pe ee
Str Ga tGes 7.Lempec mo mt. ss ti spade ASC ht
}
SPrintic sz Lemp + bes)tS oLempe. ff -ftormat LAselt
strecat( .szButt> szLemp) > // add ASCII
return © szButt™)
Chapter 13: Putting It All Together 245
//SSSSSSSSaassasaaseeaaesenesessssssseeeeseeeeeee====//
(Jo Paantr ile: itihiliines. is "greater than zero, /
ii} reads and displays some portion of the //
HE fiteleaccordind sto tthe isize-and scroll Ifif
ifif position. if
EI return: nothing ifif
Tf note: file is opened, read and closed. Files //
// should not remain open over multiple ifif
HU Window messages. //
{
iitiG tects 27 2> HDs piGaly, »
jFalUSize =e eHiDMES pilabys>
ester) Fain Size —s mlinlexates Ze
Tier SS iru lSezF
Formatline ( szButiity aupst.r2y ianoz
Gehatr: a). aCihass HHiDiisip Wa YO. OF
Textoute@ ehdac, Chir AmCeHScrl Pos, + O0r
ChrY * UPost+, szBuff,
stolen 4siz.B uit 2)ea)=;
LpStr2 += HDisplay;
We ae ws
GUVoba Uno GehBurt tor,
}
GlobalFree( hBuff );
}
mele GilorsiesGaanit al Gem y=
ne
EndPaint(€ hwnd, CLPPAINTSTRUCT) &ps );
}
Up Pnocs—
MakeProcInstance( FileTypeDlgProc, hInst );
iRecurn |= DialogBoxC ininst,;. “FILETYPE”, hwnd;
LolPRa@ MW,
FreeProcInstance( lLpProc );
return( iReturn );
}
BOOL FAR PASCAL About HWND hDlg, WORD msg, WORD wParam, LONG
lParam )
{
switch( msg )
{
case WM_INITDIALOG: return( TRUE );
case WM_COMMAND:
switch( wParam )
{
Calsicue EDO Ke-m End Diralaolg) Gani Dig) al) );
PawUMAINC WRWIES
Cera twecurn. (RUE. oT
} }
returns FALSE >;
248 BORLAND C++ 3.0 PROGRAMMING, Second Edition
/ /HH=BHEEBSET BSS S SSH SSS SSS SS SSS SSS SSS SS SSS SSS SS SSSs===//
// WndProc: handles application messages //
switch( msg )
iu
case WM_CREATE:
getcwd( szTmpFilePath, sizeof(€szTmpFilePath) );
Sercac€ szumorilelrecin, “WN? 2s
strcepy( szTmpFileExt, szFileTypeLiFileTypel );
S tiriG py.G@ siz m'p Fale Naimer-aiu es:
HDisplay = 16L;
return(€ DefWindowProc( hwnd, msg, wParam, LParam ));
case WM_COMMAND:
Switch( wParam )
{
case IDM_ABOUT:
LpProc = MakeProcInstance( About, hInst );
DiatogBox€ "hinst?; “ABOUT”, hwnd, UpProc. >:
BineeiP Bolceln Sstiainic.e! Ga ipietzO Cam E-
break;
casicerDiMmin nee
Cal tFitetypebDlgG hinst, hwnd.)
break;
case IDM_OPEN: // set initial search path
strcepy( szTmpFileSpec, szTmpFilePath );
strceatG sztmpFilespec, “* =) >
strcat( szTmpFileSpec, szFileTypeLiFileType]);
1f€ CallFileOpenDlg( hinst,-hwnd,
szTmpFileSpec,
szFileTypeliFileTypel,
szTmpFilePath,
szTmpFileName ) )
Chapter 13: Putting It All Together 249
{
strepyG szFName, sziTmpkiltePath)-
strcat(€ szFName, szTmpFileName );
SPI tt sezeuNte 6) oS ees
szAppName, szFName );
SetWindowText( hwnd, szBuff );
hPiWe== topeme eszFNamey nth") >
uve dived » Livdeterninesiile: size, etc
di
Prtsz eet itelengurentilenoGehiba los:
Falbines.=, Cint) G Gk USzFHDisplay—1L)
Hf \aliba Spoylleisy
FormatLine( szBuff, szFName,
MO Stolkes7, INWILIE >)
WndWidth = strlen( szBuff );
/eGlertead sip Gary aewald th
ViSIG OVS am S Cia Olsmn— a Oe
TaCamOlsye Gy kaise
}
else // i fe tawem opens taniued
{
SetWindowText( hwnd, "FileView" );
FilLines = WndWidth = O;
}
SetScrlRange( hwnd );
DnivialGardateiReiciG shiwnids NULL Si RUIE =)
UpdateWindow( hwnd );
Eee bineak:
case: EDM] QUIT:
PostMessage( hwnd, WM_CLOSE, O, OL );
break;
default: break;
uy break;
case WM_SIZE:
fate que Peaiitera Man
if
WndY = HIWORDC UParam ); // save vert size
WndX = LOWORDC LParam ); Li SSiawie Noirazs Sarze
Hime = © Winch 7 GhieW < a 2 t Clie
vie © Wine / lip 2 = Ci ») Wb pSspylasy = GIE¢
else HDisplay = 16L;
Fa tlanes =9cint) (CE1USz + "HDisplay-TEa/HDisplay) >
SetScrlRange( hwnd ); [ASiCvCaeS (GC1O)WniNaln gle
LParam = MAKELONG( WndX, WndY );
return(DefWindowProc(hwnd,msg,wParam,lParam)
);
+ break;
250 BORLAND C++ 3.0 PROGRAMMING, Second Edition
case WM_VSCROLL:
switch( wParam )
{
case SBE TROP:
Vine = -VSerlPos; break;
case SB_BOTTOM:
Valin Gees VESIG
Io lM aXen VEO IGiica ELOISE break;
case SB_LINEUP:
VEliniCee—sa— l= break;
case SB_LINEDOWN:
Vane Ss We break;
Galsie oD EAGE UIP:
Wins = fies 4, Utell? 7 Clot BF break;
case SB_PAGEDOWN:
Vane = tne ad, Winch’ 7 Cliey d- break;
case SB_THUMBPOSITION:
VInc = LOWORDCLParam) - VScrlPos; break;
case SB_THUMBTRACK:
VInc = LOWORD(LParam) - VScrlPos; break;
default) eVilinicm—— 0)
}
ViInice=smaxGe—VSicimePos:,
Nn Gavel niCyaVES Cina |atXan— an\IS Cli GI O;Same)maee=
vie Wine 2
{
ViSiCin POS em Velanice
scroUlWindowCchwnidy; OF =ChrYytVine, NULESINULE ):
SetscroelPos C hwnd, SB-VERT, avScrUPos, snRU Ee:
UpdateWindow( hwnd );
Ta Dineaik
case WM_HSCROLL:
switch(€ wParam )
{
Gase) sib OlP:
Hlne = SISerurPecs break;
case SB_BOTTOM:
Hinc = HSerlUMax =-HScrlPos: break;
Casicues pas eaN E UP e
HIne = -1; break;
case SB_LINEDOWN:
Hiitnicae— les break;
Galseh Sib RAIG EUR
ae Tee Se tosh break;
case SB_PAGEDOWN:
HInc¢ => 48; break;
case SB_THUMBPOSITION:
Chapter 13: Putting It All Together 251
switch (msg)
{
case WM_INITDIALOG:
getcwd( OrgPath, sizeof(OrgPath) );
SendDlgItemMessage( hDlg, IDD_FNAME, EM_LIMITTEXT,
e310) {OL NF
fJenvlilalkisty box with tilese from starting f1lemspec
DIGG)Diti eats ts Gan DAGp eS zabanlie)Si pierce mmrlaD |D mmen sla Suter
CD) Pehl A MHP Ibei\weiie 2) 5
SetDlgItemText( hDlg, IDD_FNAME, szFileSpec );
WH SIO aise TiUlesoce
Ret unniG BRULEE
case WM_COMMAND:
switch( wParam )
{
Gas.en LDMe WY PEt
1h Callhhitetypeblg GC hinst,o hDlges)>
{
strepyG szFaulespec >. wx. ys
strceat( szFileSpec, szFileTypelLiFileType]);
DigDinkist GQ hDUg,. szFileSpec emp D ohlelsil
PDDUFPATH, White Att ras
Chapter 13: Putting It All Together 2O3
switch (msg)
{
case WM_INITDIALOG:
CheckRadioButton( hDlg, IDD_BMP, IDD_ANY,
ED DEB IMiP-anibeiGeslayi Demme
OrgFileType = iFileType;
return(€ TRUE );
case WM_COMMAND:
switch( wParam )
iE
case IDD_BMP: case IDD_COM:
case IDD_CUR: case IDD_DAT:
Calsie=sl DD DIBS): Case L DIDS DING:
Galsie el) D Dm DIEIa: Galsiem UDIDMIE XEN:
case IDD_H: case IDD_PAL:
CialSiCu ID Das RiGrs calsie= LlDIDMRIES:
case IDD_TXT: case IDD_ANY:
iFileType = wParam - IDD_BMP; // set file type
SitimCD yy, GusiZ Bale xt), meS 2 hulWemny pela han Welly piesa.
// set extension
Peewipin¢ winlls 7
case IDOK:
EndDialog< hDLg, TRUE ); // end dialog box
break; iy Pel Telia
case IDCANCEL:
iFileType = OrgFileType;
EndDielog¢ehDlg? RAESE => // end dialog box
break;
default: Tent Une i Ga AVS) Em)
256 BORLAND C++ 3.0 PROGRAMMING, Second Edition
+} break;
defautt;: returner FALSE);
}
pewUienC€ IRWIE Ms
}
: FileView.DEF =;
FS SS SS SS SS SSS =
NAME FileView
SEGMENTS
_RES PRELOAD MOVEABLE DISCARDABLE
EXPORTS
WndProc
About
FileOpenDlgProc
FileTypeDlgProc
I if
//
el,
Before leaving the topic of dialog boxes, which have certainly been a major
feature in several preceding chapters, I would like to mention two additional
types of dialog boxes: one that does not require the Resource Workshop to
prepare—the message dialog box—and a second—that employs special
features of the Borland Resource Workshop and the BC++ compiler—custom
message dialogs.
Beginning with the simpler form, message dialog boxes are designed to
present a brief message. These messages may be a warning, caution, error, or
simply informational. They may also return a response to the application. At
the same time, four icons can be displayed together with up to three of seven
buttons in six combinations, as well as three modal settings.
In the second example, the form and function of the message dialog boxes
used in the first example will be duplicated, but using the dialog box handling
illustrated in Chapter 13 with a few special features provided only by the
BC++ compiler.
259
260 BORLAND C++ 3.0 PROGRAMMING, Second Edition
Pushbuttons
Symbolic Constant Value Meaning
MB_OK 0x0000 single pushbutton: OK
MB_OKCANCEL 0x0001 two pushbuttons: OK, Cancel
MB_ABORTRETRYIGNORE 0x0002 three pushbuttons: Abort, Retry, Ignore
MB_YESNOCANCEL 0x0003 three pushbuttons: Yes, No, Cancel
MB_YESNO 0x0004 two pushbuttons: Yes, No
MB_RETRYCANCEL 0x0005 two pushbuttons: Retry, Cancel
Only one button combination can be selected; combining button values (that is,
MB_ABORTRETRYIGNORE | MB_YESNOCANCEL) would be interpeted simply as MB_YES-
NOCANCEL. Other combinations might simply result in invalid flag values.
Default Button
Symbolic Constant Value Meaning
MB_DEFBUTTON1 0x0000 first button is default
MB_DEFBUTTON2 0x0100 second button becomes default
MB_DEFBUTTON3 0x0200 third button becomes default
The first button is always the default unless either MB_DEFBUTTON2 or MB_DEFBUTTON3 is
specified. Only one button can be selected as default.
Chapter 14: Message Box Dialogs 261
If a message box has a Cancel button, the IDCANCEL value will be returned
if either the Escape key or Cancel button is pressed. Otherwise the escape key
has no effect.
Figure 14-1, on the following page, shows a demonstration program with
four message box dialogs called through the menu for convenience. The
illustration is a composite because multiple dialog boxes would not normally
be displayed. The general format for handling a dialog box is to use either an
if statement, if only one return message will be tested:
The case values, of course, must match the specified button settings.
Alternatively, if the message box is for information purposes only, the
return values may simply be ignored (as shown in four of the examples in
MsgBox_1.C, below.)
Note: The menu resource for MsgBox_1 must be created using the Resource
Workshop Menu editor. The application icon, not shown, is optional.
Chapter 14: Message Box Dialogs 263
Message Boxes
Asterisk Icon [Information]
Exclamation Icon
Hand Icon (Stop)
: i
Asterisk or Information icon mes sage box with
Question Icon
OK button only
Last, the four dialog boxes shown in Figure 14-2 were created using the
Resource Workshop. These four dialog boxes, however, are the real objective
of this exercise because, as you may note on examining the illustration, the
dialog buttons are quite different from those appearing in the earlier example.
Granted, the slot machine glyph for the Retry button and the speed-limit
sign for the Ignore button are whimsical (the glyph accompanying the Abort
button is anybody’s guess). Still, the addition of graphic elements to otherwise
plebian control buttons is a nice touch.
Of course, the reason this topic is mentioned is because there are some
differences in handling required to create these custom buttons.
Chapter 14: Message Box Dialogs 265
First, instead of calling the MessageBox function, each of the dialog boxes
shown has its own exported procedure to supply the appropriate handling
and response, and each of these exported procedures requires initialization:
case IDM_INFO:
LpProc = MakeProcInstance( InfoProc, hInst )
DialogBox€ hinst >, UMSGDLGi1",ehwnd, 7tperoec_) Ne
And, for each of the remaining exported procedures, the source code is
essentially the same—no great suprises or differences from earlier examples.
Accidental Mysteries
The preceding changes are, for the most part, elementary and will compile and
execute as shown in Figure 14-2—with a slight exception.
The exception is that the program as described will execute correctly only
from within TCW (the integrated Windows compiler) or separately only while
TCW is loaded (active) under Windows. If, however, the MsgBox_2 program
is executed without TCW active, the pulldown menu will function but none
of the dialog message boxes will appear!
266 BORLAND C++ 3.0 PROGRAMMING, Second Edition
And this is because there are two important differences that must be taken
into account. The first of these is the appearance of an include reference in the
source code:
#Hinclude wcecc.h
I if MsgBox_1.C a
Lin wrt Window ss edi.tor Demo: /./
jf [PSS SSSe5so]=2SS ]—=]]S=—e=ose// //
Hinclude (windows.h)
#include "msgboxes.h"
case IDM_HAND:
MessageBox( hwnd, "Hand or Stop icon message"
UeaboOx With@Abort/Retry/Ignore buttons",
"MB_ICONHAND",
MB_ICONHAND | MB_ABORTRETRYIGNORE );
PSuwring® i Mw;
case IDM_QUESTION:
MessageBox hwnd, "Question icon message box
"with Yes/No/Cancel buttons",
"MB_ICONQUESTION",
MB_ICONQUESTION | MB_YESNOCANCEL );
eG Gul =
case IDM_QUIT:
if< MessageBox (chwnd, “AEx<it lapplica tion? 2,
a ESXolntae
Game ee GiOINIG UES ial O\Nin | MiBmeye ES NOG AN GENE
ZO MBSE BRBIU TON 2a)ae= =e Diese)
PostQuitMessage( O );
Peewring a DE
}
case WM_DESTROY:
PostQuitMessage( O );
rPeeUPMNe () 2A
b
return( DefWindowProc( hwnd, message, wParam, lParam ) );
}
#pragma argsused
if( ! hPreviInstance )
{
we.hInstance = hinstanice -
we.lpfnWndProc = WndProc;
Wice CDIGInS Exstinia = 0;
wce.cbWndExtra = OPS
we.lpszClassName = szAppName;
we.hIcon = LoadIcon( hInstance, szAppName );
we.lpszMenuName = C(LPSTR) szAppName;
268 BORLAND C++ 3.0 PROGRAMMING, Second Edition
NAME MSGBOX_1
DESCRIPTION "Windows Message Box Program"
SME VAP Iz WINDOWS
STUB GWEN SU Bisse xen:
CODE PRELOAD MOVEABLE DISCARDABLE
DATA PRELOAD MOVEABLE MULTIPLE
HEAPSIZE 1024
SuIVALGKGS MeZ4E 8192
EXPORTS WndProc
ifif MSGBOX_1.H ae
/ [SSS SS SS23 225252 SSS] SSS] Saensfesecee / //
#define IDM_ASTERISK OM
#define IDM_EXCLAMATION 102
#define IDM_HAND 103
#define IDM_QUESTION 104
#define IDM_QUIT 106
Chapter 14: Message Box Dialogs 269
// MSGBOX_2.C FF
laf Simple Windows Dialogs //
/ /(SaeS=SosSs SSS] Sea SS e5e5=// //
#Hinclude windows.h
H#Hinclude wec.h
#include "msgbox_2.h"
HANDLE hinst-;
Sswitch( msg )
{
case WM_INITDIALOG: returncetRues
case WM_COMMAND:
switch( wParam )
{
case IDABORT:
Cals eel DIR EssRivee
case IDCANCEL: EndDiavog@ hDl gp TRUE);
REecUrnniGel RUE): =
Glieufea
Ul Ute ee) Ciuliy mi Gaal
UiEa a=
D }
Pew IFALSIS We
}
switch( message )
{
case WM_COMMAND:
switch( wParam )
{
case IDM_INFO: Hi Woe
LpProc = MakeProcInstance( InfoProc, hInst Deg
DialogBox€ hInst, "MSGDLG_1", hwnd, LpProc ye
Chapter 14: Message Box Dialogs 271
EreerProcimetance vs \pProc):s
Pewwrem¢ Wie
case IDM_QUERY:
lLpProc = MakeProcInstance( QueryProc, hInst );
DralogBoxcrhinst, ="“MSGDLG 2"> hwndpelpProe )-
FreeProcInstance( lLpProc );
Re wuUhn Guar RUE =:
case IDM_ABORT:
UpProc ="MakeProciInstance( AbortProc, hinst );
DtalogBoxC hinst,. “MSGDLEGB4" shund> #UpProc )>
FreeProcInstance( lpProc );
ELC U yn) Gaal RUE a
case 1 DMS CHOn CE:
UpProce="MakeProcinstance C¥ChoiceProc, hins t ) ;
Diavogbexcehinst; «MN SGDEG Ss. hwnd, —LpPihoc
BmeeProcilns cance Gs UprirRoc sor
PEeUMPNe WRU 2)
Case LD MeQUuT Tt:
if€ MessageBox( hwnd, “Exit application?",
Sap, Cie Gea
MB_ICONQUESTION |
MB_YESNOCANCEL |
MB_DEFBUTTON2 ) == IDYES )
PostQuitMessage( O );
Perum i Xs
}
case WM_DESTROY:
PostQuitMessage( O );
return( O );
}
return( DefWindowProc( hwnd, message, wParam, lParam ) );
}
#pragma argsused
if( ! hPreviInstance )
{
wce.hInstance =—hhlinisit
am Ge:-
272 BORLAND C++ 3.0 PROGRAMMING, Second Edition
we.lpfnWndProc WndProc;
WickicbDG ESE xtra = Q;
we.cbWndExtra =O)
we.lpszClassName = szAppName;
WiGeanlcon = LoadIcon( hInstance, szAppName );
wce.lpszMenuName = CLPSTR) szAppName;
we.hCursor = LoadtursorCaNucL Zw I DCaARROW Dc;
we.hbrBackground = GetStockObject( WHITE_BRUSH );
we.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass( &we );
}
BWCCGetVersion();
hilnisit. t= hilinisita nice = // assign global instance handle
hwnd = CreateWindow( szAppName, "Custom Message Boxes",
WS_OVERLAPPEDWINDOW,
CWRUSE DER AU eee CWeeU oie DiEs AU) lene.
CW_USEDEFAULT, CW_USEDEFAULT,
NUE Ee NIU Ee hr lmsstainicie7-maN Ula as
ShowWindow( hwnd, nCmdShow );
UpdateWindow( hwnd );
while€ GetMessage( &msg, NULL, O, O ) )
x
TranslateMessage( &msg );
DispatchMessage( &msg );
}
return(€ msg.wParam );
}
7 6WINHELLCO
DEF module definiton files:
7a ENS FOS a SS SS Se eS Se eat
NAME MSGBOX_2
DES CREPT LON "Custom Message Dialog Demo"
EXE wee WINDOWS
STU "WINSTUB.EXE"
CODE PRELOAD MOVEABLE DISCARDABLE
DATA PRELOAD MOVEABLE MULTIPLE
HEAPSIZE 1024
STPAICINGS Flee 8192
EXPORTS WndProc
InfoProc
QueryProc
AbortProc
ChoiceProc
Chapter 14: Message Box Dialogs 273
f/f/SSSSsSeoSasseesSeoceEqscesesesees/ /
ifif msgbox_2.h //
f /SalSSaeSSSSsesceseLSSeooesoSasea///
‘con a oe a oe ~
DANG WIP Wile tase ou b= =ishs. 1Qnemee
7 9 7
er
fre
pes
eipetes
eee
-
ww . v4 am ob 8 _- Pro oe
an | ‘ - . ‘-
weed © i] -
aa» | . ‘ ri ee
7
— sug :
ou ° ‘ar Soe a
¥
ti :
= —
ee S
= . =
‘ -
= =
> ———
(Pigieire 5
MES
276 BORLAND C++ 3.0 PROGRAMMING, Second Edition
capabilities are present and can be used to handle all or part of the video
processing tasks.
For example, if the hardware where an application executes includes a
graphics coprocessor (as many of the newer video boards do), the GDI will
allow the display device to calculate points composing an ellipse, line, or
polygon. If not, then the GDI module executes the necessary calculations itself.
The overall intention of the GDI is to make applications independent of the
variety of peripheral devices. Thus, as new graphic devices appear, either
Windows or the hardware developers will supply appropriate drivers which
the GDI will use to adapt the application’s output for display appropriate to
the device.
To use another example, Windows can run on both color and monochrome
systems, but Windows applications are not required to make separate pro-
visions for each because Windows, on a monochrome device, will translate
colors into shades of gray.
If an application has been written to use colors supported by an 8514/A
but is executed on an EGA system with only 16 colors, the missing colors may
be simulated by dithering (mixing primary colors to provide shades not
directly available).
Of course, at the same time, your application may ask the system about the
color resolution of the attached output device and make optimum use of
whatever capacities are available. However, there are limitations because the
GDI does not have the capacity for all possible demands which might be
placed on a graphics environment. Items which are not supported, for
example, are three-dimensional hidden-line calculations, animation, or
object-rotation.
Still, while not omniscient, the GDI does provide a variety of graphic
resources, including both virtual and vectored coordinate systems and
adjustments for varying screen resolutions, combining text and graphics and
even scalar fonts while still maintaining a high degree of device independence.
dewice
chec
CHOSE 108)
At the heart of the GDI graphics system is the device context, commonly
abbreviated simply as DC. Under Windows, before anything can be drawn on
the screen, the application must begin by obtaining a handle to the device
context.
Unlike DOS, where applications own the entire video display, or printer or
other device, under Windows the output device is a shared resource, and the
device context is a method allowing peaceful coexistence between applica-
tions sharing an output display.
Asking Windows for a handle to a device context is, in effect, the equivalent
of asking permission to use the output device. After obtaining permission, the
handle is included as a parameter to the GDI output functions, telling Win-
dows which output device is needed.
The device context handle (4DC) is more than a single parameter. It is
actually a pointer that not only indicates a device, but also an information
structure detailing device (graphic) settings. For an example, a call to the
function TextOut does not include font and color information because these
have already been set for the device context (or are simply using default
settings). Each application has its own set of colors and fonts for display, but
does not need to respecify these as parameters for each output operation.
Instead, only the coordinates (position) and the text information are passed
as parameters. To change context attributes, a separate function call is used
and affects subsequent TextOut operations.
Di.
278 BORLAND C++ 3.0 PROGRAMMING, Second Edition
Thus the device context is both a handle to the actual display and a body of
information about how the display should be created, including font
selections, character spacing, line width, and foreground and background
colors.
There are two other methods of obtaining a handle to the device context:
the CreateDC and CreateIC functions. The first function, CreateDC, is com-
monly used to obtain a device context allowing operations outside of an
application’s client window area as, for example, for a screen capture utility.
The handle is obtained as:
The video display is not the only device which may be available for output.
It is probably safe to assume that at least one printer is also attached. Actually,
it really doesn’t matter if the printer or any other device is physically installed,
only if its driver has been installed in Windows.
The file Win.INI in the Windows directory contains a variety of initializa-
tion and setup information about both active and inactive programs. This is
an ASCII file and can be examined by most editors. It is created and modified
by Windows when new applications are installed, or when the configuration
is modified through the various control panel features.
To open an information context for a device other than the CRT, the first
step is to retrieve the profile string describing the device:
This will open the Win.INI file, searching first for the string "WINDOWS"
(case is ignored) in,the brackets which identify an application name, then,
280 BORLAND C++ 3.0 PROGRAMMING, Second Edition
second, for a "DEVICE" (key name) entry. The third string parameter, empty
in the example, can provide a default string for return. An excerpt from
Win.INI shows a typical entry:
Cwindows]
After this item is located, the latter portion of the string—PCL / HP Laser-
Jet,HPPCL,LPT1:—is returned in the szPrn variable, identifying the type of
printer or other output device installed.
After using the strtok function to disassemble this string, the driver name
(HPPCL), device name (PCL / HP LaserJet) and output port (LPT1:) are avail-
able to create an information context for this device. Once an information
context is available, information about the device can be requested.
The GetDeviceCaps function retrieves one item from the information con-
text as a word value. For example, the driver version number could be
retrieved as:
j = GetDeviceCaps( hDcInfo, DRIVERVERSION );
The value returned inj could be further disassembled as HIBYTE(j ) for the
major version number and LOBYTE(j ) for the minor version. In other instan-
ces, the value returned is itself the information or, in the cases of TEXTCAPS,
CURVECAPS, LINECAPS and POLYCAPS, can be treated as an array of flag
(bit) values.
The Devices program breaks these value parameters into several arbitrary
groupings for display convenience, with the basic display parameters for a
VGA video display shown in Figure 15-1, while the same parameters for a
LaserJet are shown in Figure 15-2.
As might be expected from Figures 15-1 and 15-2, the resolutions fora VGA
video and a laserjet are quite different. The resolutions for different video
cards also vary considerably, as shown in Table 15-2, which compares four
“standard” video resolutions.
284 BORLAND C++ 3.0 PROGRAMMING, Second Edition
Device
Device Capabilities
HORZSIZE and VERTSIZE are the nominal width and height of the display
area in millimeters. These, of course, may or may not correspond to the actual
display size, but are based on standard display sizes.
HORZRES and VERTRES return the actual pixel width and height of the
display area.
ASPECTX, ASPECTY and ASPECTXY are the calculated relative width,
height, and diagonal size of the pixels. ASPECTXY is calculated as
\(ASPECTX? + ASPECTY”) rounded (down) to the nearest integer.
Last, LOGPIXELSX and LOGPIXELSY are the number of pixels per "logical"
inch. A logical inch is not a physical inch, and may vary from 1.7 "real" inches
(CGA) to near unity (8514/A). Logical inches are a convenience used princip-
ally to display rulers with, for example, fixed-width fonts in word-processors
such as WORD or WRITE. Figure 15-3 shows an example of a ruler created
using "logical" inches.
In general, however, the programmer (and the application) do not need to
be particularly concerned with device resolutions and other device-specific
capabilities, because these are handled by Windows, not by the application.
There are other aspects of Windows graphics programming which are directly
relevant to the applications. One of these is mapping modes.
286 BORLAND C++ 3.0 PROGRAMMING, Second Edition
3
fe
Mapping Modes
With a few exceptions, under DOS all graphics operations have used a single
mapping mode in which the logical unit was the screen pixel, and the screen
origin—the 0,0—point was located at the upper-left corner. Of course, both
text and graphic windows operations could relocate the origin point making
operations window relative but, in essence, only one mapping mode was
employed.
As an alternative, Windows supports eight separate ‘““mapping modes’.
Each provides different features, different scalar (logical) units and, except for
the MM_TEXT mode which is the default and corresponds to the DOS mapping
mode, each uses the lower-left corner of the screen as the origin or has a
variable origin point. Mapping modes and origin points are illustrated by the
MODES.C program at the end of this chapter.
The eight mapping modes are defined, briefly, in Table 15-3.
Chapter 15: Introducing Graphics Device Interface 287
The MM_TEXT mode corresponds to the DOS mapping mode and allows
applications to work in device pixels. However, as previously discussed, pixel
size varies from device to device, as does the size (in pixels, vertically and
horizontally). In MM_TEXT mode, the default origin lies at the upper-left
corner of the screen with x and y coordinates increasing right and down.
The MM_HIENGLISH, MM_HIMETRIC, MM_LOENGLISH, MM_LOMETRIC,
and MM_TWIPS modes are useful for applications that need to draw in
physically meaningful units such as inches or millimeters. At the same time,
each of these modes uses Cartesian coordinates, coordinates whose values
increase above and to the right of the origin point, with the default origin at
the lower-left corner of the client window. The MM _HIMETRIC and
MM_LOMETRIC mapping modes use units of 0.01 and 0.1 millimeters, pro-
viding high and low resolutions respectively. The MM_HIENGLISH and
MM_LOENGLISH mapping modes use units of 0.001 and 0.01 inches, also
providing high and low resolutions. The MM_TWIPS mapping mode uses
logical units that are '/i440ths of an inch, which is also '/2o0th of a printer’s
point (72 points = 1 inch). Thus, in MM_TWIPS mode, a 10-point typeface can
be drawn that is 200 logical units in size, regardless of whether the actual
display device is capable of this resolution.
The final two modes, MM_ISOTROPIC and MM_ANISOTROPIC, provide
variable logical units as well as variable origin points. Thus, in either of these
modes the 0,0 origin point could be located at the center of the window or at
any other point desired.
288 BORLAND C++ 3.0 PROGRAMMING, Second Edition
The difference between these two modes is that the MM_ISOTROPIC mode
ensures a one-to-one aspect ratio by keeping the x and y logical units equal in
size, useful when preserving the exact shape of an image is important. On the
other hand, the MM_ANISOTROPIC mode allows the x and y logical units to
be adjusted independently.
The nMapMode variable, may be one of the eight constants identifying the
several modes or may be a byte variable to receive the mode value. The
SetMapMode function returns, if desired, to the previous mapping mode. The
default mapping mode, unless another setting has been made, is always
MM_TEXT, just like in DOS. Suppose that a different mode such as
MM_LOMETRIC, has been selected.
First, the origin point is now at the lower-left corner of the client window,
and positive y-axis values increase upwards. X-axis values, of course, behave
as before, increasing to the right. Instead of pixel coordinates, coordinates are
now specified in 0.1 mm increments which, on a VGA monitor, would make
the vertical screen resolution 2080 logical units and 1560 logical units
horizontally.
Remember, however, that the display capability of the hardware is still only
400 pixels vertical by 640 pixels horizontal, this means that vertically, 5.2
logical units are mapped to one pixel, and horizontally, 2.44 logical units are
mapped per pixel. This resolution question—mapping logical to physical
units—is not the application’s concern. Instead, the GDI uses the mapping
mode to convert logical coordinates into the appropriate device coordinates.
What is the application’s concern (and, of course, the programmer’s too) is
specifying positions in the appropriate units for the mode in use, and
appropriate for the origin used.
Chapter 15: Introducing Graphics Device Interface 289
The SetWindowOrg function sets the window origin of the specified device
context and is declared as:
Together, the device-context viewport and window origin define how the
GDI maps points in the logical coordinate system to points in the physical
coordinate system of the actual device, i.e., how GDI converts logical
coordinates into device coordinates. In both cases, the xcoord and ycoord
parameters are signed integer values, expressed in device units, and must be
within the range of the device coordinate system.
Also, SetViewportOrg returns a DWORD value specifying the previous
origin of the viewport in device coordinates with ycoord in the high-order
word, and xcoord in the low-order word. In like fashion, SetWindowOrg
returns a DWORD value with the previous window origin in device
coordinates, or the Get ViewportOrg and Get WindowOrg functions can be called
to return the current values.
Normally, only one of these two functions would be used. For example:
how are these modes used? The answer, of course, is that Windows maps
points calculated at high resolutions to the relatively low resolutions of the
actual display device(s).
However, the real point of these physical resolution modes is that shapes
can be calculated which are constant in aspect, i.e., points on a circle
calculated in a metric (or English) mode and then mapped to the video display
remain circular rather than becoming an ellipse. For example, a circle with a
radius of 100 pixels would, on an EGA display, be (approximately) 175 mm
high and 75 mm wide—not very round at all. In other systems, such as the
Borland Graphic Interface (BGI), this discrepancy was corrected by calculating
the screen aspect ratio for the display device and applying the aspect ratio to
the point calculations. For complex shapes, however, applying this type of
correction in calculating the displayed points does make the calculations
somewhat more complicated.
In Windows, by performing calculations in an essentially isotropic virtual
space, no aspect corrections are required. Corrections for translation from the
isotropic virtual space to the anisotropic screen space are automatically handled
by Windows.
MODES.C
The Modes.C program illustrated in Figure 15-5 shows window sizes using
the several mapping modes. The various modes are menu-selected while a
second pop-up menu offers a choice of origin as upper-left, centered, or
lower-right.
Text mode uses a default origin as upper-left, while the isotropic and
anisotropic modes default as centered, and the remaining modes default to
lower-right origin. However, in any mode any of the three origins can be
selected from the menu and the coordinates shown will change accordingly.
The aspect dialog box is valid only with the isotropic or anisotropic modes,
and is grayed out (not selectable) when any other modes are active. In either
the isotropic or anisotropic modes, the window and viewport x and y aspects
can be altered through the dialog box. If either the x or y viewport aspects are
zero, the window settings will be used for the viewport values.
The complete source code for Modes.C appears with script files for the
menu and dialog box resources. The dialog box for this demo application
appears in Figure 15-5 at the lower right. The menu for MODES is not shown,
but consists of three headers with two pop-up submenus. The third menu
Chapter 15: Introducing Graphics Device Interface 293
header, Aspect, calls the aspect dialog box, but is initially grayed out and is
only validated (enabled) when the Isotropic or Anisotropic modes are
selected.
UpRight: (1637,582)
__DnRight: (1937,6)|
fee
— )
> (-159,-89) UpRight: (166,-89)]||
y Viewport Window 4
Center: (8,8) I) Axis Extent |1990 1000
) YAxis Extent |ogo0 2000 ]
a Sabet LE bs
The dialog and menu resources for this application appear as script files,
but can be easily created directly using the Resource Workshop. An icon for
the Modes application appears in Figure 15-5 in the dialog box but this, of
course, is an optional element.
Summary
As an introduction to the Graphics Device Interface (GDI), demo applications
show how device (hardware) information can be derived from the Windows
device drivers, and how different mapping modes provide varying
environments and resolutions for graphic calculation.
As a bonus, the Modes program also demonstrates two menu handling
procedures as menu items are checked and unchecked according to selection.
The Aspects menu item is grayed out when not applicable, or enabled when
294 BORLAND C++ 3.0 PROGRAMMING, Second Edition
relevant. In general, mapping modes will not be selected by the user, but will
be set by the application according to the programmer’s intentions. In like
fashion, device information is not normally requested by an application, but
is used by Windows in the normal course of transfering information to the
output device(s). However, an understanding of how these mechanisms oper-
ate and what options are available can be useful in designing your
applications.
Figure 15-6 shows the menu structure for the Devices.C demo application.
DABC\DEVICES.RES DEVICE
File miEdhwe aer
Item Text
&Crt IDM_CRT
&Printer IDM_PRN
&Capabilities
&Basic Information IDM_BASIC
&Other Information IDM_OTHER
&Curves IDM_CURVE
&Lines IDM_LINE
&Polygon IDM_POLY
&Text IDM_TEXT
[L[RRS SS SSS aS I ea ia ei /) 7
Ld, Devices. ¢ fof
// C++ Windows Device Capacity //
//HSsSsSsSSSS===SSSoesss=sssss=====//
Hinclude <windows.h>
#include <string.h>
Chapter15: Introducing Graphics Device Interface 295
Ainctude™ “devices.hi
typedef struct
{ int nMask;
char *szMask;
char *szDscp; & BeiySse
VOM Cees
0) thei Gu HiDiGuahid Go, eet) DiGush Dic-LinitiOmm
{
Siti taliCas BSlilpomminarsac
ei el
{
RC_BANDING, "RC_BANDING",
"Requires banding support",
RC= Biles ee, ARG IRE YY "bitmap transfer",
RC_BITMAP64, LRGs
Lot MAR 4 4
"bitmaps larger than 64K",
RCSD SB anMAP, ANE [ie _ [BIL WP -
"SetDIBits and GetDIBits",
RC_DIBTODEV, wRiCeeD BO DIEVEu marcel DEL Bite siolDievniciems:
RE _ TPL OOD Fi klk, *REAWFLOOM FILLY,» “Tlhoodri kl",
RGaG Die OMOUM IE Uiip week. GaG Dyle2 Om OU PiU
"Windows 2.0 features",
Rica PAS ENmines. RCE ASE Tien eee bd) erttie— Dialsie dar cielvnncena:
RC_SCALING, ARG _ SCALING". sealing -
RC_BIGFONT, “RESBIGFONT 7) @tonts Carger= thaneoskue-
RiGee SileRieae Gini) Males HRC _ STRE WC heey Olt mesticin Bint
RiCmeS REG HDB: WiRiCemoM REM eGiHIDEl Bima *SUPCUCID UES”
3;
Static char *szTechfd =
Cc “Vector plotter ¢DT2PrOTRER De,
"Raster display (DF RASDIESPEAY):;
“Raster printer CDITARASPRINTER)=
"Raster camera CDT_RASCAMERA)",
"Character stream CD TSCHARSTREAMD{>
“Metafile CDT METAFILE)“,
“Display “file “<DTDILSPEITEE) Bae
cha szBud
Feo 0n-
Chapter 15: Introducing Graphics Device Interface oe
ViOMCee enitStalpe
xX,GlGant DiGuninid|cyp ae HIDiGah DiGi initOm
‘
SitiantCm lil oietiewx. Gisele
{ee Cuan) eae HAN RUA Gale) Ry, emma Geen) Pee CH AVRVAUC HINES Riva
UGirainmac vein OULc pUlitum ppMmelcarsalO mma,
eGanOl Peron RiOiKGEs, MUG OHS TROLS a.
"Stroke output precision",
(leCamn GPa OM OlKSEs, MPG CIP. SIRO 5
"SePoke Clas Pree Sion” -
Conc Rams Or al Geme GiRae Olea
"90-degree char rotation",
TCACRAANYS UTC HORSAN
YC
WAY ech ak actein ah O taltalonin,
Pe Sle _ Nf INDIE MIE SF WINDER
"independent! x=/ tyascaling’,
Ca SAm DOUB EE, ia GanSyAweD OUIBIE Ete,
"Doubled character scaling",
Gass) Agel Nite GE Ry HE SIN IN Wea
298 BORLAND C++ 3.0 PROGRAMMING, Second Edition
VON lesteSit
GCUlivier Gas HID Gahiclice aah DiGash DiC ilanath Ome
{
static BITS curves] =
{ Cem CasRiG lcs, oy Ca TaR Gi EtSiaur, Cun Glie'S aes
(Oe _APIME - dea Cambs Ee "pie wedges",
CCmiGHORD:, eoGwC HOR Die. LCINOGime alinC Siew.
SG TIL IL UP SIES,, ACG AS ILIL MIP SiS VON SBOs” -
CCmW:E DIE SC GRWTDiEss, "wide borders",
CCS E Dy MCE. SP yible™ "styled borders",
CCRW EDIE S TYEE Dr, MCE MAO Say Wbie pb -
"wide and styled borders",
CCS ENTER LORS: aC Cel Nie ReOIR Sie Enstes ha Oln Sie HE
SiGaltnCmmcinidines 2 BU timoWnp
int ae
ee Xa) OlUnta Gus nicl CeemaGoc China -ameCiy Chine weSiZ GBUitate,
SID Gali eGnta GueSi2aB Uitte einem Cra} p alo aUaiatalhe:cmmn Gla lINIEIGAVP:S®) am) mee
for(€ i=0; i<sizeof(Clines)/sizeof(lines[L0]); i++ )
ike xs OU Gaaniclicey mc OC hie macy Chis tanto mS 216 Ultate,
SorPimney¢ SsaBMri, MIEAGA SufppOrUS Z=22s | 48%
GetDeviceCaps( hDcInfo, LINECAPS ) &
Uetiiners! Dede MiaSiKes cea) Xa) Ses a ater
Simiers! ane lensiz DiSic py laninesi ne llersrziM ars Kamas
}
switch( msg )
a
case WM_CREATE:
hdc = GetDCC hwnd );
SelectObject(¢ hdc,
GetStockObDjectC SYSTEM FIXED FONT >) ye
GetTextMetrics(€ hdc, Stm 7
cxChr = tm.tmAveCharWidth;
cyChr = tm.tmHeight + tm.tmExternalLeading;
ReleaseDC( hwnd, hdc );
PeTwWRinC
Wd) 5
case WM_COMMAND:
hMenu = GetMenu( hwnd );
switch(€ wParam )
if
casiee LD MaCRih-= case IDM_PRN:
CheckMenuItem( hMenu, nCurDev, MF_UNCHECKED );
nCurDev = wParam;
CheckMenuIltem( hMenu, nCurDev, MF_CHECKED );
InvalidateRect( hwnd, NULL, TRUE );
Recurnmco
case IDM_BASIC: case) DME ORmHER =
ease IDM ICURVE: case IDM_LINE:
case LDIMEPOIEY: Cralsicue esDiMmeilneyxene=
CheckMenultem(€ hMenu, nCurInfo,
MiRPSUN CHECKED:
nCurInfo = wParam;
Chapter 15: Introducing Graphics Device Interface 301
case WM_DEVMODECHANGE:
InvalidateRect( hwnd, NULL, TRUE );
Recur ni GOD r
case WM_PAINT:
hdc = BeginPaint( hwnd, &ps );
Ssiellec
tib ject. Gah dic,
GetStockObject( SYSTEM_FIXED_FONT ) );
switch( nCurDev )
{
case IDM_CRT:
ndiclinmiom="CreatelGG@s 2 pDils PAY
Mele INWIEIL A INWIBIL 2) 5
SetWindowText( hwnd, "Video Display" );
break;
case IDM_PRN:
Sethrotmitestrang WINDOWS?) i bewilce | ost.
SPP. Sw4@@Ore SrlPPm 2) 5
iv€ © SznbevVIdeGe |S SvPvols( SPP. BnY 2 2 Ee
GEsiZ DinaiVielpee =e Sic tpolka NIUE IRS Py ONDE Si6
( SZOuu PUL =& SePuOk@ NULLA @," 2 » 2
hdcInfo = CreateIC( szDriver, szDevice,
S20 miePuie A IKMWIEIL yy
else hdcInfo = NULL;
SetWindowText(€ hwnd, szPrn );
}
laf Quah Gicelth f Ol)
{
SwrtcncGenGurinto..)
{
Grals\cue iD Mm BAIS
iG. tm lars
t Bars ic @ whidicy saniGicinn fom)
break;
vease LDM OTHERS ListOtherG hdc, hdcinto 2>
break;
Gasiew LD MaeEX an: ListText(¢ hidicy-mahicdic len Omer
break;
Case LOM ICURVEAsGistCurve® hdc; hdcinfo );
break;
case IDM_LINE: rSstlaninieS| Gal dice aaInd C-Lin tf On):
break;
case IDM_POLY: PrssclolleyaC RiGicy maaicelinitOme >
302 BORLAND C++ 3.0 PROGRAMMING, Second Edition
break;
}
DeletedDcCG@ hdclintor»
}
EndPaint( hwnd, &ps );
return(Q);
case WM_DESTROY:
PostQuitMessage(Q0);
return(0);
}
return( DefWindowProc( hwnd, msg, wParam, lParam ) );
}
#pragma argsused
if( ! hPreviInstance )
{
we.hInstance = hInstance;
we.lpfnWndProc = WndProc;
we.cbClsExtra = 0;
we.cbWndExtra = 0;
we.lpszClassName = szAppName;
wce.hIcon = LoadIcon( hInstance, szAppName );
wce.lpszMenuName = (CLPSTR) szAppName;
we.hCursor = LoadCursor( NULL, IDC_ARROW );
we.hbrBackground = GetStockObject( WHITE_BRUSH );
wce.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass( &wc );
}
hwnd = CreateWindow( szAppName,
"Hardware Device Capabilites",
WS_OVERLAPPEDWINDOW,
CW2USEDEFAULER ICWeUSEDEEAULT-,
CW_USEDEFAULT, CW_USEDEFAULT,
NUEL? ANUEL? *hinstances aNULIA =)
ShowWindow ( hwnd, nCmdShow );
UpdateWindow( hwnd );
while€ GetMessage( &msg, NULL, 0, O ) )
Chapter 15: Introducing Graphics Device Interface 303
{
TranslateMessage( &msg );
DispatchMessage( &msg );
5
return(€ msg.wParam );
}
: DIEVASGIERSre DIESE ;
NAME DIENVALICIESS
j/ fPoesesessseesa=seseneee//
ia Modes.C Lf
// C++ Windows Modes //
/ PSS saopaSeeSss=Ssessee/ /
Hinclude <windows.h>
#include <string.h>
#Hinclude "modes.h"
#pragma argsused
SiWitcrc
ni Gaamis: Guan,
fo
case WM_INITDIALOG:
sprintf ( szAspect, “Ad, XViewAspect 2;
SendDlgItemMessage( hdlg, IDD_XVIEWASPECT,
EMU Pets WO, GIL dF,
SetDlgiltemText ( hdlg> ILDD XVIEWASPECT, “szAspect Dy
Spraintiic szAspect,. "Ad°4, wWVTewAspect. |
SendDlgIltemMessage( hdlg, IDD_YVIEWASPECT,
EM hirer, IO, OL X-
SetDlgItemText( hdlg, IDD_YVIEWASPECT, szAspect );
Sip imitans GueS Z/AS)Delc
tema mu mm XAW ATS Die (Catan ir=
SendDlgItemMessage( hdlg, IDD_XWINASPECT,
EM LIMITTEXT,210,.0— );
SetDlgItemText( hdlg, IDD_XWINASPECT, szAspect );
SIDinal
Cat eG SitZAS DIGG tema 4 Cua ny, WilenALS
Pier Cita >
SendDlgItemMessage( hdlg, IDD_YWINASPECT,
EM FCIMITTEXT, 10.0 )-
Sere Diligiiatie
milne xat Gee hic Gy seis DiDin YaWr luNIA}S PIE Cale-amSt zrANS Dielctam r
PAU TPAC TWIRWHS oe
case WM_COMMAND:
switch( wParam )
{
case IDOK:
GetDigd temlext.C
hdlg7 1DDUXVIEWASPECT
SIZ/AS pie CLuy mek O mE
XViewAspect = atoi(€ szAspect );
GetDlgItemText(€ hdlg, IDD_YVIEWASPECT,
SiZIANS|DIcICit ae Ome ee
YViewAspect = atoi(€ szAspect );
GetDlgItemText( hdlg, IDD_XWINASPECT,
SIZIANSDIC IG te, mee
XWinAspect = atoi(€ szAspect );
GetDlgItemText( hdlg, IDD_YWINASPECT,
SIZA\S/PieCity, wmlO)
YWinAspect = atoi(€ szAspect );
EndiDiatog<( hdiltq7a TRUE >»:
break;
Chapter 15: Introducing Graphics Device Interface 305
case ILDCANGEL:
EndDiatlogt-hdtda, FALSE. 3+
break;
default: POW NC TF/NLSIE 6
}
détaute: returnG FALSE, )':
SwacchnGaumsig: )
{
case WM_CREATE:
SHePumver. Seaway. Ws : icSus,
szCurModeLnCurMode-IDM_TEXT]1,
szcourorglLncurdOrg—T DM UUPLEFAD ps
SetWindowText( hwnd, szBuff );
RecunniGopE
case WM_COMMAND:
hMenu = GetMenu( hwnd );
CheckMenuItem( hMenu, nCurMode, MF_UNCHECKED );
CheckMenulIltem( hMenu, nCurOrg, MF_UNCHECKED );
switch( wParam )
306 BORLAND C++ 3.0 PROGRAMMING, Second Edition
case IDM_ASPECT:
LpProc = MakeProcInstance( AspectProc,
helinisscae ss
DialogBox( hinst, "ASPECT, hwnd, “—pProce);
FreeProciInstance( lLpProc );
break;
case IDM_TEXT:
EnableMenuIltem( hMenu, IDM_ASPECT,
MF_GRAYED );
DrawMenuBar( hwnd );
nCurMode = wParam;
nCurOrg Sy WIRE Ws
break;
case IDM_TWIPS:
caisie SDM EO MEMRANG case IDM_HIMETRIC:
case IDMEEOENGEISH: case IDM_HIENGLISH:
EnableMenuIltem( hMenu, IDM_ASPECT,
MF_GRAYED );
DrawMenuBar( hwnd );
nCurMode = wParam;
nCurOrg = IDM_DNLEFT;
break;
case WM_DEVMODECHANGE:
InvalidateRect( hwnd, NULL, TRUE );
return(Q);
case WM_PAINT:
hdc = BeginPaint( hwnd, &ps );
SelectObject( hdc,
SéetStockObjectCASYSTEMZELXEDEFONT 2 Oo
Getihex tMetinics © ihidic,s& tim»):
CoG Nina tm.tmAveCharWidth;
cyChr = tm.tmHeight + tm.tmExternalLeading;
OrgDC = SaveDC( hdc );
case WM_DESTROY:
PostQuitMessage(0);
return(Q);
}
return(€ DefWindowProc( hwnd, msg, wParam, lParam ) );
}
#pragma argsused
Chapter 15: Introducing Graphics Device Interface 309
XViewAspect = YViewAspect = 0;
XWinAspect = 1000;
YWinAspect = 2000;
if€ ! hPrevInstance )
{
wce.hInstance = hInstance;
we.lpfnWndProc = WndProc;
we.cbClsExtra = 0;
wce.cbWndExtra Says
we.lpszClassName = szAppName;
we.hIcon = LoadIcon( hInstance, szAppName );
wce.lpszMenuName = CLPSTR) szAppName;
we.hCursor = (Loacleurse@r@ WEIL, ItIDte JKR OM) D6
we.hbrBackground = GetStockObject( WHITE_BRUSH );
wce.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass( &we );
i
hInst = hInstance;
hwnd = CreateWindow( szAppName, "Mapping Modes",
WS_OVERLAPPEDWINDOW,
CWRUSEDEBAUET GCWlUSEDERAU Ei
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL F-
ShowWindow ( hwnd, nCmdShow );
UpdateWindow( hwnd );
while(€ GetMessage( &msg, NULL, 0, O ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
} ;
return( msg.wParam );
}
NAME MODES
310 BORLAND C++ 3.0 PROGRAMMING, Second Edition
}/PSeassesesescecsesesesase/ //
iif D:\BC\MODES.H Ifi
jf SeSSeoeSSaeseseeesse///
MaOxX dso co 49 ae
CONTROL "Window" OD ac StATIC!| WS CHIL Desi WS] VES TBILE
HO XT LUA rar a On eli
CONTROE 2 S07 “EDIT, WS CHILD | “WS2VISTBDEMIEWSeEORDER
LeWSHAGS MOP 55> (56,1549. cle
CONTROL = 5 025° “EDIT? WS CHILD | WS VISIBLE i) WS BORDER
EWo@LABS NOP 1 555,646, 49,5 12
CONTROL" 50555 sEDI T'S WS CHILD | WS VISTBUE 1 WS2BORDER
[EWS AB SOP el OA ESO ao oA
CONTROL = 504 VEDI, WS_CHIED. | eWS_VESTBLE! WS BORDER
| Woe tABSTOP,. 1047 "48> 49.59 12
CONTROL "Ok" Jd, MEU TONY. MS chek) |) WS. Wars itisilis
WS PASS TORE In62). "6% 254.5. 0474
GOINTRIO) Eee Gainic cllan ce mS UnTelnO)N
(neem WSC Hel Dann |eeWESie Vt Sola 3)[et
USMUAB
Sa OPvel 1G 6r2S 1 161
END
Pf SssescesesoesSeassenscesess// //
if MODES Menu Script Lo
/ /SSoS3Seoqoes]
Sess Seeea// /
plier” Kw \=aauees a - +e
rw @Fsi0,4W: 7TH sige a
ay
7 pairs sp Ee.
Ciao pSehOG ou i GuEe
0 emus
Pas yare wr FERN OG RULE Ue ts
ey eerie Law) earH,. ze.a8 Ee, ey
Sf. we (ee te
bt Pemugagu_tv'e TUSiStN. 80) ita eu wit ~ Pe
£f, G20 68 (30
ert’ ia H Lith, 2 Me res andl
sr ceri ae! sasaa ae
somneavley are AyeeleaOTTuA” VP “4
‘hoa pete EPSSE! Tey Seannem ih ;
ee vantace OW: PTE" 6 “sagnes> JORteE
4Piucre oe gett <TOSPBAT SV fr
= a
sy a Op
’
Powe Tt \ cerece ined ete? geet are: {
ae
:
— ‘otyhiene >
=e f + Bé Ak veBE
e oo
oe 4 bl Bek guee ot a
ee -“Setes2e* parties
“jz “ted Vevoeia> @p9t) .
y= aw 7
Mr Jt Vdtega kts” @ a din
7 a)
| 4EoKi Py
) wi Se ieo vier
periin
a
aa
és. La@ ite.)
Chapter 16
Colors In Windows
Since most devices use either multiple color planes or multiple color bits,
in general a device will return either nColorPlanes or nBitsPerPixel as one.
Table 16-1 shows the results reported for four devices.
313
314. BORLAND C++ 3.0 PROGRAMMING, Second Edition
yielding a result of 16 for the VGA and two for the laser jet. However, for the
8514A monitor and the plotter, as you can readily see, the number of colors
reported does not match the calculated result. So, what happened?
The discrepancy is not in the color planes or bits per pixel reported, both of
which are accurate, but in the way colors are used. For instance, for the plotter
only two colors can be drawn, the pen color or the background color.
However, the device driver has reported that the plotter in question has a
carousel of eight pens—which,when the paper color is included, makes the
actual number of colors nine. For the 8514A monitor, the 20 colors reported
are those reserved for use by Windows, even though the remaining 236 colors
can still be assigned and used by Windows programs.
Also, note that the colors provided by Windows do not correspond to the
standard DOS color palettes. This means, in part, that images transported
from Windows to DOS may appear very strange indeed.
Creating Colors
Individual colors in Windows are defined by a long integer (unsigned) in
which the three bytes specify intensities of the primary colors: red, green and
blue.
Under DOS, a similar arrangement is used, except that the primary colors
are specified as flag bits, rather than ranges, and a fourth bit, intensity, is used
to create both high and low versions of the eight basic colors.
In a CGA system only eight colors are supported, corresponding to the
colors shown in Table 16-2.
In an EGA system, a fourth bit is added for intensity, changing the eight
principal colors into a 16-color palette with high and low intensity (light and
dark) versions of each.
In VGA systems, six flag bits are used, providing one intensity bit for each
primary color and supporting a basic palette of sixteen colors. VGA systems,
however, are capable of far more than sixteen colors; the Windows device
drivers recognize this capacity and use an actual color palette quite different
from the standard (DOS) VGA palette.
The actual values for the standard palette(s) under Windows—or names for
the corresponding colors—are not important, because any application can
redefine colors as desired. Thus, under Windows, several hundred yellows
316 BORLAND C++ 3.0 PROGRAMMING, Second Edition
can be defined by varying the relative values of red, green, and blue, while
keeping the red and green in the same general range, and keeping the blue
value small.
Dithered Colors
In theory, Windows supports 256° hues of color, over 16 million shades.
However, since few monitors are actually capable of quite such fine color
resolution, Windows adapts colors to the device capabilities by displaying
selected colors as ‘‘dithered” colors, in which an 8x8 grid of ‘‘pure’’ colors
simulate the hue desired.
Figure 16-1 shows four dithered colors, three of which are shades of pure
red, pure blue, and pure green. Ona VGA system, the shading is accomplished
by mixing red and dark red, blue and dark blue, and green and dark green.
The numerical value shown for each bitmap is the 0..255 value identifying the
specific color.
The fourth pixel map is the color generated when the preceding three color
values are combined by using the RGB macro to produce a single screen color,
a shade of "cloud blue". The 8x8 pixel map shown consists of 23 white pixels
(=36%), 19 light cyan pixels (=30%), 18 light blue pixels (~28%) and four dark
gray pixels (6%). This is only one of literally hundreds of shades of blue.
Darker shades of the same color tone could be generated as RGB(75,150,225),
RGB(50,125,200), RGB(25,100,175), etc., with 72 other shades falling between
the lightest and darkest examples cited.
Chapter 16: Colors in Windows 317
The Colors.C program demonstrates how values for red, green, and blue are
combined to yield a Windows color. The four pixel grids shown in Figure 16-1
were derived directly from the default settings in Color.C as shown ona VGA
monitor.
Colors.C
The Colors.C program provides three scrollbars, as shown in Figure 16-2, to
adjust the red, green, and blue color values that define the background color
shown in the lower portion of the client window. Each of these scrollbars can
be adjusted for any value from 0 to 255. While the scrollbars themselves show
individual red, green, or blue densities, the bottom of the client window
background shows the color generated by the combined RGB values.
In actual applications when a feature is desired for permit the user to adjust
colors interactively in a fashion similiar to Color.C demonstration, a more
reasonable approach would be to use a pop-up dialog window rather than
employing the main client window for the controls and display. And, using
318 BORLAND C++ 3.0 PROGRAMMING, Second Edition
this suggested approach, the pop-up could be created as a dialog box via the
Resource Workshop instead of directly invoking scrollbar creation functions
as shown in Colors.C.
Still, while more convenient, using WRT to create a dialog box does not
absolve the application from setting scrollbar ranges and handling scrollbar
events as well as setting the color values both for the individual and resultant
colors. Most of the handling demonstrated, however, will be applicable to
most scrollbar controls.
Custom Brushes
Adjusting the background color to show color changes is relatively simple,and
is accomplished by creating a new brush with the desired color. But before
creating a new brush object, the existing brush object should be deleted, as
shown here:
First, GetClass Word is called to return a handle to the existing brush, which
is then deleted. But before SetClass Word creates the new brush object, the RGB
macro is required to convert the three red, green, and blue values into a single
unsigned long integer.
For the scrollbar colors—to make the scrollbars match their individual color
settings—a similar operation is used, except that only one of the three color
values are set:
case WM_CTLCOLOR:
if€ HIWORD(C LParam ) == CTLCOLOR_SCROLLBAR )
{
i = GetWindowWord( LOWORD( LParam ), GWW_ID );
DeleteObject( hBrushLlil] );
SiWolcic
Ni Guim)
{
case O: RGBcolor RGBCCVall01707 00>" break=
case 1: RGBcolor RGB.CO7CVail Ed ds OD break
case 2: RGBcolor RGBCO,0 7CVall2d) > break:
Chapter 16: Colors in Windows 319
UnrealizeObject
The UnrealizeObject function is called with a handle to an object. If the handle
specifies a brush object, the GDI is directed to reset the origin of the brush
when next selected. However, a brush specified by the hObject parameter must
not be the currently selected brush of any display context.
There is a conflict in documentation concerning this function, with some
documentation cautioning that when processing the WM_CTLCOLOR mes-
sage, the application must align the origin of the intended brush with the
320 BORLAND C++ 3.0 PROGRAMMING, Second Edition
window coordinates by first calling the UnrealizeObject function for the brush,
and then setting the brush origin to the upper-left corner of the window.
UnrealizeObject must also be called whenever a new brush origin is set (by
means of the SetBrushOrg function). However, the default brush origin is
always the upper-left corner of the window (0,0), and does not normally
require resetting.
Alternately, UnrealizeObject is also used with logical palettes, instructing
the GDI to remap the logical palette to the system palette. A palette specified
by the object handled can be the currently selected palette of a display context.
Caution: The UnrealizeObject function should not be used with stock objects.
Normally, return values from functions can be used or ignored as desired,
but if an application processes the WM_CTLCOLOR message, it must return
a handle to the brush that is to be used for painting the control background.
Failure to return a valid brush handle will place the system in an unstable
state.
case WM_DESTROY:
DeleteObject( GetClassWord( hwnd,
GCW_HBRBACKGROUND ) );
for€ t=07 i<=2;7 i++ ) DeteteObjeetC hBrushtid d>
PostQuitMessage(Q);
RecunmcOoy
In previous examples only stock brushes were used, and no provisions were
required for destroying custom brushes before the application exited. Each of
these custom brushes, however, does require some memory, and if the brush
is not deleted prior to exit, it will continue to occupy memory space.
Chapter 16: Colors in Windows 321
The first ROP2 mode shown, R2_COPYPEN, is the default mode in which
the pen simply overwrites the existing screen, and rewrites screen pixels using
the current drawing color. This corresponds to the default DOS drawing mode
as well.
The next mode listed, R2_ NOP, does not affect the screen at all, but is still
useful since the current position is still updated by a LineTo or LineRel
operation.
The R2_NOT mode insures absolute visibility by simply inverting the
existing screen image. Of course, the effectiveness varies according to the
screen color, and if the existing screen is roughly 50% gray, the inversion may
not be visible.
Rather than attempting to describe all possible interactions, however,
Figure 16-3 shows a typical display produced by the PenDraw.C demo applica-
tion where lines are drawn in blue, using all sixteen modes, against a back-
ground of both gray-scaled and color panels. The screen illustrated was
created by the PenDraw demo, which allows selection of pen color from the
menu, then draws lines across the background using each of the sixteen
drawing modes. The order in which the lines are drawn does not correspond
to the listings preceding Table 16-3: instead it follows the value order of the
mode constants.
Chapter 16: Colors in Windows 323
Mode Value
R2_BLACK (8)
R2_NOTMERGEPEN
(1)
R2_MASKNOTPEN (2)
R2_NOTCOPYPEN (3)
|R2_MASKPENNOT (4)
| R2_NOT (5)
R2_XORPEN (6)
R2_NOTMASKPEN (7)
| R2_MASKPEN (8)
R2_NOTXORPEN (9)
| R2_NOP (18)
R2_MERGENOTPEN
(11)
R2_COPYPEN = (12)
R2_MERGEPENNOT
(13)
R2_MERGEPEN (14)
R2_WHITE (15)
Summary
Colors in the Windows environment are introduced briefly here and will be
discussed further. For the moment, two demo programs are provided, which
permit further experimentation. However, lines and color selections are only
a part of graphics operations, and more sophisticated capabilities will be
examined in subsequent chapters.
}/ /[SesSeseSssssoseoeessses
//
// Color sirC //
tif Windows Colors bl:
{i f/fPeSeSa Sa SessSeoseseses///
Hinclude <windows.h>
Hinclude <stdlib.h>
FARPROC Dosicrlrnebo37
HWND hwndScrlC3], hwndTagl3J, hwndValC3]1, hwndRect;
mnt MnOcuSe = CV aliLon =P te 100%, 1755. 250° 35
324 BORLAND C++ 3.0 PROGRAMMING, Second Edition
switch( msg )
{
case WM_KEYDOWN:
if€ wParam == VK_TAB )
{
T£C GetKeyStateC VK eSHihie) eri,
else i++;
TetGe ot Ol) en piece
ells emeit Garis Cc. 8 =" OF
Slest Flore SiGaal WimGiSiCs ll ee el
+ break;
case WM_SETFOCUS: a oon0 ken Ba break;
}
return® CallWindowP
roc © slpScorlEncliaysashiwind:, msc
wParam, lParam ) );
}
switch( msg )
{
case WM_CREATE:
hBrushlLO] = CreateSolidBrush( RGBC CValC[0],0,0
hBoushild = CreateSolidBrush« -RGBG 0,CValii140
nBrushE2iy = CreatesSoUidBrush( RGB GO, 0; G¢Valnen
ReLGuUnhnGODr-
case WM_SIZE:
cxWnd = LOWORD( LParam );
vSize = HIWORDC LParam ) / 5;
hde = GetDC( hwnd );
GetTextMetrics€ hdés Sturt) +
cyChr = tm.tmHeight;
Chapter 16: Colors in Windows 325
cxChr = tm.tmAveCharWidth;
ReleaseDCC( hwnd, hdc );
Toms G=aOre 1 <S2 5S is +e
x
vSize = i*cyChr*4;
MoveWindow( hwndTaglil, 2*cxChr, vSize,
CpdGinitea(e, CyAChite mali Emmi
MoveWindow( hwndValClid, exiWinid
=O Xo xiCinite-asv Suze,
CxiGhne 75 Cty Chie Eee
MoveWindow(€ hwndScrllild, 2*cxChr, vSizetcyChr,
CXWNGH=4e cx Ohree ce cyche, TRUE) >
}
MoveWindow( hwndRect, O, O,
CXWING,s VSizer4a*coyChr, TRUE 0:
SetFocus( hwnd );
return(0Q);
case WM_SETFOCUS:
SIE] CAROICIUNS Gea niWiniGisiCrnani OGUISmmel map
return(Q);
case WM_HSCROLL:
i = GetWindowWord(€ HIWORD( LParam ), GWW_ID );
switch( wParam )
if
case SB_PAGEDOWN:
(CWEVUIES a) Spe Sheyes // no break!
case SB_LINEDOWN:
GVial GE een Garcon Crash lccae) meine alk =
case SB_PAGEUP:
OWEIEWa) =o ules // no break!
case SBE bINEUP:
CValLCidl = max(¢ OF mC Vial Sel see-ae Dievalke-
case SB_TOP:
CVia GE a= OF break;
case SB_BOTTOM:
CWeligyal = 2555 break;
case SB_THUMBPOSITION:
case SB_THUMBTRACK:
CValiti? = LOWORD Cs .Learam®?) ; break;
default: break;
}
SarSepollPos¢ hwmcdSerlliva, SB Cri, CWallleta, wikWle D5
SetWindowText( hwndValLlild,
ieo@ae GCWellda, SAMI, WH 2 DF
DeleteObject( GetClassWord( hwnd,
326 BORLAND C++ 3.0 PROGRAMMING, Second Edition
GCW_HBRBACKGROUND ) );
SetClassWord( hwnd, GCW_HBRBACKGROUND,
CreateSolidBrush(
RGB¢ CVakEOIs.CVa CRT I GVaGk 2h) yy
InvalidateRect( hwnd, NUEEPSRRUE@-:
return(Q);
case WM_CTLCOLOR:
if€( HIWORDC LParam » == CTLCOLOR_SCROLLBAR )
fi
al GetWindowWord( LOWORD( lParam ), GWW_ID );
DeleteObject( hBrushLli] );
Siwiistcicin: Gale
{
case OQ: RGBcolor RGBiGGV a EOmrORODF- break;
case 1: RGBcolor RIG BIGOR CVal Wedel OD = break;
case 2: RGBcolor RGBIGORO PG VialaicaD y= break;
}
hBrushlid = CreateSolidBrush( RGBcolor );
UnrealizeObject( hBrushLiJ );
return( CDWORD) hBrushlil );
}
break;
case WM_DESTROY:
DeleteObject(¢ GetClassWord( hwnd,
GCW_HBRBACKGROUND
fon@ 20 -ssi<= 27; itt ) DeleteObject( hBrushLlild 7
PostQuitMessage(Q);
return(Q);
}
return( DefWindowProc( hwnd, msg, wParam, lParam ) );
}
#pragma argsused
ec! hPreviInst )
a
we.hInstance helinsites
we.lpfnWndProc WndProc;
WCecDGUSE x tina 0;
wce.cbWndExtra 0;
we.lpszClassName szAppName;
we.hIcon LoadIcon( hInst, szAppName yy,
wce.lpszMenuName (LPSTR) szAppName;
we.hCursor LoadCursor(¢ NULL, IDC_ARROW );
we.hbrBackground CreateSolidBrush(
RGBCCValLLOJ >CVal bia -eéValb2i 9° )>
we.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass( &we );
D
hwnd = CreateWindow( szAppName, "Creating Colors",
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
CWRUISIED
El AUIie-aeGiW aU SIE DIE RA UIs.
CW_USEDEFAULT, CW_USEDEFAULT,
NEIL, INTE, Tits, INUILIL Des
hwndRect = CreateWindow( "static", NULL,
(CRNEDOSE |) SS MO leRtse 1
OP aOe eeO60,
Ninel, WO, Initmeis, IWC 5
lpKeybdProc = MakeProcInstance( (FARPROC) KeybdProc,
Msmse 2
for a=0 ei <=27 54 ++)
{
hwndScrlCid = CreateWindow( "scrollbar", NULL,
CHILD=STYLE™ |"°WS=TABSTOP | SBS_HORZ,
07.07 Ope ocehwrd, 17 nainises, aN eee
hwndTaglil] = CreateWindow( "static",
SiZiGo Wo mlmalbieui
ie
CHL SW || SSUGCENTER,
0, ©, OW, Intinc, ara. Intimsien, INWILIL DF
hwndValCil = CreateWindow( "static",
1uoem@ CWELIEDAA, Saint, a0) 2),
GHG
ED aeSHp als Eee Sen Cle NINE)
Ry,
O20 0, OL nwa, ate irste NULL ©):
LpScrlFnclLild = CFARPROC) GetWindowLong( hwndScrllid,
GWL_WNDPROC );
SetWindowLong( hwndScrllLil, GWL_WNDPROC,
(LONG) LpKeybdProc );
SetscrotlRange© tiwhdscrilia, sB CTL, 0, 255, FALSE 9;
Se tocol Pioisn( hiWin\clSici
nl Geile Bam CalelermG Viallboelp-amirsAlSSicuas=
328 BORLAND C++ 3.0 PROGRAMMING, Second Edition
: GOROIRS
=) DIE Fase
pas SSS SS S35 55> 5
NAME COLORS
#Hinclude <windows.h>
#include S18 1 TA} ole
#include "“pendraw.h"
switch( msg )
xf
case WM_COMMAND:
hMenu = GetMenu( hwnd );
CheckMenuItem(€ hMenu, nColor, MF_UNCHECKED );
nColor = wParam;
CheckMenultem(€ hMenu, nColor, MF_CHECKED );
DinivanWairdiaskelRec:t Guniwindsya NULL SEAL SIEs i
met UimniGoDr
case WM_PAINT:
hidicte=—— 6 elguinipadinit Gash Windy ss piSmmD Er
SetMapMode( hdc, MM_ANISOTROPIC );
GetClientRect( hwnd, &rect );
Sie taVaire wipOim tie xet) Guanidicy wahiec
Gen ilGihity, a melcet
DO t Ons
SetwWaindowextG hdc, HUniIts, VUniIts Dy
case WM_DESTROY:
PostQuitMessage(Q);
return(0Q);
}
return( DefWindowProc( hwnd, msg, wParam, lParam ) );
}
#pragma argsused
1G ! hiPrevins tance) )
a
we.hInstance = hInstance;
we.lpfnWndProc WndProc;
we.cbClsExtra = 0;
we.cbWndExtra = 0;
we.lpszClassName = szAppName;
wce.hIcon LoadIcon(€ hInstance, szAppName );
we.lpszMenuName C(LPSTR) szAppName;
we.hCursor LoadCursor( NULL, IDC_ARROW );
we.hbrBackground GetStockObject(€ WHITE_BRUSH );
we.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass( &we );
}
hwnd = CreateWindow( szAppName, "Pen Draw 1",
Chapter 16: Colors in Windows 331
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFRAULT,
NUCL NUE ninstance, NUL s).:
ShowWindow( hwnd, nCmdShow );
UpdateWindow( hwnd );
while€ GetMessage( &msg, NULL, 0, 0 ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
return(€ msg.wParam );
}
e PENDRAW.DEF_ ;
NAME PENDRAW
f/f SHSSssesessesae
//
// PenDraw.H “//
/ jf SSeS Tee =]>so=e8// //
i f[faeSseclsseoSSSseesoseseasea/ //
332, BORLAND C++ 3.0 PROGRAMMING, Second Edition
An old adage holds that a picture is worth a thousand words. While this adage
has been disputed, picked at, and supported in a thousand philosophies as
well as used, misused, and outright subverted by everyone from Madison
Avenue to political demagogues to would be-tyrants, the truth remains that a
picture is, more often than not, preferred to a thousand words.
Frequently in computer applications, the pictures in question are graph-
ics—pictures composed of relatively simple shapes such as bar or pie graphs,
flow charts, diagrams or schematic outlines generated by an application to
illustrate non-pictorial information—or to bitmapped pictures such as icons
or the "wallpaper" bitmaps provided as the background screens in Windows
operations in 386 Protected mode. (This is not to belittle bitmapped images.
They have their own purposes and also may be usefully combined with
graphic images. Bitmapped images, however, will be covered in Chapter 18.)
For the present, the topic will be creating graphic images based on appli-
cation instructions and/or data internal or external to the application. How-
ever, before creating an application, the first step is a look at the tools
available.
Graphic Tools
Windows’ colors and line drawing modes were introduced in Chapter 16, but
Windows also supplies a variety of other drawing features, including stan-
dard shapes, line styles, and fill styles.
ooo
334. BORLAND C++ 3.0 PROGRAMMING, Second Edition
Beginning with shapes for drawing figures, which may be solid or outlined,
Windows provides support with a series of eight functions to create the
standard shapes listed in Table 17-1.
Logical Pens
All shapes are drawn using the current logical pen. The default pen, if no other
pen has been created, is a solid black line with a width of one logical unit.
Logical pens (and logical brushes) can be created and selected in two
separate steps, as:
The CreatePen function specifies the style, width, and color of the custom
pen and returns a handle to the new logical pen. The SelectObject function
Chapter 17: Drawing Shapes and Figures 335
associates a pen or brush with the device context: that is, makes a specific
logical pen (or brush) the currently active drawing object. More than one
logical pen and/or brush can be created at any time by using an array of
handles and then selecting as needed via the SelectObject function. Each logical
pen or logical brush created, however, consumes memory, and when no longer
in use, should be disposed of via the DeleteObject function called as:
DeleteObject( hPen );
This deletes the logical object from memory. However, a created pen or
brush should not be deleted while associated with a device context—unless,
of course, the device context is about to be closed.
The pen styles are defined in Windows.H and also appear in Appendix A at
the end of this book. The Redefined pen (line) styles appear in Table 17-2.
While most of these pen styles are self-explanatory, the PS_INSIDEFRAME pen style requires some
explanation. For a pen width greater than one logical unit/pixel PS_INSIDEFRAME insures that
the line is drawn inside the frame for all primitive shapes except polygons and polylines. Also, if the
pen color does not match an available RGB color, the pen is drawn with a dithered (logical) color. Of
course, if the pen width is one or less, then PS_INSIDEFRAME 1s identical to PS_SOLID.
Logical Brushes
When a figure (shape) is drawn, with the exception of the Arc figure which is
not closed, the current logical brush is used to fill the figure.
Four types of logical brushes can be created: solid, hollow, hatched, or
patterned. For these brush types, separate Create...Brush functions are used:
CreateSolidBrush, CreateHatchBrush or CreatePatternBrush. For the present, the
CreateHatchBrush is used to select from the six fill (hatch) patterns as defined
in Table 17-3.
336 BORLAND C++ 3.0 PROGRAMMING, Second Edition
A hatched brush is created in the same fashion as a logical pen and is subject
to the same restrictions:
Please note, however, that in the PenDraw.C demo, the handles returned for
logical pens and logical brushes are not saved, and no provisions have been
made in this example to delete these objects after use. That is, in PenDraw.C,
the actual code used appears as:
SelectObject( hdc,
CreateHatchBrush( nHatch-IDM_HORIZ,
CEO LO aD es
While this is not the recommended practice and does use memory, which
is not recovered until either Windows exits or the system is rebooted, this does
demonstrate that there are no guards against this type of error except, of
course, for careful programming practices.
Creating Figures
The PenDraw2.C program demonstrates six of the eight figure functions using
the predefined line and hatchfill styles, and a palette of eight colors
corresponding to the DOS CGA color palette. Figures (shapes), line styles,
hatch styles, and colors are all menu-selected. The eight colors provided are
defined as RGB color values (long integer) as described in Chapter 16 (see
Table 16-2).
Rectangles are the simplest of the figures, requiring four parameters
specifying the coordinates of the upper-left and lower-right corners:
Chapter 17: Drawing Shapes and Figures 337
ROM
nid Rec. tyGen nicicysms x Wiley any, Uilee mnexalnRemmy alse
xCornerRadius, yCornerRadius );
In most cases, you will probably prefer for xCornerRadius and yCornerRadius
to be equal, but the curve describing the corner of a rounded rectangle can be
elongated in either dimension as desired. Figure 17-1 shows three corner
examples, first with x>y (x=2y), then x=y and, at the right, x<y (2x=y).
x=2y
Ellipses are drawn much like rectangles, requiring only corner coordinates
for a rectangle enclosing the ellipse, thus:
Also, a circle is simply a special case of an ellipse and can be defined from
the Ellipse function in the same fashion that a square was created using the
Rectangle function.
338 BORLAND C++ 3.0 PROGRAMMING, Second Edition
Figure 17-2 shows three ellipses together with the rectangles defining the
ellipses. The enclosing rectangles, of course, are not drawn by the Ellipse
function.
The Arc, Chord, and Pie functions also use coordinates for an enclosing
rectangle. They determine the position and basic curve, but add an additio-
nal four parameters: The first pair (of the last four) specifying the beginning
position of the arc, and the second pair setting the end position of the arc.
Figure 17-3 shows examples of arc, chord, and pie figures, together with the
enclosing rectangles which define the figures and the radii intersecting the
begin and end points of the arc segment.
Chapter 17: Drawing Shapes and Figures 339
In conventional DOS C++, arc and associated figures are drawn by defining
the center point, the radius (or radii), and by defining the begin and end points
as angles with the 0° point located horizontally to the right.
In Windows, however, the arc segment is defined by an enclosing rectangle,
and the begin and end points for the arc segment are not defined as angles,
but as points located anywhere except the center point of the arc. For example,
in Figure 17-4, the x/y coordinates specifying the starting angle could lie
anywhere along the radii, including the upper-left corner of the rectangle or
outside the rectangle itself. The arc itself does not necessarily pass through
the point specified. Instead, the actual starting point of the arc is determined
by the intersection point of the arc and a radius drawn from the center through
the specified point.
For the Arc function, the process ends with determining the start and end
points for the arc. For the Chord function, the next step is to join these
endpoints (the arc’s endpoints, not the reference endpoints) to produce a
closed figure before the completed figure is filled using, in the example, the
current hatch-brush.
For the Pie function, instead of a chord two lines are drawn from the center
point, one to each endpoint of the arc, before filling the closed figure as before.
340 BORLAND C++ 3.0 PROGRAMMING, Second Edition
Figure 17-4: Start and End Points for Arc, Chord, or Pie Figures
alll
i WW
Business Graphs
Business graph displays are one obvious use for the figure-drawing
functions. Granted, these are not my favorite topic, and probably not yours
either, but business graphs are a common requirement for applications.
They can illustrate how the Rectangle and Pie functions can be used with data
sets, and how Pie sections are calculated.
Both the BarGraph and PieGraph demo programs use arrays of data which
are built into the application. In more common circumstances, similar
applications would use data values which were either calculated or drawn
directly from outside files, but this approach is simpler for demonstration
purposes. Note that the same data set is used in both examples.
The BarGraph demo charts four years of data in eight categories. It uses a
solid brush and colors to identify each year’s data, but groups each category
in clumps. Since separate vertical and horizontal ranges are useful here, the
MM_ANISOTROPIC mode is used. The origin point is set near the lower-left
342 BORLAND C++ 3.0 PROGRAMMING, Second Edition
corner of the grid, but slightly up and to the right, leaving room for labels
below the groups of bars.
Unlike the PenDraw2.C program, both BarGraph and PieGraph delete custom
pens and brushes after use, and also save the original (entry) mapping mode,
restoring this mode after painting the client window.
The principal elements of the WM_PAINT code from BarGraph follow:
This first rectangle is written next to the year label, then the entire year’s
data is written before another pen and brush color are selected:
FOr G0 oe ft)
Rectangle( hdc, j*15+1+1i1*70, 0,
Cove taal (On, Z2eNeCOUMuUSie
jae gal
DeleteObject( hPen );
DeleteObject( hBrush );
Both the pen and brush objects are deleted after use. The alternative would
be to create an array of pens and brushes, then use the SelectObject function to
make each active as needed. Finally,the arrays are deleted before exiting. The
BarGraph demo is illustrated in Figure 17-6.
For the PieGraph demo, a different approach is used, displaying data for
only one year at a time data in a piegraph. Since the pie graph should be
roughly circular, the MM_ISOTROPIC mode is used with the viewport origin
in the center of the client window.
Since C does not have a predefined value for pi, PI2 is a macro defined as
(2.0 * 3.14159 ), providing a means of converting values to angles (in radians)
before using the angles to calculate points on the circumference.
Chapter 17: Drawing Shapes and Figures 343
“a @
Acsry
Since the data for the pie graph is an array of individual values, a loop is
used to create a series of magnitude values:
OLCV a) GO eee —= 0)
Fore TaeWOe2 Wels Geese 2
nO) CVicls eet tlle — sen) GV allGe Tele ei AIG CGOUI tS! eral) eae
LOCK 1 =O 21 SS 7) Let)
{
And that’s how the hat trick works—each pie section is created as a fraction
of the total circle and colored with a different solid brush. The results appear
as shown in Figure 17-7.
Source code for both the BarGraph and PieGraph demos appear at the end of
this chapter.
Drawing Polygons
The Polygon and PolyPolygon functions also draw bordered, closed, and filled
figures, but with a few differences. First, the figures drawn can be more
complex than simple rectangles but must still be constructed from lines, not
curves. Second, the polygon functions require an array of points defining the
figures to create:
Annual Report
For polygons, two fill modes are supported as ALTERNATE and WINDING,
describing the algorithms used to determine which points are inside or outside
of the polygon figure. In ALTERNATE mode, the default polyfill mode, only
regions which are reached by crossing an odd number of boundaries (1, 3, 5,
etc., lines) are considered interior and are, therefore, filled.
The WINDING mode, on the other hand, is somewhat slower to calculate,
but it has the advantage of filling all interior (bounded) regions regardless of
the number of boundaries crossed.
As an example, Figure 17-8 shows two polygons as five- and seven-pointed
stars where the interiors consist, respectively, of six and fifteen interior
regions. In the illustration, the two polygons have been filled using the
ALTERNATE fill mode. The WINDING fill mode can also be selected from the
demo’s menu and will fill all interior spaces in both figures.
PenDraw3
PenDraw3.C calculates point arrays for the two figures using simple
trigonometric operations much the same as used for the PieGraph demo:
TORG TSaySO ye ae Were Vet scr2 4s. 2D, [etal VieumDOn nitiSea/a/,
{
pC SOR aeleex Ci nite “sin'Cie AR heh De ee 0.0Eh) Bae
PtlLOl LaAy Cats GaC 0:5: Cli." Bel 24 Sie)a en Ona
}
For the five-pointed star, the points are calculated in the order 0, 2, 4, 1,3
using the formula j=(j+2)%5. If these same points were calculated in success-
ive order, the result would be a simple pentagon with a single, continuous
interior.
For the seven-pointed star, the formula j=(j+3)%7 serves the same purpose
except that the resulting point order is 0, 3, 6, 2, 5, 1, 4:
Fill Mode
¥ Alternate Fill
Winding Fill
The -110 and +110 constants are supplied simply to position one figure to
the left of center and the other to the right.
To use the PolyPolygon function, instead of calculating the points for these
two stars, a static array of points would be needed:
The brief array poly declares the number of points in each polygon. With
these declarations, PolyPolygon could be called as:
If you install this code in the PenDraw3 listing, be sure to remove the
#include <math.h> reference. Otherwise, a conflict will occur between the array
poly and the function poly (in Math.H) which has quite a different purpose.
Summary
Brush and pen styles and the shape functions are useful but these are only a
small part of Windows’ graphic capabilities. While business graphs are useful,
these may also seem rather pedestrian in view of the other, more elaborate
graphics which are possible. In Chapter 18, another aspect of graphics will be
demonstrated in the form of bitmapped brushes and paint styles, bimapped
images, and, just to round out basic business graphics, a line graph using
bitmapped images.
#Hinclude windows.h
Hinclude "pendraw2.h"
case WM_SIZE:
350 BORLAND C++ 3.0 PROGRAMMING, Second Edition
case WM_PAINT:
hdc = BeginPaint( hwnd, &ps );
SetMapMode( hdc, MM_TEXT );
GetClientRect( hwnd, &rect );
SétVilewoortExt GC ihdc,> inect night; erectiibottom 97
SetWindowExt( hde, rect onight; rectsabottom );
SelectObject( hdc, CreateHatchBrush(
nHatch=IDM_HORIZ, cColor ) );
SelectObject( hdc, CreatePen( nPen-IDM_SOLID, 1,
CEO@lolr 2 HF
SiWalatic
Nia Ee ICCum
{
case IDM_ARC:
Airc Gahidic, =xUiLy yl Bry RIB)
xp2, yoy xp, “y paws;
break;
case IDM_CHORD:
Chionid Ga hidicy Uae y UI ee xcrUBy amy Ripe
XP Wray Pl, XPepuy pie 7);
break;
casiee lDMSRRE
Pile C hrdexr xl pxyul, mxRBS sy RB,
XP, YPC; aXPoAsyYDS I,
break;
Caisieme DIM meE SPS)Ee:
EULtp'se( Shidc AgxUil| «yu Sx RB tyke a
break;
case IlDMECTRCEE =
EG Uipse Gahde, axl” ylile
XUL + MinGexRB=xUl yay RBayULy
yUL SS miin © oy RB sy Ul P ex RE=x UL):
break;
case IDM_RECT:
RectangUe:G thdc i xUL> syULy IXRB aly Re Oe
break;
case IDM_SQUARE:
RectangleC hdc, xULy ayUi
Chapter 17: Drawing Shapes and Figures 351
case WM_DESTROY:
PostQuitMessage ODF.
return(0Q);
}
return(€ DefWindowProc ( hwnd, msg, wParam, lParam ) );
}
#pragma argsused
if€ ! hPrevInstance )
{
wce.hInstance = hInstance;
we.lpfnWndProc WndProc;
we.cbClsExtra 0;
we.cbWndExtra 0;
we.lpszClassName szAppName;
we.hIcon = LoadIcon( hInstance, szAppName );
we.lpszMenuName = CLPSTR) szAppName;
we.hCursor = oad Guins Onn GaN Ulery aml) Gam AIR RO]Wime) =
we.hbrBackground GetStockObject( WHITE_BRUSH );
wce.style CS_HREDRAW | CS_VREDRAW;
RegisterClass( &wc );
}
hwnd = CreateWindow( szAppName, "Pen Draw 2: Figures",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CWEUSEDEFAULTC CWLUSEDEPAULT
NUL NULL, Nnins tance, NULL >) >
ShowWindow( hwnd, n CmdShow );
UpdateWindow( hwnd );
while(€ GetMessage( &m eq, NUL, 0,70 ) )
352. BORLAND C++ 3.0 PROGRAMMING, Second Edition
x
TranslateMessage( &msg );
DispatchMessage( &msg );
}
return(€ msg.wParam );
}
; PENDRAW2.DEF ;
NAME PENDRAW2
/{PseSeese]ss]52SSSS55///
if PenDraw2.RC id
// menu script iif
f/f SS SSS SSeS eesees///
POPUP "&Color”
BEGIN
Menultem "&Black", Si0 OF ee GHIE CKED
MenuIltem "B&lue", 501
Menultem "&Green", S072
MenuIltem "&Cyan", 303
MenuIltem "&Red", 304
354 BORLAND C++ 3.0 PROGRAMMING, Second Edition
POPUP "&Figure"
BEGIN
MenuIltem "&Rectangle", 401, CHECKED
Menultem "&Square", 402
Menultem "&Arc", 403
Menultem "C&hord", 404
Menultem "&Pie", 405
Menultem "&ELlipse", 406
MenulIltem "&Circle", 407
END
END
Passel aesSSSeoSeeeecessse/ /
// BarGraph.C (al,
// C++ Windows Drawing //
of SSS SSS] SS ]S SSS SSesessoee
/ //
#Hinclude <windows.h>
#Hinclude <stdio.h>
Sswitch( msg )
{
case WM_SIZE:
cxWnd = LOWORD( LParam );
cyWnd = HIWORD(C LParam );
Return Cope
case WM_PAINT:
Lome ih 0 ARS Aitsto
Pore
- pete asa aaecr )
MaxVal = max(€ MaxVal, AccountsLjJICild );
hdc = BeginPaint(€ hwnd, &ps );
OrgDC = SaveDCC( hdc );
SetMapMode( hdc, MM_ANISOTROPIC );
SetWindowExt (¢ hdc, cxWnd, MaxVal );
SetViewportExt(€ hdc, cxWnd, -MaxVal );
GetClientRect( hwnd, &rect );
SetWindowOrg( hidice- wen |Ol-wmmelcetre bOnty to mic) Omer:
for(€ i=-1; i<2*MaxVal; i+=50 )
{
MoniesoOrGan nidice, mam 00- anime o>
LiMmearoe hele, cxlniel, i 2-
}
TOT Gaia Oire i <8 x re)
Te sXatiOUG GaainiCice- mana AOL Dem Ce mmnG ZS iC fete,
Sprint hauszBUrthy =48, Acchypeshil )? )-;
LOPE VITSOS eps ee+e)
{
qe x tO OUtiGe nidicy-m Gti O 42.0) ene iM ax Viale 2 Ole siz. BiUnh tp
SprantnGeszoura, 70... .earsi adc). )}
hPen = SelectObject( hdc,
Createren > tes “SOLID, a1, "pcolorljrtd ) d+
hBrush = SelectObject( hdc,
Ginea celsolad BisuisiniGmupGolloin ley +t) se).
RectangleG@ hdc, Gyr) 70, 2*MaxVal+20,
356 BORLAND C++ 3.0 PROGRAMMING, Second Edition
CjH1)*704TSy 2*MaxVal+5 );
Lorn t=O 2s. vite)
Rectangle® hdc¢e, 214 15+1ene70, 07
Cjt1) 1541" 70> 22mecounts hj 10 sie
DeleteObject( hPen );
DeleteObject( hBrush );
y
Reis sone DIG Gadi cya Ole
GiDiG mr
return(0);
case WM_DESTROY:
PostQuitMessage(Q);
return(0Q);
}
return( DefWindowProc( hwnd, msg, wParam, lParam ) );
}
#pragma argsused
5 BARGRAPH.DEF ;
PPS SS SS SSS SSS SS 55%
NAME BARGRAPH
Hf PieGraph.C //
// C++ Windows Drawing if
ff = ee
#include <windows.h>
#Hinclude <stdio.h>
#include <math.h>
#include "niegraph.h"
switch( msg )
{
case WM_COMMAND:
switch( wParam )
{
case Y1988: case Y1989:
case Y1990: case Y1991:
CheckMenuItem( hMenu, Year+Y1988,
Mine UIN CHIEKE Das
Year = wParam —- Y1988;
CheckMenultem( hMenu, wParam, ME CHECKED 2);
break;
}
InvalidateRect( hwnd, NULL, TRUE );
return(0Q);
case WM_SIZE:
cxWnd = LOWORD( LParam );
cyWnd = HIWORD(C LParam );
return(Q);
case WM_PAINT:
TotValflOJ] = O;
for® 1205" 1<3 71445)
Chapter 17: Drawing Shapes and Figures 359
case WM_DESTROY:
PostQuitMessage(Q);
he turniGoD):
}
return( DefWindowProc( hwnd, msg, wParam, lParam ) );
}
#pragma argsused
360 BORLAND C++ 3.0 PROGRAMMING, Second Edition
fe tT
NAME PIEGRAPH
| {/Sesesss=s]Sees///
// PIEGRAPH.H itif
/ (Sees Ssseseses //i/
j/ {SS SSSSoSeSeS—Sse55seees
/ //
/f PenDraw3.C Le
fi C++ Windows Drawing Itif
/ { SSeess=SoSSS]sooSsesossee
///
Hinclude <windows.h>
Hinclude <math.h>
#Hinclude "pendraw3.h"
switch( msg )
{
case WM_CREATE:
fort =y=0re a5 ta, =. G7t 2h) 2 Hi PIV fpooOdmMes //
{
pelosi eGinthers inGe Nee he > mE s TOO): =~ 11.05
StULOa tiie Gini) CLcos G ji Pl2/o. ot e100 8);
ip
for Gei=ie0, MiG with > a) = G45) 22) LS ENC aDOM tse e/a/:
362 BORLAND C++ 3.0 PROGRAMMING, Second Edition
case WM_COMMAND:
hMenu = GetMenu( hwnd );
switch( wParam )
{
case IDM_ALTERNATE:
case IDM_WINDING:
CheckMenuItem( hMenu, nFillMode,
MF_UNCHECKED >>
nFillMode = wParam;
CheckMenuIltem( hMenu, nFillMode,
MESEC HEC CED Es:
break;
}
InvalidateRect( hwnd, NULL, TRUE );
return(Q);
Caisien WiMaeorleZ
Es
cxWnd = LOWORD( LParam );
cyWnd = HIWORD(C LParam );
PeewirMncoD) -
case WM_PAINT:
hdc = BeginPaint( hwnd, &ps );
hiPene =m Cimerait2 Pein Ge P:SmeS OLDS) Oey
SelectObject( hdc, hPen );
SelectObject( hdc, GetStockObject( LTGRAY{BRUSH®) )+;
SetMapMode( hdc, MM_ISOTROPIC );
SetWindowExt( hdc, PANO) ee7(0) )) 5
setViewportExt€ hdc, cxWnd, cyWnd )-
SetWindowOrg( hdc, =220, Ome
SetPolyFillMode(€ hdc, nFillMode );
PolygonG hdc ya pelle Dr:
Polygons vidicy Sptilainiies: er
EndPaint.c hwnd &p.s-)
return(0Q);
case WM_DESTROY:
PostQuitMessage(Q);
return(Q);
}
return€ DefWindowProc( hwnd, msg, wParam, lParam ) ye
Chapter 17: Drawing Shapes and Figures 363
#pragma argsused
tig ¢ InPrawimesk »
He
wce.hInstance = hInst;
we.lpfnWndProc = WndProc;
Wice ciOG ESE Xxatina = 0;
we.cbWndExtra = 0;
we.lpszClassName = szAppName;
wce.hIcon = LoadIcon( hInst, szAppName );
we.lLpszMenuName = CLPSTR) szAppName;
we.hCursor = LoadCursor( NULL, IDC_ARROW );
we.hbrBackground = GetStockObject( WHITE_BRUSH );
we.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass( &we );
}
hwnd = CreateWindow( szAppName,
Penne Div alwieo ee Fall lleidum ROleyiqionisus,
WS_OVERLAPPEDWINDOW,
GCWRUSBEDE RAU Ts 6 WUISE DER AUIS Ts,
GWRUS EDER AUR eC WaaU:S EDIE AUIEsTe,
NOME, INWILIL, Inittingsie 5 IN WILIE ws
ShowWindow( hwnd, nCmdShow );
UpdateWindow( hwnd );
while(€ GetMessage( &msg, NULL, 0, O ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
return( msg.wParam );
364 BORLAND C++ 3.0 PROGRAMMING, Second Edition
NAME PENDRAW3
j / HSeSeses2eeseeee
// //
if PENDRAW3.H ah
(jf SeSesSsesees][oen/ j/
#define IDM_ALTERNATE 1
#define IDM_WINDING 2
Chapter 18
In previous chapters, both hatched and solid brushes have been used to fill
figures. A third type of brush remains to be introduced, the custom patterned
brush using a bitmap image for the fill pattern.
Of course, fill patterns are only one of many uses for bitmaps. Other graphic
applications will also be examined, including line graphs using bitmapped
images, accessing Windows’ own bitmaps, device independent bitmaps
(DIBs), and block transfers of patterns (BLT functions).
First, however, we will begin with the simplest of these graphic features,
using bitmaps to create patterned brushes.
Bitmapped Brushes: |
The first step in creating a bitmapped brush is to create the bitmap itself. For
brushes, a minimum 8x8 bitmap image is required. Bitmaps can be defined
within your source code as arrays of WORD. For example:
Sibatuc BY TE wBricksEd =
PeOxrr, “Ux0S,- Ux0G,, UOx0S, UOxkF, Uxs0, Oxc0;7 0x80 73>
This defines an 8x8 bit pattern that describes a bitmap similar to the BRICKS
image shown in Figure 18-1. It could be used to produce a pattern brush
similar to the left half of the pentagonal star in Figure 18-3. Note, however,
that these statements are qualified by using the word "similar", because this
365
366 BORLAND C++ 3.0 PROGRAMMING, Second Edition
bitmap is monochrome and the bitmaps and brushes in the illustrations are
polychrome. Still, the pattern itself is the same.
DIAMOND.BMP BRICKS.BMP
In order to use this bitmap as a brush pattern, the first step is to call
CreateBitmap to convert the value array into a bitmap image (in memory):
The SelectObject function makes the new brush the current brush object.
Don’t forget, though, that having created both bitmap and brush, these
must be deleted when no longer needed:
Bitmapped Brushes: Il
When bitmaps are defined as arrays of WORD, there are two principal
drawbacks: first, that the bitmaps are device-dependent and, second that color
bitmaps are difficult to code. However, the Resource Workshop provides an
excellent bitmap editor (see Chapter 7) that avoids both disadvantages. First,
bitmaps created using Workshop are device-independent bitmaps (DIBs), and
second, since the bitmaps are drawn in color, no coding is required. (DIB
structure is explained in Appendix A.)
Since it is device-independent, a bitmap created in color on a VGA color
monitor can be displayed in black and white on a monochrome system, or
transported to a system using an 8514A video.
This approach does require images created using the Bitmap Editor (or
Microsoft’s SDK utility), so before proceeding further, you need to create the
PENDRAW4.RES resource file containing four bitmap images: BRICKS,
CHAINS, DIAMOND and STRIPES. Three of these are shown in Figure 18-1 as
8x8 bitmaps, while the fourth, CHAINS, is illustrated separately in Figure 18-2
as a 24x24 bitmap. The colors used are not particularly important and can
easily be adapted to monochrome systems as well.
After the bitmap images have been created as a part of the resource file
(.RES), they become part of the compiled and linked .EXE file. However, this
does not make these bitmapped images part of the executable application
itself. Instead, within the application, an array of HBITMAP is declared
globally, and in the WinMain procedure, the bitmaps are loaded as external
resources: }
static HBITMAP hBitMapLl4];
if( ! hPreviInst )
{ Pee }
368 BORLAND C++ 3.0 PROGRAMMING, Second Edition
return( msg.wParam );
The LoadBitmap function loads the bitmap resource that is named by the
[pBitmapName parameter from the executable file or module specified by the
hInstance parameter. The /pBitmapName is a null-terminated character string
naming the bitmap. LoadBitmap can also be used to access Windows’ pre-
defined bitmaps. In this case, the hInstance parameter is passed as NULL, and
the [pBitmapName parameter must be one of the values shown in Table 18-1.
: Bitmap names beginning with OBM_OLD._... represent bitmaps used by Windows versions prior
to 3.0.
DeleteObject( hBrush );
Also notice that the DeleteObject is not called for the bitmaps themselves,
since the CreateBitmap function was never called. In this application, the
bitmap handles are a static array, not created on the fly.
PenDraw4.C
PenDraw4 uses four bitmaps to create four different patterned brushes. The
brushes are menu-selected and are used to paint the same five- and seven-
pointed stars that were created in PenDraw3.
There is one other difference: In PenDraw3, the figures were calculated,
but in PenDraw4, a static array of coordinates are used to demonstrate the
PolyPolygon function which was discussed (but not shown) in Chapter 17.
Figure 18-3 shows the four bitmapped brushes in a composite illustration.
In the actual application, only one brush is used at a time.
370 BORLAND C++ 3.0 PROGRAMMING, Second Edition
Storing Images
In Windows, two quite different methods are used to store image information,
bitmap files and metafiles. A bitmap file is, essentially, a pixel-by-pixel copy
of an image, while a metafile is a description of an image written as a record
of the GDI function calls (such as Rectangle, MoveTo, Arc, TextOut, etc.,) used
to create the picture. The most important difference between these is the
amount of information required.
Chapter 18: Brushes, Bitmaps, BLTs, and DIBs 371
For example, for a 640x480 pixel color screen, even with data compression,
a bitmap requires over 150 Kbytes. Metafile images, in general, create the same
screen from a much smaller file of information. Nevertheless, metafiles have
their own limitations, as you will see in Chapter 19.
Old-Style Bitmaps
The old-style bitmap format originated with Windows 1.0, and has the
principal drawback of being highly device-dependent, that is, bitmaps are
structured for a specific display format and are not readily transported to, or
compatible with, other video/device formats.
A monochrome bitmap was described previously using the CreateBitmap
function and an array of eight WORD values defining a simple brick pattern
with an 8x8 pixel dimension. Old style bitmaps are not limited in size, though
the 8x8 size is the practical limit for brushes. However, all bitmaps—
monochrome or color—must be an even number of bytes in width.
Windows provides four functions for creating old-style bitmaps:
372 BORLAND C++ 3.0 PROGRAMMING, Second Edition
The cxWidth and cyHeight parameters define the width and height of the
bitmap in pixels.
The nPlanes and nBitsPixel parameters, in CreateBitmap, define the number
of color planes and number of bits per pixel. At least one of these values must
be set to one but if both are one, they result in a monochrome bitmap.
However, in the CreateCompatibleBitmap and _ CreateDiscardableBitmap
functions, the device context handle (hdc) permits Windows to access the
number of color planes and color bits per pixel directly.
Using CreateBitmap, if lpBits is NULL, the bitmap is uninitialized, that is,
it contains random image data. Both the CreateCompatibleBitmap and
CreateDiscardableBitmap functions create uninitialized bitmap images (see
SetBitmapBits and GetBitmapBits following).
The CreateBitmapIndirect function uses the structure BITMAP to define the
bitmap data, as shown on Table 18-2.
SetBitmapBits/GetBitmapBits
The SetBitmapBits function can be used to copy a character (or BYTE) array
into an existing bitmap, such as an uninitialized bitmap. For example,
SetBitmapBits is called as:
The GetBitmapBits function copies dwCount bits from hBitmap to the char (or
BYTE) array addressed by IpBits.
The GetObject function can be used to retrieve information about hBitmap
as:
dwCount = (DWORD)
bm. bmWidthBytes * bm.bmHeight * bm.bmPlanes;
Last, since these bitmaps are GDI objects, the DeleteObject function should
be used to cancel the object when no longer needed:
DeleteObject( hBitmap );
Monochrome Bitmaps
Monochrome bitmaps were introduced previously with the wBricks array, and
used to create an 8x8 monochrome brush from an array of eight WORD values.
For actual bitmaps, of course, the 8x8 bit limitation does not apply, though
each scan line of the bitmap must be an even number of bytes (some multiple
of 16 bits using zeros to pad right).
For example, a simple bitmap consisting of a square with an x in the center
could be defined as:
In order to make the bitmap 9x9, each scan line is padded with seven zeros
for a total width of 16 bits, or two bytes. The actual bitmap structure could be
defined as:
Note: In the old bitmap style, images are coded from the top down. But in
the new DIB style, images are coded from the bottom up.
NiBaatimalpe —wC ela tie Ba timialp Gur 9s, mov mele mene eC NeIC;KIB
0 Xam
There is one limitation to this format: Since Windows may move data
around as necessary, the address returned for CheckBox may be invalid unless
it is used almost immediately after it is accessed.
This potential problem can be avoided, by first creating the bitmap and then
transferring the bitmap image:
Color Bitmaps
For color bitmaps, the old Windows style is a bit more complex than for
monochrome bitmaps, and it is extremely device-dependent. To show how
and why, we will begin by constructing a bitmap similar to CheckBox, but using
two colors, dark green and white (assuming a standard palette) instead of
black and white.
The bitmap image can be calculated as:
Chapter 18: Brushes, Bitmaps, BLTs, and DIBs 375
Notice that the bits are still padded toa WORD width by adding three zero
(black) pixels at the end of each scan line. This array can be defined as:
Sua twcepy le. CheckBoxld = <€ OxFF, OxFF, OxEF, OxFF, Oxkhos
OxOO TUX rn UX2e ee Uxee om Oxe i, OX FO, uO x0, UXt2e Ome,
Ory ux re, UXT, OxOU, Oxtz, Oxz2r, Ox22e, Oxia, Uxro,
DXOOFOxXk2,. Oxee, Uxrizoe0x22 0x0 0x00, Oxk2e tOxcry
Oxctemuxec OXEOL 0x00, 0xF2,. OxF2,~0x22 > -0Oxreo Ox FO;
OxDOS@Ox hi wOx22, (Ox227Ox2r,. Ox k0f.0x00, eOx Eee Ox Ae,
Oxhih Ox Fre Ox FO 0x00 >>
Device-Independent Bitmaps
The device-independent bitmap (DIB) format, an extension of the OS/2 Presenta-
tion Manager bitmap format, provides device independence, in part by including
an RGB color table for all colors used in the bitmap. Device-independent bitmaps
can be created automatically by using the Bitmap Editor accompanying Bor-
land C++, as well as the SDKPAINT program in the Windows Software
Development Kit, the PaintBrush program included with Windows, and many
third party programs.
A sample bitmap file appears in hex format in Figure 18-5, where alternate
fields, as defined by the data structures, have been half-toned for ease of
identification. The several data sections and structures will be identified further.
376 BORLAND C++ 3.0 PROGRAMMING, Second Edition
76 66 66 6H 28 44
Notice that all data is arranged in /sb..msb order, that is, the data bytes 96
00 00 00 do not represent the value 96000000h but 00000096h.
At the same time, color is represented only as multiple color bits per pixel,
regardless of how the physical device handles color, and may specify one bit
per pixel for monochrome, four for 16-color bitmaps, eight for 256 color
bitmaps, or 24 bits for 16 million colors.
= First, each row of the bitmap image is always some multiple of four
bytes (DWORD), with the row padded on the right with nulls if
necessary. Each row begins, of course, with the left-most pixel.
Chapter 18: Brushes, Bitmaps, BLTs, and DIBs 379
For a monochrome bitmap with one color bit per pixel, the bit image begins
with the most significant bit of the first byte in each row. If the bit value is
zero, the first RKGBQUAD color value is used; if it is one, the second RGBQUAD
value is used. For a monochrome bitmap, the BRICKS bitmap data would be
coded as:
Remember, the bitmap is coded with the bottom row first, working up.
For a sixteen color bitmap—four color bits per pixel—each pixel is
represented by a four-bit value that serves as an index to the entries in the
color table. Thus, the bitmap image for BRICKS appears as:
0
ooaocn-—--—
00
=)
06
= 00
=)
—
= od
aed
OO
ad
ak 0- =a
3100 O&O
OO
WO
= OO
=)
od,
00
=),
= =35.'00
fed
=a
OO
ack
ed
jah
380 BORLAND C++ 3.0 PROGRAMMING, Second Edition
OS/2 Bitmaps
OS/2, version 1.1 and later, uses a bitmap structure very similar to Windows
3.0. The differences are that instead of a BBTMAPINFOHEADER structure, a
BITMAPCOREHEADER structure is used (see Appendix A), and the color
table consists of RGBTRIPLEs instead of RGBQUADs.
Bitmap types (OS/2 vs. Windows) can be identified by identifying the
structure used. To do so, examine the first DWORD in the structure (the biSize
or bcSize field) for the structure size.
The xUnits size is found in the low word of dwBMSize; the yUnits size in the
high word.
HANDLE niGeanisse.-
hGInst = hInst;
with four parameters: The window handle, the bitmap name, and the x and y
window coordinates for position.
Finally, the function is ready to load the bitmap which, during the final
binding, was joined with the .EXE file. Notice that here is where the global
hGlInst handle is used:
If for any reason the load fails, DrawBitmap returns FALSE, but this is the
only error check which will be made. Now that the bitmap is loaded, the next
step is to establish an appropriate device context.
compatible device (that is, the client window or other output device). Note
that when a memory device context is created, the GDI automatically assigns
a ‘display surface’’ with a one by one monochrome bitmap, i.e., the initial
device context contains one monochrome pixel, hardly enough space for any
real operations. This last deficiency, however, can be corrected immediately
by calling SelectObject to make the current bitmap the active object for the
device context:
The SetMapMode function assigns the same mode to the memory device
context as to the window device context (hdc).
Making the bitmap the active object for the device context and setting the
map mode is only part of the job, because there is still a lot of information from
the selected bitmap that needs to be transferred to the local bitmap record
(bm).
There is still one very important task remaining, because the bitmap image
information has not yet been retrieved.
the present operation of choice, and completes the task of writing the bitmap
image to the client window:
BitBlt moves a bitmap from the source device (idcMem) to the destination
device (hdc), with the xSrc and ySrc parameters ((0,0) in the example) specify-
ing the origin in the source device context of the bitmap to be moved. If the
dwRop parameter (raster operation type) specifies a raster operation which
does not include a source, the source device context must be NULL.
The xPos, yPos, width, and height parameters specify the origin point and
size of the rectangle in the destination device context to be filled by the bitmap
image. Note that unlike most previous operations, instead of a rectangle
(RECT) structure, the origin point is set in device context coordinates, but the
width and height values are passed directly, rather than as point coordinates.
The final parameter is a raster-operation code, that is described in Table
18-6. This defines how the graphics device interface combines colors in output
operations which can involve a current brush, a possible source bitmap, and
a destination bitmap. For the DrawBitmap operation, the SRCCOPY code
copies the source bitmap image directly to the destination (hdc).
Note: A total of 256 ROP codes exists but only the preceding 15 ROP codes
are identified by name constants. Details for the remaining ROP codes can be
found, if desired, in the Microsoft Windows Programmer's Reference.
Monochrome operations using ROP codes are relatively straightforward:
Bits are either on or off. For color, however, Windows executes separate
operations on each color plane or each set of color bits, depending on the
organization of the device memory. These operations will be easiest to
understand simply by experimentation with the various ROP codes.
Step 6: Clean Up
The Bit Blt function completed the task of drawing the bitmap, but there is still
a bit of clean-up required before the task is finished:
Once the three local memory allocations are cleaned up, DrawBitmap is
free to report success. Everything’s done, for this operation, at least.
The DrawBitmap function is demonstrated in PenDraw5.C. Complete
source code appears at the end of this chapter.
Stretching Bitmaps
Drawing a bitmap on a one-for-one pixel basis is probably the operation that
will be most useful to you. However, there is another bitmap operation that
allows you to stretch or distort a bitmap to fit any space desired: the StretchBlt
function.
386 BORLAND C++ 3.0 PROGRAMMING, Second Edition
In the StretchBlt example program, a RECT structure has been used to pass
the size of the destination rectangle. In the BitBlt example, only a location was
passed, and the bitmap’s own width and height values were used to set the
size.
The second difference is that StretchBlt has four, rather than two,
parameters specifying what portion of the source device context to transfer.
Further, since the bitmap image is distorted in the process, StretchBlt uses the
stretching mode of the destination device context (set by the SetStretchBltMode
function) to determine how to stretch or compress the bitmap. As with
BitBlt, the raster operation specified by the dwRop parameter defines how
the source bitmap and the bits already on the destination device are com-
bined (see Table 18-6).
Last, StretchBlt can also create a mirror image of a bitmap if the signs of the
source and destination width, or source and destination height, are different.
That is, if the destination width is negative and the source width is positive
(or vice versa), StretchBlt creates a mirror image rotated along the x-axis. For
the height parameters, the mirror image is rotated along the y-axis.
The SetStretchBitMode
The SetStretchBltMode function sets the stretching mode used by the StretchBlt
function. The stretching mode defines which scan lines and/or columns
StretchBlt eliminates when contracting a bitmap. Stretch mode values are
shown in Table 18-7. The stretch mode is set for the destination device context
and is called as:
Value Meaning
BLACKONWHITE Eliminated lines are ANDed with retained lines,
preserving black pixels at the expense of white pixels.
COLORONCOLOR Eliminated lines are deleted without trying to preserve
their information.
WHITEONBLACK Eliminated lines are ORed with retained lines,
preserving white pixels at the expense of black pixels.
If you really need to move bitmaps around the screen, you may find better
results by using two bitmaps in combination, with the second bitmap created
as a screen mask. This prepares the image background and restores the
background when the image is moved.
For hints on how screen masks operate, refer to mouse cursor operations.
Some details on this subject can also be found in Chapter 16 of Graphics
Programming In Turbo C++, published by Addison Wesley.
AUTHOR_LOGO
BOX — top left OIL_DROP — top right
ELECTRIC — bottom left WHEEL — bottom right
Summary
Bitmap operations are powerful tools, and extend well beyond the few uses
demonstrated in this chapter. For example, bitmaps can be copied from the
screen, generated or modified off-screen, and pasted from window to
window.
However, attempting to demonstrate all of the potentials inherent in bitmap
operations would probably try our patience and prevent us from moving on
to other important areas.
Chapter 18: Brushes, Bitmaps, BLTs, and DIBs 389
#Hinclude <windows.h>
Hinclude <stdio.h>
#Hinclude "pendraw4.h"
case WM_SIZE:
cxWnd = LOWORD( LParam );
cyWnd = HIWORD( LParam );
return(Q);
case WM_PAINT:
hde = BeginPaint( hwnd, &ps );
hBrush = CreatePatternBrush(
hBitMapC nBitMap-IDM_BRICK J] );
SelectObject( hdc, hBrush );
SetMapMode( hdc, MM_ISOTROPIC );
SetWindowExt (¢ hidice, te\0) 5 S220) 5
SetViewportExt( hdc, cxWnd, cyWnd );
SetWindowOrg( hidicyyaae—=2ziOl Ose
SietiPouybauuuMode GC hid cs ALT ERINAN E>)
Roy, Oley CON Gaanid Gram Lem DIYs mca
DeleteObject( hBrush );
Enid Paint Ganiwinidsne& pisae s
return(Q);
case WM_DESTROY:
{for Ga=1) 1<5" irs) Deveted
hj ect. ne ith apeda.
PostQuitMessage(0);
return(0Q);
}
return(€ DefWindowProc( hwnd, msg, wParam, LParam ) );
}
#pragma argsused
iG ee hie Rmevelnsst a)
we.hInstance =Eenlinisices
we.lpfnWndProc = WndProc;
WIG. C DIC. US Exch nia =) (Fs
wce.cbWndExtra = 0;
Chapter 18: Brushes, Bitmaps, BLTs, and DIBs 391
we.lpszClassName = szAppName;
we.hIcon = LoadIcon( hInst, szAppName );
wce.lpszMenuName_ = (LPSTR) szAppName;
WiGeEniG Ui Siols = LoadCursor( NULL, IDC_ARROW );
we.-hbrBackground = GetStockObject( WHITE_BRUSH );
we.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass( &wec );
7 PENDRAW4.DEF :
NAME PENDRAW4
|}fessase]sase5es/ /
Hal PENDRAW4.H WU
/ fesse se seaSeseces/ //
| essa se=sS2Ses25=5e/ //
// PenDraw4.RC KY
// application menu //
/) (SS SseeSeSeeseseesss/ /
fefpa a S f
Hf PenDraw)5.C Ii
iif C++ Windows Drawing //
ji fees EoS—eSSeSSoeeesose/ /
#Hinclude <windows.h>
H#Hinclude <stdio.h>
#Hinclude "pendraw5.h"
HCURSOR HiGUunpSIonn:
switche msg ?)
(i
case WM_COMMAND:
hMenu = GetMenu( hwnd );
switch( wParam )
396 BORLAND C++ 3.0 PROGRAMMING, Second Edition
switch(€ BitmapOp )
case IDM_DrawBM:
SetWindowText( hwnd,
Brptimalpenc OO CulinlaneelsmyAltan UWpilaentituam a
break;
case IDM_CenterBM:
SetWindowText( hwnd,
"Bitmap Centered On Coordinates" );
break;
case IDM _StretchBM:
SetWindowText( hwnd,
“Ba tmaipars t het chedm hon ruktekec tang bens»)
break;
case IDM_Stretch2Client:
SetWindowText( hwnd,
"Bitmap Stretched To Fit Client Window"
break;
case IDM_BMTrackMouse:
SetWindowText( hwnd,
"Bitmap Tracks Mouse (LButton Down)" );
break;
case IDM_BMLineGraph:
SetWindowText( hwnd,
"Simple Line Graph Using Bitmaps" );
break;
}
InvalidateRect( hwnd, NULL, TRUE, )=
me Gunmicop):
case WM_SIZE:
cxStep = LOWORD(C LParam )/4;
cyStep = HIWORD( LParam )/4;
ReuulrniGcops:
case WM_MOUSEMOVE:
Chapter 18: Brushes, Bitmaps, BLTs, and DIBs 397
case WM_PAINT:
hidics =" BeginPaint Gthwnd 9 2ps >
Lee hey,
// all other paint operations here //
emer ie Ap ii
EndPaint(€ hwnd, &ps );
switch(€ BitmapOp )
{
case IDM_DrawBM:
DrawBitmap( hwnd, "BOX",
cxStep, cyStep Yee
DrawBitmap( hwnd, "OIL_DROP",
CXS ES Cy Sivep Sie
DrawBitmap( hwnd, "ELECTRIC",
cxStep*3, cyStep yee
DrawBitmap( hwnd, "WHEEL",
CoGSiielDit Sr nC Y:SIelDitom. >
DrawBitmap( hwnd, "AUTHOR_LOGO",
GxXS te piticy mG y oitelpinicam
break;
case IDM_CenterBM:
DrawCenBitmap( hwnd, "BOX",
Cx Sitelp, cyStep )
DrawCenBitmap( hwnd, "OIL_DROP",
cxStep, Gy Sitelp=so) i
DialwiGeiniBiistimiaip: Gash WitniClymue 11s EG Rell Cun,
cxStep*3, cyStep E-
DrawCenBitmap( hwnd, "WHEEL",
OIEWIeESD, CV Siweo+ sd)
DrawCenBitmap( hwnd, "“AUTHOR_LOGO",
CxrYSeGo22, GYSEGpYv ez WF
break;
case IDM_StretchBM:
Switvcncursorm® LDC OWATT: 2 >
SetRect( &rect, cxStep, cystep,
398 BORLAND C++ 3.0 PROGRAMMING, Second Edition
2*cxStep, 2*cyStep™);
StretchBitmap< hwnd, “BOX*; rects);
SwitchCursor ©’ IDelARKOW 7;
break;
case IDM_Stretch2Client:
SiwiineiG
MiCulm SiO) hiGurli Cm WIAt lala
StretchBitMap2Client( hwnd, "AUTHOR_LOGO" );
SwitchCursor( IDC_ARROW );
break;
case IDM_BMTrackMouse:
if€ 'bMoveBitmap )
a
ptBitmap.x = 10;
ptBitmap.y = 10;
bMoveBitmap = TRUE;
hBitmap = LoadBitmap( hGInst, "OIL_DROP"
}
MoveBitmap( hwnd, hBitmap, ptBitmap );
break;
case IDM_BMLineGraph:
PainieGiralp
niCahiwn dy same EC Rol Clas:
break;
}
return(Q);
case WM_DESTROY:
PostQuitMessage(Q);
return(Q);
}
return(€ DefWindowProc( hwnd, msg, wParam, lParam ) );
#pragma argsused
NAME PENDRAW5
Pp SeSeeseesssesseses
/ //
// PENDRAWS.H //
// fPseeSsaeassaSesseeee/ /
f/f SeSsasesessaesesoss/ /
Leh PenDraw5.RC Lh
//e*menuSstructure’ //
f /fPSSeee sos] SeSseeosses
////
Metafile Operations
Recording A Metafile
The first step in creating a metafile is to have an image produced by a series
of GDI drawing functions such as the five-or-seven pointed stars originally
created in Chapter 17.
For demonstration purposes, we will use the seven-pointed star and enclose
it with a circle drawn by the Ellipse function. Normally, once the calculations
401
402 BORLAND C++ 3.0 PROGRAMMING, Second Edition
for the points of the star are done, this could be drawn in response to the
WM_PAINT message as:
Not a particularly onerous task, first drawing a filled circle in light gray,
and then drawing the star in dark gray (see the logo above the chapter title),
but this will serve to demonstrate metafile operations.
Metafile operations are really not particularly different from conventional
drawing operations, but they do require a few additional variables:
The hdc variable, of course, has appeared in all of the previous examples,
only the hdcMeta handle is new.
For the metafile, instead of drawing the image in response to the
WM_PAINT command, the image is drawn in response to the WM_CREATE
command. The first step, before actually drawing the image, is to open the
metafile, as:
case WM_CREATE:
Limcalcuate. thes pomnit:s a/4/
hdcMeta = CreateMetaFile( NULL );
Because CreateMetaFile has been called with a NULL parameter, the metafile
created will be a memory metafile only. That is, the file will be stored in
memory, not in a disk file. (However, disk files can also be created, as
alternatives will show presently.)
After creating the metafile and receiving a handle to the file, the GDI
operations are carried out in almost exactly the same fashion as if an image
was being drawn directly to the screen:
The differences are fairly obvious: The usual BeginPaint instruction is replaced
by the CreateMetaFile instruction, and, instead of the customary screen device
context handle (hdc), the metafile device context handle (hdcMeta) appears in
all graphic drawing instructions. Finally, instead of an EndPaint instruction,
the metafile receives its own close file instruction:
DeleteObject( hPen );
return(Q);
The metafile, itself, is also a logical object and, in like fashion, should be
disposed of ... but only when the metafile, and the information contained in
it, is no longer needed. Therefore, the appropriate point in this application is
when the application closes, thus:
case WM_DESTROY:
DeleteMetaFile( hMetaFile );
PostQuitMessage(0Q);
return(Q);
Simply creating and eventually disposing of the metafile is all very well,
but there’s also the matter of using this information—replaying the metafile
—to actually create an image on screen.
Within the WinMain procedure, all normal screen paint operations are
carried out in response to the WM_PAINT message, and this case is no
exception.
404 BORLAND C++ 3.0 PROGRAMMING, Second Edition
case WM_PAINT:
hde = BeginPaint( hwnd, &ps );
SetMapMode( hdc, “MMVANTSOTROPLC):,
SetWindowExt (¢ hdc, LO00,e) LO00m);
SetViewportExt( hdc, cxWnd, cyWnd );
Remember, when the metafile was created, no specific mapping mode was
set, and no window or viewport extent or origin points were set up. Instead,
during metafile drawing operations all drawings were done strictly in logical
units, and now that these are about to be “‘played back’, the reproduction will
be “mapped” onto whatever mapping mode and coordinate system have been
established for the output device context.
When the graphics drawing instructions were encoded in the metafile, the
drawing was centered around a hypothetical (0,0) origin point. Nothing
requires this origin point; it was simply convenient, and the meta-drawing
could have been located anywhere in this meta-space that is, the “image”
could have been drawn around some other origin coordinate, and all drawing
operations would have been recorded at points relative to this origin and
offset from the theoretical 0,0 origin.
For the present, these drawing instructions as recorded are centered around
a 0,0 origin. To position the replay within the current device context, the
device context window origin point can be changed for each successive replay.
This will permit several images to be replayed from a single recording, that
is, the recorded image will be replicated by repeating the instructions necess-
ary to create the image, but changing the window origin point each time:
This results in six images, though only five will appear on screen since the
center image is drawn twice, overlying itself.
Chapter 19: Metafile Operations 405
Either before or after replaying this or any other metafile, other drawing
instructions could be carried out. But remember, these are not images being
copied to the screen, these images are being drawn on command, just as any
other drawing instructions might be carried out. This could also include ROP
instructions affecting how the new image was combined with background
images, including other images created by replaying metafiles. Once the
drawing instructions are completed, just as in all previous examples, the
EndPaint instruction closes the process:
This illustration also shows one possible pitfall in using metafiles, because
the figures illustrated are intended to be round. This error is not unique to
metafiles, but is caused simply by using the MM_ ANISOTROPIC mode;
switching to MM_ISOTROPIC would correct the error.
Such errors aside, there remain a few other metafile operations which are
worth noting.
Neither the filename nor the extension have any particular significance,
though you may prefer to use the .WMF extension as a convenient convention.
This could also be written with an indirect reference, thus:
In either case, when the metafile is written to a disk file, the DeleteMetaFile
instruction which was issued in response to the WM_DESTROY message does
not affect the disk file itself, only the local handle to the file.
As a further alternative, a temporary file can be created. A temporary file
is more ephemeral than a conventional disk file, but less ephemeral than a
memory file. To create a temporary file, in response to the WM_CREATE
message, the GetTempFileName function is called as:
case WM_DESTROY:
DeleteMetaFile( hMetaFile );
unlink(€ szMetaFileName ); WH «eirelol) i
PostQuitMessage(Q);
return(Q);
The unlink function erases most files without requiring a file handle or other
handling provisions. Note: Read-Only files can not be unlinked.
retrieve or create a metafile handle for a file which was not created by the
application, or which was discarded by the DeleteMetaFile function. This is
provided by the GetMetaFile function, which is called as:
Once this is done, the metafile can be used as before. When done, the new
metafile handle is discarded as before, using the DeleteMetaFile function.
Of course, if a metafile is being created by one application for use by a
second application, then the creating application should not unlink the disk
file, but the second application most definitely should!
Note: Leaving trash files on the disk is simply bad manners for any pro-
gram, not to mention bad programming.
Metafile Structures
Metafiles are structured records using the METARECORD and METAHEADER
structures listed in Appendix A under the heading, "MetaFile Picture
Structures". A third structure, the METAFILEPICT, is discussed further in
Chapter 22, "Using The Windows Clipboard." For those who are curious,
however, Figure 19-2 shows a sample metafile as created by the PenDraw6
demo program.
The first two WORDs in each record identify the number of words in the
record, including the first DWORD value. This value is, of course, expressed
in Isw,msw order while each word value is expressed in Isb,msb order.
The third WORD in each record is the function identifier while the remain-
ing WORD(s) are parameters (arguments) passed to the function.
Within the function identifier, the low byte identifies the specific GDI
function call, while the high byte is normally the number of word
parameters passed to the function. Thus the hex value 0418 identifies the
(18) Ellipse function which receives (04) four parameters, excluding the
hdcMeta parameter.
Metafile codes can be found in Appendix A under the heading "Metafile
Constants", both in alphabetical and numerical order. (In Appendix A, to save
space, the prefix META_ has been omitted from the individual listings.) Also,
the arguments following the function call are in reverse order, that is, a GDI
call which originally appears as:
O7e0 0s OOOUr B16) 04. 646 00.9764 70088 9'C RES >9 CsaEE
Remember, each WORD value appears here in /sb,msb order. Therefore, the
seven word values can be rewritten in a more comprehensible format as:
and read as: seven words in length (a double-word value), 418 identifying
META_ELLIPSE with four parameters, with the parameters themselves follow-
ing, in reversed order, as (64h) 100, (64h) 100, (FF9Ch) -100, (FF9Ch) -100.
The complete metafile contents would be deciphered as shown here (note
that all values have been normalized for the reader’s convenience):
CREATEPENINDIRECT
SelectObject( hdcMeta, hPen );
00000004 012D 0000
SESE
GO Brice)
SelectObject( hdcMeta,
GetStockObject( .LTGRAY BRUSH ) );
00000007 O2FC 0000 coco O0cO 0000
CREATEBRUSHINDIRECT
00000004 012D 0001
SRE
GH OBIE Cm
EV Gi pse. GahdcMetaa. —100;. 10071007 00s;
UVOODCU07 C418 OC0O64 C064 FFIC FEVE
SILIL
WP Sle
SelectObject( hdcMeta,
GetStiock0bject:GaDKGRAYLBRUSH! Dy >>
00000007 O2FC 0000 4040 0040 0000
CREATEBRUSHINDIRECT
00000004 012D 0002
SE EEG ROBECa
SetPolyFillMode( hdcMeta, ALTERNATE );
00000004 0106 0001
SEARO
Yar Sie MO DIE
Rolsyicon GuandicMic tars pity mn(ame
00000012 0324 0007 O000 0064 002B FFA6
POLYGON
FiRae MOS OOM RiFEN FEOF Rls
OO4E OO3E FFD5 FFA6
00000003 0000
(NULL RECORD)
MetaFile Cautions
When using metafiles, there are a few characteristics which you should
keep in mind, because an awareness of these may help prevent errors and
confusion:
= The metafile is not a true device context. It does not correspond to any
actual device, does not include a mapping mode, or window or
viewport sizes and origins.
= All parameters entered in the metafile are entered as values, not as
formulas. For instance, an argument such as cx Wnd/2 will be recorded
as the calculated value at the present time, and will not be affected by
future changes in the client window size. (This conflict was avoided in
the PenDraw6 example by using the isotropic mapping mode.)
= The metafile is always interpreted in terms of the existing mapping
mode, with the exception that the metafile may include instructions
setting a specific mapping mode.
There are also instructions which can not be used in metafiles. All per-
missible instructions begin with a device context parameter (hdcMeta) as the
first argument.
The following five categories of GDI instruction are not valid in a metafile
context:
Finally, if you are in doubt about whether a GDI function call is permitted
in a metafile, check the table of Metafile Constants in Appendix A. But
remember, some GDI functions may not appear simply because the compiler
will automatically choose a more compatible variation, such as substituting
CREATEPENINDIRECT for CREATEPEN.
and after the metafile is finished, the original device context can be restored
as:
Ries tone DiICiGe hidic > a— la) y
Second, the metafile itself could include these instructions, and so would
save and restore the device context when executed. Remember, each SaveDC
function call must have a corresponding RestoreDC with the -one parameter.
Summary
Metafiles not only provide a powerful means of storing and replaying complex
drawing instructions, but also provide a means of transferring graphic
information between applications, as will be discussed in Chapter 22. For the
present, the PenDraw6 example following provides a convenient platform for
experimentation with metafiles, with a second version showing the revisions
necessary to create temporary disk files.
For your own understanding and for general practice, you might like to
create two new programs for metafiles: One to record a metafile as a disk file,
and the other to read the metafile from disk using the GetMetaFile instruction
and play back the instructions.
Chapter 19: Metafile Operations 413
#include <windows.h>
#include <stdio.h>
Hinclude <math.h>
switch( msg )
{
case WM_CREATE:
roOrg VeEyeOs WS Narr, seis 4e 2 // seven points //
{
Dae ax GCintyG sinG je Pl2/7? D -*> 100n0-
VeEdido”v = Chnex€ COSC FePUZ/¢r » & WOW WX-
}
hdcMeta = CreateMetaFile( "D:\\METAFILE.MTA" );
hPen = CreatePen( PS_NULL, 1, OL );
SelectObject( hdcMeta, hPen );
SelectObject( hdcMeta,
GetStockObject( LTGRAY_BRUSH ) );
Eltipse® hdcMetale =—1005>-1007 100, 1000);
SelectObject( hdcMeta,
GetStockObject( DKGRAY_BRUSH ) );
SetPolyFillLMode( hdcMeta, ALTERNATE );
Polygon( hdcMeta, pt, 7 );
hMetaFile = CloseMetaFile( hdcMeta );
DeleteObject( hPen );
return(Q);
414 BORLAND C++ 3.0 PROGRAMMING, Second Edition
case WM_SIZE:
cxWnd = LOWORD( GRaiwaimnmee:
cyWnd = HIWORD( lParam );
ResuUinMmiGune:
case WM_PAINT:
hdc = BeginPaint( hwnd, &ps );
SetMapMode( hac, MBL ANISOTROPIC
SetWindowExt ( nee, WOOO, WOW Hw;
SetViewportExt (¢ hidicy wc Winid), macy Wind aso
Torn i= Oe ai< 57 j++ )
{
SetWindowOrg( hdc, -200-(€i*300), -500 );
PlayMetaFile(€ hdc, hMetaFile );
SetWindow0rgiG@ hdc, —5007. -200—Ci*300) >
PlayMetaFile(€ hdc, hMetaFile );
}
EndPaint(¢ hwnd, &ps );
return(Q);
case WM_DESTROY:
DeleteMetaFile( hMetaFile );
PostQuitMessage(0);
return(Q);
}
return(€ DefWindowProc( hwnd, msg, wParam, lLParam ) );
}
#pragma argsused
17 1 INnPrawinsw 2
£
Wc. hInstance hiinisst }
wc. LpfnWndProc WndProc;
wc 2ODIGUSIE Guna 0;
wc. cbWndExtra 0;
wc .-lpszClassName szAppName;
we. hicon LoadIcon( hInst, szAppName );
wc. lpszMenuName CLPSTR) szAppName;
Chapter 19: Metafile Operations 415
5 PENDRAW6.DEF
NAME PENDRAW6
ji / Sse sesSeoSeese
i //
a, PENDRAW6.H Mfif
/ fSeSSsSeessesessee/ //
/ fSesSsssosesesees/ /
Hfif PENDRAW6.RC //
f/f fPoscesssSssssece//
/ esses]
3955 56453 = S568 ////
// PenDraw6.C //
Lif C++ Windows Drawing //
// If
// alternate version df
// for temporary disk ifI)
Fi Filas if
i / SS Sasa sasse=seessaeeeaa/ /
Hinclude <windows.h>
Panic Gudies <sitda oOmin>
Hinclude <math.h>
switch( msg )
c
case WM_CREATE:
GemeunMiGoD
case WM_DESTROY:
DeleteMetaFile( hMetaFile );
unlink€ szMetaFileName ); // add Ved
PostQuitMessage(0Q);
rect unmnmGop
}
return(€ DefWindowProc( hwnd, msg, wParam, UP arama).
no further changes //
face
ElrHE
Chapter 20
417
418 BORLAND C++ 3.0 PROGRAMMING, Second Edition
Thus far, TextOut has been viewed as a plain vanilla output function,but when
it is combined with the SetTextAlign function, it is capable of somewhat more
sophisticated performance.
The SetTextAlign function is used to control the alignment of output text
relative to the xPos and yPos arguments passed with the TextOut and ExtTextOut
functions and is called as:
The wFlags argument consists of one or more flag specifications. These are
combined using the OR operator, which set the relationship between a specific
point and a rectangle bounding the text displayed. As shown in Table 20-1,
these flags consist of three groups: horizontal alignment, vertical alignment,
and current position, and only one flag can be specified from each group.
ikalbibie
dike x tOlu ti Ganhidicg, aX Pioisy-snyzrO'S),saliDis tGhs,eenic OUlnite,
nTabPositions, lLpnTabStopPositions,
Nira biOwma gain);
Tabs are included in the /pStr string argument by using embedded \t (or
0x09) characters. The nCount parameter specifies the number of characters in
the string.
nTabPositions is an integer argument specifying the number of tab-stop
positions in the [pnTabStopPositions array. If nTabPositions is zero and IpnTab-
StopPositions is NULL, tabs are expanded to eight average character widths.
If nTabPositions is one, all tab stops will be separated by the distance specified
by the first value in the /pnTabStopPositions array. |pnTabStopPositions points
' to an array of integers containing the tab-stop positions specified in pixels.
Tab stops must be sorted in increasing order and back-tabs are not allowed.
If IpnTabStopPositions points to more than one value, a tab stop is set for
each value in the array, up to the number specified by nTabPositions.
The nTabOrigin parameter is an integer value specifying the logical x-
coordinate from which tabs are expanded.
420 BORLAND C++ 3.0 PROGRAMMING, Second Edition
Value Meaning
Horizontal Justification
DT LEFT text is aligned flush-left
DT_CENTER text is aligned centered
DISRIGHT text is aligned flush-right
Vertical Justification
DIATOP. text is top-justified (single line only)
DT_VCENTER text is centered vertically (single line only)
DT_BOTTOM text is bottom-justified; must be combined with DT_SINGLELINE
DT_EXTERNALLEADING adds font external leading to line height
DT NOGLIP clipping to rectangle is disabled, operation is marginally faster
DT_SINGLELINE sets single line only; carriage returns and line
feeds do not break the line
Format Instructions
DT_EXPANDTABS expands tab characters (default is 8 characters per tab)
DT_TABSTOP sets high-order byte of wFormat as the number of characters per tab
DT_NOPREFIX disables processing of prefix characters
DT_WORDBREAK enables word breaks; lines are broken between words if a word
would extend past the edge of the rectangle set by the /pRect
parameter (CR/LF sequences function normally)
Normally DrawText interprets the mnemonic-prefix character "&" as a directive to underscore the
character following, and "&&", as a directive to print a single "&".
For multiple lines of text, DrawText uses the width of the rectangle indicated
by IpRect, extending the base of the rectangle to bound the last line of text. For
a single line of text, DrawText modifies the right boundary of the rectangle to
bound the last character in the line. In both cases, DrawText returns the height
of the formatted text but does not draw the text.
Note: The DT_CALCRECT, DT_EXTERNALLEADING, DT_INTERNAL,
DT _NOCLIP, and DT_NOPREFIX values cannot be used with the
DT_TABSTOP value.
422 BORLAND C++ 3.0 PROGRAMMING, Second Edition
Sleltane
> &Gol Cols Gun CiGr-mmING| D1GOMOD E
or
In the first instance, the rgbColor is, like pen and brush colors, converted to
a pure color; dithered colors (which are not directly supported by an output
device) are converted to colors that are supported. In the second instance, the
window text color reported is presumably already a color which is supported
by the device. In either case, the resulting color can be retrieved by calling the
GetTextColor function.
Drawing text also affects the display background because, in the default
OPAQUE background mode, the areas between the character’s strokes (or
pixels) are filled using the current background color. The background mode
is changed using the SetBkMode function as:
Or,
If the GetSysColor function is used to retrieve system color settings, you may
want to add a provision to repaint the entire client window if or when these
colors are changed, that is, if the control panel is used to alter the system
colors. Such a provision is simple to create, requiring only:
case WM_SYSCOLORCHANGE:
InvalidateRect( hwnd );
break;
Stock Fonts
Windows 3.0 provides a variety of fonts, both bitmapped and stroked. These
can be selected, sized, and modified in several fashions as we will see pre-
sently. First, however, there are six stock logical font settings which can be
selected for text output without the details required for custom fonts.
The six stock logical fonts are defined in Windows.H as:
Of course on earlier computers, there was little demand for larger typefaces.
Only with the advent of graphic display systems did the advantages of sizable
fonts became every bit as obvious as the disadvantages of sizing bitmapped
fonts.
One possibility was to create libraries of bitmap fonts in incremental sizes.
This was never seriously considered as a solution, because the sheer size of
the data required for this approach was also obvious. Instead, a second type
of computer font was invented, known as the stroked or vector font. In these
fonts, instead of a bitmap image each individual character is described as a
series of lines or vectors that form an outline of the character. The advantage
is that these stroked fonts can be sized (enlarged or reproportioned) with
much less loss of image fidelity, as shown in the right side of Figure 20-1. Still,
while stroked fonts provide a number of advantages, one disadvantage is also
apparent in Figure 20-1. As you can see, a sufficiently enlarged font is con-
siderably lighter than the equivalent bitmap font, simply because the strokes
comprising the character paint only the relatively few pixels which these
strokes actually intersect, thus leaving large areas of unpainted pixels within
the character.
The original Borland Graphic Interface (BGI) took one approach to cir-
cumventing this problem by creating the Triplex font that increased the
number of strokes defined for each character.
Other approaches used by various software packages include provisions to
paint enclosed regions and, frequently, provisions to smooth enlarged vectors.
426 BORLAND C++ 3.0 PROGRAMMING, Second Edition
Typefaces
To a printer or a typographer, the term "typeface" refers to the style of type,
differentiating not only between families of type such as Times Roman and
Helvetica, but also between the italic, bold, extra bold, condensed, etc., vari-
ations within a family of type styles.
The term "font" refers to a complete set (alphabet plus numbers, etc.) of
characters in a single size, style, and typeface. Thus a specific font might be
referred to as 12pt (size) Helvetica (family) Bold (style).
In computer terms, the specific size of a font is variable, and is determined
by several factors, including mapping mode, height, width, and weight, as
well as style option settings such as italic.
In Windows 3.0, five families of typeface are provided: Decorative, Modern,
Roman, Script, and Swiss. These are not firm descriptions, but general categor-
ies based on the appearance of the type style. Depending on the mode and
selection, Windows may choose different typefaces regardless of your font
selections.
Chapter 20: Graphic Typefaces and Styles 427
Constants for the six font families are defined in Windows.H, as shown in
Table 20-4.
Family ID Characteristics
FF_DONTCARE don’t care or don’t know
FF_ ROMAN variable stroke width, serifed as Times Roman,
Century Schoolbook, etc
FF_ SWISS variable stroke width, sans-serif as Helvetica, Swiss, etc
FF_MODERN constant stroke width fonts, may be serif or sans-serif
as Pica, Elite, Courier: that is, typewriter fonts
FF_SCRIPT . cursive, imitating handwriting
FF_DECORATIVE catch-all, Old English, Symbols, Zapf Dingbats, etc
For example, in Figure 20-2 both the ANSI and OEM character sets are
stepped through three pitch selections and five family (font) selections, with
the selected typeface resulting shown for each selection ona VGA display. The
results are eight different typefaces: Courier, Helvetica, Modern, Roman
Script, System, Terminal, and Times Roman. As an example of the potential
confusion, the Roman family selection appears using four typefaces: Times
Roman, System, Roman and Terminal. At the same time, in the OEM character
set, the Roman typeface appears in response both to Roman, Swiss and Don’t
Care family selections.
However, such idiosyncracies are not worth any particular study. These are
simply the font selections which best fit the specified criteria in each case, and
when selecting custom fonts for your own applications, such idiosyncracies
will not be a problem because your selections can be much more explicit.
Three types of fonts are included, beginning with three video specific (that
is, system or terminal) fonts: VGASYS.FON, VGAFIX.FON, and
VGAOEM.FON. For a CGA system, the equivalent font files would be
designated as CGAxxx.FON or, for an EGA system, as EGAxxx.FON.
Next are the GDI stroked fonts: MODERN.FON, ROMAN.FON, and
SCRIPT.FON. These are installed on all systems.
And, last, the bitmapped typeface fonts: Courier (COURx.FON), Helvetica
(HELVx.FON), Times Roman (TMSRx.FON), and Symbol (SYMBOLx.FON).
In each case, the last letter of the file name identifies the device (video or
printer) the font was designed for, as shown in Table 20-6.
You may also find that 40 and 80 column CGA/EGA fonts have been
installed for use in the DOS shell. These are not, however, accessible or needed
within Windows applications. These four fonts are named as:
CGA40WOA.FON, CGA80WOA.FON, EGA40WOA.FON — and
EGA80WOA.FON.
430 BORLAND C++ 3.0 PROGRAMMING, Second Edition
Italics
The/fItalic field is a byte value used as a flag. When non-zero, Windows creates
an italic version of GDI fonts by slanting the characters to. the right. With
bitmapped fonts, italics tend to appear unusually grainy, particularly when
enlarged. For non-video output devices, check the TC_IA_ABLE bit in the
TEXTCAPS returned by GetDeviceCaps ( see Chapter 15).
Chapter 20: Graphic Typefaces and Styles 433
Character Sets
The /fCharSet field is a byte value selecting the desired character set. Four
constants are defined in Windows.H, as shown in Table 20-9.
Font Clipping
The [fOutPrecision byte instructs Windows how to clip characters which would
fall partially outside the clipping region. Three options are defined in
Windows.H as:
Typeface Names
The /fFaceName field is an array of byte, defined as:
This field contains the name of a specific typeface such as Courier, Helvetica,
Symbol or Times Roman, which can be retrieved using the GetTextFace function
as:
When the font is no longer needed, the logical font can be deleted using the
DeleteObject function. However, remember that a logical font (like any other
logical object) should never be deleted while in use, that is, while selected in
a valid device context, and, of course, stock logical fonts should never be
deleted at all.
CreateFontIndirect( lLpLogFont )
The /fHeight value is always used, with Windows attempting to match the
specification even if this requires, as it often does, enlarging the height of an
existing font. The resulting character size will always be less than, or equal to,
the /fHeight specification unless there simply is no smaller font available. For
small fonts, the IfHeight may take precedence over other considerations, if
necessary, to find a font small enough.
The /fQuality value can be set to PROOF_QUALITY to prevent font scaling.
The SetMapperFlags function controls how Windows matches fonts and
aspect ratios, affecting how values of [fHeight and IfWidth are treated when
these do not match the aspect ratio of the device. This is called as:
Sent Mialpipielmiralealgiss Gas hidice- maul a=
Windows is instructed to select only fonts with the same aspect ratio as the
output device. Fonts of other aspect ratios can be selected by first restoring
the default mapper flag setting as:
SetMapperFlags( hdc, OL );
logical units per logical inch. This Logical TWIPS mode provides an easy
correspondence between the logical inch mapping modes and the font sizes
which are expressed in points.
Last, along the right side of the dialog box, the current logical font’s text
metrics are reported.
For the most part, the Fonts1.C program is simply a dialog handler and
contains little that is new except for the Logical TWIPS mapping mode.
pT EDS EGIL GE
as red. dag. 127456 7890 |
remember, the point size still needs to be set in logical units, and therefore for a
30-point line spacing in Logical TWIPS, the ImHeight value would be set as 30
(points) times 20 units per point for a height of 600 logical units. More often,
however, the point size desired is not the line spacing, but the actual font size,
which requires a slightly different calculation. Remember, the ImHeight value is
tm.tmHeight + tm.tmInternalLeading, while the typeface point size is determined
only by the tm.tmHeight value. Therefore, to calculate an actual typeface point
size, a little subterfuge is needed, and ImHeight can be calculated as:
After all, once a typeface has been selected, fnRatio will be a constant for all
point sizes in this typeface.
The line spacing (ImHeight) can be calculated in a separate step as:
LmHeight = fnRatio * PointSize;
For example, in Logical TWIPS, the Roman font (OEM) has a height of 274
units with an internal leading of 17 units. This calculates a value for fn Ratio as
1.06614. Therefore, to create a 30-point font you need to set a value of 640 for
ImHeight (1.06614 * 30 * 20 = 639.688). Granted, this result is not 100 percent
correct, but the results are accurate enough for the display, and certainly well
within the available pixel accuracy. In other words, the results are good
enough for most practical purposes.
Summary
As demonstrated, Windows 3.0 fonts are both flexible and convenient, even
though not all of the features expected are present quite yet, and the quality
of the enlarged fonts falls somewhat short of dedicated graphics/draft-
ing / typesetting programs such as Ventura Publisher or CorelDraw. Further,
the few short comings mentioned exist largely because of very necessary
trade-offs between speed and elaboration, since more elaborate text-graphics
would present their own aggravations in the form of slow drawing. Nonethe-
less, you have seen the basic operations necessary for any type of screen
440 BORLAND C++ 3.0 PROGRAMMING, Second Edition
elaboration desired. In Chapter 21, font operations will continue, but with the
focus on printer output devices.
j/ Pesessss2esssseesseseee//
lf BOmusi lec IfUf
(ia eCt+. Windows, BOntSs.d/
ji f/fSSS SSeoSoscSeoessseses/ /
Hinclude <windows.h>
Avni thc Cm eutO
nit Secunia
char SzZrormtNameLRPLFACESIZEI;
BOOL bTrans;
HFONT Mir Ome -
HDC hidicr
int ip CharpSews
aps sel @al @jlyie = GetDlgItemInt( hDlg, IDD_HEIGHT,
Sbigans, |TRUE.)
teen
Get Witidetan = GetDlgItemInt( hDlg, IDD_WIDTH,
SbT rans; sCALS es:
fee eaWean Git = GetDlgItemInt( hDlg, IDD_WEIGHT,
SiDilewainisy-as b AlSSiEe
iach
Lt Lac = (BYTE)
IsDlLgButtonChecked( hDlg, TDI Deel pA eli YP
Lf.-lfUnderline = (BYTE)
IsDlgButtonChecked( hDlg, IDD_UNDERSCORE );
Choi Stra kedute= (BYTE)
IsDlgButtonChecked( hDlg, IDD_STRIKEOUT );
dwAspMatch = (LONG)
IsDlLgButtonChecked( hDlg, IDD-ASPECT );
GetDlgItemText( hDlg, IDD_FACENAME,
UiieenUehaharcre Naimicy-am le hie AUG ENSnle7ZE amen
hdc = GieleDIG Ge hiDiEgm= sr
SetMapModeProc( hdc );
SetMapperFlags( hdc, dwAspMatch );
DiFOmites— SelectObject( MNCiCy- me Camerantaes OmatelaniGelmine)G: ts Gumclara) =
GetTextMetrics( hdc, &tm );
GetTextFace( hdc, sizeof ( szFontName ), szFontName );
DeleteObject( SelectObject( hdc, Miroame 2 MX,
ReleaseDC( hDlg, indicus:
TOR T=0)s i<sizeof(FontData)/sizeof(FontDataLl0Ol]); eh ae)
SetDlgItemInt( hDlg, FontDataLil.nDlgID,
SFOMEDACAIC Td afr, WiRWls p<
SetDlgItemText( hDlg, IDD_TM_PITCH,
tm.tmPitchAndFamily & 1 ?
"Variable" MP a Sel =
SetDlgItemText( hDlg, IDD_TM_FAMILY,
SzFontsl tm.tmPitchAndFamily >> 4 J );
switch(€ tm.tmCharSet )
if
case ANSI_CHARSET: Gihiae Sicstea— a Or eee Dimeraike>
case SYMBOL_CHARSET: CharSet = 1;. break;
case OEM_CHARSET: CihaiS eater break;
}
SetDlgItemText(¢ h Diligy DI Dae Me GHiAR Ss!Eativ,
Sacha seit mc hdinole tells,
SetDlgItemText ¢ nOUg, 1 DD= rE NAME, ‘sz PontName )-;
2
#pragma argsused
BOOL FAR PASCAL DlgProc(€ HWND hDlg, WORD msg,
442 BORLAND C++ 3.0 PROGRAMMING, Second Edition
switch( msg )
{
case WM_INITDIALOG:
CheckRadioButton( hDlg, TDD TEXT, 1DDLEOGIWIPS,
TD Diese Xoaleee
CheckRadioButton( hDlg, IDD_ANSI, IDD_OEM,
IDD_ANSI );
CheckRadioButton( hDlg, IDD_QUALDEFAULT,
IDD_QUALPROOF, IDD_QUALDEFAULT );
CheckRadioButton( hDlg, IDD_PITCHDEF,
DID EP GHINIAIRY, IDD_PITCHDEF );
CheckRadioButton( hDlg, aD DEEDIONT GARE, IDD_DECO,
IDD_D ONTCARE );
Lf.lfEscapement 7
(wParam - IDD_PITCHDEF );
break;
switch( msg )
{
case WM_CREATE:
helenSs Ga Gen GIS PC RIEVAGESmaRUIGHiD mm eral Mi sehen Siteainicer.
lLpfnDlLgProc = MakeProcInstance( DlgProc, hInst );
hDlg = CreateDialog( hInst, szAppName, hwnd,
WpimiDil GiPirolcm s-
PAU UTEMCO) 5
case WM_SETFOCUS: Sielt Fiore uisi@ mini DI gm r PET
WP iC (8)) 7
case WM_PAINT:
hdc = BeginPaint( hwnd, &ps );
SetMapModeProc( hdc );
SetMapperFlags( hdc, dwAspMatch );
GetClientRect< hDlg,. Srecit? );
rect.bottom += 1;
DPtobee hdc, <LPPOINT) &rect, 2 );
444 BORLAND C++ 3.0 PROGRAMMING, Second Edition
return(€ msg.wParam );
NAME FONTS1
DESCRIPTION "WINDOWS TEXT FONTS"
EXE ee E WINDOWS
STUB “WINSTUB.EXE"
CODE PRELOAD MOVEABLE DISCARDABLE
DATA PRELOAD MOVEABLE MULTIPLE
HEAPSIZE 1024
STACKSIZE 8192
EXPORTS WndProc
DlgProc
5 les eOOrel2
CONTROL "Character Set" -1, "BUTTON",
WS. CHILD Ol WSL VISIBLE | MOX chp > 220, 8607 8a
CONTROL "Quality" -1, "BUTTON",
WS CHILD | WS. VISIBLE | Ox7lke 1697 °1087 043742
CONTROL "Pitch" -1, "BUTTON",
WS _CHLLDE| WSHVISIBLE |MOXZle les 11065 a6 07 eae
CONTROL "Type Style" -1, "BUTTON",
WS CHLCD. | WSSVISIBLE |"O0x7L7869, 159, 102-eac
CONTROL "Set Font" 1, "BUTTON",
WS_CHILD | WS_VISIBLE | WS_TABSTOP| Ox1E, 5,54, 607714
CONTROL "Text Metrics" -1, "BUTTON",
WS CHILDu} DWSSVESIBLE |"OxZEyet76,00> '26>2450
CONTROL “Height”. —4,_ "STATIC",
WS CHILD. WS_VISIBLE | WS_GROUPJS180;012) 244778
CONTROL “Ascent™ =-1, "STATIC",
WS -CHIUDs | WS-VISIBLE..| AWS=GROUPP 13807 e215044508
CONTROLS“ Descent vu-1, “STATICS
WS CHICDA[ WS=VESIBLE | WSEGROUP, 1180, 930e544e08
CONTROL. “Intebead® —1) “STATIGZ-
WS CHILD. [OWS VISIBLE. [MWS “GROUP E180 ,439Rn44 588
CONTROL "Ext Lead" \-1)-"STATIC®Y,
WS. CHILD | WS_VISIBLE [MWS@GROUR SE TS0p res e44ers
CONTROL “Avg Width" -1, "STATIC",
WS. CHILD | WS _VISIBLE* [> WS2GROUP 3180.45 75464558
CONTROL “Max Width" -1, "STATIC",
WS CHITD [ WS_VISIBLE | WS GROUP. .1809266-044) 60
CONTROL “Weight” /-1, “STATIC",
WS=CHIED | -WS2VISTBLE | WS*GROUP.. 160, 45 Gees
CONTROL “Pitch" -1, "STATIC",
WS_CHILED |MWSLVISTBLE*[6WS_GROUP,/180+1845 44548
CONTROL “Type Face" -1, "STATIC",
WS CHILD) [EWSEVISIBLEG| AWS. GROUPS 118024954 .445 18
CONTROMU Charset r=15 “STATI GN,
WS CHILD" |) WSZVISIBLE | “WSU GROURSETSO 2 =102e044uN8
CONTROL "Overhang" -1, "STATIC",
WS-CHILD [ WS. VISIBLE. | WSLGROUP, 180)11385'44- "8
CONTROL "X Aspect" -1, "STATIC",
WS_CHILD | WS_VISIBLE | WS_GROUP, 180, 120, 44,<8
CONTROL “Y Aspect: —-1) “STATICE,
WS_CHILD | BWSSVISTBLE?| WSS GROUPS I98020129 5144-98
CONTROL "Font Name" -1, "STATIC",
WS_CHILD | WS-VISIBLE*| WS_GROUP, 180, 111, 44,78
CONTROL “O" 200, "STATIC" 7
WS_CHILD | WS_VISIBLE [EWS-GROUPS |SOx2E S234 ieee s:
CONTROL "0" 201. “STATIC?
WS CHILD [°WSOVISIBLE f WSZGROUP, | Ox2her2s4ee21, ana
CONTROLS 0 a2 02 yeas WA Cue
WSa Chip. | Woo VIC LB IE | WS_GROUP |>Ox2U7 E234 SOF 5-4
CO NW ROI Soe 2 Ole ue deamon
WS CHIED™
_ | WSEVESTBEE f WS_GROUPS S020 8234 ,0398 2158 co
0UCOlmlrllCOT
Chapter 20: Graphic Typefaces and Styles 449
+ ; » »>« = 3°
5 ry so a _
Ce - a)
1 ® : 7
i “ '
> : : - waa
7 Ke > =
f =. f
Chapter 21
Thus far, we've spent quite a bit of time talking about device independence,
including device-independent bitmaps (DIBs), and device-independent video
displays using the isotropic mapping mode and automatic dithering of colors.
These are all devices used by Windows to make applications transportable
from one system to another.
But what about printers? After all, printer capacities vary even more than
video displays, ranging from 9-pin to 24-pin dot matrix printers to daisy-
wheels and spin writers to laser jet and ink jet devices—quite a diversity, not
only in mechanisms but also in print resolutions and device capacities. In the
past, it has been quite a headache trying to guess what escape codes were
supported by any specific printer.
Previous solutions to the mystery printer problem have ranged from treat-
ing all printers as ‘‘bare-bones’’ systems supporting virtually nothing except
the simplest character output to elaborate printer support libraries which
queried the user about types and features. Neither extreme was an ideal
solution to the problem. Unfortunately, Windows 3.0 does not have an ideal
solution either, but it does provide a better solution than previously available.
Under Windows, a variety of printers are supported by printer device
drivers that provide largely device-independent operation for both text and
graphic operations. These are not miracle device drivers, and they do not make
non-graphic devices suddenly graphics-capable. Also, these drivers do not
support absolutely every printer available, although they do support almost
45]
452 BORLAND C++ 3.0 PROGRAMMING, Second Edition
all contemporary printers. Most important, using the Windows drivers, your
application does not need to worry about printer control sequences, com-
munications protocols, and interpreting error messages, because these are
tasks handled by the drivers. Of course, the question always remains, did the
end-user install the correct printer driver(s) to match their hardware? But
under Windows, this question occurs only once, (when Windows is installed),
and is no longer the application programmer's concern.
In Windows, however, both device output and error messages are handled
by a single function, the Escape function, which will be discussed presently.
First, however, the topic will be setting up printer operations:
read from the input file into Buffer, and then write the contents of Buffer to the
printer (hdcPrn).
454 BORLAND C++ 3.0 PROGRAMMING, Second Edition
The only difference between writing the output to the screen instead of the
printer would be the device driver handle, that is, changing hdcPrn to hdc.
Graphics instructions can be written to the printer in a similar fashion. For
an example, in Chapter 17, five- and seven-pointed stars were written to the
client window to demonstrate the difference in the winding and alternate fill
modes. With a few minor revisions, these same two diagrams could be written
to the printer. The first change required would be replacing the instruction:
hdc = BeginPaint( hwnd, &ps );
Of course, when you are finished, the device context needs to be closed, but
instead of:
This section has been a bit over-simplified because there are some printer-
specific instructions which have been omitted here, but which will be dis-
cussed in a moment.
Escape Subfunctions
One very important element is missing in the preceding examples: The Escape
function calls which are used to send printer specific instructions to the printer
driver.
Chapter 21: The Printer Device Context 455
The hdcPrn parameter identifies the device context, and the nEscape para-
meter identifies the subfunction requested. IpInData is a LPSTR pointer to a
structure containing the input data required by the escape subfunction, while
nCount indicates the number of bytes in the input structure. Last, [pOutData
points to a data structure receiving data returned by the subfunction. In most
of the examples discussed here, nCount is zero, while the [pInData and IpOut-
Data parameters will be NULL.
Escape calls made by an application are translated and sent to the device
driver by Windows, returning an integer value specifying the outcome of the
function. If successful, a positive value is returned. The exception is the
QUERYESCSUPPORT escape, which checks implementation only, and
returns zero if the escape is not implemented.
A negative value indicates an error with five error codes defined as:
the Escape function, it’s time to amend the example and show how printer-
specific instructions would be included.
The first instruction needed, after getting the printer device context, is an
Escape STARTDOC subfunction call as:
hdcPrn = GetPrinterDC( hwnd );
Teta EsSiGral pies Gas NGG) Pilea OuINAU RED IOlCey
$i zeor GszD ocd = 1525.20.06) NUL > 0)
{
drawing instructions
After the NEWFRAME instruction, but before closing the device context,
an Escape ENDDOC function call is made to tell the spooler that the document
is finished.
Note that the ENDDOC call is made only if all previous Escape functions
have reported no error. If any of the prior Escapes have reported an error, the
GDI will already have aborted the current document. Requesting an ENDDOC
subfunction without an active document may result in the spooler and/or
printer being reset.
That is the brief but blunt fact. You can, if you insist, use C functions to
access the printer port directly, assuming that you can identify which port is
being used (said information is available from WIN.INI), and send anything
you want directly to the port. But there are a few small problems with this:
First, other applications may have the present output queue and your applica-
tion would be stepping on another application’s output. Second, your applica-
tion will not know what information is appropriate. That is, is the actual
printer a dot matrix? A spinwriter or daisy-wheel? A laser jet? A PostScript
printer? Remember, control codes differ greatly for different device types.
Therefore, leave the print handling to the Print Manager.
Lastly, don’t tamper!
required for .TMP files. For example, if large bitmaps are being printed and
banding is not used, the GDI creates a single large metafile which is the same
size as the eventual printer output file. With banding, a series of smaller
metafiles are created, but only one at a time.
Using Banding
When using explicit banding to print a page, the first thing to remember is
that the GDI will be responsible for setting the individual band areas. To use
this, you will need to declare a variable of type RECT, as:
RIEGa mercite:
At this point, the NEXTBAND call has not only returned rect coordinate,
but also has set the clipping region to the rect coordinates.
Before any graphics operations are carried out, the IsRectEmpty function is
called to determine if rect is empty, that is, if the width and/or the height are
zero:
while€ ! IsRectEmpty( &rect ) )
{
If rect is empty, this is a indication that the page is finished and there is no
point in repeating the graphics operations again. If rect is not empty, then
graphics operations continue for the current band:
drawing instructions
Escape(€ hdcPrn, NEXTBAND, 0, NULL, CLPSTR) &rect );
}
The loop concludes with a repeat Escape NEXTBAND call. This serves two
purposes: First, it tells the GDI that the present band is completed, and second,
it retrieves the coordinates for the next band.
This loop will continue until an empty rect is returned by the NEXTBAND
call, indicating that the page is completed.
Chapter 21: The Printer Device Context 459
Now, in the previous example, graphics operations for a page were con-
cluded with a final pair of Escape functions as:
IMC Escapet hdcern, NEWFRRAME, 0, NULL, NULL 39> OQ-)
Escape indcrrn, ENDDOC, OF NUE, NUE. os
}
DeleteDC( hdcPrn );
You will recall that you were warned earlier about the potential problem
of making an ENDDOC call if printing had already been aborted for any other
reason. This version lacks any conditional provisions to prevent an
inappropriate ENDDOC call. So, what now?
Well, having brought the problem to your attention, the answer to this
quandary must be deferred temporarily while another topic is introduced:
Abort procedures.
MSG msg;
files, or, if a problem occurs, halting operations that would create new disk
files.
If this explanation seems sketchy, what is needed most is simply to know
how to create an abort procedure ... and then leave it alone and let Windows
continue on its own convoluted way.
An Abort Dialog
In addition to the abort procedure used by the GDI to halt operations, two
other provisions are needed: First, to show that printing is continuing, and
second, to provide a means for the user to interrupt (abort) printing. Both of
these can be accomplished in a single package by creating an abort dialog box,
as shown in Figure 21-1, that identifies the current operation and, at the same
time, contains an interrupt provision, that is, a Cancel button. This second
abort provision is also declared in the .DEF EXPORTS and requires two further
global declarations as:
FARPROC LpAbortDlg;
HWND hAbortDlg;
Printing: |D:\BC\PRINT.C
But beyond this point, the similarity ends. AbortProc was registered for use
by Windows, but AbortDlg is intended only for the application user’s benefit
and, to be useful, must be displayed:
bAbortPrn = FALSE;
if( !© hwndDlg = CreateDialog( hInst, "PRINT_DLG",
462 BORLAND C++ 3.0 PROGRAMMING, Second Edition
The dialog box is defined in the resource file (.RES) with the name
PRINT DLG, and is created at the same time as the AbortProc function.
However, creating the dialog is only half the process. It concludes with
Show Window to make the dialog visible, and EnableWindow/(...FALSE) to dis-
able the main client window.
The reason for the first step should be obvious—a dialog box isn’t much
good unless it is displayed—but why disable the main window? Principally
because this application, at the time this dialog box is displayed, is now
engaged in a printing operation and cannot respond to any other processes
until printing is finished. The main client window is disabled partially to show
the user that the application is busy, and partially to prevent futile (or abor-
tive) attempts to use the mouse or keyboard to operate other features of the
main window.
Granted, in this example, the main client window does not implement any
other features, but as long as it is disabled, the user cannot attempt to switch
focus away from the AbortDlg dialog box. This does not prevent activating
some other application and placing this one in the background, but this at least
is perfectly in keeping with Windows multitasking. Further, if a long print job
is involved—long in terms of sending the output to the Print Manager for
spooling—the user is free to switch over to play a game of Solitaire while
waiting. Try it.
When the print job is finished, that is, when all output has been sent to the
spooler, the AbortDlg dialog box is erased and the application’s client window
is enabled again.
AbortDlg performs two tasks: First, it displays the name of the file being
printed, and second, it responds to the Cancel button by setting the boolean
bAbortPrn flag. It does not directly interrupt the printing process. This is left to
the original AbortProc procedure which, if you remember, returns !bAbortPrn.
However, when AbortDlg changes the setting for bAbortPrn, this results
indirectly in AbortProc returning a new result, and the operation is aborted as
desired.
Chapter 21: The Printer Device Context 463
As you can see, the two abort processes, AbortProc and AbortDlg, are
designed to work together, one providing the GDI with an abort process, and
the other providing the user with an abort process.
Does any of this solve the potential problem mentioned earlier with calling
Escape ENDDOC when using print banding? It should, because the problem
statement can now be written with a conditional test as:
ii bAbortPrn >
Escape hderrin, ENDDOC,.-"~O> NULL, NULL Ds
J
DeleteDC( hdcPrn );
In this fashion, if the print job has been aborted, then ENDDOC is not called.
Caution: Thus far, only part of the preventive process has been described
because AbortProc does not set a new value for bAbortPrn. However, if you
will examine the listing for Print.C, you will find a second provision insuring
that bAbortPrn is set when either process calls for an abort. Hint: First look for
the Escape ABORTDOC statement.
Aborting A Document
The Escape ABORTDOC subfunction is used to terminate a specific print job
and is called as:
Escapes, hdcPrn, ABORTDOC, 0, NULL, NULL )>
This instruction not only tells the Print Manager to terminate the current
document print job, but also, if no other jobs are in the spooler, closes the Print
Manager. If, however, any other applications are using the Print Manager, or
if the present application has another job in the Print Manager, only the
current job is terminated. Very useful.
Now, having shown you how to interrupt print jobs, we will return to the
subject of outputting print.
Text Output
Like graphics, text output to the printer is essentially quite simple, and is
demonstrated in the PRINT.C program as:
Whileta fetTeoht Fate.
{
fdertsGuput tempus 1Ze01G Buiter), 2Fate. 0;
TextoutG@ hdcern, O, nlLineCcnt * nLineVert,
464 BORLAND C++ 3.0 PROGRAMMING, Second Edition
or even to Windows itself. Font information can be retrieved from the printer
device driver(s) through the EnumFonts function. EnumFonts is called as:
EnumFonts(€ HDC hdc, LPSTR lLpFaceName,
BAR CROCHUpFonehunc,: (PST RialpDetat DF:
case WM_CREATE:
hMenu = GetSystemMenu( hwnd, FALSE );
AppendMenu( hMenu, MF_SEPARATOR, O, NULL );
AppendMenu( hMenu, OQ, SC_PRINT, "&Print File" );
return(Q);
Summary
Printer output (or plotter or other device types) does require a few considera-
tion which were not required for screen displays. At the same time, Windows
has supplied features and functions that make output to a variety of devices
both easier to manage, and almost device-independent.
Following are the source listings for the Print.C demo application, as well
as a partial listing showing one example of how the EnumFonts function can
be called. In both cases, experimentation is suggested.
j/Pp2EsaesseHsSeessessesesessessesssesese//
17 Rican Gee ig.
fi “C+* Windows Printer Support § /7/
= eS SS Se ll 0,
#Hinclude <windows.h>
#Hinclude <string.h>
468 BORLAND C++ 3.0 PROGRAMMING, Second Edition
#Hinclude <stdio.h>
H#include "print.h"
static FARPROC lLpAbortDlg;
SiGaltliCumAk
FAR OlG ss UDIA DION Ge molcE
HANDLE hInst;
char szAppNameL] = "Print";
char SAGE OMea = “PrPIMES SomdPpea Pilet,
char Siz) palace Nalme) eels ay Di GNIS CaN Vile Rela Nilieee Camere
BOOL bAbortPrn;
HWND hAbortDlg;
#pragma argsused
int FAR PASCAL AbortDlg(€ HWND hwndDlg, WORD msg, WORD wParam,
LONG LParam )
{
switch( msg )
af
case WM_INITDIALOG:
SetDlgItemText( hwndDlg, IDD_FILENAME, szFileName );
SetFocus( GetDlgItem(€ hwndDlg, IDCANCEL ) );
return Gl) RUE)
case WM_COMMAND:
switch(€ wParam )
{
case IDCANCEL: bAbortPrn TRIES = break;
default: bAbortPrn RASS Ee
}
ReiUinni Gal RU ampe=
}
RelCUniGee FiAllSS Ems
iy
#pragma argsused
&
HWND hwndDlg;
bAbortPrn = FALSE;
if€ !€ hwndDlg = CreateDialog( hInst, "PRINT_DLG",
hiWinidy UIpIAbOnt Digna) saa»
ReLCUINLGeaN UIE
ShowWindow( hAbortDlg, SW_NORMAL );
EnableWindow( hwnd, FALSE );
return(€ hwndDlg );
}
#pragma argsused
HDC GetPrinterDC(€ HWND hwnd )
{
SitlarcniCaac NiaihmeeeSiZ raeaiintren 4 Olle
char *szDevice, *szDriver, *szOutput;
GSvVPPOTTLESEPIMNG¢ “MimMcowsY’, VclowieGt, Pprran,
SAPP IMeAr, GO Ws
17 © SRDEWICR S SipwokC Se2iPrirdiears, Maw ) 2 Be
GsiZi DIGI eine e—aes tinetokiGs NIU Slay Le De) aa
Gees ZOU DItlLGae— eeSeta trol ka GemIN| UL lay. LORE ey
return( CreateDC( szDriver, szDevice,
SyZOlU pty IU nceeaan NU) oe)ee) ae
return(Q);
}
BOOL PrintFile€ HWND hwnd, HDC hdcPrn, LPSTR LpszFileName )
{
TEX TWEE IIR IE eliny
FILLE *File;
char > (RUG 7 ARIE ZS Als
int nPageLen, nLineVert, nLinesPg,
MLIMeChre, IMNIPRPMNS we) -
DeleteDC( hdcPrn );
ert Ciena eA SiEam a=
}
ifG PPrinthite.c hund,whdcrrn,,szriveNamer) >
RekUi nin GaAs Emme
EnableWindow( hwnd, TRUE );
DestroyWindow( hAbortDlg );
FreeProcInstance( lpAbortDlg );
FreeProcInstance( lpAbortProc );
DeleteDC( hdcPrn );
Retin Gai UiEe.
}
Long FAR PASCAL WndProc(€ HWND hwnd, WORD msg,
WORD wParam, LONG lParam )
Chapter 21: The Printer Device Context 471
{
HMENU hMenu;
Switch( msg )
ct
case WM _CREATE:
hMenu = GetSystemMenu( hwnd, FALSE );
AppendMenu( hMenu, MF_SEPARATOR, O, NULL );
AppendMenut hMenu, 0, SC_PRINT, "“&Print File" )-;
return(Q);
case WM_SYSCOMMAND:
if€ wParam == SC_PRINT )
‘C
MessageBox hwnd, "Ready to print?",
szAppName, MB_OK | MB_ICONQUESTION );
Ite eo seternpi eC ihwnd, Minsit....szriclename) )
MessageBox( hwnd, "Can't print file",
szAppName, MB_OK | MB_ICONEXCLAMATION );
ne turnin GOD)
}
break;
case WM_DESTROY:
PostQuitMessage(0);
meLuulmn GODr
}
return( DefWindowProc( hwnd, msg, wParam, lParam ) );
}
#pragma argsused
if€ ! hPrevInstance )
{
we.hInstance hInstance;
we.lpfnWndProc WndProc;
we.cbClsExtra 0;
we.cbWndExtra 0;
we.lpszClassName szAppName;
we.hIcon LoadIcon( hInstance, szAppName );
we.lpszMenuName CLPSTR) szAppName;
WCE nGUInSOl EoadiGuirsom GNU Ell DiGaARIRO Wm»)
we.hbrBackground GetStockObject( WHITE_BRUSH );
we.style CS_HREDRAW | CS_VREDRAW;
RegisterClass( &w TTD
UN
TOE
Ue
0ao)
Ce
Gee );
472 BORLAND C++ 3.0 PROGRAMMING, Second Edition
hInst = hInstance;
hwnd = CreateWindow( szAppName, szCaption,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
GWMUISIE:DIE
FsA UIE Te weeWiens EDI ENR AIU Este,
NULL NULLS Shinstance, Nubt. 7
ShowWindow( hwnd, nCmdShow );
UpdateWindow( hwnd );
while€ GetMessage( &msg, NULL, O, O ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
return( msg.wParam );
}
is Pid
eo DIS P
pee SSS S]SoSS Ser
NAME PRINT
jf SSSSsSsSeeee////
(Le PRET Hsad /
fj [SSeS Sesssess ////
LPSTR LpFaces;
iE Xen MEM: Relac tm;
PAINTSTRUCT pis:
int ‘le
BOOL bRetrieved;
switch( msg )
{
case WM_CREATE:
return(0Q);
case WM_PAINT:
Lt bReteiTevieat)
Chapter 21: The Printer Device Context 475
if€ EnumData2.hGlobal )
GlobalFree(€ EnumData2.hGlobal );
EnumData1.hGlobal = GlobalAlloc( GHND, 1L );
EnumDatal.nCount = OQ;
EnumData2.hGlobalt = GlobalAlloc(€ GHND, 1L );
EnumData2.nCount = 0;
hdc = "GetPrinterl¢c C)+>
iPS Ane ee)
{
lpFaces = GlobalLock(€ EnumDatal.hGlobal );
for€ i=0; i<EnumData1.nCount; i++ )
Tate Es UM) EAOInites) Guaniclice,
UpibalCeiSm hel cen bee rAVC ESileee,
LpfnEnumFontTypes,
CLPSTR) &EnumData2 ) )
-..» memory error ... out of memory
GlobalUnlock(€ EnumData1.hGlobal );
EnumData2.nCount; /e/aaG Merc Keed OuibiGememn hiuisenes
DeleteDC( hdc );
bRetrieved = TRUE;
a
GlobalFree(€ EnumData1.hGlobal );
}
case WM_DESTROY:
a2
return( DefWindowProc( hwnd, msg, wParam, lParam ) );
}
#pragma argsused
int PASCAL WinMain( HANDLE hinstance, HANDLE hPreviInstance,
UPS TTR lLpszCmdParam, int nCmdShow )
{
.
cas
a So ite
— ‘
~ _ = a
eee ° :
aan ; se
®
.
EE
[@
¥ »
es
a
>
2 nS 7 : >
75 :
The Windows Clipboard is actually two quite different entities: One clipboard
is an application: titled ClipBrd.EXE, which is actually a clipboard viewer,
while the real clipboard is a feature of the Windows USER module that
provides a series of functions used to facilitate the transfer of information
between applications. The subject of this chapter will be the clipboard facilities
provided by the USER module. First, a brief look at the ClipBrd.EXE program.
477
478 BORLAND C++ 3.0 PROGRAMMING, Second Edition
material has been copied to the clipboard, any application can access the
clipboard, inquire what type of material is present, and, if desired, copy the
material from clipboard.
The ClipBrd.EXE application, if active, does this automatically. It watches
to see if any material is written to the clipboard and, if there is, determines the
data format. If possible, it displays the material in its own client window.
ClipBrd.EXE normally only retrieves a copy of the material, and does not alter
or erase the contents of the clipboard.
Clipboard Drawbacks
The clipboard works very well, but it does have two particular disadvantages:
First, there is only one clipboard, and second, material written to the clipboard
is public, that is, accessible to any application. Even if all applications are well
behaved, the two elements can still present definite potential for error.
The first problem is that there is only one clipboard, and all applications
wanting access to a clipboard must use the single facility. Because of this, if
application A has written, for example, a bitmap to the clipboard and applica-
tion B needs to write a block of text data, the bitmap written by A is erased.
Now, if the destination application had already retrieved the bitmap data,
everything would be fine ... but "if" is not a guarantee against problems.
The second problem is that the clipboard is always public. There is no way
for Application A to write something to the clipboard with a directive that this
can only be retrieved by Application X, and therefore cannot be accessed,
erased, or otherwise affected by any other application.
Because of these potentials for error, a strong recommendation is made that
applications using the clipboard should not transfer data in or out of the
clipboard, or erase the clipboard contents except under explicit instructions
from the user.
All of these problems, however, are minor and, as you will see in Chapter 23,
can be circumvented by using Dynamic Data Exchange. Further, irregular and
generally unlikely problems aside, the clipboard remains a useful and convenient
method of exchanging data—many types of data—between applications.
To use the clipboard, applications begin by using the Global Alloc function
and the GHND flag (defined as GUEM_ MOVEABLE and GMEM_ZEROINIT)
to initiate a memory block which, for the moment, belongs to the application
instance. Normally, when the application instance exits, the memory block is
deleted by Windows.
However, when an application calls SetClipboardData with the global handle
to the memory block, Windows transfers ownership of the memory block from
the application to itself, that is, to the clipboard feature, by modifying the
memory allocation flags for the global memory block.
To transfer ownership, Windows uses the GlobalReAlloc function as:
CF_TEXT
The simplest clipboard format is the CF_TEXT format, consisting of null-
terminated ANSI character strings, each ending with a carriage return (0x0D)
/ line feed (Ox0A). In CF_TEXT format, data is stored in a global memory
block, and the handle to the memory block is transferred from the source
480 BORLAND C++ 3.0 PROGRAMMING, Second Edition
CF_OEMTEXT
The CF_OEMTEXT format is the same as the CF_TEXT format, except that the
OEM character set is used instead of the ANSI character set.
CF_METAFILEPICT
The CF_METAFILEPICT format is used to transfer memory (not disk)
metafiles between applications. The CF_METAFILEPICT format uses the
METAFILEPICT structure defined in Windows.H as:
S {Cin UlG GamcaGit Es Athel SE ralecal)
{ int mm; int NEXT FE
int YE SGtae HANDLE hMF; AMMEN
UL SIR IoC
CF_BITMAP
The CF_BITMAP format is used to transfer Windows 2.0 compatible bitmaps
by transferring the bitmap handle to the clipboard. As always, the originating
application should not attempt to use the bitmap handle after transfer to the
clipboard.
CF_DIB
The CF_DIB format is used to transfer Windows 3.0 device-independent
bitmaps to the clipboard. The DIB bitmap is transferred as a global memory
Chapter 22: Clipboard Data Transfers 481
CF_PALETTE
The CF_PALETTE format is used to transfer a handle to a color palette, and is
used in conjunction with CF_DIB to define the color palette used by the
bitmap.
CF_SYLK
The CF_SYLK format uses a global memory block to transfer data in the
Microsoft “Symbol Link’’ format. It was designed to exchange data between
Microsoft’s Multiplan (spread sheet), Chart, and Excel applications. The
format is an ASCII string format, with each line terminated by a carriage
return /line feed pair.
CF_DIF
The CF_DIF format uses a global memory block to transfer data using the Data
Interchange Format (DIF). This was created by Software Arts for use with the
VisiCalc spreadsheet program, but is now controlled by Lotus Corporation
(Lotus 1-2-3). The format is an ASCII string format, with each line terminated
by a carriage return/line feed pair.
While both the CF SYLK and CF_DIF formats are similar to the CF_TEXT
format, the strings transferred are not always null-terminated. Instead, the
format defines the end of the data.
482 BORLAND C++ 3.0 PROGRAMMING, Second Edition
For further information on these two formats, consult File Formats For
Popular PC Software by Jeff Walden, published by John Wiley & Sons.
CF_TIFF
The CF_TIFF format uses a global memory block to transfer data in the Tag
Image File Format (TIFF), a format devised jointly by Aldus, Hewlett-Packard,
and Microsoft, together with hardware manufacturers. Details on the TIFF
format are available from Hewlett-Packard Company.
Clipboard Access
Allowing access to the clipboard by only one application at a time is one
mechanism that prevents a conflict of purposes. The OpenClipboard function is
used to request access, and returns a boolean value indicating that the clipboard
is available and opened, or that access is denied because another appication has
the current access. When finished with the clipboard, CloseClipboard is called to
relinquish access, yielding the clipboard access to anyone else.
Note that the OpenClipboard function is always matched with a CloseClipboard
call. Emphasis is on ALWAYS! An application should never—ever —attempt
to hold the clipboard open, and should always relinquish control of the
clipboard as quickly as possible.
An example of a clipboard transfer function (copying to the clipboard):
Last, the clipboard is closed again. However, there are a few restrictions on
clipboard operations:
First, before an item can be copied to the clipboard, EmptyClipboard must be
called to erase the present contents, if any, of the clipboard. Remember, simply
accessing the clipboard does not change (assign) ownership of the existing
clipboard contents. On the other hand, the EmptyClipboard function does
assign ownership and, at the same time, clears any existing contents.
And, while any application can access the contents of the clipboard, only
the clipboard owner can write material to the clipboard ... and the clipboard
can have only one owner at any time. Previous owner’s contents are simply
discarded.
Second, while multiple items can be copied to the clipboard, they must all
be transferred in a single operation. The clipboard cannot be opened, one item
copied in, closed, and then reopened to transfer another item ... at least, not
without erasing the first item transferred.
484 BORLAND C++ 3.0 PROGRAMMING, Second Edition
Third, only one item of each type can be transferred at any time, simply
because there is no means of distinguishing between multiple items of a given
type. However, when multiple items have been transferred to the clipboard,
another application can request only one item, or several items, or all items, but
it must request each item separately. It may also open the clipboard repeatedly
to request different items, or to request the same item more than once.
In general, however, when an item is requested from the clipboard, the best
option is to make a local copy of the desired item, rather than attempting to
request the same item repeatedly. After all, there is nothing to insure that the
data item desired will remain available indefinitely.
The second method is to query all available clipboard formats, using the
EnumClipboardFormats function. EnumClipboardFormats can be called in two
different fashions, either with a type parameter requesting a specific format,
or with a zero parameter requesting any format. For example, to request a list
of all formats available, the following code could be used:
wFormat = OQ;
OpenClipboard( hwnd );
while€ wFormat = EnumClipboardFormats( wFormat ) )
{
code handling various formats
y
CloseClipboard();
Chapter 22: Clipboard Data Transfers 485
The first value returned to wFormat is used for the next call, causing
EnumClipboardFormats to step through the list. If necessary, the wFormat value
could be reset to any value to repeat the list from that point. When no further
formats are found, or if the clipboard is empty, EnumClipboardFormats will
return zero. Note that the assignment in the conditional statement used here
will return the probably-familiar compiler warning message: Possibly incorrect
assignment.... This, of course, is not an error here, but often is in other cases.
The number of formats available in the clipboard can be obtained by calling:
nFormats = CountClipboardFormats();
options: Data To Clipboard and Data From Clipboard. Each of these has a
three-item submenu with such equally imaginative options as Write-datatype-
and Retrieve-datatype-.
There is one exception: Under Data To Clipboard for bitmaps, the menu item
reads Capture Bitmap. This is because the demo program uses a simple screen
capture procedure that allows you to grab and store a portion of the screen as
a bitmap image. Thus, when Capture Bitmap is selected, the mouse cursor is
changed to a cross mark, then, when the left mouse button is pressed, bitmap
capture begins. It is completed when the button is released. The captured
bitmap will not be displayed in the client window, only stored in the clipboard
until the Retrieve Bitmap option is selected.
A simple text sample is provided for the text demo. For the metafile demo,
the metafile from PenDraw6 has been adapted. Please note that in this demo
program, provisions are made to store only one item on the clipboard at any
time.
Within the TextToClipboard subroutine, four local variables are needed, but
only two, hGMem and IpGMem, should require any explanation:
int i, wLlen;
GLOBALHANDLE hGMem;
EEPiSaaR lLpGMem;
After the wLen variable is initialized with the length of text parameter,
hGMem becomes a pointer to sufficient globally-allocated memory to hold a
copy of the text. Notice that wLen is increased by one to provide allocation for
a null terminator because clipboard text is always stored in an ASCIIZ (or
ANSIZ) format:
wien =—as tr tenGelpl <t. )y
hGMem = GlobalAlloc(€ GHND, (DWORD) wlen + 1 );
LpGMem = GlobalLock( hGMem );
Last, [pGMem becomes a pointer to the memory block via the GlobalLock
function. The GlobalLock function serves two purposes because, in addition to
returning a pointer to the memory block, it also locks the memory block,
preventing it from being moved by Windows. Remember that normally,
memory allocations are declared as moveable.
At this point, the memory allocated is empty, or, more correctly, has been
entirely zeroed out because the GHND specification includes the
GMEM_ZEROINIT (initialize as zeros) flag. Therefore, the next step is to copy
the local string, pointed to by [pTxt, into the memory block:
for(€ i1=0; i<wLen; i++ ) *lpGMem++ = *lLpTxt++;
GlobalUnlock(€ hGMem );
ert Uin MiGuleimalnis
fe hilo} CaUnmpiBIDIGs hiwinidy, = MiG Melmi-sn CFM
EX imap) >>
Just as during the transfer to the clipboard, GlobalLock locks the memory
block and returns a pointer to the memory address which is held by IpText.
This time, however, instead of using a loop, the Istrcpy function is used to
copy the string contents from the memory address (IpText) to the local variable
TextEStr:
Sitinc Diy; Gunlbe>xat Site al PalC
DG tans
GlobalUnlock( hTextMem );
CloseClipboard();
To finish up, GlobalUnlock releases the lock on the memory block, and
CloseClipboard completes operations. Note: A memory block should never be
left locked. Always call GlobalUnlock after calling GlobalLock.
However, the actual image has not yet been copied, so StretchBlt copies the
image from hdc to hdcMem, using the inital coordinates in pt1 and the image
size in pt2:
StretchBltC hdeMem,. 0,9 07eabs (pticex>,eaps (pte
y
hdc, -pt.x7) ep tleYew Die ck ee Dey
SRG COR Nes
Chapter 22: Clipboard Data Transfers 489
Now the bitmap image has been copied to hdcMem and hBitmap has pro-
vided a handle for it. Now hwnd, hBitmap, and the format specification
CF_BITMAP, are passed to TransferToClipBD, just as was done for the text
demo:
TransferToClipBD( hwnd, hBitmap, CF_BITMAP );
+
DeleteDC( hdcMem );
ReleaseDC( hwnd, hdc );
In this case, a bit of cleanup remains because, while the memory block has
been transferred, the temporary device context needs to be deleted, and of
course the screen device context also needs to be released.
Retrieving the bitmap image from the clipboard is relatively simple, as well
as quite similar to the process of storing the bitmap. As usual, the operation
begins by opening the clipboard and then retrieving a handle to the bitmap
memory block:
OpenClipboard( hwnd );
hBitmap = GetClipboardData( CF_BITMAP );
hdcMem = CreateCompatibleDC( hdc );
The mapping mode also needs to be set for the memory device context
before the actual image can be copied from the clipboard memory block.
GetObject gives us the dimensions of the bitmap:
GetObject( hBitmap, sizeof(BITMAP), C(CLPSTR) &bm );
BitBlt( hdc, O, O, bm. bmWidth, bm.bmHeight,
ndcMem, 0, UO; ocRCCOPY OF
This leaves only the image information to be copied. Since all that’s wanted
is a one-to-one copy, the BitBlt function is quite adequate, and the image is
positioned at the origin (upper left) of ClipBd’s client window.
This leaves only cleanup (releasing and deleting device contexts) and closing
the clipboard:
ReleaseDC( hwnd, hdc );
DeleteDC( hdcMem );
Closet lipboard ©)’;
490 BORLAND C++ 3.0 PROGRAMMING, Second Edition
Actually, the clipboard could have been closed earlier, as soon as a handle
had been retrieved to the memory block because, remember, closing the
clipboard does not delete the memory block. Pretty simple, wasn’t it?
The remaining clipboard operation, for metafiles, has a few new wrinkles
to demonstrate.
The window extent and origin are set just as if this were a normal metafile,
and the metafile drawing operations are carried out exactly as in previous
examples.
Chapter 22: Clipboard Data Transfers 491
Now it’s time to create the memory block which will be transferred to the
clipboard, beginning by allocatiing memory. This should be familar by now:
hGMem = GlobalAlloc( GHND, (DWORD) sizeof(METAFILEPICT) );
LpMFP = CLPMETAFILEPICT) GlobalLock( hGMem );
LpMFP->mm = MM_ISOTROPIC;
Last, the hMF handle is given the handle of the metafile proper (hMetaFile).
This finishes setting up the metafile for transfer. The allocated memory is
unlocked, and TransferToClipBD is called to complete the transfer to the
clipboard, reporting the results back to the calling process:
GlobalUnlock(€ hGMem );
return( TransferToClipBD( hwnd, hGMem,
CleISR IC 2) WE
Thus far, setting up and transferring the metafile hasn’t been that difficult.
In a moment, when the metafile is retrieved, a new element will be introduced.
The process begins by opening the clipboard, asking for a handle to the
memory block containing the metafile, and locking the block while returning
a pointer:
OpenClipboard( hwnd );
hGMem = GetClipboardData( CF_METAFILEPICT );
EpMFP = (LPMETAFILEPICT) GlobalLock(€ hGMem );
Now that we have a pointer to the metafile, it’s time to do something with
it. The first step is to save the present device context. Next, a call to a
subroutine, CreateMapMode, sets the appropriate mapping mode for this
metafile. This is a new element to which we will return in a moment:
Savepc t hde >);
CreateMapMode( hdc, LpMFP, cxWnd, cyWnd );
PlayMetaFile(€ hdc, lpMFP->hMF );
RestoredDe¢ hdcy = Wey;
492 BORLAND C++ 3.0 PROGRAMMING, Second Edition
Once the appropriate mapping mode is set, PlayMetaFile executes the metaf-
ile. This done, the original device context can be restored. To finish, the
memory block is unlocked and the clipboard closed:
GlobalUnlock( hGMem );
clos eC: Gaipibio.aid
@:-
For convenience, there can be several local variables, but [MapScale is the
critical value that may need to be calculated. The operational word is "may",
because not all metafiles will require this calculation. In all cases, the first step
is to set the map mode indicated by the [pMFP->mm field:
SetMapMode( hdc, lLpMFP->mm );
if€ LpMFP->mm != MM_ISOTROPIC &&
LpMFP->mm != MM_ANISOTROPIC )
PEEwMRiANG WWE MF
Granted, this is the same device where the metafile was created and none
of these values have changed, but the application still needs this information
for calculating the appropriate viewport extents.
There are three circumstances under which the viewport extents can be
calculated: First, the xExt and yExt parameters (both are assumed to have the
Chapter 22: Clipboard Data Transfers 493
same sign or general magnitude) are positive, and therefore are a suggested
metafile image size. Note that the word is "suggested", not absolute, and the
size is expressed in MM_HIMETRIC units (0.01mm):
1f€ lpMFP->xExt > 0 )
SetViewportExt( hdc,
Cint) (€Clong) UpMFP->xExt * nHRes / nHSize / 100),
Cant GGlong a WpMFr=>yeExt * nHRes: 7 nHSize / 4100.) >
In this case, the calculations are slightly more involved, but still relatively
simple.
In the third case, xExt and, by presumption, yExt, are both zero, in which
circumstance the viewport extent is simply set to the present client window
width and height:
else SetViewportExt( hdc, cxWnd, cyWnd );
neturn® TRUE) =
This is moderately complicated but, happily, it does not present the com-
puter with anythingthat the CPU can not handle quite readily. After all, these
are minor calculations compared with the image mapping required during the
actual replay.
494 BORLAND C++ 3.0 PROGRAMMING, Second Edition
If more than one item needs to be passed to the clipboard, some items may
be passed as data, and others passed as NULL for delayed rendering.
When an application calls for data which was passed as NULL, Windows
recognizes that the data has been delayed and calls the clipboard owner, the
application which last called EmptyClipboard, with a WM_RENDERFORMAT
message. WM_RENDERFORMAT is an instruction requesting the delayed
data with the format type requested contained in wParam.
In response to WM_RENDERFORMAT, instead of calling OpenClipboard
and EmptyClipboard, the only call required is SetClipboardData with the global
memory block handle, and, of course, the format identifier.
If, however, another application calls the clipboard with an EmptyClipboard
call, taking ownership of the clipboard away from the original application, the
original clipboard owner receives a WM_DESTROYCLIPBOARD message,
simply indicating that ownership has been lost.
The final special message is WM_RENDERALLFORMATS. When an
application is ready to terminate itself but is still the current clipboard owner,
and the clipboard contains NULL data handles, Windows sends the
WM_RENDERALLFORMATS message. In response, the owner application
496 BORLAND C++ 3.0 PROGRAMMING, Second Edition
has two choices: Either clear the clipboard, or replace the NULL data handles
with the actual data blocks.
Normally, the latter choice would be preferred. After all, if another instance
or application was expected to retrieve the data from the clipboard in the first
place, presumably this data will still be wanted. Further, since the reason for
using a NULL handle in the first place was to avoid duplicating memory,
transferring the data to the clipboard before the original application exits
serves essentially the same purpose.
Note, however, that the response to a WM_RENDERALLFORMATS mes-
sage is not the same as the WM_RENDERFORMAT message. Instead, the
appropriate response to the WM_RENDERALLFORMATS message is essenti-
ally the same as creating normal clipboard entries: Opening the clipboard,
erasing the previous contents, writing new data block(s) without using null
handles, and, closing the clipboard, just as if delayed rendering had never
been used at all.
The returned wFormat identifier will be a value between 0xC000 and OxFFFF,
and can be used subsequently as the format parameter in SetClipboardData and
GetClipboardData. However, before another application or instance can
retrieve data from the clipboard, this same wFormat id is required, although
it could be passed via the clipboard as CF_TEXT format.
EnumClipboardFormats, discussed previously, can also be used to return all
format identifiers. The GetClipboardFormatName function can then obtain the
ASCII name of the format, as:
Summary
The clipboard is a useful method of exchanging data between separate instances
of a single application, and between different applications. A variety of dif-
ferent standard formats are available, as are custom formats that applications
can define for themselves.
There are also a few disadvantages which, while not major, should still be
given appropriate consideration. These disadvantages, however, can be cir-
cumvented using the Dynamic Data Exchange methods discussed in Chapter 23.
The complete source code for the ClipBd demo program follows, demonstrat-
ing text, bitmap, and metafile transfers via the Windows clipboard utility.
#include <windows.h>
A niGUUudes<S t diorhi>
#include <math.h>
#include <clipbd.h>
HANDLE hGInst;
HANDLE hMetaFile;
Chapter 22: Clipboard Data Transfers 499
HDC nIGicH
NiCGiG aaa Cae antee DG) Gee Dil Sia vAy Yacue miNU)
en ee NUE Lemme NLU) UL) =
ClientToScreen( hwnd, &pt1 );
PraitibaeciGaenicliGe-mnp) cal acnXe- mm PitanYam CiCa-e XapmenDitrCuan
ye memDI Oni) NAVE Rule
PacBiultts adc. pice% pt 12,2 Pitic.. Xe Pit cavsreD SIVLUNVERT <) 3
DeleteDC( hdc );
}
BOOL CreateMapMode( HDC hdc, LPMETAFILEPICT lULpMFP,
; int cxWnd, int cyWnd )
{
long LMapScale;
int nHRes, nVRes, nHSize, nVSize;
SetMapMode( hdc, LpMFP->mm );
if( lLpMFP->mm != MM_ISOTROPIC &&
LpMFP->mm != MM_ANISOTROPIC )
return (, TRUE 2D (e/a NOCau Smasteuty warts alt ucSmmraleg lm /0//
switch(€ msg )
{
case WM_CREATE:
nClipRetrieve = bCaptEnable = bCapturing = 0;
return(O);
case WM_LBUTTONDOWN:
if(€ bCaptEnable )
{
bCapturing = TRUE;
ptl = MAKEPOINTX UParam 0;
}
return(0);
502 BORLAND C++ 3.0 PROGRAMMING, Second Edition
case WM_MOUSEMOVE:
jnfaGab GalpiGemaibiuen=
SetCursor @iboadcursonG NULE YS -IDCECROSS? I De
TefaGeb Gajpscutalinigae
{
pt2 = MAKEPOINT(C LParam );
Een SH feos
PIectys*= =) Play,
FlashRect( hwnd, pti, pt2 );
}
return(Q);
case WM_LBUTTONUP:
ive Y DEAOEWRIME 2 PErUrMCO)s.
da@ 2 peeox TAL! fEe2.v7 ) PeewrmeWys
hdc = GetDCC( hwnd );
hdcMem = CreateCompatibleDC( hdc );
hBitmap =
CreateCompatibleBitmap( hdc, abs(pt2.x),
aiDiSiGDiticyebyY mee
eh Guay Decne
{
MessageBeep(0Q);
SelectObject( hdcMem, hBitmap );
StretchB utc thideMem, 07. 0 abisiGpitic x) abs Coitizey)),
NidiCyemepit. Xs, ep talie Y, mUDLCican Xorg Pptecy enya
SiIRG.C
OP Name
Ipmainisaue
1,01G Walp BiDIGehiwinidy- sah) Balatimia)
Dean G Real) LanM/ Al Pan
InvalidateRect( hwnd, NULL, TRUE );
}
DeleteDC( hdcMem );
ReleaseDC( hwnd, hdc );
bCaptEnable = bCapturing = FALSE;
vetcCursorG@avoadCursorG NULL. TDC CARROW: u)-:
ReleaseCapture();
return(Q);
case WM_COMMAND:
switch(€ wParam )
{
case IDM_PUT_BITMAP:
11s bcCaptpenabte )
{
bCaptEnable = TRUE;
SetCapture( hwnd );
setCursor( =LoadCursorG@ NULULOH DOUC ROSS: ) \)F-
}
else
‘C
bCaptEnable = FALSE;
ReleaseCapture();
SetCursor(€ LoadCursor( NULL, IDC_ARROW ) );
Chapter 22: Clipboard Data Transfers 503
}
MessageBeep(Q);
return(0O);
case IDM_PUT_METAFILE:
DrawMetafile( hwnd, cxWnd, cyWnd );
MessageBeep(Q);
break;
CaisiG we D)Mime Ui Exel
TextToClipboard( hwnd,
“The equick=brRown “fox jumped "
"over the lazy red dog" );
MessageBeep(0Q);
break;
Cid) Sicne i DIMaEGiEWiaeMiE dA El Es
case IDM_GET_BITMAP:
Galsicmel DiMas GiE [eal exon:
nClipRetrieve = wParam;
TinivallaicdartelRieiciti Ga Miwinidias NIU! llernen TERE se
break;
:
return(0Q);
case WM_SIZE:
cxWnd = LOWORDC LParam );
cyWnd = HIWORD(C LParam );
meGuinnCODE:
case WM_PAINT:
InvalidateRect( hwnd, NULL, TRUE );
hdc = BeginPaint( hwnd, &ps );
switch( nClipRetrieve )
{
case IDM_GET_TEXT:
nClipRetrieve = 0;
lf Ques Canto DOlaln ClFOmmmMlantyAlv)
alia Dil ei Cas GiFemi) EXqian) ae)
{
MessageBeep(0Q);
OpenClipboard( hwnd );
hTextMem = GetClipboardData( CF_TEXT );
lpText = GlobalLock( hTextMem );
Lstrepy Gitextstr;, Uptext >’;
GlobalUnlock(€ hTextMem );
CloseClipboard();
Ke DXeHOU)ChGan GICe mal Ol meLOP ume xXets Sutin,
strlien€ TextStr ) D3
+} break;
case IDM_GET_BITMAP:
nClipRetrieve = 0;
if(€ IsClipboardFormatAvailable(€ CF_BITMAP ) )
504. BORLAND C++ 3.0 PROGRAMMING, Second Edition
MessageBeep(0);
OpenClipboard( hwnd );
hBitmap = GetClLipboardData(€ CF_BITMAP );
Setcursor© CoadCursor C NULL, LDC WAL Ie 2)
hdcMem = CreateCompatibleDC( hdc );
SelectObject( hdcMem, hBitmap );
SetMapMode( hdcMem, GetMapMode( hdc ) );
GetObject( hBitmap, sizeof(BITMAP),
CEP SHR» secbim n=
BitBlt( hdc, O, O, bm. bmWidth, bm.bmHeight,
hdeMem,.0, 602 SROGCOPY )::
SetCursor€ LoadCursorGeanuce, ekDC ARROW) De
ReleaseDC( hwnd, hdc );
DeleteDC( hdcMem );
CloseClipboard();
+ break;
Gralsieul)D) MmaGi Esmee M Eg leAl rela
nClipRetrieve = QO;
if€ IsClipboardFormatAvailable(
Cr WETARIILERP
Mey » 2
{
MessageBeep(Q);
OpenClipboard( hwnd );
hGMem = GetClipboardData(
Cle Me WANMIEPs
LpMFP =
CLPMETAFILEPICT) GlobalLock( hGMem );
SaveDC( hdc );
CreateMapMode( hdc, LpMFP, cxWnd, cyWnd );
PlayMetaFile(€ hdc, LpMFP->hMF );
Rieis:toime) DICiGaihidicy, wasn lle)
GlobalUnlock(€ hGMem );
CloseClipboard();
+} break;
default: break;
}
EndPaint(€ hwnd, &ps );
FeeunnicOyy-
case WM_DESTROY: PostQuitMessage(0);
return(Q);
a
return€ DefWindowProc( hwnd, msg, wParam, LParam ) );
}
#pragma argsused
int PASCAL WinMain(€ HANDLE hInst, HANDLE hPrevInst,
ESueR LpszCmdParam, int nCmdShow )
{
Chapter 22: Clipboard Data Transfers 505
nGinst. = hinstt:
ice Prey Uns ty)
{
we.hInstance =enelim Sates
we.lpfnWndProc = WndProc;
wich cp lsiExst ra = 0;
wce.cbWndExtra =a (Gs
we.lpszClassName = szAppName;
wce.lpszMenuName = CLPSTR) szAppName;
wce.hIcon = LoadIcon( hInst, szAppName );
WICt=aniGU 2S Oia = LoadCursor( NULL, IDC_ARROW );
we.hbrBackground = GetStockObject( WHITE_BRUSH );
we.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass( &we );
}
hwnd = CreateWindow( szAppName, "ClipBoard Demo",
WS_OVERLAPPEDWINDOW,
ChWUISIE
DIEM VAIUIISile nC WaetU oie Dies ETA\ UI Sule.
CWHUSEDER AU e- eC.WasUSIEDE EAU eats,
NU INE. ine, INWIRIE ds
ShowWindow ( hwnd, nCmdShow );
UpdateWindow( hwnd );
while€ GetMessage( &msg, NULL, O, O ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
return(€ msg.wParam );
/ fPressssceeasses////
Hi CllipBeath WH
//S2esesseseseee
//
/ (/SSS2SSsesceSssesesesSeSeessea// //
Hfi/ Clap Bide RiGee mule tiallenn/a/,
}j // SSS a=aeooesoe=Seoeeee2// //
An Introduction To DDE
DDE message traffic is a conversation between two applications and, like
human conversations, consists of both protocols and redundancies. While less
than 100% theoretically efficient, the conversations provide a high degree of
poz
508 BORLAND C++ 3.0 PROGRAMMING, Second Edition
surety. Under the DDE, conversations are always dialogs, with the application
initiating the conversation taking the role of client, while the second applica-
tion, known as the server, responds to requests from the client application.
In actual practice, any one application can carry on multiple DDE con-
versations with one or more other applications. An application can act as
client in one conversation and server in another, even if both conversations
are being carried on with the same second application. Or, a single client might
be conversing with several servers or a single server might be conversing with
several clients, each in separate conversations. In general, when multiple
conversations are used, in order to keep these conversations separate and
unique, the application will create a hidden child window—that is, a window
ID—for each conversation. However, for demonstration and discussion, only
a single DDE conversation will be used, and one application will always be
the client in converse with a dedicated server.
DDE Terminology
In order to carry ona conversation, DDE applications require three identifiers,
or character strings. These are the "application name", the data "topic", and
the data "item".
conversation. Note that the server might also initiate the WM_DDE_TERMIN-
ATE message, and that it would be the client’s responsibility to respond.
Figure 23-1: Cold Link Message Traffic
Client Server
Application Application
WM_DDE_INITIATE———>| [topic] _
[positive 1|}<—_t+__WM_DDE_ACK
WM_DDE_REQUES T————> | Liteni
Litem i <——————_WM_DDE_DATA
WM_DDE_ACK ————_—_—_—_-> | positive.
WM_DDE_REQUEST————+| Litem!
[negative | }*—Y—W—_Wn_DDE_ACK
WM_DDE_ TERN INATE———*
[conf irms | }<———_WM_DDE_TERMINATE
Client Server
Application Application
WM_DDE_INITIATE———> Eu
[positive] |< _wM_DDE_ACK
{positive ] <«———————_WD_DDE_ACK
{item 1 <———————WM_DDE_DATA
Client Server
Application Application
WM_DDE_INITIATE———>| (topic!
[positive ] <———————WM_DDE_ACK .
WM_DDE_ADV ISE—————> ||changing
;;, ; |
{positive ] |}<«——\ CK F em oni
WD_DDE_A
[NULL ] |}<————wm_DDE_DATA
WM_DDE_ACcK——————> | [ positive]
WM_DDE_REQUES T————_+| [iten!
Litem] }<—————_WM_DDE_DATA
WM_DDE_ACK——————_ | [positive]
WM_DDE_TERMINATE———*
[conf irms 1 <———-WM_DDE_ TERMINATE
By using warm link protocol, the client has been notified of a change and,
when ready, sends a WM_DDE_REQUEST message, just like in the cold link,
and this time receives from the server a WM_DDE_DATA response with the
changed data. As ina hot link,a WM_DDE_UNADVISE message may be sent
to cancel notification of changes. Last, a pair of WM_TERMINATE messages
are exchanged to end the conversation.
Instead, the actual strings are stored in an atom table in the application’s
default data segment. The message traffic passes "atoms", WORD values that
ignore case and uniquely identify the string values. Atom variables are
declared as:
ATOM altem, aName;
If the character string specified does not exist in the atom table, AddAtom
adds the string to the atom table. If the string does exist in the atom table, the
existing string’s reference count, initially one, is incremented. In either case,
AddAtom returns a unique number in the range 0xC000..0xFFFF to identify the
string.
When no longer needed, strings can also be removed from the atom table
by calling the DeleteAtom function, as:
DeleteAtom( altem );
The return value identifies the atom associated with the given string, and
returns NULL if the string is not in the atom table.
To use the atom value to retrieve the string value, the GetAtomName
function is called as:
DDEADVISE (struct)
{ unsigned reserved:14,
fDeferUpd:1,
tAhCKRe
Gs I;
int cfFormat; }
DDEDATA Structure
The actual size of the WM_DDE_DATA parameter structure for hData
(LOWORD(IParam)) depends on the size of the Value array:
DDEDATA (struct) <new version>
{ unsigned unused:12,
fResponse:1,
fRelease:1,
reserved:1,
fAckReq:1;
Tie Cam Foinmiaite.
Bivene Valuel1]; }
DDEPOKE Structure
The actual size of the WM DDE POKE parameter structure for hData
(LOWORD(IParam)) depends on the size of the Value array:
DIDIEPIO
KES Gsiciuicat.) <new version>
{ unsigned unused:13,
fRelease:1,
fReserved:2;
int cfFormat;
Bivalee Valuel1]; }
Perhaps the most noticeable difference between these two programs is that
when Dde_Main is executed, Dde_Main begins by initiating a conversation
directed to Dde Data. Since Dde_Data is not active, that is, has not been
loaded, Windows searches the current directory and the directories specified
in the PATH statement, looking for Dde_Data and loading the server program
automatically.
The server program, however, is loaded as an icon, not as a full-sized
window, and actually can neither be restored nor maximized. Since the server,
in this example, exists only to communicate with the client application, the
server does not require display space (a client window), and includes no
WM_PAINT provisions to manage a display.
Figure 23-4 shows Dde_Main with the Dde_Data icon (DDE Server Demo)
superimposed in the lower-right corner. Normally, of course, the Dde_Data icon
would appear at the bottom of the screen and not within the Dde_Main window.
Box Link
Chain Stock
Clipboards
Colors
Devices
Diamond Link
Font Stock
Pen Grids
Pie YYidgits
Printer Jaks
Round Stock
Shim Stock Pee
One of the first differences is that Dde Data can not have more than one
instance of the application active at any time. This provision is not normally
made but is accomplished as:
int PASCAL WinMain( HANDLE hInst, HANDLE hPrevIinst,
RSulek: lpszCmdParam, int nCmdShow )
{
In all of the previous examples, the hPrevInst parameter was tested to decide
if this was the first instance of the application, and the window class needed
to be registered. In this application, not only should only the first instance
register the window class(es), but any attempted second instance should not
be allowed to load at all.
Where previous examples have registered a single window class in the
WinMain procedure, the Dde_Data application registers two separate window
classes. The first window class registered is the regular window class. It is
similar to previous examples, but has a couple of provisions to allow for the
fact that this application will never have a client window or scrollbars, menu
or background color:
we.style = 0; LP MNotcez non sity Wes sf lagis
we.lpfnWndProc = WndProc;
we.cbClsExtra = 0;
we.cbWndExtra = 0;
we.hInstance = hInst;
we.lpszClassName = szAppName;
we.lpszMenuName = NULL;
wce.hIcon LoadIcon( hInst, szAppName );
we.hCursor Roa dictins Or © NUS eh DIC arAIRIR
OW s=
we.hbrBackground GetStockObject( WHITE_BRUSH );
RegisterClass( &we );
Of course, Dde Data does have an icon, but this is about all of Dde_Data
which will be visible.
It is the second window class registered that is really different, because this
window class is used for the ServerProc, an exported subprocedure which
handles the DDE communications with the client application:
// register window for DDE server //
wce.hInstance = hinst;
we.lpfnWndProc anOlGy,
=m SIClmVielIa
520 BORLAND C++ 3.0 PROGRAMMING, Second Edition
This window class consists of very little except the ClassName and the
exported WndProc (ServerProc). However, since Dde_Data will create a separ-
ate server instance in the form of a "hidden" window for each client conversing
with Dde_ Data, the cbWndExtra field is declared as a two WORD value. It
provides one word in memory to store the window handle that identifies each
server window’s client, while the second word is used for a handle to the
global memory block containing the application’s data.
Next, the CreateWindow provision, which is essentially the same as in any
other application, is followed by provisions to set a timer which uses another
exported procedure.
If the CreateWindow provision is familiar, the Show Window instruction used
is not, and insures that the application remains an inactive icon as:
ShowWindow( hwnd, SW_SHOWMINNOACTIVE );
The OxFFFF value for the addressee makes this a broadcast message
because, at this point, the server application’s handle is not known. It may not
even be established yet if Dde_Data has not yet been loaded.
Remember, though, that Dde_Data has been identified by the aApp atom
identifier, and Windows will, if possible, load Dde_ Data in response to this
message. When Dde_Data receives the initiate message (in Dde_Data’s WndProc
procedure), the client window’s handle (Dde_Main) appears in wParam, but
instead of immediately retrieving the two atoms from /Param, Dde_Data
insures that its own application name (szAppName) and topic (szTopic) are
included in the global atom table:
case WM DDE INITIATE:
nC lent = wParam;
aApp = GlobalAddAtom( szAppName );
aTopic = GlobalAddAtom( szTopic );
Once these strings are included in the global atom table and two local atoms
(values) are returned, instead of retrieving strings which the calling process
placed in the atom table, a simpler test can be made using the atom values as:
522 BORLAND C++ 3.0 PROGRAMMING, Second Edition
In this example, the test is written to accept either a match on the requested
application (the server) and the topic, or to accept a null (wild-card) on either
parameter. If there’s a match, the server application creates a hidden window
that uses szServerClass to handle communications with the client:
{
hServer = CreateWindow(
szServerClass, NULL,
WS] GHLUD 00.0, Oy
aime, MULL. InGumeswe, NWI »d-
SetWindowWord( hServer, O , hClient );
SendMessage( wParam, WM_DDE_ACK, hServer,
MAKELONG( aApp, aTopic ) );
}
LpDdeAdvise->fDeferUpd FAISSiEs
LpDdeAdvise->cfFormat = CF_TEXT;
GlobalUnlock( hDdeAdvise );
The first step in the process involves setting up a global memory block for
each item on the list, then adding each of the stock numbers to the atom list,
receiving altem as the identifier for each:
altem = GlobalAddAtom( stockslLil.szStkNum );
Uf CelpPostMessageCshServien,. WMoDDEAADVISE, hwnd,
MAKELONG( hDdeAdvise, alItem ) ) )
If you really want to track the intricacies of this traffic, the Dde_Data.C and
Dde_Main.C examples that follow will lead you through the major part of the
maze. If and when you get too confused, refer back to Figures 23-1 thru 23-3.
However, in the long run the DDE maze isn’t really that complicated, just
tedious.
Summary
Between the growing interest in multitasking environments and the demand
for individual applications to handle more and more complexity, there is also
a very real need to subdivide tasks between semi-independent or even com-
pletely independent applications.
In the past, there has been a determined attempt on the part of many
applications to become multitask systems, with word processors attempting
to integrate typesetting and spread sheets and databases and drafting utilities
and virtually anything else that anyone can imagine a use for—all in one
package.
The result, of course, is a completely unwieldy and generally unworkable
package.
There is a second approach which began, in a primitive fashion, with the
Windows clipboard. This allowed separate applications to execute con-
currently, and to share data in several different forms between themselves.
Thus far, this inter-application cooperation has not yet set the world on fire
and is generally limited in cooperative examples but nonetheless, it is still a
direction which should be watched carefully for future developments. The
DDE processes provide tools toward this aim.
{ff SeeesSesoeesessesnes
/i/
I) DDE_DATA.C //
// DDE Server Demo //
j/(/SSSSS=SSe5eoSseeeses/ /
Hinclude <windows.h>
#Hinclude <dde.h>
#inelude <string.h>
Hinclude <stdlib.h>
#Hinclude <time.h>
static char szAppNamel] "DDE_DATA";
static char szServerClassl[] "DDE_DATA.Server";
526 BORLAND C++ 3.0 PROGRAMMING, Second Edition
hDdeCmnds, hDdePoke;
int i
1;
HWND hClient;
MSG mMsg;
STOCKRPT FAR *lLpStkReport;
WORD cfFormat, wStatus;
Switch( msg )
{
case WM_CREATE:
hStkReport = GlobalAlLloc(€ GHND, NUM_ITEMS *
SalizZie1Out Gu SulaOGKeRPill ae =
if€ ! hStkReport ) DestroyWindow( hwnd );
else SetWindowWord( hwnd, 2, hStkReport );
SQuuMirm¢ (@) 5
case WM_DDE_REQUEST:
hClient = wParam;
cfFormat = LOWORD( LParam );
altem HIWORDC LParam );
176 CrROrhEeG SS CR Wey
{
GlobalGetAtomName( altem, szItem,
SepzieiOnf Gars 27letiemia mes
Of Geet — Oe NU Mie lant EM See ce)
eh Gres tiG MN pi CmeStZeletieinymmeSitlOlCK:S!
leben SizeSctik NIUMm me
break;
1G DSO UVES »
{
GlobalDeleteAtom( altem );
PostDataMsg( hwnd, hClient,
WRU, PALSIE, IFAILSIE. a 25
return(0O);
} }
DdeAck.bAppReturnCode = QO;
DdeAck.reserved = 0).
DdeAck.fBusy =e AVL SIES
DdeAck.fAck = TSE AMSIE:
wStatus = *(€ WORD *) & DdeAck;
if€( ! PostMessage( hClient, WM_DDE_ACK, hwnd,
MAKELONG( wStatus, altem ) ) )
GlobalDeleteAtom( altem );
meluuinniGODy-
case WM_DDE_ADVISE:
hClient = wParam;
hDdeAdvise = LOWORD( LParam );
altem = HIWORD( LParam );
LpAdvise = (DDEADVISE FAR *)
GlobalLock( hDdeAdvise );
if( lpAdvise->cfFormat == CF_TEXT )
{
528 BORLAND C++ 3.0 PROGRAMMING, Second Edition
DdeAck. bAppReturnCode 0;
DdeAck. reserved 0;
DdeAck.fBusy FALSE;
DdeAck.fAck RIUIEV:
hStkReport GetWindowW ord( NiWinidy acon ee
LpStkReport CSTOCKRPT FAR *)
GlobalLock( hStkReport );
Cech Porm@at wiccd Format ==" Cr) EX hs)
{
fe Gus aap lete imnime)
for(€ i=0; i<NUM_ITEMS; i++ )
UpStkReportlid.fAdvise = FALSE:
else
if
GlobalGetAtomName( altem, szItem,
sizeof(szItem) );
fom 70 <NUMSLDT EMS» a+)
if'C Vstnemp© szitetiyestocksLidrszstkNum)
break;
Ta ISIN eS.)
LpStkReportLild.fAdvise = FALSE;
else DdeAck.fAck RAESE:
yp
else DdeAck.fAck = FALSE;
wStatus = *(€C WORD *) & DdeAck;
if€ ! PostMessage( hClient, WM_DDE_ACK, hwnd,
MAKELONG( wStatus, ‘arlcemnn a a
if€ altem ) GlobalDeleteAtom( altem );
GlobalUnlock( hStkReport );
return(0);
case WM_DDE_EXECUTE:
hClient = wParam;
hDdeCmnds = HIWORD(C LParam );
DdeAck.bAppReturnCode = OQ;
DdeAck.reserved =) 0%
DdeAck.fBusy = FALSE;
DdeAck.fAck =FALSE->
wStatus = *€ WORD *) & DdeAck;
if( !' PostMessage( hClient, WM_DDE_ACK, hwnd,
MAKELONGC( wStatus, hDdeCmnds ) ) )
GlobalFree( hDdeCmnds );
return(0);
case WM_DDE_POKE:
hClient = wParam;
hDdePoke = LOWORD( LParam );
aIltem = HIWORD( LParam );
DdeAck.bAppReturnCode = QO;
DdeAck.reserved = QO;
DdeAck.fBusy = Als oes
DdeAck.fAck = FALSE;
530 BORLAND C++ 3.0 PROGRAMMING, Second Edition
i,
Long FAR PASCAL WndProc( HWND hwnd, WORD msg,
WORD wParam, LONG LParam )
a
static FARPROC LpTimerProc, LpCloseProc;
Sitraitaicmcinralts S20 Pl Chas) “LeCR.S COCKS. =
ATOM aApp, alopic;
HWND hClient, hServer;
int Ws
switch( msg )
{
case WM_CREATE:
LpTimerProc MakeProcInstance( TimerProc, hGInst )
LpCloseProc MakeProcInstance( CloseProc, hGInst ) Ne
Ree uinin GOD
case WM _DDE_INITIATE:
hClient = wParam;
aApp = GlobalAddAtom( szAppName );
aTopic = GlobalAddAtom( szTopic );
if€ € !LOWORDC(LParam) | LOWORD(CLParam) == aApp ) &8&
( 'HIWORDCLParam) |
HIWORDC(LParam) =
{
hServer = CreateWindow( szServerClass, NULL,
WSa CHI LDew 0s 054 0¢8 05
Diwinidy ae NUE een Gelumisste, am NIU) [eleme) =
SetWindowWord( hServer, O , hClient );
SendMessage( wParam, WM_DDE_ACK, hServer,
MAKELONG( aApp, aTopic ) );
}
else
{
GlobalDeleteAtom( aApp );
GlobalDeleteAtom( aTopic );
};
me cumn cUDe-
case WM_TIMER:
case WM_TIMECHANGE:
Tome = 0s NUM Le MS) eeite)
{
Tice ts randoms) yD
stocksLil.lLQuantity = (long)
C Ssto@e@ksle
tal. Leman Ties %
€ 905+ random(20) ) / 100 );
i
EnumChildWindows( hwnd, lLpTimerProc, OL );
return(Q);
case WM_QUERYOPEN: return(Q);
532 BORLAND C++ 3.0 PROGRAMMING, Second Edition
case WM_CLOSE:
EnumChildWindows( hwnd, lLpCloseProc, OL );
break;
case WM_DESTROY:
PostQuitMessage(0Q);
Recuinn GPs
}
return( DefWindowProc( hwnd, msg, wParam, LParam ) );
}
BOOL PostDataMsg( HWND hServer, HWND hCLlient,
BOOL fResponse, BOOL fAckReq,
BOOL fDeferUpd, int iState )
{
ATOM altem;
char szPopL101];
DDEACK DdeAck;
DDEDATA FAR *lLpDdeData;
DWORD dwTlime;
GLOBALHANDLE' hDdeData;
MSG mMsg;
WMESDDESACK, PM REMOVE ) )
{
DdeAck = *( DDEACK *) & LOWORD( mMsg.lParam );
aIltem = HIWORD( mMsg.lParam );
GlobalDeleteAtom( altem );
break;
+
Tet Goe ee DidlerAIG
ke fiAICiKanD
ne
if€ hDdeData ) GlobalFree( hDdeData );
mextuinin) Guiry Sica
pF
Recunn.G TRUE)!
uy
#pragma argsused
we.cbWndExtra OFyA
we.hInstance hinst
we.lpszClassName szAppName;
we.lpszMenuName NUE
wce.hIcon LoadiIcon( hInst, szAppName );
we.hCursor Roald|G Urns Ol GaN) EE LoD Cam ALRO)\Wikeo n=
we. hbrBackground Geis cock0biject Guwhi TEs RUSH»:
RegisterClass( &wec );
// register window for DDE server //
wce.hInstance hlinisites
we.lpfnWndProc ServerProc;
we.cbClsExtra 0;
wce.cbWndExtra Cee SuleZiClONA GuUWiOIRID eae
we.lpszClassName szServerClass;
wce.lpszMenuName NUE
we.hIcon NIUEEse
we.hCursor NULL;
we.hbrBackground ETL s
we.style OF
RegisterClass( &we );
hwnd = CreateWindow( szAppName, "DDE Server Demo",
534. BORLAND C++ 3.0 PROGRAMMING, Second Edition
WS _OVERLAPPEDWINDOW,
CWLUSEDE FAULT? = CWLUSEDE FAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NUE SNUEE SS bins, eeNIUIEIE a)e
SendMessage( hwnd, WM_TIMER, O, OL );
itf¢ ! SetTimerGohwnd, 01> S000 NUL Ds)
{
MessageBox( hwnd, "Too many clocks or timers!",
szAppName, MB_OK | MB_ICONEXCLAMATION );
Pecurn GubALs Ems
}
ShowWindow( hwnd, SW_SHOWMINNOACTIVE );
UpdateWindow( hwnd );
while€ GetMessage( &mMsg, NULL, O, O ) )
{
TranslateMessage( &mMsg );
DispatchMessage( &mMsg );
}
KO WAIMGRG Aneinel, Aa 6
return( mMsg.wParam );
}
7 SRSS SSS SS SS SSS SSS 5
NAME DDE_DATA
#include <windows.h>
#Hinclude <dde.h>
#include <stdlib.h>
#Hinclude <string.h>
static char szAppNameC] = "DDE_MAIN";
struct { char *szItemName;
char. *szStkNum>
Chapter 23: Dynamic Data Exchange 535
long lQuantity; }
StockstiGawt 2BartsS tock > MYBS SY pAO,
M3@p< (Latiile- ZB Se Ol
“Cheailin SeroOel? “GS OL Oe
CT pibioind sus, “GC Big aObe
Golo sina, Gtk Ova a Ol
"Devices", a DV ae
PDunanond einkaSOyD EAS OF
"Font Stock", DYSeite See Oy
Leinae Gin Sua, AdGia
CPrerWitdgTts oF tupwen -o0>
PiPiiMicaip delet. . MPs’ (0),
MeRNOIUIM Cl OuClOlG Kaur. ARCOM eyomeOa
MSinimMm Seo@ekY Josey aden aUh ine
#define STOCK_ITEMS GE SEZ GSihOOks m/meSmiZieOn GSitioc ksil Ona
#define WM_USER_INIT WR SUSER te)
long FAR PASCAL WndProc( HWND hwnd, WORD wMsg,
WORD wParam, LONG LParam )
{
static BOOL dinitrate ==" TRUE?
Sitialtel Gasc hiail szServerL] = "DdeFiles",
SiZ1kO Dilicie) alee MEGLE OICIKS mer
static HWND hServer = NULL;
Sitjaital Caaainit CxCnibeucy.c nine.
ATOM aApp, aTopic, altem;
char szButii2es)> szPoplié],7"szitemePté];
DDEACK DdeAck;
DDEDATA FAR *lpDdeData;
DDEADVISE FAR’ *lLpDdeAdvise;
DWORD dwTime;
GLOBALHANDLE hDdeAdvise, hDdeData;
HDC hdc;
MSG msg;
PAINTSTRUCT ps;
int ine
TE XonM Eve Rete tm;
WORD WS tatus scm hommat.
switch( wMsg )
{
case WM_CREATE:
hdc = GetDCC( hwnd );
GetTiextMetrnicsCchde,;,&tm 1;
cxChr = tm.tmAveCharWidth;
cyChr = tm.tmHeight + tm.tmExternalLeading;
ReleaseDC( hwnd, hdc );
return(Q);
case WM_USER_INIT:
aApp =-GlobalAddAtom( szServer );
536 BORLAND C++ 3.0 PROGRAMMING, Second Edition
}
hee 1 <STOCK ITEMS)
MessageBox( hwnd, "“WM_DDE_ADVISE msg failure!",
szAppName, MB_OK | MB_ICONEXCLAMATION );
Return GODr
case WM_DDE_ACK:
due loithwwaiawee 2
Ht
hServer = wParam;
GlobalDeleteAtom( LOWORD( LParam ) );
GlobalDeleteAtom( HIWORD( LParam ) );
}
Re euinneGopr=
case WM_DDE_DATA:
hDdeData: = LOWORD( LParam );
LpDdeData = (DDEDATA FAR *) GlobalLock( hDdeData );
altem = HIWORDC LParam );
DdeAck.bAppReturnCode = QO;
DdeAck.reserved = 0;
DdeAck.fBusy = FALSE;
DdeAck.fAck =P ANSE:
if: GealpiDidielDiaytiai=>
cf Fioinmialtai=—" Gham) EyXeleen)
{
GlobalGetAtomName( altem, szIitem,
sizeot€ szitem J»)
FOr TEOS TRSTOCK UTES Fs asp 2
et Geet CMD iGuS Zalat em) mS tCOlCIK Siti snS?275) CGKNICIN ID ae)
break;
THC TSSTOCKX. wiles »
{
Ustropy€ szPop, (pDdeData->-Value )>
stocksCLil].lQuantity = atol( szPop );
InvalidateRect( hwnd, NULL, FALSE );
DdeAck.fAck = TRUE;
+ 3
if( lLpDdeData->fAckReq )
{
wStatus = *€ WORD *) &DdeAck;
if( ! PostMessage( wParam, WM_DDE_ACK, hwnd,
MAKELONG( wStatus, aIltem ) ) )
{
GlobalDeleteAtom( altem );
GlobalUnlock( hDdeData );
GlobalFree( hDdeData );
return(Q);
1S
else GlobalDeleteAtom( altem );
if( lLpDdeData->fRelease || ! DdeAck.fAck )
A=
GlobalLUnlock( hDdeData );
538 BORLAND C++ 3.0 PROGRAMMING, Second Edition
GlobalFree( hDdeData );
}
else GlobalUnlock( hDdeData );
return(0Q);
case WM_PAINT:
hdc = BeginPaint( hwnd, &ps );
for C 7205 S70 CK 211. EMS 2. i tees 5)
{
SetTextAlign©® hdc,. TA _LEF TA |STALLOPRIO:
Lextoutte bdic -exChry i*¢y Chr e sz8utte,
wsprintt (sz Butt at 72-20607
CLPSTRD “stockslild..szitemName )4)-
SetTextAlLiagn® hde> TALRIGHT |; TALTOPA»;
Tex t.O.u tC. -ncdio, cx CiNitee.o) ae wc Y.GN ees Z.biUne,
MSprimer(€ SaBwii, KA-58",
GEPSiPRD ms worciks are siz oct NUM »lintJF
exc tiOlUrt Ga hidics- mG x Ginihe Sion menial CNiheemmes ZB Uae,
WwSprimer€ SHzBuir A, VA 10el",
SvOCKSIEDIalOwanhiedit, w MF
}
SEuvTeExeAuLT GAC Incleé, UWALIKEFU |) TA-VOR 5
EndPaint(€ hwnd, &ps )->
return(Q);
case WM_DDE_TERMINATE:
PostMessage( hServer, WM_DDE_TERMINATE,
DiWwiniGy, waN UI) s=
hServer = NULL;
return(Q);
case WM_CLOSE:
if€ ! hServer ) break;
PostMessage( hServer, WM_DDE_UNADVISE, hwnd,
MAKES O)NIGiGSS Ciraeal: EpXaliep een N|UII eee)ee) =
dwTtTime = GetCurrentTime();
while€ GetCurrentTime() - dwTime < 3000 )
if€ PeekMessage( &msg, hwnd, WM_DDE_ACK,
WM_DDE_ACK, PM_REMOVE ) ) break;
PostMessage( hServer, WM_DDE_TERMINATE, hwnd, OL );
dwlime = GetCurrentTime();
while€ GetCurrentTime() - dwTime > 3000 )
if€ PeekMessage( &msg, hwnd, WM_DDE_TERMINATE,
WM_DDE_TERMINATE, PM_REMOVE ) )
break;
break;
case WM_DESTROY:
PostQuitMessage(0Q);
return(Q);
i;
return(€ DefWindowProc( hwnd, wMsg, wParam, lParam ) Me
Chapter 23: Dynamic Data Exchange 539
#pragma argsused
int PASCAL WinMain( HANDLE hInst, HANDLE hPrevInst,
; LPSTR LpszCmdParam, int nCmdShow )
HWND hwnd;
MSG msg;
WNDCLASS WC;
iG ! hPrevinst )
{
we.hInstance = hInst;
we.lpfnWndProc = WndProc;
WiGrer CID) G lESiExct Ina = (0)
we.cbWndExtra = 0;
wce.lpszClassName = szAppName;
we.lpszMenuName = (LPSTR) szAppName;
we.hIcon = LoadIcon( hInst, szAppName );
we.hCursor =n Oval Cuil S Olt GumN IUey er Gam ALR RO) Wine =
we.hbrBackground = GetStockObject( WHITE_BRUSH );
we.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass( &wec );
}
hwnd = CreateWindow( szAppName, "DDE Client Demo",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CWRUISIE DIESAU Te G Wee Wis EDIE FA UEe,
NULL, NULL, hInst, NULL );
ShowWindow( hwnd, nCmdShow );
UpdateWindow( hwnd );
SendMessage( hwnd, WM_USER_INIT, O, OL );
while€ GetMessage( &msg, NULL, O, O ) )
if
TranslateMessage( &msg );
DispatchMessage( &msg );
}
return( msg.wParam );
}
; DDE_MAIN.DEF =;
PISS SSSSosSSSSno y
NAME DDE_MAIN
DESCRIPTION "DDE Client”
EX eerie WINDOWS
STUB "WINSTUB.EXE"
CODE PRELOAD MOVEABLE DISCARDABLE
DATA PRELOAD MOVEABLE MULTIPLE
HEAPSIZE 1024
STACKSIZE 8192
EXPORTS WndProc
’ 7
a aden a mA
7 SA thle
a
os,
S ee ot Lh be
binds (vane! topebate ty
2 panne sinned butane oj
( \oudiwete thas oer revewroent: Af
>tee WA Parga)..
BGs 2 Beal akesnete Seas
mea Gua Le | Ee any ae
as Dey tet,
© ates 0@ es,
iene
laat oué Sons) ae 7 rae
-
- Svit Pasee sisted >
AP & Sse
16 ?
; Lee) S He Gs BZ
‘ hha | Pe ee
; ner ar ae e
éndbegaee CRIA 9
Ni eu Se —e - 4qA L0n@i Poa Fe A
WAees 26 adin J ew
’gRies |] 911; 0°
a4 or ert ier: PIAGLLN ab
y err a
48! Relea) § @easaen,
pa Same ain
wires
ag lim. f ve ¢ yoat Be 2»
et nee spe os é : : : a al eae
= VS
oe) 10A6) TS SEE we SOHunds? | Hees \efag es re
ri fetery
o VLA Arosa
(ine 4ase
i ses ta
u77
“Sehr ee Taal a 1a - o4@ 4)
pede
nd 4 20g (ar l@ CAN 2am
Uidsnelé Wood robe
‘ :
7 bye? (eghata
1 JD 08 5 PHAR SIsAe coeud Jiwas onbh
ae iite ,.went dtiexeenen
a a4
Bry
i s ‘ee ange 7
7601) + U0= <3. e@hae ‘e
a be eNel
G fe (ganas
A c s6ne (Oj Set wider
— P
7
reuthe
» yy 5
oir,
{
Cane x ths
os i] oi atalngs ' rn
aeeu eee ee ye e |
H<«@ o -
S211, -§SR” 7 OG
Sytcudy
2¢8 ap CAter;'
1O9RA94 1 2+-31 GATE EE ewes.
S19 T Lea Les oven a 86
Ay
ae eer Oy
Appendix A
Constants/Flags/Macros/Structures
FALSE 0 TRUE 1
Type Equivalents
Macro Definitions
max(a,b) CEGa) aaa GbD® mara Gap) (b))
min(€a,b) CECaD eae Cb) Danae Gap maa GDP)
MAKELONG(Ca,b) CCLONG) CCCWORD) Cad) | CCCDWORD)CCWORD)(6b))) << 16)))
LOWORD(CL) CCWORD)CL))
HIWORD(CL) CCWORD)CCCDWORD)I
CL) >> 16) & OXFFFF))
LOBYTECw) CCBYTE)Cw) )
HIBYTECw) CCBYTE) CCCWORD) Cw) >> 8) & OXFF))
OpenFile() Flags
OF_ READ 0x0000 OF_PARSE 0x0100
OF_READWRITE 0x0002 OFZECREATE 0x1000
OF_WRITE 0x0001 OF_DELETE 0x0200
OF_ SHARE _COMPAT 0x0000 OF_PROMPT 0x2000
OF_SHARE EXCLUSIVE 0x0010 OFAVERIEY 0x0400
OF_SHARE DENY WRITE 0x0020 OF_EXIST 0x4000
OF SHARE DENY READ 0x0030 OFTCANCEE 0x0800
OF_SHARE_DENY_NONE 0x0040 OF_REOPEN 0x8000
GetTempFileName() Flag
TF_FORCEDRIVE 0x80 (BYTE)
GetDriveType Return Values
DRIVE REMOVABLE 2 DRIVE _FIXED 3 DRIVE REMOTE 4
GDI Section
Binary Raster Ops
R2_BLACK 1 R2_MASKPEN 9
R2_ NOTMERGEPEN 2 R2_NOTXORPEN 10
R2_MASKNOTPEN 3 R2_NOP iil
R2_NOTCOPYPEN 4 R2_MERGENOTPEN ip
R2_MASKPENNOT 5 R2_COPYPEN 13
R2_NOT 6 R2._MERGEPENNOT 14
R2_XORPEN 7 R2_MERGEPEN i)
R2_NOTMASKPEN 8 R2_WHITE 16
StretchBIt() Modes
BLACKONWHITE il WHITEONBLACK 2 COLORONCOLOR 3
PolyFill() Modes
ALTERNATE 1 WINDING 2
Metafile Constants
Logical Palettes
PALETTEENTRY (€ struct tagPALETTEENTRY )
{ BYTE peRed; Biyeiee peGreen; BYTE peBlue; Biase peFlags;
}
EPP AIS esa Ee Nae, (pointer) PALETTEENTRY FAR
LOGPALETTE ( struct tagLOGPALETTE )
{ WORD palVersion; WORD palNumEntries; PALETTEENTRY
palPalEntryl11]; }
ROG PAVE Eee (pointer) LOGPALETTE
NPLOGPALETTE Cpointer) LOGPALETTE NEAR
ELPLOGPALETTE (pointer) LOGPALETTE FAR
Logical Font
LOGFONT (¢ struct tagLOGFONT )
{ int LfHeight; int LfWidth; TanG LfEscapement;
int LfOrientation; int lLfWeight; Bayene ter tiarUiice
BYTE LfUnderline; BiyaE UifeSitine
ke Olurte-
BY Eee tC nalmolel
te Biyeiee EROMtPIne cuts aonys BYime
GrelipP rec sion.
SNe UieMe tse Biveiee LfPitchAndFamily; Biyaie
UffaceNameE LiPo PACES T ZEA): }
PLOGFONT Cpointer) LOGFONT
NPLOGFONT (pointer) LOGFONT NEAR LPLOGFONT Cpointer) LOGFONT FAR
LF _FACESIZE 3
OUT_DEFAULT_PRECIS 0 OUT_CHARACTER_PRECIS D
OUT_STRING_PRECIS 1 OUT_STROKE_PRECIS 3
CLIP_DEFAULT_PRECIS 0 CLIP_STROKE_PRECIS 2
CLIP_CHARACTER PRECIS 1 PROOF_QUALITY 2
DEFAULT QUALITY 0 DRAFT_QUALITY 1
DEFAULT_PITCH 0 FIXED_PITCH if
ANSI CHARSET 0 SHIFTJIS_ CHARSET 128
SYMBOL_CHARSET 2 OEM_CHARSET 255
VARIABLE PITC 2
Font Families
FF_DONTCARE (0<<4) FF_SWISS (2<<A) FF_ SCRIPT (4<<4)
FF_ ROMAN (1<<4) FF_MODERN (3<<4) FF_ DECORATIVE (5<<4)
Font Weights
FW_DONTCARE 0 FW_NORMAL 400 FW_EXTRABOLD 800
FW_THIN 100 FW_MEDIUM 500 FW_HEAVY 900
FW_EXTRALIGHT 200 FW_SEMIBOLD 600
FW_LIGHT 300 FW_BOLD 700
FW_ULTRALIGHT == FW_EXTRALIGHT
FW_REGULAR ==> FW_NORMAL
FW_DEMIBOLD ==> FW_SEMIBOLD
FW_ULTRABOLD —— > FW_EXTRABOLD
FW_BLACK ==> FW_HEAVY
EnumFonts Masks
RASTER FONTTYPE 0x0001 DEVICE FONTTYPE 0x0002
RGBC(r,g,b)
CCDWORD) (CCBYTE) (Cr) | CCWORD) Cg) <<8))| CC CDWORD) CBYTE) (b))<<16)))
PAP ET WVERGB Gn
grip» COxO2000000UL | RGB(r,g,b))
PALETTEINDEX(1) CCDWORD)(OxO01000000UL | (CWORD)Ci)))
GetRValue(rgb) (CBYTE) (rgb) )
GetGValue(rgb) CCBYTE) CCCWORD) (rgb)) >> 8))
GetBValue(rgb) CGB YanEo |GGrgiby>>1op»
Appendix A: Constants/Flags/Macros/Structures 551
Background Modes
TRANSPARENT 1 OPAQUE 2
Mapping Modes
MM_TEXT 1 MM_LOENGLISH 4 MM_ISOTROPIC 7
MM_LOMETRIC 2 MM_HIENGLISH 5 MM_ANISOTROPIC 8
MM_HIMETRIC 3 MM_TWIPS 6
Coordinate Modes
ABSOLUTE 1 RELATIVE D
Stock Logical Objects
WHITE_BRUSH 0 NULL_PEN 8
LTGRAY_ BRUSH 1 OEM_FIXED_FONT 10
GRAY _ BRUSH 2 ANSI FIXED_FONT 11
DKGRAY_ BRUSH 3 ANSI VAR_FONT 12
BLACK BRUSH 4 SYSTEM_FONT 13
NULL_BRUSH 5 DEVICE DEFAULT_FONT 14
WHITE_PEN 6 DEFAULT PALETTE 15
BLACK PEN 7 SYSTEM_FIXED_ FONT 16
HOLLOW_BRUSH = NULL_BRUSH
Brush Styles
BS_SOLID 0 BS HATCHED D BS_INDEXED 4
BS_NULL 1 BS_PATTERN 3 BS_DIBPATTERN 5
BS_ HOLLOW =e BS_ NULL
Hatch Styles
Curve Capabilities
CC_NONE 0 CC_CHORD 4 CQGISDYVED 32
€€ CIRCLES 1 C@sEELIRSES 8 CC_WIDESTYLED 64
CGrrik 2. CC_WIDE 16 CC_INTERIORS 128
Line Capabilities
LC_NONE 0 LC_POLYMARKER 8 LC_WIDESTYLED 64
L@SPOLYEINES 2 LC_WIDE 16 LC_INTERIORS 128
LC_MARKER 4 LersTYEED 32
Polygonal Capabilities
PC_NONE 0 PC_WINDPOLYGON 4 PEISTYEED 32
PC_POLYGON 1 PC_SCANLINE 8 PC_WIDESTYLED 64
PC_RECTANGLE 2 PC_WIDE 16 PC_INTERIORS 128
Polygonal Capabilities
CP_NONE 0 CP_RECTANGLE 1
Text Capabilities
TEIOPSCHARACTER 0x0001 TC_SA_CONTIN 0x0100
TC_OP_ STROKE 0x0002 i GEAS DOUBLE 0x0200
TEICRsSoTROKE 0x0004 TC_JA_ABLE 0x0400
TC_CR_ 90 0x0008 TC_UA_ABLE 0x0800
TEIERTANY. 0x0010 TESSOFABLE 0x1000
TGSEOSYINDER 0x0020 TC_RA_ABLE 0x2000
TEeSATDOUBLE 0x0040 TE@W ASABE 0x4000
TC_SA_INTEGER 0x0080 TC_RESERVED 0x8000
Raster Capabilities
RC_BITBLT 1 RC_SCALING 4
RC BANDING 7 RC_BITMAP64 8
RC_GDI20_OUTPUT 0x0010 RC_BIGFONT 0x0400
RC_DI_BITMAP 0x0080 RC_STRETCHBLT 0x0800
RC_ PALETTE 0x0100 RC_FLOODFILL 0x1000
RC_DIBTODEV 0x0200 RC_STRETCHDIB 0x2000
Palette Entry Flags
PC_RESERVED 0x01 PESEXPEICH: 0x02 PC_NOCOLLAPSE 0x04
DIB Color Table Identifiers
DIB_RGB_ COLORS 0 DIB_PAL_COLORS 1
Constants for Get/SetSystemPaletteUse()
SYSPAL_STATIC 1 SYSPAL_NOSTATIC 2
Constants for CreateDIBitmap
CBM_INIT 0x04L // initialize bitmap
DrawText() Format Flags
DISTOR 0x0000 DT_EXPANDTABS 0x0040
DIZEEET 0x0000 DT_TABSTOP 0x0080
DT_CENTER 0x0001 DT_NOCLIP 0x0100
Appendix A: Constants/Flags/Macros/Structures 553
USER Section
Scroll Bar Constants
SB_HORZ 0 SbaGiile WD
SB_VERT Sail SB BOTH 3
SB_LINEUP 0 SB_THUMBTRACK 5
SB_LINEDOWN il SbaLO@r 6
SB_PAGEUP 2 SB_BOTTOM 7
SB_PAGEDOWN 3 SB_ENDSCROLL 8
SB_THUMBPOSITION 4
SW_HIDE 0 SW_SHOWNOACTIVATE 4
SW_SHOWNORMAL 1 SW_SHOW 5
SW_NORMAL 1 SW_MINIMIZE 6
SW_SHOWMINIMIZED 2 SW_SHOWMINNOACTIVE Wf
SW_SHOWMAXIMIZED 3) SW_SHOWNA 8
SW_MAXIMIZE 3 SW_RESTORE 9
HIDE_WINDOW 0 SHOW_FULLSCREEN 3
SHOW_OPENWINDOW 1 SHOW_OPENNOACTIVATE 4
SHOW_ICONWINDOW 2
Region Flags
ERROR 0 SIMPLEREGION D
NULLREGION 1 COMPLEXREGION 3
CombineRgn() Styles
RGN_AND 1 RGN_XOR 3 RGN_COPY 5
RGN_OR yD RGN_ DIFF 4
Window Styles
WS_OVERLAPPED 0x00000000L WS_DLGFRAME 0x00400000L
WS_POPUP 0x80000000L WS_VSCROLL 0x00200000L
WS_CHILD 0x40000000L WS_HSCROLL 0x00100000L
WS_MINIMIZE 0x20000000L WS_SYSMENU 0x00080000L
WS_VISIBLE 0x10000000L WS_THICKFRAME 0x00040000L
WS_DISABLED 0x08000000L WS_GROUP 0x00020000L
WS_CLIPSIBLINGS 0x04000000L WS_TABSTOP 0x00010000L
WS_CLIPCHILDREN 0x02000000L WS_MINIMIZEBOX 0x00020000L
WS_MAXIMIZE 0x01000000L WS_MAXIMIZEBOX 0x00010000L
WS_BORDER 0x00800000L
WS_CAPTION 0x00CO00000L // WS_BORDER | WS_DLGFRAME
WS_TILED ==> WS_OVERLAPPED
WS_ICONIC ==> WS_MINIMIZE
WS_SIZEBOX = WS_THICKFRAME
MENUITEMTEMPLATEHEADER (¢ struct )
{ WORD versionNumber; WORD offset; }
MENUITEMTEMPLATE ( struct ) :
{ WORD mtOption; WORD mtID; char tMeEeSterime@e4
dip 2
MF_END 0x0080
Parity Codes
NOPARITY 0 EVENPARITY 2 SPACEPARITY 4
ODDPARITY 1 MARKPARITY 3
ONESTOPBIT 0 ONESSTOPBITS 1 TWOSTOPBITS 2
IGNORE 0 INFINITE OxFFFF
Error Flags
CE RXOVER 0x0001 GEECISTO 0x0020 CE_IOE 0x0400
CE OVERRUN 0x0002 CE_DSRTO 0x0040 CE DNS 0x0800
CE _RXPARITY 0x0004 CE _RLSDTO 0x0080 CHIOOE 0x1000
CE FRAME 0x0008 CE _TXFULL 0x0100 CE_ MODE 0x8000
CE BREAK 0x0010 CE PTO 0x0200
IE_BADID -1 IE_ MEMORY 4 IE_BYTESIZE -11
IE_OPEN —2 IE DEFAULT -5 IE BAUDRATE- -12
IE NOPEN -3 IE HARDWARE _ -10
Events
EV_RXCHAR 0x0001 EV_DSR 0x0010 EV_RING _ 0x0100
EV_RXFLAG 0x0002 EV_RLSD 0x0020 EV_PERR 0x0200
EV_TXEMPTY 0x0004 EV_BREAK 0x0040
EVEGRS 0x0008 EV_ERR 0x0080
Escape Functions
SETXOFF 1 CLRRTS 4 RESETDEV 7
SELXON 2 SETDTR 5 LPTx 0x80
SETRTS 3 GERDIRG S66
DCB Ges tructestvagDCBap
ff TBC “abele WORD BaudRate; BYTE ByteSize;
BYTE Parity; BYTE SstopBmits WORD RlsTimeout;
WORD CtsTimeout; WORD DsrTimeout;
BYTE fBinary: Pe Bae met RoGSDalnS alDiuenatn ENOINS alPevedes7s “Ws
BY ali Ee Olt xG tesibaloiwienies BYTE fOutxDsrFlow:1;>
BN gue ae On BYai eet DitieDaEs aoe male BivaleEee OU Goxe- mele
BYTES tin ‘- Bae fiPelGinvalr ames. Bane rine e les
Baie fChEvt 1h Biel) Eee Ditanatn Owe mane ENIME wees rilerrs hs
Biel Ee Diumm yes char XonChar; chin XottChanr->
WORD XonLim; WORD XoffLim; char PeChar;
cians E.On Ghai Gina nme WiG Cinjalne WORD TxDelay; }
COMSTAT (€ struct tagCOMSTAT )
& ENA wee Shlolets 4s Vile LOSRMOLes WE BiYeEea Ries dino dismal.
Bane el 8 Nae i@ai Same 13 BiyYaiiEsetyE:Ontacmmnles
BiygiEmtalexe tLe WORD cbInQue; WORD cbOutQue; }
MDICREATESTRUCT ( struct tagMDICREATESTRUCT )
Ce PSiRe as ZiGilealsist- Sie Re Sizlieintamer= HANDLE hOwner;
TEMA > SRA Tie he Yeree LONG style; LONG lParam; }
// app-defined stuff
LPMDICREATESTRUCT (pointer) MDICREATESTRUCT FAR
CECE NT CREA ESaliR.UiG lem Gees timtic tanta GLa eNilnG RIEsAGmeE:SuleR.U Golam,
{ HANDLE hWindowMenu; WORD idFirstChild; }
565
566 BORLAND C++ 3.0 PROGRAMMING, Second Edition
WM_xBUTTONDBLCK message,
113-114
WM_xBUTTONDOWN message, 113
WM_xBUTTONUP message, 113
WM_xxxx event messages, 72-73, 83-84
WM_xxxxDBLCK message, 114
WM_xxxxDOWN message, 114
WM_xxxxUP message, 114
WNDCLASS data structure, 27
WndDefProc function, 123
WndProc procedure, 14, 20, 22-25, 82,
118-119, 141, 238, 520-521
WORD array, 365, 367, 371
WORD data type, 27
wParam variable, 73, 77-79, 82, 115,
124, 144
wsprintf function, 53, 85, 417
WS _OVERLAPPEDWINDOW mess-
age,16
Zz
Zoom tool, 175
7
~ pa A000 28
7 —s), 7
ar
AVS <mnad pe. 00
oti. m2) . : ; ; -
Ws nm, 0JET npenpy MDS
nent © a _
Wi Om M10 iene, TD
Ste 92 tA
<< T<. 1°36
A Suwa ok
=» ov;
wad Te Swati.
: "
TP ed . yr
—
TiAd: ees ' : : patentee = ; a oF
co el ae
7 : < : -
_ oe 5
— ¢ : d eee o mM
o a) ke
ile y ad _s . lante “a
"y aa Vv vy
A vy ;
: = 7 ¥- : sn
oe) mins i 2 f ‘
J i
7
oye i 1 NAA
2% (74 ws
VIVAUI OW
as =) (LS 2 mye
7 ' - 4
hah
-
eo
5") devise
“a
\
fe
€
eee
ae
age ee
Boa ae
a an
wnat} __—
-—|—
ee ee
| —
i eagae enee
eases
GAFLORD $
can cones PRINTEDINU.S«A.
VATA
00023 5911
UNIVERSITY OF THE PACIFIC LIBRARY
ogramming — >$28.95 USA
= Ca Ol
>$37-95 CANADA
| ~ Borland! C++ 30
I eee ts
Includes Window be Programming
A Ne WwEdi ition Ccof theBestselling Guide to Borla:
This eeecaing book has been chinnlevely updated ie) ecidé a comprehensive
guide to the many functions and features of the newest release of Borland’s C++
fol=\V-}(e) oant-\al@=laluicolalaat-ialem =1e)¢/-161¢) C++ 3.0 Programming, Second Edition also
recep le(=H Merelanle)(=iccMiaicoye [Ole (olamcom Naveen7molfove]e-lanlanliale Melero lave Malm A Aare(eo) oem
parallelsto the many tricks and shortcuts popular in the DOS environment. C and
. C++ plosrarniners Var ll (V= MUI Milare MUlst=| ©)(=McVol] ger-Mevele(-¥- lato mole-Void(er-1M->.¢- lan]6-1-8
Veteran programmer Ben Ezzell provides in- depth fofe\{=16-(0(- Me)IBorland’s newest lan-
guage release. He discusses:
Addison-Wesley P