0% found this document useful (0 votes)
248 views612 pages

Borland C++ 3.0 Programming

Uploaded by

ghent2008
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
248 views612 pages

Borland C++ 3.0 Programming

Uploaded by

ghent2008
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 612

PE FNS

atwee pe

RATER ONO aR

Includes Windows"
Programming —
WITHDRAWN
Borland®* C++ 3.0 Programming
Second Edition

Ben Ezzell

A
vv

Addison-Wesley Publishing Company, Inc.

Reading, Massachusetts Menlo Park, California New York


Don Mills, Ontario Wokingham, England Amsterdam Bonn
Sydney Singapore Tokyo Madrid San Juan
Paris Seoul Milan Mexico City Taipei
Many of the designations used by manufacturers and sellers to distinguish
their products are claimed as trademarks. Where those designations appear
in this book and Addison-Wesley was aware of a trademark claim, the
designations have been printed in initial capital letters.

Copyright © 1992 by Ben Ezzell

All rights reserved. No part of this publication may be reproduced, stored in


a retrieval system, or transmitted, in any form or by any means, electro-
nic, mechanical, photocopying, recording, or otherwise, without the prior
written permission of the publisher. Printed in the United States of America.
Published simultaneously in Canada.
The author and publishers have taken care in publication of this book, but
make no expressed or implied warranty of any kind and assume no
responsibility for errors or omissions. No liability is assumed for incidental or
consequential damages in connection with or arising out of the information
or programs contained herein.

Managing Editor: Amorette Pedersen


Production Editor: Andrew Williams
Set in 10.5-point Palatino by Benchmark Productions

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

In the 1450s, Johann Gutenberg created a revolution in communications


with the development of practical moveable type which was first used to print
school grammar books, indulgences, and a Bible. He took books from the
hands of scribes and placed them in the hands of the populace. And the world
has not stood silent since!
Just as printing began the revolution of communication, computers con-
tinue the destruction of the old order, making information available to all.
Because communications—first in printed form—brought about the
Reformation and, later, the Declaration of Liberty (and, more recently, the fall
of the Berlin Wall). The future is in your hands, just as the tools for com-
munications rest in your hands at this moment—because communications
come in many forms.
Some years ago, my wife remarked—with great satifaction—that “‘the power
of the press belongs to she who has one.”’ But, as the students in Tieneman Square
taught us, using an underground network of fax machines, the power of the
press is simply one form of the power of communications ... just as Windows
3.x is another.
This volume—which is also an example of the power of the press—is
respectfully dedicated to one of our first communicators:

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

Chapter 2: Transporting Text Applications to Windows 39


Design for Uncertain Circumstances 39
Processing WM_PAINT Messages 43
Graphic Text Display Elements 44
Windows’ Font Metrics 46
Windowing Text Output 50
Scrollbars 54
Window Sizing/Resizing 59
Scrolling Errors 61
Summary 62
Chapter 3: Keyboard, Caret, and Scrollbars 71
Keyboard Drivers 71
The WM_xxxx Event Message Le
Virtual Key Codes Ti
Character Event Messages 81
v1 BORLAND C++ 3.0 PROGRAMMING, Second Edition

The KeyCodes Program 83


Text Input Handling 85
Caret (Cursor) Positioning 89
Fonts and Character Sets 95
Supporting the ANSI Character Set 98
Summary 100

Chapter 4: The Mouse in Windows 111


Mouse Messages YS
Mouse Events in Windows 116
Mousel: Mouse Tracking 116
Mouse2: Mouse Cursors TZ
Mouse3: Hit Testing ie
Non-Client-Window Messages 123
A Final Mouse Message 125
Summary 126

Chapter 5: Child Windows and Control Elements 137


Windows Buttons 137,
Button Types 138
Button Operations 141
Button Control Communications: to and from 144
Button (Window) Labels 148
Part 2: Introducing the Resource Workshop 149
Resource Scripts 150
Developing Resources—Not Source Code 150
Resource Workshop Versus Resource Toolkit om!
Resource Components 152
Chapter 6: Application Resources and Resource Files 153
Editable vs. Untouchable Los
Files and File Types 156
The Resource Manager 158
Creating A New Resource Element 162
Editing An Existing Resource Element 163
Resource Identifiers 165
Summary 168
Contents vii

Chapter 7: Bitmaps, Cursers, Icons and Fonts 169


Editing Graphics Images 169
Testing SVGA and VGA Colors 172
The Graphics Editor Tools 174
Cursor Resources Wo
Icon Resources 181
Two Icons for FileView.C 183
Custom Fonts 183
Summary 186
Chapter 8: The Dialog Box Editor 187
The Dialog Box Editor 188
Dialog Box Styles 190
Dialog Box Control Elements 194
The Controls Menu 201
Aligning, Positioning, and Sizing Control Elements 204
Dialog Boxes with Menus 207
Header Files 207
Three Dialogs for the FileView Application 208
Summary 212

Chapter 9: The Menu Editor 213


Using the Menu Editor 215
Creating a Menu for FileView 219
Summary 220

Chapter 10: The Accelerator Editor 221


Defining Accelerator Keys 221
Accelerators for FileView 224
Summary 22)

Chapter 11: Editing String Resources 227,


Defining Strings 227,
The String Editor 228
A String table for FileView 230
Summary 230

Chapter 12: Header Files with the Resource Workshop 231


viii BORLAND C++ 3.0 PROGRAMMING, Second Edition

CHAPTER 13: Putting It All Together:


The FileView Application 235
Preventing Compiler Warning Messages 236
Loading Resources in WinMain 236
Creating the Dialogs 238
Initiating Dialogs 239
Summary 241

CHAPTER 14: Message Box Dialogs 259


Example One—Message Box Dialogs DEN,
Example Two—Borland Windows Custom Controls 263

Part 3: Graphics Programming in Windows DES

CHAPTER 15: Introducing Graphics Device Interface 2/7


Selecting The Device 279
Device Context Information 280
Mapping Modes 286
Screen (Window) Origins 289
Using Mapping Modes With Physical Units 290
Variable Mapping Modes Page|
MODES.C 292
Summary Zoe
CHAPTER 16: Colors in Windows 313
Colors.C O17
Colors and Drawing Modes S21
Summary D0
CHAPTER 17: Drawing Shapes and Figures 333
Graphic Tools 333
Creating Figures 336
Business Graphs 341
Drawing Polygons 344
Summary 348
CHAPTER 18: Brushes, Bitmaps, BLTs, and DIBs 365
Bitmapped Brushes: I 365
Bitmapped Brushes: II 367
Contents ix

Using Larger Bitmaps 370


Storing Images 370
SetBitmapBits /GetBitmapBits Bf.
Monochrome Bitmaps S73
Device-Independent Bitmaps 375
Creating and Using Device-Independent Bitmaps 380
Stretching Bitmaps 385
Demonstrating Bitmap Operations:PenDraw5 387
Summary 388

CHAPTER 19: Metafile Operations 401


Recording A Metafile 401
Writing Metafiles To Disk 406
Accessing Disk MetaFiles 407
MetaFile Cautions 411
Summary 412
CHAPTER 20: Graphic Typefaces and Styles 417
The TextOut and SetTextAlign Functions 417
Modifying The Device Context 422
Stock Fonts 423
A Brief History of Typefaces 424
Typefaces 426
Font Resource Files 427
Using Logical (Custom) Fonts 430
Creating A Logical Font 435
The Fonts1 Demo Program 437
Calculating Point Sizes In Logical TWIPS 438
Summary 439
CHAPTER 21: The Printer Device Context 451
Printers vs. Video 452
Initiating Printer Operations 452
Output To A Printer Device Context 453
Escape Subfunctions 454
Amending The Output Example 455
Gaining Direct Access To The Printer 456
x BORLAND C++ 3.0 PROGRAMMING, Second Edition

Banding Graphics Output 457


Using Banding 458
Printer Abort Procedures 459
An Abort Dialog 461
Aborting A Document 463
Text Output 463
Available Fonts and Device Capabilities 464
The System Menu Entry 467
Summary 467
CHAPTER 22: Clipboard Data Transfers 477
How The Clipboard Functions 478
Clipboard Data Formats 479
Special Purpose Formats 481
Clipboard Access 482
The ClipBd.C Demo 485
Text Clipboard Operations 486
Bitmap Clipboard Transfers 488
Metafile Clipboard Transfers 490
Other Clipboard Formats 494
Delayed Clipboard Rendering 495
Owner Displayed Clipboard Data 496
Other Uses For Private Formats 497
Summary 498
CHAPTER 23: Dynamic Data Exchange (DDE) 507
An Introduction To DDE 507
DDE Terminology 508
DDE Conversations: Atoms and Character Strings BiZ
Other DDE Communications Elements 514
The Dde_Data and Dde_Main Programs Se
The Dde_Data Server 518
DDE Message Traffic 521
Summary a e83.
Appendix A: Constants/Flags/Macros/Structures 541
Index 565
Acknowledgments

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

te EERE Tho scktumhveiyg eh aa elmee ome


Vans CRRA Rie . ies palling Pianarst tal
Pavese yySUAEREICSWy tee k (iti io Peg ee
jee ped eatin » Srktiweeiye
a ye
e + geil ead 2
: gp a, tem Sis bid sii @ halt ee greet fae
ne ™ (<4 a ow
Deion gee ta-Alasuss, 2) leo D3 Bish \230 ingest
a 7 - jabctthgues, Ane tnoid ipdere Selbreres cule 2¥adl Gy
eae
=* gM laze sinh oy,bali ek |nwritagad
Gi ne
“32 ‘— ile wad ok, i vy idl ies J) vetbridlnclt ty aeda ee
* (ASA mar qese oad rhieecias end etn rirt
wp a sen- % piers Cpe S sn watsarin aids ade * :
ira _— = ,
yin ais £4 wOLepite Sete me Wath =aetes sh «ara
gi = iS na ae aa >
-_

=— xs - al oy % ‘> Pieetin sé weet <0


— =< eeu “wmattig wwe ass we su an
een [hed 2172 2a ribet baw CA
= a:
Pare]

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 C/C++ for Windows 3.0

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).

Figure 1-1: Creating the Borland C++ Group

= ‘Create Groups for Borland C++ 3.0 |

@ Do you wish to create groups and icons?

Cancel

Tiffany Plus -E:\WRed Button NITOOO1.PCX

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.

Figure 1-2: Borland C++ Applications

WORKSHOP WINSIGHT IMPORTLIG DEBUG

PROFILE BORLAND C++ FCONVERT


Chapter 1: Installing C/C++ for Windows 3.0 5

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.)

QEMM-386 Version 6.0


QEMM is an expanded memory manager. Among other features, on
80386/80486 systems, QEMM permits loading TSR utilities into high memory
(the memory addresses between 640K and 1024K). While this area in memory
is traditionally reserved for system hardware, normally only a portion of the
area is actually used. By using this space for TSRs such as your mouse driver,
ANSLSYS, and other drivers or utilities, free memory in the lower 640K for
use by your principal applications, such as your compiler(s), or for use by
Windows.
Unfortunately, earlier versions of QEMM-386 have not been 100 percent
compatible with Windows, thus necessitating provisions for alternate boot
configurations for DOS and Windows 3.0.
Quarterdeck has recently released a new version of QEMM-386 which is
compatible with Windows 3.0. Upgrading to QEMM-386 version 6.0 (or to 5.1
or later) offers all the advantages of loading DOS TSRs in high memory and,
at the same time, does not require separate boot configurations for DOS and
Windows.
In most cases, QEMM-386 will install itself in a fully Windows 3.0 compati-
ble format and, together with the OPTIMIZE utility, requires little or no
revision for appropriate operation.
Unfortunately, most cases are not all cases and, depending on your video
system, you may get the message: Cannot run Windows because of video device
conflict when you attempt to load Windows 3.0.
Chapter 1: Installing C/C++ for Windows 3.0 7

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:

DEVICE=C:\QEMM\QEMM386.SYS RAM X=O000-3FFF X=AQQ0-C7FF


X=FOOO-FFFF
The balance of the CONFIG.SYS file might look like this:

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:

= X=0000-3FFF—Optional. This excludes the low memory area that is


normally used in part, by DOS, for system services and, in part, by
applications. This does not affect QEMM-386 or LOADHI operations
in any fashion and does not affect normal DOS operations, but may
prevent some Windows conflicts.
= X=A000-C7FF—Essential. This excludes the entire video memory area.
This blanket exclusion does cover some regions that might otherwise
be used by LOADHI to relocate some TSR utilities. It does, specifically,
prevent the video conflict that may prevent Windows 3.0 from loading.
8 BORLAND C++ 3.0 PROGRAMMING, Second Edition

You might fine-tune by using the Manifest utility from


Quarterdeck to examine exactly which areas within this range are
used, and test different exclusions against both normal DOS operation
and for conflicts with Windows 3.0.

= X=F000-FFFF—Optional. This excludes the high memory area norm-


ally used by the system ROM. (ROM data/code may be remapped to
this area in RAM for improved speed.) This exclusion may prevent
some Windows 3.0 conflicts.

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.

Table 1-1: Memory Usage (DOS Active)


System Memory
Memory Area Size Description Notes
0000 - 003F 1K Interrupt Area exclude X=0000-3FFF
0040 - 004F 0.3K BIOS Data Area excluded
0050 - 006F 0.5K System Data excluded
0070 - OCEF 50K DOS (ver 4.01) excluded
OCFO - 1720 40K Program Area excluded
172] - OFFF 547K [Available] excluded through 3FFF
Conventional memory ends at 640K (9FFF:0000)
A000 - AFFF 64K VGA Graphics exclude X=A000-C7FF
BO00 - B7FF 32K Unused excluded
B800 - BFFF 32K VGA Text excluded
C000 - C5FF 24K Video ROM excluded
C600 - C7FF 8K Unused excluded
C800 - DFFF 96K High RAM available
E000 - EFFF 64K Page Frame
F000 - FFFF 64K System ROM exclude X=FO00-FFFF

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.

Environments and Terminology


While Windows 3.0 operates within the DOS environment (referring either to
MSDOS or DRDOS), DOS and Windows will often be contrasted within this
book as though they were separate and independent operating environments.
While technically inaccurate, this contrast is used to recognize the effective
reality as it affects users, programmers, and programming language con-
ventions.

Compiling Programs for Windows


Because compiling for the Windows environment is different than compiling
for DOS, an explanation of the steps required to compile a Windows program
is presented here before introducing a program example.
Figure 1-3 diagrams the steps required to compile a Windows application.
If you are familiar with OS/2 programming, this diagram may strike a chord
in your memory. This is not accidental; OS/2 and Windows programming
have a lot of similarities.
10 BORLAND C++ 3.0 PROGRAMMING, Second Edition

Figure 1-3: Steps in Compiling a Windows Application

BC “ BCX / BCC Whitewater


BCCX Compilers Resource Toolkit

TLink Resource
Conpi ler

ASCII Source Files

Compiled Products
Resource
Compiler

The first step in compiling a Windows application involves a .C or .CPP


source file and an .H header file, which TCW (or BC) will compile to an .OBJ
file. Optionally, .OBJ files can also be compiled by TASM from .ASM and .INC
source files. Once the .OBJ file(s) are created, TLINK is invoked together with
the .DEF file and library file(s) to create an .EXE program.
Up to this point, the process should be familiar to all C/C++ programmers.
For Windows, the .EXE file exists, and might execute, but the process isn’t
finished.
The second leg of the compiler process begins with Borland’s Resource
Workshop or with the Whitewater Resource Toolkit (WRT). The Resource
Workshop will be explained in detail in Part 2 of this book.
Chapter 1: Installing C/C++ for Windows 3.0 11

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.

WinHello — An Introductory Windows Program


Traditionally, a "Hello, World" message has been the introductory C example.
For Windows, although the traditional example will still serve it will be
somewhat longer than usual, requiring closer to 70 lines rather than the
customary half-dozen lines of source code.
In both the traditional example, "Hello.C," and the new Windows version,
"WinHello.C," the program objective is quite simple: to write a message to the
screen. And, for the traditional version, this is a simple task requiring little
more than a print statement. In the traditional environment, irrespective of the
operating system, any application program was considered to own the entire
system, whether the output device was a teletype, printer, or CRT.
12 BORLAND C++ 3.0 PROGRAMMING, Second Edition

With Windows, however, the output device is a shared display. Any


application, not just the present example, has to share the display (as well as
other system resources) with all other active applications. The principal dif-
ference in size between the traditional and Windows examples is simply the
overhead necessary for coexistence in a multitasking environment.
In the traditional "Hello.C" program, the program consisted of a single main
procedure with a single simple instruction:

main()
© Vprante “Hello, sworld™ 2s }

In traditional C programming, the entry point for a program has always


been titled main; but, for Windows, the entry point is now titled WinMain and
begins with four parameters supplied by Windows:

int PASCAL WinMain( HANDLE hInstance,


HANDLE hPreviInstance,
LPSTR LpszCmdParam, int nCmdShow )

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

Internet Archive Reno NS 7 o this


> Run
could

Borland C++ 3.0 Programming/Includes ae


s = OlwO
Window Programming ist for
argu-

Ezzell, Ben This


layed
ation

6D-16-01-C2 Noe hes


Used - Verv Good

R1-BSJ-703 9780201608663
Picker Notes:
M

'Wnd
class

[DirectSales] IA - Reno: 999-0000000-0384084 =

2186 Items 1084395497 sl

Reno Internet Archive 1 Ship. Created: 7/1/2021 1:08:00 PM


Date Ordered: 7/1/2021 4:02:00 PM
14 BORLAND C++ 3.0 PROGRAMMING, Second Edition

The Windows environment manages multiple application or application


instances by passing messages to specific application instances. Before
Windows can pass messages, each application type, not each instance, must
be registered as a window class. This registration identifies the application
procedure that processes messages sent to the application window.

Registering a Window Class


In WinMain, if no previous instance of this application has registered as a
window class (that is, hPrevInstance is null or 0), then initial values are
assigned to the WNDCLASS structure (we).
Note: The second and last fields in this record structure are the most
important, with the second field providing the address of the window proced-
ure used for all window creation and window message handling. The last field
supplies the name of the window class, which is normally the same as the
program name. The remaining eight fields describe characteristics used for
each window instance.

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

Subsequent instances of the application will not need to register as window


classes because the first class registered (and the initial values) are available
to all class instances.

Creating an Application Window


While registering the window class has defined several general characteristics
for all windows of this class (that is, all instances of this application), the
window is not yet created; only the default characteristics have been
established. You can only register the window class by the first instance of the
application; it is not called by any subsequent instances. Every window
instance needs to call the CreateWindow function to create its own actual
screen window, while the hWnd window handle that is returned will be used
to address operations within this window instance. Unlike RegisterClass,
CreateWindow is called with a parameter list instead of a record structure. The
first two parameters are the application name (szAppName or window class
name) and the window caption, which will appear at the top of the window:
hWnd = CreateWindow(
szAppName,
MTN tike, MoOrlke) = Usimeona Seyler”,

The third parameter is a window-style message which, for most


applications, will be WS_OVERLAPPEDWINDOW:

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,

These default conditions position successive overlapped windows at step-


ped horizontal and vertical offsets from the upper left of the display, with the
right window border at the right of the screen and the bottom border above
Chapter 1: Installing C/C++ for Windows 3.0 17

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,

The program instance handle should never be passed as a null parameter,


but is always passed as the hInstance parameter that was originally passed to
WinMain by the setup code:

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 );

After CreateWindow has been called, the application window is created


internally in Windows but has not yet been written to the screen display.
Therefore, the returned hWnd window handle is used in calling the Show Win-
dow procedure together with the nCmdShow parameter which was passed as a
calling parameter to WinMain:

ShowWindow€ hWnd, nCmdShow );

If the value of nCmdShow is SW_SHOWMINNOACTIVE (7), the application


window is initially displayed as an icon.
The UpdateWindow procedure is called to update the client area of the
window:

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.

The Message Handling Loop


The WinMain function still has one very important task required for all
window applications: the message handling loop, which processes keyboard
and mouse events.
Windows creates and manages a message queue for each current Windows
program instance. Thus, when a keyboard or mouse event occurs, Windows
translates the event into a message value that is inserted into the program’s
message and must be subsequently retrieved by the application for handling.
The message handling loop begins by calling the GetMessage function which
(as long as the retrieved message is anything except WM_QUIT [0x0012]) will
return a nonzero value, and will cause the loop to continue:

while€ GetMessage( &msg, NULL, O, O ) )


{

The GetMessage Function


The syntax for the GetMessage function is defined as:

BOOL GetMessage( LpMsg, hwnd, wMsgFilterMin, wMsgFilterMax )

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

Message Programming or Event-Driven Programming


Message programming, often referred to as event-driven programming, is not
new, but may still be unfamiliar to many programmers. Its previous appearances
in OS/2, in earlier versions of Windows, and, most recently, in Borland’s
Turbo Vision have received only limited exposure. Whether you refer to this
process as message programming or event-driven programming, it is a con-
cept that is an integral part of Windows programming. It is also becoming
increasingly common in nonWindows applications.
In its simplest form, message programming is a process by which various
subapplications communicate with each other. In Windows, it is the process
by which Windows itself is able to manage a multitasking system and share
keyboard, mouse, and other input information among different applications
or application instances. Thus, in Windows, keyboard and mouse events are
received by the actual applications but are intercepted by Windows. These
events are translated, if necessary, by the TranslateMessage procedure and
parceled out in the form of message records by the DispatchMessage procedure
to the appropriate processes.
This process is not limited to the keyboard and mouse events. It also
includes all input devices and messages that can be generated by application
processes and addressed to other applications, child processes, or, most
frequently, to Windows itself. Since an abstract description of this process
does not really give you much of an idea of how to use message programming,
the real explanations will be reserved for demonstration with hands-on
examples.
Note that the WinMain procedure does not at any point call or pass informa-
tion to the WndProc function in WinHello, where much of the actual processing
of this program occurs. And, in subsequent examples, this divergence from
conventional programming practices will be even more obvious as large
sections of the application execute with no apparent connection within the
program structure.
Before going further into how messages are handled, even in this very
simple example, it will help to see how messages are organized.
Chapter 1: Installing C/C++ for Windows 3.0 21

The Message Record Structure


The MSG (message structure) message type is a record structure defined in
Windows.H as:

typedef struct tagMSG


¢ HWND hWnd;
WORD message;
WORD mParam;
LONG lParam;
DWORD time;
ROM Nie pite ) MSG;

The POINT data type is a second structure, also defined in Windows.H as:

typedef struct tagPOINT


C Wits — SOF
dent Cammenya De OrleNilie-

The following message (event) fields are used:

= hWnd—the handle of the window to which the message is directed. In


WinHello, this is the handle of the client window; but if multiple
windows have been created in other applications, this is the handle of
the selected or active window. For mouse events, this is the window
where the mouse cursor appears.
m™ message—a 16-bit value identifying the message. Constants
corresponding to all message values are defined in Windows.H and
begin with the WM_ prefix ("window message"). For a mouse left-
button event, for example, the message value might be
WM_LBUTTON-DOWN.
= wParam—a sixteen-bit message parameter with the value and meaning
dependent on the specific event message.
= I/Param—a 32-bit message parameter with the value and meaning
dependent on the specific event message.
time—the time the message was placed in the message queue.
= pt—the mouse coordinates at the time the message was placed in the
message queue (irrespective of the message event type or origin).

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

exercises specific message-handling provisions, many of these message fields


may be validly ignored as irrelevant to the response; or, in other cases, they
may be quite relevant.

The WndProc Procedure


The WndProc procedure in WinHello.C provides local response to two event
messages: WM_PAINT and WM_DESTROY. All other event messages are
handled, by default, by Windows itself. One very important aspect of
Windows programming, however, is that in any application there is no direct
connection between the WndProc procedure and the WinMain procedure.
While this is completely contrary to conventional programming practices,
where one routine calls another routine, passing parameters and accepting
responses in an event-driven, multi-tasking environment, this lack of direct
connection is perfectly normal because there is an indirect connection!
In the WinMain procedure, the address of the WndProc subroutine was
passed to Windows as:

we.lpfnWndProc = WndProc;.

Given this address, Windows is able to call WndProc directly, passing event
messages to WndProc in the form of four parameters:

Long FAR PASCAL WndProc( HWND hWnd, WORD message,


WORD wParam, LONG LParam )

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

Updating the application window frame, captions, and so on, is handled


automatically by Windows without intervention by the application; but, at the
same time, Windows erases the invalidated client window area by repainting
the area using the brush specified in the hbrBackground field in WINCLASS.
Response to the WM_PAINT message always begins by calling BeginPaint,
which returns a handle to the output device (hdc) and loads the ps structure
with information about the client area colors and brush information. The
handle, hdc, will be released when finished by calling EndPaint.

case WM_PAINT:
hdc = BeginPaint( hWnd, &ps );
GetClientRect( hWnd, &rect );

After retrieving the device handle, the GetClientRect procedure is called to


return the rect structure with coordinates for the client window. The left and
top fields are returned as Os and the right and bottom fields are returned as the
current width and height of the client window (in pixels). Once the client
window information is available, the next step is specific to the WinHello
program: an instruction creating the display, in this case, using the DrawText
instruction:

Dinfaiwiresct:G=shidier. aHellion Wolrm lcihue sella a ercutee


DTIMSINGEELINE |. iDTILCENT ER: ol DT VGENTD ERalD

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.

EndPaint( hWnd, &ps );


ret umn.05) )55

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

In all responses to the WM_PAINT message, the BeginPaint, GetClientRec,


and EndPaint procedure calls are stock provisions even though the actual
screen painting instructions vary according to the requirements of the applica-
tion. The second message that all applications need to respond to is the
WM_DESTROY message:

case WM_DESTROY:
PostQuitMessage( O );
ReturnniG 20m ps
}

The stock response, however, is quite simple: calling PostQuitMessage to


placea WM_QUIT message in the program’s message queue. In response, the
GetMessage provision in WinMain will receive a zero, allowing the loop to
terminate and the program to exit.
Since the switch..case decision structure responds only to two of the possible
messages that may be sent to the application’s WndProc procedure for handI-
ing, a default alternative is required to insure correct processing. This default
is provided along with a final instruction to first call the DefWindowProc
function with the same four parameters that were initially passed to the
WndProc procedure, and then to return the result to the Windows calling process:

return( DefWindowProc( hWnd, message, wParam, l|LParam ) );

This last provision should also be considered standard for all WndProc
message handler procedures.

Notations, Constants, and Variables


Many OS/2 and Windows programmers use a variable -naming convention
commonly known as Hungarian notation—apocryphally named in honor of
Microsoft programmer, Charles Simonyi.
With Hungarian notation, variable names begin with one or more lowercase
letters that denote the variable data type, thus providing built-in identifica-
tion. For example, the prefix h identifies a handle as in hWnd, which is a handle
to a window, while the prefix [psz identifies a long pointer to a null-terminated
string (ASCIIZ).
This inherent type notation helps to prevent errors caused by mismatched
data types. Common prefixes are shown in Table 1-2.
26 BORLAND C++ 3.0 PROGRAMMING, Second Edition

Table 1-2: Hungarian Notation Conventions


Prefix Data Type
b BOOL (boolean, int)
by BYTE (unsigned char)
c char
Cx, Cy short used as X or Y length
dw DWORD (double word or unsigned long)
fn function
h handle
i int
| LONG
n short or int
s string
SZ ASCIIZ (null-terminated) string
Ww WORD (unsigned int)
x,y short used as X or Y coordinates

Windows also uses an extensive list of predefined constants that are


employed as messages, flag values, or other operational parameters. These
constant values are always uppercase and most include a two- or three-letter
prefix set off by an underscore:

CS_HREDRAW CS_VREDRAW CW_USEDEFAULT


DT_CENTER DT_SINGLELINE DT_VCENTER
IDC_ARROW IDI_APPLICATION WM_DESTROY
WM_PAINT WS_OVERLAPPEDWINDOW

The prefix indicates the general category of the constant as shown in


Table 1-3.

Table 1-3: Constant Prefix Identifiers


Prefix Category
CS class style
IDI icon id
IDBXC cursor id
WS window style
CW create window
WM window message
DT draw text
Chapter 1: Installing C/C++ for Windows 3.0 27

There are also several new data-type identifiers defined in Windows.H as


shown in Table 1-4.

Table 1-4: Window Data Types


Data Type Meaning
FAR same as far
PASCAL same as pascal
WORD unsigned integer (16 bit)
DWORD double word, unsigned long integer (32 bit)
LONG signed long integer (32 bit)
LPSTR long (far) pointer to char string

The five data structures displayed in Table 1-5, again defined in


Windows.H, appear in the WinHello program.

Table 1-5: Five Window Structures

Structure Meaning
MSG message structure
PAINTSTRUCT paint structure
ati point structure (mouse position)
RECT rectangle structure
WNDCLASS window class structure

Four handles are also defined with uppercase identifiers as shown in


Table 1-6.

Table 1-6: Four Handle Identifiers


Handle Meaning
HANDLE handle, generic
HWND handle, window
HDC handle, device context (CRT)
HBRUSH handle, paint brush

A complete list of types, structures, constants, and macros declared in the


Windows header file appears in Appendix A.
28 BORLAND C++ 3.0 PROGRAMMING, Second Edition

Using the Command-Line Compiler


For those who are dedicated to using the command-line compiler, compiling
a Windows application in C/C++ is initially no different than compiling any
other type of application. It does, however include a couple of additional
steps, as shown in Figure 1-3.
First, of course, you must begin by creating the WinHello.C and Win-
Hello.DEF files (complete listings follow). The command-line version of the
C/C++ compiler is then invoked from either DOS or from Windows as:

G=N\BiGe-=bic Ge —Weewninine lalorrcr

Note: The -W directive calls for a Windows application. Other directives,


in the form -Wxxx, provide the compiler with more specific instructions on
compilation and code generation; for example, -WD creates a Windows .DLL
file. A complete list of directives can be found in the Borland C++ Version 3.0
User’s Guide.
The preceding command-line compiler directive compiles to an .EXE pro-
gram. Sometimes, however, you may prefer to compile only to an .OBJ file,
leaving the link operation for a separate step, which can be accomplished by
including a -c directive in the original invocation.
If this is done, the linker can be invoked separately as:

Ca\BCe>tLINK iw fvefo=\be\LibVeOws swinne Ulo, owanhe. Vor,


eat boNlib\Cwins sce sb cMLGbNcs .c\.bc\b
db \ impo mtn Wa O.MelLbo

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:

C:\BC >re -R winhello.rc.

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:

C:\BC >re winhello.res winhello.exe.

These last two steps could be combined as a single operation,

CNBC o>re wanhe Lo.

In this form, compiling and combining is accomplished in a single step. No


filename is specified for the .EXE program, which is assumed to have the same
name as the .RC file.
30 BORLAND C++ 3.0 PROGRAMMING, Second Edition

To load Windows and run the application, assuming that you are compiling
from DOS, simply type:

C:\BC >win winhello.

The .DEF Module Definition File


The .DEF module definition file is, in part, redundant under Borland C/C++
because IDE options provide many of the settings previously controlled by
parameters in the .DEF file.
Still, the .DEF file is required for Windows programs. The structure is
relatively simple, beginning with the NAME declaration and followed by an
optional descriptive title:

NAME WINHELLO
DESCRIPTION "Windows Hello, World Program"

The EXETYPE statement declares this application to be a Windows pro-


gram, while the STUB statement provides for inclusion of the stub program,
WINSTUB.EXE.

ExX(Eiiey RE WINDOWS
STUB SeWiENSTUB
SEX Exe

WINSTUB.EXE is a brief utility segment that does absolutely nothing under


Windows but, under DOS, displays a message informing the user that the
application requires Windows for execution. Programs can be compiled and
executed without including WINSTUB, but, as a courtesy to the user, you
should include this feature.
The next two instructions set flag conditions for CODE and DATA handI-
ing. These instructions state that the Code segment of the program should be
preloaded, can be moved in memory as required, and can be discarded as
necessary.

CODE PRELOAD MOVEABLE DISCARDABLE


DATA PRELOAD MOVEABLE MULTIPLE

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

the program. The HEAPSIZE and STACKSIZE instructions state memory


requirements, again, affectations for such a simple program.

HEAPSIZE 1024
STACKSIZE 8.192

The EXPORTS statement lists application functions that are declared


exportable and will be called by external applications; in this case, by
Windows:

EXPORTS WndProc

An IMPORTS statement may also be used to declare functions that will be


imported from external libraries such as .DLL modules. The present
applications, however, can be compiled without requiring a .DEF file by using
the integrated compiler, BC or BCX, the Project options, and by setting any
special memory requirements through menu options.

Compiling with a MAKE File


Even if you prefer to use the command-line compiler, you would probably
prefer to not type the full command line instructions for BCC and TLINK each
time an application is compiled. The makefile, therefore, provides a way to
automate this process.
The makefile, WinHello.MAK, is provided for the WinHello program and
appears as:

winhello.exe : winhello.obj, winhello.def, winhello.res


tala) Keaneal W/V IAG? SAND CANG Un NCC OlWISmEW alan nieneiLiOn-mmaN
winhello, \
i4 \
eeNpc\lab\cwins. co: \DCN LID Vesoc H\benlibVinport,' 7\
winhello

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

Using the Integrated Compiler


The simplest method of both compiling an application and of writing the source
code file(s) for the application is using the TCW IDE. The IDE provides both
speed in compiling, convenience and in correcting and debugging. Please note,
however, that the .MAK file is unnecessary with the IDE and, instead, a .PRJ
(project) file will be created within the IDE.
The first step is to start the compiler by invoking TCW from Windows, and
then enter the WinHello.C and WinHello.DEF files, as shown below. You can
also copy these files from the program example disk, which you can order
using the coupon in the back of this book. Within the IDE, change to the
directory where the .C and .DEF files are located using the File/ Change dir
menu options.
To create the .PRJ file, select Project/Open project from the IDE menu, then
enter WinHello.PRJ in the Project Name box. Press Enter or click on the Ok
button to open a new project.
Chapter 1: Installing C/C++ for Windows 3.0 33

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).

Figure 1-4: Building a Project File

Turbo C++
Options Window

1) winhello.res
inhello.c

To compile the application, select Compile/Build, from the IDE menu.


That’s it!
34. BORLAND C++ 3.0 PROGRAMMING, Second Edition

Figure 1-5: Setting the Primary Compiler Options

File Edit Search Run Compile Project Browse |= Window Help


RESHEAHD

Compiler »
Make...
Linker >
Librarian...
Directories...

Ann ation Optic

After successfully compiling an application, the next obvious step is to


execute the completed progra, using one of two options
The first choice is to execute the application directly from within the IDE
by selecting Run / Run (Ctrl+F9) from the menu. In most cases, this is
perfectly satisfactory.
One caution, however! If an application is using features from the BWCC
dynamic-link library but has not included the BWCC.H header or included
BWCC.LIB in the project file, the application will execute correctly as long as
TCW is active. But this same application will not execute correctly when TCW
is not loaded.
For this reason, it can be a good idea to test applications from Windows
directly, either using the File Manager or by installing the application in a
Windows control group.
Chapter 1: Installing C/C++ for Windows 3.0 35

Other Compiler Options


The Set Applications Options dialog box provides a convenient means of
switching between DOS, Overlay, Windows, and DLL compilation. But, there
are other compiler options that you will want to set for various applications.
These options are controlled within the IDE through four menu selections:

= Code Generation—sets memory models, selects usage for pre-compiled


headers, etc. Select Options/ Compiler/ Code generation.
= = Entry/Exit Code—selects compiler options for prolog and epilog code
generation and export options. Select Options/ Compiler/ Entry /Exit
Code.
= Linker—the Linker dialog box, selected as Options/ Linker, sets
options for the output type desired from TLINK, including the DOS
-EXE, Windows .EXE, DOS overlay, Windows .DLL, and others.

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

Long FAR PASCAL WndProc( HWND hWnd, WORD message,


WORD wParam, LONG LParam )
{
HDC hdc;
PAV NeeOm
RRUC alas DISr-
RECT rect:

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

3 WinHello.DEF module definition file ;

NAME WINHELLO
DESCRIPTION "Windows Hello, World Program"
EX caver WINDOWS
STUB “WINSTUBSEXE*
CODE PRELOAD MOVEABLE DISCARDABLE
38 BORLAND C++ 3.0 PROGRAMMING, Second Edition

DATA PRELOAD MOVEABLE MULTIPLE


HEAPSIZE 1024
SILAGKS LE 8192
EXPORTS WndProc

ia SSeS SSS eee Shea SesSeSsus

# WinHello.MAK make file


a) eee fl Oe eS eS eae

# alternate— no resource file(s) included at this time


# winhello.exe : winhello.obj, winhello.def, winhello.res
winhello.exe : winhello.obj, winhello.def
cami Keue/ Aline seem aN CuNN ai ONGC OWiSmm WATE
en Op-mmnN
winhello, \
aN
eaibe\Lib\cwinssc:\bc\
Ui bNes= coNben.
Gib Vimport.. aw
winhello

# omit — no resource file(s) included at this time


# re winhello.res

PCr OIDy
bcc -c -ms -W -v winhello.c

# omit — no resource file(s) included at this time


# Sricmenes
# fCl—e— 1G Dichwinc
Uuder whele Wominc
Chapter 2

Transporting Text Applications to Windows

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.

Design for Uncertain Circumstances


Under Windows, your application’s output is limited to its own client
window, but once the client window is written, the display is not inviolate.
Under Windows, an application does not "own" the display. Other
applications or resident popups may assume the focus and partially or com-
pletely overwrite your display. And, your own application may create dialog
boxes or pull-down menus which may also overwrite the client window.
When any of these intruders are removed, or when an overwritten application

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).

The WM_PAINT Message


The WM_PAINT message is posted to an application as notification that an
invalidated screen display needs to be restored. Thus when a window has been
hidden entirely, or is partially resized (assuming that CS HREDRAW and
CS_VREDRAW are set), or when Scroll Window is called to scroll the client area
(vertically or horizontally), Windows issues a WM_PAINT message, notifying
the application to update its own display.
There are also circumstances when an application may need to issue its own
WM_PAINT message.
Most applications call the UpdateWindow function during initialization
before entering the message processing loop. This instructs Windows to return
a WM_PAINT message which, subsequently, after the message loop begins,
Chapter 2: Transporting Text Applications to Windows 41

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.

Invalidated Window Areas


Windows maintains the paint information structure, PAINTSTRUCT, for each
application window. PAINTSTRUCT is defined in Windows.H as:

typedef struct tagPAINTSTRUCT


Cen DIG hdc;
BOOL fErase;
RECT PCRPAVAGS
BOOL fRestore;
BOOL fIncUpdate;
BYTE rgbReservedl[16]; RAGIN
T SaERiU Cis:

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:

typedef struct tagRECT


© ite ertetas Tinie iwOlsyé
Hine rien Tite loCwwous % (Ucar s

Anytime an application’s client window has been overwritten, whether by


an overlying dialog box, by a pull-down menu, by another application’s
display, or by a Windows’ message box, Windows keeps track of the "damage"
to the application’s display. It does this by recording the invalidated
rectangle’s coordinates in the paint information structure maintained for each
application; specifically in the rcPaint record fields. Any time another applica-
tion or another screen element belonging to the current application overlies
an application’s client window, Windows records the upper-left and lower-
right corners of the overwritten area. Or, if an invalidated rectangle has
already been recorded and a second rectangle is invalidated, the information
is updated as a single larger rectangle including both areas, if necessary.
When the application is finished processing a WM_PAINT message (by
calling EndPaint), the invalidated rectangle is reset. This validates the entire
client window. Then, if any portion of the client window is still overwritten
by another application or dialog box, a new invalidated rectangle is calculated.
When any window is moved, closed, or resized, Windows checks for other
affected applications by sending WM_PAINT messages to each Window as
necessary.
But why all this trouble to repaint windows selectively? The main reason,
of course, is simply that it is faster to update a portion of the screen than to
repaint the entire screen or even to repaint an entire window. Further, while
text-based displays can afford less-than-optimum updates without being
visually apparent, optimum updating is essential in presenting a smooth,
visually seamless display for a graphics display such as Windows which
simply takes longer to recreate.
At the same time, rcpaint operates as a clipping rectangle as well as an
invalidation rectangle, allowing Windows to restrict painting to the necessary
area. There are also times when an application will need to set its own areas
for update. This can be done with the InvalidateRect as:
Chapter 2: Transporting Text Applications to Windows 43

InvalidateRect( hwnd, NULL, TRUE );

The first parameter is a window handle. The second parameter, NULL in


this example, invalidates the entire client area; otherwise, an HRGN para-
meter provides a handle to a data structure that specifies the rectangle to
invalidate. The third parameter erases the background for the region if passed
as TRUE, and leaves the current background as is if passed as FALSE.

Processing WM_PAINT Messages


While Windows is normally responsible for issuing WM_PAINT messages,
the application is responsible for responding to the message and creating or
recreating the client window as necessary. But, before an application can write
(paint) anything to a client window, there is another essential precursor
operation: getting the Device Context Handle.
In the previous demo program, WinHello.C, the BeginPaint function was
called as:

hdc = BeginPaint( hwnd, &ps );

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:

EndPaint( hwnd, &ps );

A second method of accessing the device context is :

hdc = GetDC¢ hwnd );

The GetDC function is normally used in any context in which immediate


client window paint operations are desired without waiting to respond to a
message WM_PAINT. For an example, the Clock program distributed with
Windows needs to repaint its client window regularly, not just when a
WM PAINT message is issued. This is a typical use of GetDC rather than
BeginPaint. The GetDC function, however,is not restricted to paint operations.
44 BORLAND C++ 3.0 PROGRAMMING, Second Edition

It is also used when an application needs to retrieve information from the


device context, such as text metrics, as shown later.
GetDC, requires the following corresponding end statement:

ReleaseDC ( hwnd, hdc );.

Note: EndPaint must always be used in response to a BeginPaint function


call, while ReleaseDC must always close GetDC function call operations.

= Caution: mixing these functions incorrectly will have undesirable


effects on your program’s execution.

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.

Graphic Text Display Elements


Within the Windows environment, there are four principal elements which
must be taken into account when creating a text display:

= Position—in Windows, window-relative positions replace screen-


absolute positions and use pixel coordinates rather than character cell
Chapter 2: Transporting Text Applications to Windows 45

coordinates. Positioning must take into consideration the font metrics


(text sizing), horizontal and vertical scrolling, and variable window
limits.
= Text size—in conventional displays, characters are fixed size and are
determined by the active text mode. In Windows, as in other graphic
environments, not only can text size and font styles be varied, but
different fonts and sizes can be mixed ina single display. And, regard-
less of the font chosen (fixed-width fonts excepted), individual
characters may vary in width.
= Scrolling—most conventional displays are limited to vertical scrolling.
Scrolling is accomplished by text line. Even when horizontal scrolling
is used, movements are based on character cells. In Windows, both
vertical and horizontal scrolling are always common; both
adjustments can be made in single pixel steps. But, since text display
sizes can vary as can hardware video resolutions, any type of scrolling
must take into consideration both display and hardware variables.
= Window limits —along with scrolling, text displays (and non-text dis-
plays) must conform to window limits, both vertical and horizontal. If
a string is too long for the window or screen in conventional, non-
graphic displays, the display can automatically wrap to the next line.

In a graphics window, text displays do not normally wrap at the windows


borders. Provisions can, however, be made to reformat text per window limits.
Instead, in most cases, text is allowed to flow beyond the window limits while
scrolling the active display over a larger, virtual display.
Note: While a fixed-size window could be created and text window-wrap-
ped, this approach is not common and would still require essentially the same
handling as a variable-sized window.
In Windows, these four elements are not entirely separate considerations
but are interrelated or synergistic in their effects. But, Windows also saves you
many of the problems that would otherwise be a part of the process of sharing
a screen display with other applications. First, since all display operations are
window-relative (that is, relative to the limits of the client window), window
applications can be moved around the screen without the application (that is,
the programmer) making any special provisions for repositioning. The real point,
46 BORLAND C++ 3.0 PROGRAMMING, Second Edition

however, is that Window applications must take a different approach to


writing screen displays of any type than applications operating in a DOS
environment.

Windows’ Font Metrics


In WinHello.C (Chapter 1), text output was limited to a single brief line that
was passed to the DrawText function with flags centering the output in the
client window. This was a rather restricted output and probably will not form
the heart of most applications.
A different approach is required for displays with multiple lines of text
because there are at least two critical pieces of information required in a
graphics display before multiple lines of text can be displayed: vertical line
spacing and horizontal line length. Both vertical line spacing and horizontal
line length are characteristics that will vary depending upon the font used.
But, for present uses, the Windows 3.0 default system font, the font used for
text in caption bars, menus, and dialog boxes, will also be used for text
displays.
Under earlier Windows versions, the system font was a fixed-width font,
similar to the character font used under DOS. With Windows 3.0, however,
while the system font remains a "raster," or bit-mapped font, the widths of
individual characters are variable with "W" as the widest character in the font
and" |" as the narrowest.
System font characteristics are not the same for all computer systems
because the Windows installation selects from seven system fonts with vari-
ous size characteristics to match different video display adapters. At the same
time, video board manufacturers may design and distribute their own system
font(s). The only guarantee is that the system font is designed to display at
least 25 lines of 80 characters.
Even when using the system font, applications must treat this as a variable
font and ask for the available font size information through the GetTextMetrics
function:

EX DME TRANGS Gm,

hde = GetDC( hwnd );


GetTextMetricsC hdc, &tm );
ReleaseDC( hwnd, hdc );
Chapter 2: Transporting Text Applications to Windows 47

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.

Figure 2-1: Windows’ Font Metrics

tmExterna lLeading

| tmAscent

FinMaxChardidthi

OTpAueChariidthe.

Beginning at the top, the values are as follows:

= tmExternalLeading—the font designers suggested interlinear spacing


because the white space between lines makes the text readable.
= tmlInternalLeading—the space allocated for accent marks above
characters as shown by the "S" character with the caret accent. Note:
while accents are not commonly used in English, many other
languages depend heavily on these forms and these are supported by
most extended ASCII character fonts.
= tmAscent—the height of an uppercase character including the tm-
InternalLeading space.
= tmDescent—the space provided for character descenders as in the "g",
ip) q: and, y characters.
= tmHeight—the overall height of the font, including tmAscent and tm-
Descent but not incorporating tmExternalLeading.
48 BORLAND C++ 3.0 PROGRAMMING, Second Edition

In the present application, the vertical line spacing, cyChr, will be


calculated as:

cyChr = tm.tmHeight + tm.tmExternalLeading;

The length of a line of text is not as easily calculated. TEXTMETRICS


supplies two values for character width:

= tmAveCharWidth—a weighted average of the lowercase character


widths.
= =tmMaxCharWidth—the width of the widest character(s) in the font,
usually the "W" and/or "M" characters.

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

Figure 2-2: Window Coordinates and Invalidated Areas

Caption Bar

Client Window

Invalidated Area

Windowing Text Output


After retrieving the TEXTMETRIC information and deriving height and width
information for the system font, text output to the application’s client window
is the next obvious step. In WinHello.C, the function DrawText sufficed for a
single, brief line that was centered in the client window; but, in PainText.C, a
more sophisticated display is the intention. The WinProc response to the
WM _PAINT message is still the occasion for writing the screen and begins
again with the BeginPaint function to retrieve the device context handle, hdc,
and the PAINTSTUCT information, ps:

case WM_PAINT:
hdc = BeginPaint( hwnd, &ps );

This time the application needs additional information and begins by


calculating two values from information contained, in part, in ps. The field
Chapter 2: Transporting Text Applications to Windows 51

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:

cBeginPaint = max( OQ,


CY SCrUros + DS. ECralnt. OD 7 CcyYyGhr ) >
cEndPaint = min(€ NumLines,
Cy SiC RMR Ost ipisiticP.ainte
bot tom y/accy. China!

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:

Sie Givendt AlirginiG@eh dice. lA EF) a= ipAeeInOlP) )s-


forC. =seBbBegqin Paint iiisc= (cEnd Paint Aah 2
{
Xe Cin pee Ge CXC CII O}SmE ta De
Vous = Cy Cnt sa © l= CYS: CrUPOs= ft a1, 5

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

This is done, in part, to allow demonstration of horizontal scrolling but, also,


to demonstrate a subtle but unusual error. The error, however, will be
deferred for a later discussion.
Second, the x and y window positions are calculated taking into account the
scrollbar positions. Either or both may be negative integers, indicating that
the current text line begins outside of the client window. This is not an error
and there is no problem in beginning a paint operation outside of the window
limits because the actual screen paint operations that do appear are clipped,
in this case, by the invalidated rectangle limits which will never fall outside
the window limits.
The alternative, in this example, is to calculate the part of the written string
which actually falls within the window and then clip or truncate the output
string before writing to the window. In text-based, rather than graphic-based
environments, such an approach is possible and sometimes necessary.
For a graphics-based environment, and particularly when a variable width
font is being used, the only reasonable approach is to simply calculate relative
positions that may lie outside the actual window limits. Then pass only those
pixel operations whose coordinates fall within the valid display. In this fash-
ion, a line or curve can begin anywhere but will appear on screen only within
the active window or, in this example, within the application’s client window.
But, and this is important, the application does not need to worry about
clipping; that is Windows’ job. Instead, as described previously, the applica-
tion simply treats output operations as if they were operating in a larger
virtual window, leaving clipping to the actual display window; or, more
accurately, to the invalidated rectangle, for Windows to handle.
Some applications that are using intensive graphic calculations, may per-
form better by restricting their own operations to approximate the active
window area; but the actual clipping should be left to Windows, and not
duplicated by the application.

The TextOut Function


In conventional C, formatted text output is a simple matter using the printf
function (or any of the equivalent forms):

gotoxy (ax pays


printie “"“This is. Caine. 7d betingidisolayedmatexs:dm7 Nace/sCiueea,
Tp KPAop
Chapter 2: Transporting Text Applications to Windows 53

For Window C, however, output is somewhat more complicated. The Text-


Out function is called with five parameters:

TexXcOULC HDG, Int XPOS, Int yYpos,


EPSTR “S280 Ter, Int icStrlen™):s

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:

POxEOUEC MCh» ~. Vn SHOWPTER, WSserIiMerC Slur Tar,


MOINS 1S lame Cel Meine, Cehisllanzacl are Mawel 7 VeaelY4>
13, Xs Yas ee

The szbuffer text buffer is still, as the identifier suggests, a conventional


null-terminated string buffer. Both the sprintf and wsprintf functions write the
formatted string argument to the buffer, returning an integer that specifies the
length of the finished string.
There is, however, one difference. wsprintf, defined in Windows.H, does not
handle floating-point values but it does have the advantage of being a part of
Windows and, therefore, does not increase the size of .EXE applications or
DLL libraries. On the other hand, if floating-point values are needed in the
formatted string, sprintf will serve quite well.
Note: The savings of approximately 1.75K are quite significant.
54 BORLAND C++ 3.0 PROGRAMMING, Second Edition

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

generate a SB_THUMBPOSITION message that indicates movement is com-


plete and that a new thumbpad position has been set. While the mouse button
is held down, even if the SB_THUMBTRACK messages are ignored, an outline
of the thumbpad image is generated which follows the mouse cursor.
Windows documentation states that SB_TOP and SB_BOTTOM messages
can be generated to indicate that the scrollbar has reached minimum or
maximum positions. Neither message is returned by scrollbars that are
created by application windows, and handling for these two cases may safely
be omitted.

Figure 2-3: Scrollbar Messages

|p SB_LINEUP (press)
F=3| SB_ENDSCROLL (release)
oe

SB_PAGEUP
af
elles _—_ (press)
B_ENDSCROLL (release)

(press and drag) B_THUMBTRACK


(release) _THUMBPOS ITION

SB_PAGEDOWN (press)
SB_ENDSCROLL (release)

SB_LINEDOWN (press)
"SB_ENDSCROLL (release)

Handling Scrollbar Messages


Because these SB_xxxx messages are secondary messages, applications must
begin by responding to either a WM_VSCROLL or WM_HSCROLL message,
indicating the mouse action in the vertical or horizontal scrollbar respectively.
Either message is accompanied by the wPARAM value that contains the
secondary message information; but it is the primary event message to which
your application’s WinProc procedure must initially respond. In PainText.C,
56 BORLAND C++ 3.0 PROGRAMMING, Second Edition

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 )
{

The response structure for the WM_VSCROLL message is simply a subsidi-


ary switch...case statement that lists the scrollbar messages to be handled and
the response appropriate for each.
While the order in which these are handled is not important, the first two
are the event messages generated by the endpads, SB_LINEUP and
SB_LINEDOWN:

case SB_LINEUP: cyScrlStp =") 5 break;


case SB_LINEDOWN: cyScrlStp lee Dimeraike-

In both cases, the appropriate response is quite simple: to select a movement


of one line up or down. And, since the thumbpads will generate repeated
messages if the mouse button is held down, virtually any range of movement
desired is available without complex provisions. For the SB_PAGEUP and
SB_PAGEDOWN messages, the handling is slightly more complex. It
establishes a minimum movement of one line up or down but attempts to set
a more reasonable movement step based upon the vertical font size and the
vertical window size. (Or the average character width and window width for
the horizontal scrollbar.)

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;

The macro LOWORD, defined in Windows.H (see Appendix A), returns a


word value within the minimum..maximum range for the scrollbar position.
In this example, cyScrIStp is calculated as the difference between the new
thumbpad position and the current cyScr!Pos position.
Normally, an application would respond only to the SB_THUMBPOSITION
or the SB_THUMBTRACK message, but not to both. In most cases, the
SB_THUMBPOSITION message, which is returned when the mouse button is
released, would be the preferred choice. This allows the application to respond
with a single screen update instead of attempting to track continuous move-
ment of the scrollbar. Or, as an alternative, an application could offer a choice
of scrollbar configurations as a menu or installation option, permitting either
form of response with the scrollbar message code appearing something like
this:

case SB_THUMBTRACK:
if €" TrackContinuous) break?
case SB_THUMBPOSITION:
ChYsS
1G alShG Dae — i OLWIO)RIDIG Praia Ma sec 29 CU Pioisy.
break;

In this form, a boolean flag, TrackContinuous, would prevent a response to


the SB_THUMBTRACK message if false or, if true, would allow the response
to fall through to the SB_THUMBPOSITION case, providing continuous
window updates as the thumbpad is moved.
The drawback, of course, is that a slow system may not be able to keep up
with rapid movement of the scrollbar thumbpad, resulting in a jerky response
and a rough program appearance. On the other hand, continuous tracking can
also provide finer control by permitting the user to see precisely where a
scrollbar adjustment reaches.
The choices to use and the way in which they are implemented will depend
upon your applications’ needs, but testing should always be done using
extreme conditions.
Returning to the example program, a default provision is made that sets
cyScrlStp to zero or no movement:
58 BORLAND C++ 3.0 PROGRAMMING, Second Edition

default: cySenlStp .=90;


}

After responding to the scrollbar messages, however, there remain a few


tasks to be handled within the WM_VSCROLLBAR message response. One is
to test the new step value for a valid range result:

Te GEoyS CHUST DO) Soimax Cli ey S cr ERs,


Muli Gen GaysoyCulsalsonty
py,
cyScrlMax - cyScrlPos ) ) )
{

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

Remember, the ScrollWindow function call causes Windows to set


invalidated rectangle parameters that correspond to the area revealed by the
scroll operation. Thus, when the WM_PAINT message is returned, only the
region uncovered by scrolling is repainted. This is much faster than repainting
the entire window.
The response provisions for the WM_HSCROLLBAR message from the
horizontal scrollbar in PainText.C are essentially the same, though the vertical
and horizontal scrollbars could, when needed, respond in entirely different
manners.

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:

cyScrlMax max€ 0, NumLines + 2 - cyWin / cyChr 7


eysicmUPios Mani G@uiciy. Sich WPOS mG y SIG mu Niapam ae

A check is also in order to insure that the present scrollbar thumbtab


position remains valid.
The next two steps are the critical operations because they insure that the
scrollbar matches the resized window and that the thumbpad is correctly
positioned.
The SetScrollRange function sets minimum (0) and maximum (cyScrMax)
ranges for either the horizontal or vertical scrollbar as specified by the nBar
(oie) parameter. The limits set are integer arguments and, in this application,
refer to text lines vertically or to characters (using the average character width)
horizontally. The final, boolean parameter, if true, instructs Windows to
redraw the scrollbar image based on the new range:

SetScrotlURange€ hwnd, SBLVERT, O, ‘cyScrlUmMax,. FALS Ee:

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:

SetScrollPos( hwnd, SBAVERT, cyScrlUPoce sERUE ie


Chapter 2: Transporting Text Applications to Windows 61

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.

Figure 2-4: Redraw Errors—When the Information Changes

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>

1 aie NumLines = 99;

Long FAR PASCAL WndProc( HWND hwnd, WORD message,


WORD wParam, LONG LParam )
{
Static short exChr, cxCap, cyChr, cxWin, cyWin,
GYSCRUPOS, mGXSCRLPOS, CYSC PLM ax, CX SC IL Macc:
short 1X7 ave BY OOCCUSLD. -CXSCE USUD,
cBeginPaint, cEndPaint;
HDC hidice PANS T RUC ies piss
REG rec ty: pe xt MIES Relic ‘Gimp
char szBufferl1801J;

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

ex SenkMang; sr Als ERDT


Set Sic rollPos< hwnd, SB_HORZ,
exS crl Ros 7pwoURUE MS 7
ret urm¢e Oy MF

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_THUMBTRACK: iif


// CxS Cimusitip = LOWORDGs UP amram l—i9cx SicmUPos:: i if
i if break; ah
i / SSSsSaSSSeesesesReeSeoeSeel
esos Sss5eeeoSeseesoreeeass=// /

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

SetScrollPos(chwnd>;"*SBAHORZ, cxScrlRos;, TRUE Ue


}
eLuiUiinh) Gm Ome Ee=

case WM_DESTROY:
PostQuitMessage( O );
PeCuirnG © Ds
}
return( DefWindowProc( hwnd, message, wParam, lParam ) >

int PASCAL WinMain(€ HANDLE hInstance,


HANDLE hPreviInstance,
EPSTR lLpszCmdParam, int nCmdShow
{
static char szAppNameL] = "Paint Text";
HWND hwnd;
MSG msg;
WNDCLASS WC;

Tf Ge nee envelin Satanic Cum)


we.lpfnWndProc = WndProc;
we.cbClsExtra = 0;
we.cbWndExtra = OQ;
we.hInstance = hInstance;
wce.hIcon = LoadIcon( NULL, IDI_APPLICATION
we.hCursor = LoadCursor( NULL, IDC_ARROW );
we.-hbrBackground = GetStockObject( WHITE_BRUSH );
we.lpszMenuName = NULL;
wce.lpszClassName szAppName;
RegisterClass( &wce );
}
hwnd =
CreateWindow( szAppName,
“PAI METMG) wee Im Yinclows” -
WS_OVERLAPPEDWINDOW | WS_VSCROLL WSTHSCROLIES
CWRUSED EAU ISG WaeUS EDIE AU eitic-
CWUSB DIE AUI-S ey CW enULS EIDIESE
AU) lame:
NUELY NULLY hinstance,. NULL
ShowWindow( hwnd, nCmdShow );
UpdateWindow( hwnd );
while€ GetMessage( &msg, NULL, 0, O ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
Chapter 2: Transporting Text Applications to Windows 67

return msg.wParam;
}

PS SS SS SS HRS SSSR RSS SS SS SSS SSS SSSS===>7

5 PainText.DEF module definiton file F-


Toe SS SS SS = SSS SS == ==] 5555S SSS == 5

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

ff fPRASP Hehe SSeS See SS SesSeeSsoSeseese//


Ifif Panini xt 2 //
// revisions to use sysmets.h //
// and display system metrics //

#include <windows.h>
#include "sysmets.h" // add include statement

// int NumLines = 99; // delete NumLines statement

Long FAR PASCAL WndProcC ...


{
static short cxMax; // add cxMax declaration

switch( message )
{
case WM_CREATE:

cxMax = 40*cxChr+18*cxCap; // add cxMax definition


POvU(Pm« (0) Ws
CASE Vi Siu7Aee aoc // no changes
case WM_PAINT:

SetrextAlngnGendc, aA CEPT | TA-TOP );


fonG iescbeqinpaint, 1.<= cEndPaint; i++ _)
68 BORLAND C++ 3.0 PROGRAMMING, Second Edition

TextoutGihde, <cx Chit. conned,


sysmetricsiid. szlabety
UstrienCesysmetricst tizszvabet ==) >
Textoudtc¢ *hdie, exchr+ils *éxCap, "cy Chir*a7
Ssysmetnicslhtiaszveserrpt,
LstrlenG sysmetricsLid.szDescript » 0;
SetTextAlign( hdc, TA_RIGHT | TA_TOP );
TextoOut€ hdc, exChrt18*cexCapr4e0*exthr, cyChr*1,
szBurter,. WsprinttG szBut ten... 54a.
GetSystemMetrics( sysmetricsLil.nIndex ) Phy a
SetilextAlignk hdc. “LTAKCEFT =| > TASTOP= 7;
}
EndPaint( hwnd, &ps );
PEE Pe 18) 5
GarsicuwW MmaViSiG ROS Ee= sarnem. UI ye changes
Case WME HSiGRONL Essen // no changes
Caisicm WiMaeDIESmiRONpsaeeste // no changes
}
return( DefWindowProc( hwnd, message, wParam, lParam
}

int PASCAL WinMain€ ...- i We changes

// SysMets.H == System Metrics Display Struct //

#define NumLines (sizeof sysmetrics / sizeof sysmetricsL[O]J] )

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

“Caption, bar height,


SM_CXBORDER, "SM_CXBORDER", L.Blommd
elim whilcit hia,
SM_CYBORDER, iS MEG BORDERue, "Border height",
SM_CXDLGFRAME, Stace xDEGERAME:,
"Dialog window frame width",
SM_CYDLGFRAME, eeON emCayaD)LGi
ryRUA) Mesure
"Dialog window frame height",
SM_CXHTHUMB, Sly) EY
"Vert scroll thumb height",
SM_CYVTHUMB, ZOM_CYVTHUMB™,
HOG Zers Ciro leat humbsanenigihit cu,
SMEGXE CON, "SM CXTCON”, AN Coya -Welt ly
SIMMGY, GOIN: Sil EN (ECON LaTaCOnae nie sikg) nutes:
SM_CXCURSOR, "SM _CXCURSOR", aadCOUNTS IO) [ee WCities
SM_CYCURSOR, bai Mam Coa GIUIRESIO)Rauaee te CU SIO mene) eg nitueus
SM_CYMENU, MS (CVIEINW "Menu bar height",
SM_CXFULLSCREEN, SS Mae CoxaRUi EE iGiRu Ese Nia
"Client window PTlel
SM_CYFULLSCREEN, LSOMS CY FULLSCREEN.3
"Client window height",
SM_CYKANJIWINDOW, "SM_CYKANJIWINDOW",
"Kanji window height",
SM_MOUSEPRESENT, SiMe MOUS Eiki Sie Nile,
"Mouse present talaigue,
S MEG YAViSiGRIONE ar. atSN Ca VES) CaRKO) LE tae
WNVICInCeESiC OlUUmmalln OW MEnIenLG)Mitnar,
SM_CXHSCROEL, ESL Gril (EG (OULIES
EHO Z ES Cun) lah MOWeeWrIGLc hia,
SM_DEBUG, eo Mae Di DUG,
"Debug version tal aicluur,s
SM_SWAPBUTTON, "SM_SWAPBUTTON",
"Mouse buttons swapped flag",
SM_RESERVED1, "SM_RESERVED1", "Reserved" id
SM_RESERVED2, USMS RESERVED?s'; "Reserved" 4
SM_RESERVED3, "SM_RESERVED3", "Reserved" v4
SM_RESERVED4, aS MEER EIS EIRIVIE Dicer, "Reserved",
SiMaGXMALN, "SM_CXMIN",
"Minimum window width",
SM_CYMIN, "SM_CYMIN",
"Minimum window height",
Shi CxS i215, WoMECXSUZEN,
"Min/max icon Hsteheln4
SIME CAYaSPLEZ4 Ee 5 6 SCY S02:
Es;
"Min/max icon height",
SM_CXFRAME, "SM_CXFRAME"”,
"Window frame fadehely
70 BORLAND C++ 3.0 PROGRAMMING, Second Edition

SM_CYFRAME, 4 StuL EY ERY Mule Oo".


"Window frame height",
SM_CXMINTRACK, "SM_CXMINTRACK",
“Wiis VIrelOl<U
ine) Tiaielely
SM_CYMINTRACK, "SM_CYMINTRACK",
“Witt eRaACKame) NGi@inw” -
SiMe GCMESsRia Gor. i>) Ma GiM Esp Reis G Sie
"Number of system metrics"{
Chapter 3

Keyboard, Caret, and Scrollbars

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

Routines supplied by Windows decode the key events storing them as


queued messages. After the hardware event has been interpreted, control is
restored to the interrupted program which can subsequently retrieve the key
event messages through a GetMessage request.
The principal difference between a DOS application retrieving keyboard
events from the keyboard queue (using interrupts 16h and 21h) and a
Windows application retrieving keyboard events through a GetMessage requ-
est, is the amount of information available under Windows. For example,
when the "a" key is pressed, the DOS keyboard queue would simply contain
the character "a". Under Windows, three key event messages are generated: a
WM_KEYDOWN event reporting the key being pressed, a WM_CHAR event
message reporting character code, and a WM_KEYUP event after the key is
released.
Windows reports eight different keyboard event messages in all. These
messages are identified as WM_xxxx, and each message is accompanied by
the wParam and |Param variables; thus comprising a total of eight separate
pieces of information about each key event. Fortunately, applications are not
required to use all of the information provided by these messages; but the
information is there if and when it is needed.

The WM_xxxx Event Message


The first piece of information provided with each key event is the window
message; which can be either application messages (non-system keystrokes)
as WM_KEYDOWN, WM_KEYUP, WM_CHAR and WM_DEADCHAR or
system keystroke messages as WM_SYSKEYDOWN, WM_SYSKEYUP,
WM_SYSCHAR and WM_SYS-DEADCHAR.
The system keystroke messages, which contain "SYS" in their identifiers, are
generally more important to Windows than to applications and are normally
passed to DefWindowProc for handling. These would include ALT key com-
binations such as Alt-Tab or Alt-Esc used to switch the active window or system
menu accelerators (Alt-function key combinations are discussed in Part 2).
The WM_KEYUP and WM_KEYDOWN messages are also normally
ignored by applications. These messages are generated by keys that are
pressed or released without the Alt key but are ignored by Windows.
The two "DEAD" key messages are also normally ignored. These are
generated by non-U.S. keyboards whereby certain keys are designated to add
Chapter 3: Keyboard, Caret, and Scrollbars 73

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.

The wParam and IParam Variables


The wParam variable reports a virtual key code which, for a WM_CHAR
message, provides the character code, but, as discussed in the list below, may
also contain much more.
First, the [Param variable is broken down into three fields: the repeat count,
the OEM scan code, and the flag bits as illustrated in Figure 3-1.

Figure 3-1: The lParam Message Variable

The lParam Variable

AFAELDACABHANS
Flag Bits Scan Code Repeat Count
6-bits 8-bits 16-bits

xtended Key Flag


ontext (CALT-Key) Code
rior Key State
ransition State
74 BORLAND C++ 3.0 PROGRAMMING, Second Edition

= Repeat Count—gives the number of keystrokes represented by the


event message and, in most cases, is one. However, when a key is held
down and an application cannot process key-down messages fast
enough (the default typematic rate is approximately 10-characters-
per-second), Windows will combine multiple WM_KEYDOWN,
WM _SYSKEYDOWN, WM_CHAR, and WM_SYSCHAR messages
into a single message, increasing the repeat count appropriately. For
WM_KEYUP or WM_SYSKEYUP event messages, the repeat count is
always one.
The KeyCodes.C demo program, even on fast (386/33MHz) sys-
tems, will demonstrate typematic overruns simply because of the time
required to write a full-line screen message for each key event. This
does not mean that all applications are likely to be overwritten. There
are cases, however, when applications may prefer to ignore the repeat
count in order to reduce the potential for overscrolling—a problem
that is not specific to Windows, but has often been encountered in DOS
as typematic keystrokes piled up in the keyboard buffer faster than
applications could respond. This is an area in which experimentation
is the best guide. An application option could be provided to allow
toggling or variable responses for repeat counts reported by certain
keys.
= OEM Scan Code—the keyboard scan code generate by the system
hardware which identifies the physical key that was pressed. For
example, on an enhanced keyboard, a "1" could be generated by the
normal number key in the top row of characters or by the keypad, but
each physical key returns a different and unique scan code. In other
cases, different scan codes may be generated by the same physical key
under different Alt, Ctrl or Shift states. Windows applications norm-
ally ignore the scan code information, using other approaches to derive
this information.
=m Extended Key—set (1) if the keystroke derives from one of the additio-
nal keys on the enhanced keyboard. These keys include the separate
(non-keypad) cursor and page keys (including the Insert and Delete
keys), the slash (/) and Enter keys on the keypad, and the NumLock key.
= Context Code (Alt-Key)—set (1) if the Alt key is pressed. Also, the context
code is always set for WM_SYSKEYUP and WM SYSKEYDOWN
Chapter 3: Keyboard, Caret, and Scrollbars 75

messages and cleared (0) for WM_KEYUP and WM_KEYDOWN


messages—with two exceptions:
(1) Some non-English keyboards use combinations of Shift,
Ctrl, and Alt keys with conventional keys to generate
special characters. For these characters, |Param has the
context code flag set (1), but these are not reported as
system keystroke messages.
(2) When the active window is an icon, it does not receive
the input focus. Therefore, all keystrokes generate
WM_SYSKEYUP and WM_SYSKEYDOWN messages so
that the active window (icon) does not process these
strokes. The context code flag is set only if the Alt key is
down.
m= Prior Key State—reports the prior state of the current key. For WM_CHAR,
WM_CHARDOWN, WM_SYSCHAR, and WM_SYSCHARDOWN mes-
sages, the prior key state flag is set (1) if the same key was previously
down, or cleared (0) if the key was previously up. For WM_KEYUP or
WM_SYSKEYUP messages, the prior key state flag is always set
(since, obviously, the key had to already be down before it could be
released).
= Transition State—virtually a redundancy and cleared (0) if a key is
being pressed (WM_KEYDOWN or WM_SYSKEYDOWN)), and set (1)
if the key is being released (WM_KEYUP or WM_SYSKEYUP). The
Transition State flag is also cleared for WM_CHAR or WM_SYSCHAR
messages, but means nothing at all in such a case.

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

Figure 3-2: Virtual Key Codes

Message Char Cnt Scan

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 Codes


While the /Param variable may contain information useful in specialized cases,
the virtual key codes contained in the wParam variable comprise the principal
identifiers for key events. These codes do not include all keyboard characters,
only the principal characters that might be used for controls or menu
selections. Thus, the VK_xxxx definitions are a means of identifying specific
key codes in a device-independent format.
In Table 3-1, the column labeled "Req," ("Required") indicates by asterisks
(*) the keys that are required for all Windows implementations and which will
always be available. The remaining VK_xxxx definitions, unless labeled as
not supported, are found on most conventional or enhanced keyboards.
VK_xxxx definitions listed as not supported are not implemented on IBM/com-
patible keyboards but may be supported by other manufacturers’ keyboards.
Note: For text input, file names, or data of any type, character codes should
be used, not the VK_xxxx key codes.

Table 3-1: Virtual Key Codes (Defined in Windows.H)


Hex Dec Constant Reg Keyboard Comments
Olh 1 VK_LBUTTON (mouse emulation)
02h 2 VK_RBUTTON (mouse emulation)
03h 3 VIGGAN GE Ctrl-Break same code as Ctrl-C
04h 4 VK_MBUTTON (mouse emulation)
08h 5 VK_BACK 2 BackSpace
09h 9 VK_TAB e Tab
OCh 1 VK_ CLEAR numeric keypad 5 NumLock OFF
ODh 13 VK_RETURN * Enter CR
10h 16 VK_SHIFT 7 Shift
11h 17, VK_CONTROL * Ctrl
78 BORLAND C++ 3.0 PROGRAMMING, Second Edition

Virtual key definitions listed as not supported are not found on IBM/com-
patible keyboards but may be supported by some variant keyboards.

Table 3-1: Virtual Key Codes (Defined in Windows.H) (cont.)


Hex Dec Constant Req Keyboard Comments
12h 18 VK_MENU > Alt
13h 19 VK_PAUSE Pause
14h 20 VISCAFIFAL = Caps Lock
1Bh 27 VK_ESCAPE “i Esc
20h 32 VK_SPACE 3 Spacebar
21h 33 VK_PRIOR ! Page Up
22h 34 VK_NEXT m Page Down
23h 3D VK_END End
24h 36 VK_HOME a3 Home
25h oF VK_LEFT Left Arrow
26h 38 VK_UP bs Up Arrow
27h ey VK_RIGHT ps Right Arrow
28h 40 VK_DOWN i‘ Down Arrow
29h 41 VK SELECT: not supported
2Ah 42 VK_PRINT not supported
2Bh 43 VK_EXECUTE not supported
2Ch id VK_SNAPSHOT Print Screen
2Dh 45 VK_INSERT . Insert
2Eh 46 VK_DELETE * Delete
2Fh 47 VK_HELP not supported
30h. 39h 48. on O29 main keyboard
4th. Ah 65. 90 Ae LiaZ main keyboard
60h VK_NUMPADO numeric keypad 0 NumLock ON
61h VK_NUMPADI numeric keypad 1 NumLock ON
62h VK_NUMPAD2 numeric keypad 2 NumLock ON
63h VK_NUMPAD3 numeric keypad 3 NumLock ON
64h VK_NUMPAD4 numeric keypad 4 NumLock ON
65h VK_NUMPAD5 numeric keypad 5 NumLock ON
66h VK_NUMPAD6 numeric keypad 6 NumLock ON
67h VK_NUMPAD7 numeric keypad 7 NumLock ON
68h VK_NUMPAD8 numeric keypad 8 NumLock ON
69h VK_NUMPAD9 numeric keypad 9 NumLock ON
6Ah VK_MULTIPLY numeric keypad * enhanced kbd
6Bh VK_ADD numeric keypad + enhanced kbd
Chapter 3: Keyboard, Caret, and Scrollbars 79

Table 3-1: Virtual Key Codes (Defined in Windows.H) (cont.)


Hex Dec Constant Req Keyboard Comments
6Ch 108 VK_SEPARATOR not upported
6Dh 109 VK_SUBTRACT numeric keypad - enhanced kbd
6Eh 110 VK_DECIMAL numeric keypad .
6Fh tit VK_DIVIDE numeric keypad / enhanced kbd
70h 112 VK _F1 S function key F1
71h 113 VK_F2 - function key F2
72h 114 VK_F3 : function key F3
73h 115 VK_F4 e function key F4
74h 116 VIROPS z function key F5
75h 117 VK_F6 3 function key F6
76h 118 VRP? ; function key F7
77h 119 VK_F8 * function key F8
78h 120 VK_F9 id function key F9
79h 121 VK_F10 ss function key F10
7Ah 122 VK _Fl1 function key F11 enhanced kbd
7Bh 123 VK_F12 function key F12 enhanced kbd
7Ch 124 ViK-FIS not supported
7Dh 125 VK_F14 not supported
7Eh 126 VES not supported
7Fh 127, VK_F16 not supported
90h 144 VK_NUMLOCK Num Lock
The VK_LBUTTON, etc key codes are not returned by keystroke messages but do correspond to
mouse button messages.
The virtual key definitions do not include punctuation and/or symbols. Instead virtual key codes in
the range 80h..FFh (128..255) are used but assigned values in this range may vary on non-English
keyboards. Therefore, applications should not depend on keystroke values not defined in Windows.H.

The GetKeyState Function


While the wParam and |Param variables contain considerable information, they
do not include specific shift-key information. The current state of any of the
defined virtual keys can be queried by the GetKeyState function which is
customarily used to query the shift keys (Shift, Ctrl and Alt keys) and the
toggle keys (CapsLock and NumLock). This does not, however, provide the
status of a specific shift key since no differentiation is made between the right
and left Alt keys.
80 BORLAND C++ 3.0 PROGRAMMING, Second Edition

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):

Ft CC GetkeystateC VK -SHLFEL oo! = OF)

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:

if( GetKeyState( VK_NUMLOCK ) != 0) )

returns TRUE if NumLock is toggled ON or FALSE if it is OFF because the


function call, GetKeyState(VK_NUMLOCK) returns with the low-bit set (posi-
tive) if the corresponding system flag is set.
The GetKeyState function can also be used with the VK_LBUTTON,
VK_RBUTTON and VK _MBUTTON virtual key codes to query a mouse
button status; even though this is unnecessary because the mouse event
messages, as shown in Chapter 4, contain complete key status information.
Caution: This is not a "real-time" keyboard status check. It is a test of the
keyboard status at the time the last keyboard event message was retrieved by
the application. Thus, GetKeyState cannot be used in while control loops
because the application must retrieve the event message from the queue before
GetKeyState can query the status. Ergo, the example:

Whi lerGuGe tktely


Sit a te Gavia NUM OIG Ka») m=O

could wait forever since no change in status would ever be detected. But the
alternative

do ... while(€ GetKeyState€ VK_NUMLOCK ) != 0 );

could viably execute as long as the message queue is queried within the loop.
Chapter 3: Keyboard, Caret, and Scrollbars 81

Responding to Keyboard Messages


The KeyCodes program, which demonstrates interception and reporting key-
board event messages, also shows some of the hazards of attempting to
process every message received. As suggested earlier, if an application is not
fast enough to process all messages, Windows will combine duplicate
messages using the repeat count field in the [Param variable. But, processing all
keyboard messages is neither necessary nor desirable.
First, WM_SYS messages are intended for Windows systems functions, not
for the application to handle, and can be safely ignored.
Second, WM_KEYDOWN and WM_KEYUP messages are largely
duplicates and most Windows applications confine themselves to watching
for WM_KEYDOWN messages and ignore the WM_KEYUP events.
Third, even when WM_KEYDOWN messages are processed by
applications, they are generally confined to cursor and special key events and
should not be used to retrieve conventional character key strokes. For con-
ventional characters, only the WM_CHAR messages should be used.
Finally, repeating an earlier caution, Windows applications should not
attempt to rely on special key combinations since they may easily be quite
different on non-English keyboards (and non-English keyboard drivers).
While this may appear to invalidate a number of favorite "tricks", there are
more than a few alternatives. Not relying on deciphering such key com-
binations can considerably reduce the time required for processing keyboard
messages.
Note: Under Windows, the capability of processing messages is paralleled
by the capability of generating and sending messages. This will be
demonstrated in Chapter 4 when the cursor keys are used to generate mouse
messages.

Character Event Messages


Most applications dealing with text input depend upon processing
WM_CHAR character event messages. As stated previously, the WM_CHAR
messages do not always correspond to the familiar ASCII codes. So, what is
available and dependable? First, Windows does provide translation services.
This has been incorporated in all program examples presented thus far in the
code segment in WinMain:
82 BORLAND C++ 3.0 PROGRAMMING, Second Edition

white( GetMessage( &msg, NULL, 0, 0) )


{ TranslateMessage( &msg );
DispatchMessage( &msg ); yy

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 .

The KeyCodes Program


The KeyCodes.C program provides a window to examine keyboard event
messages, but introduces very little in terms of Windows programming. Most
of the actual program is copied directly from previous examples with the
exceptions of the ShowKey subprocedure and the addition of eight case
statements that call ShowKey in response to keyboard WM_xxxx messages.
There are, however, a few other provisions. Since this display will be
formatted, the default proportionally spaced character font would be difficult
to use, so the WM_CREATE response calls GetStockObject to select a fixed-
width font instead:

case WM_CREATE:

SelectObject( hdc, GetStockObject(OEM_FIXED_FONT) );

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:

SetBkMode( hdc, TRANSPARENT );


Texeoucc nadceecxcCnhr> 1 > 's2capt, <sizeot szCaptiate) >
rwextoutc hdc, cxChr, 5, szUnin, Csizeof -szUniEno=—1>);
SetBkMode( hdc, OPAQUE ); // restore default mode //

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

TRANSPARENT, TextOut paints only the character, leaving the background


unchanged. When this is finished, SetBkMode is called a second time with the
OPAQUE instruction to reset the default conditions.
The eight WM_xxxx keyboard event messages then each call the ShowKey
procedure, passing, in addition to the window handle (hwnd), two flag
parameters, a string identifying the message and, finally, the wParam and
[Param variables:

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,

In ShowKey, the wsprintf function is called with the typecasting instruction


LPSTR preceeding the szMsg parameter. LPSTR is defined in Windows.H as
a far pointer to a string. This explicit typecasting is necessary, in this case,
because szMsg has already been passed as a local pointer reference from the
WndProc procedure and must be redefined from a local to a far pointer. On
the other hand, the first two string pointer parameters, szBuff and szFormat|...],
are pointer references to variables declared within the ShowKey procedure and,
therefore, are automatically passed to wsprintf as the far pointer references
expected. Still, when in doubt, typecasting pointers shouldn’t hurt.

Text Input Handling


A majority of applications, even without being word processor/editor pro-
grams, are text oriented and must process text input as well as display text
characters. While processing text input can range from input boxes that
86 BORLAND C++ 3.0 PROGRAMMING, Second Edition

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 );

The HideCaret.function should be called before DestroyCaret to conceal the


caret . Refer to the notes below on individual caret functions.

CreateCaret/DestroyCaret When the system caret is called by an application


using the CreateCaret function, the application can first set the shape and type
of caret used. Since any call to CreateCaret automatically destroys the previous
caret shape, if any, assigning caret parameters is absolutely necessary before
88 BORLAND C++ 3.0 PROGRAMMING, Second Edition

ShowCaret can be called to reveal the caret; no matter which window owned
the caret.
The parameters for CreateCaret are:

hwnd —identifies window owning the new caret


hBitmap—identifies the bitmap that defines the caret shape; NULL
selects a solid caret, and 1 selects a gray caret. Bitmapped caret shapes
will be discussed later.
nWidth—specifies caret width in logical units
nHeight—specifies caret height in logical units

Examples of three popular caret styles include:

Gierart.e) Calmie.t Galiwin,dy-aul yan Co GiniGe aC y, G Niue // gray block caret ih


Greate Cayest Coun winids, Opuc x Gini las // underscore caret
CreacecCaretC hwnd, 0) 1. cy.chr Dy // vertical var caret //

The DestroyCaret function is called without parameters.

SetCaretPos/GetCaretPos SetCaretPos moves the caret to the position


specified in logical coordinates by the X/Y parameters. Coordinates are client
window-relative and are affected by the window’s mapping mode. So, the
exact position in pixels depends upon this mapping mode. SetCaretPos moves
the caret only if it is owned by a window in the current task but is not affected
by the caret being hidden.
Note: The caret is a shared resource and an application window should not
move the caret if it does not own the caret.
GetCaretPos retrieves the caret’s current position, in screen coordinates,
copying the values to a POINT structure addressed by the IpPoint parameter.
The caret position is always given in the client coordinates of the window
which contains the caret.

ShowCaret/HideCaret ShowCaret and HideCaret are complementary functions


that reveal or conceal the caret only if the window indicated by the hwnd
parameter owns the caret. If hwnd is NULL, the function shows or hides the
caret only if a window in the current task owns the caret. ShowCaret and
HideCaret are also cumulative. Thus, if HideCaret has been called five times in
a row, ShowCaret must be called five times before the caret will be visible.
Chapter 3: Keyboard, Caret, and Scrollbars 89

SetCaretBlinkTime/GetCaretBlinkTime | SetCaretBlinkTime sets the caret blink


rate as elapsed milliseconds between flashes and is called with a WORD
parameter setting. The caret flashes on or off with each wMSeconds
milliseconds, making the total flash cycle (on-off-on) 2 * wMSeconds.
The complementary GetCaretBlinkTime returns the blink rate (wMSeconds)
as a WORD value.

Caret (Cursor) Positioning


The Editor.C program used for demonstration creates a gray-block caret (see
Figure 3-3) suitable for use with the SYSTEM_FIXED_FONT (fixed-width)
character set. Since this editor uses a fixed-width font, cursor positioning
becomes quite simple. Begin by watching for the WM_KEYDOWN message:

case WM_KEYDOWN:
switch(€ wParam )
{

Figure 3-3: A Simple Windows Editor

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

ote that line breaks are not based on word


breaks but
the
cursor
keys

reen
| positioning

Many of the WM_KEYDOWN messages simply will be ignored, but the


wParam message parameter will identify messages which correspond to the
cursor and page control keys:
90 BORLAND C++ 3.0 PROGRAMMING, Second Edition

case VK_HOME: xCaret = QO; break;


case VK_END: raBEeGe S tydiay = 44 break;
case VK_PRIOR: yCaret = 0; break;
case VK_NEXT: yCaret = cyBurt = 1), break;
case VK EE rir:
xCaret = .maxcG-0 ,ixCanet—1 )7 break;
case VK_RIGHT:
EAPO S Min Crdsiiiriwol, serait 0-5 break;
case VK_UP:
ViGawet. = maxG 0 yGa
ne t— iar break;
case VK_DOWN:
yGarev = mine cyBut fp ey Caret+i. > break;

Repositioning the cursor is fairly straight-forward in most cases but could


be elaborated to provide wrapping at screen margins plus other features not
supported here. For other non-character keys, a more elaborate response is
required. The VK_DELETE message produced by the Delete key is an example:

Calsie anv Ken EsExess


ONE XDCELIPEWP SSCYAUN HUA saese 2
Buiter Gy Canet) s—— BU hemGx-raly.yicane toy
BiUshaeels GeeCCB
Ub fad cane aay, Gal eaten) ae eee

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:

SetCanretrosG xcCaret “eextir, yCaret * cyChm-):


West UiriniGa Omr // end cursor / keypad keys //

The WM_CHAR message is also subject to several types of processing. But,


since a single WM_CHAR message may be responsible for several keystrokes
ona single character, handling begins with a loop using the repeat count value
from the /Param variable:

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:

CiayS\Cimua\ Dine // backspace if


jt GuexaGralne taal Ome
{
xCaret;
SendMessage( hwnd, WM_KEYDOWN,
VKU DELETES eT E?
+ break;

To respond to a backspace character which, unlike the Delete key, appears


as astandard character instead of aWM_KEYDOWN message witha VK_xxxx
identifier, the SendMessage function is called after testing and adjusting the
caret position (xCaret). This generates a WM_KEYDOWN, VK_DELETE
92 BORLAND C++ 3.0 PROGRAMMING, Second Edition

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:

Cals; mmaNGGhaes // tab Ws


do SendMessage( hwnd, WM_CHAR, ' ', 1L );
while(€ xCaret % 8 != O );
break;

The next two special character codes simply change the caret position in
this application:

casie Mi\nws // carriage return


ONG rae
xCaret = OQ; hjemfalalcSmnt hio ug nae to
casen, \.ni: // lLinefeed
if( ++yCaret == cyBuff ) yCaret = 0;
break;

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:

Claisieea\ixal| Bile: // escape ial


if€ MessageBox( hwnd, Reset text buffer?,
Editor Query,
MB_ICONEXCLAMATION | MB_OKCANCEL |
MB_DEFBUTTON2 ) == IDOK )

for(€ y=0; y<cyBuff; ytt )


for(€ x=0; x<cxBuff; x++ )
Butter tx, y=) a yf
xCaret = O; yCaret 0;
owes

InvalidateRect( hwnd, NULL, FALSE );

break;

Dialog boxes will be discussed at greater length in Part 2.


For every character that was not handled by one of the preceding special
cases, which will include most text entries, the default case inserts the character
Chapter 3: Keyboard, Caret, and Scrollbars 93

in the buffer; then, as before, hides the caret, updates the screen and restores
the caret (text cursor).

default: // all other characters ays


Buffer(€ xCaret, yCaret ) = (char) wParam;
HideCaret( hwnd );
hdc = GetDCC( hwnd );
SelectObject( hdc,
GetStockObjectCSYSTEM_FIXED_FONT) )->
TextOut( hdc, xCaret*cxChr, yCaret*cyChr,
SBuUs Ter cxCaret sy Caneéeta. plas
ShowCaret( hwnd );
ReleaseDC( hwnd, hdc );
if( ++xCaret == cxBuff )
{
xCaret = QO;
if€ ++yCaret == cyBuff ) yCaret = 0;
i
break;
oes
The default case also has provisions to recalculate, that is, advance, the caret
position. And, since other preceding provisions may also have affected the
caret position, the WM_CHAR message handling also ends with a call to
SetCaretPos:
SetCaretPos( xCaret * cxChr, yCaret * cyChr );
meuurniGs Ones

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.

The SendMessage Function


The flip-side of processing keyboard messages is the ability to send your own
messages that request actions. The SendMessage function provides this
capability. The SendMessage function is called with the same four parameter
types that were passed to the WndProc procedure:
SendMessage( HWND hwnd, WORD msg,
WORD wParam, LONG lParam );
94 BORLAND C++ 3.0 PROGRAMMING, Second Edition

SendMessage passes its message parameters to Windows which is then


responsible for placing the message in the message queue for the window
procedure identified by hwnd. This destination could be the same window
procedure that posted the message, another window procedure belonging to
the same application or an entirely different application.
As another example, the program PainText.C in Chapter 2 demonstrated
scrollbars that responded only to mouse events. There are still some com-
puters that lack a mouse and rely on the keyboard to operate Windows
applications. Therefore, to demonstrate the SendMessage function, PainText.C
will be modified to provide an alternative means of scrolling the client
window using the keyboard.
Rather than attempting to actually emulate the mouse using the keyboard,
it’s easier for this application to directly generate scrollbar messages in
response to the left, right, up, and down arrow keys. This can be accomplished
by adding aWM_KEYDOWN response:
switch( message )
{

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.

Fonts and Character Sets


Thus far, most of the examples have used a fixed-width font either for simplic-
ity in formatting output or for simplicity in positioning the cursor. However,
Windows supplies several character sets (fonts) as well as supporting the
hardware character set that is embedded in the ROM chips in video adapters,
printers, and BIOS system chips. But, as you will see, these are two quite
different character sets.

International Character Sets


In the very early days of computers, several keyboard translation schemes
were used, ranging from the early EBCDIC coding to the old 7-bit ASCII. Even
the original teletype code is still embedded, fossil-fashion, in the ASCII codes
OOh..1Fh. And, even today, when 8-bit character codes are effectively
standard, few keyboards directly generate the extended keyboard characters,
80h..FFh. Both screen and printer character sets in this range are roughly as
standardized as automobile models; all have similarities, but are very far from
identical. As computers have become more and more international, the built-
in character set for most computers sold in Euro-American nations has been
expanded beyond the familiar English/American alphabet. It now includes
the superscripted characters illustrated in the two character sets displayed in
Figure 3-4.
The first character set, SYSTEM FIXED FONT, omits the frame or box-
drawing characters that appear in the DOS character set OEM_FIXED_FONT.
This is natural enough since, in Windows, the box-drawing characters are not
needed because in a graphics environment it is easier and more flexible to
draw lines where they are needed than it is to display strings of box characters.
The Greek alphabet and mathematical characters provided in the old extended
ASCII set are also omitted in favor of the accented letters used in many
European languages. The old ROM-coded character set is still supported and
is available. In many countries, such as Thailand (to cite a first-hand example),
non-Roman alphabets are often installed as ROM chips that provide local
supplementary character fonts.
96 BORLAND C++ 3.0 PROGRAMMING, Second Edition

Figure 3-4: Two Character Sets

SYSTEM_FIXED_FONT (Windows character set)


6 61 2 3 4 6S 6 7 8 9 A B C D E F
| | 8 a a iz) | 8 a | a g | ] |
ie fee ee lecee) eel Peeeel Eee ed Ce ee fom lee oe ed ee
2 ee See ame Sl Se r Ree ey My po et
3 Speges 8) eo Oe 7 BG ere SRR
| @ A B C D E F G H I J K L M N 0
yb Le Q R S$ T U U WX Y 2 [ \ ] ye
60) th oaly b 62 od) sO a fF <Q hy dj) ak ene
7} p qer s t uy wex oY z { | poeta ee
8 | E a | a | 8 a a a a 8 | a a B a
9 | u g a a | a @ a a | 5 | & L
a ee ee aL Fae | Pas, re se Be eee
CeCLita’e t ALA
er at he
CAL Ge 3
eS
€<"\C) CE ick
ee es,in assleeelFOP
Euibiadia
pulsed oR x02 6:66, 5..b.. x8. O...0.. 0 U- 9 6. 6
E.|.as 6 <a 14 4) a> ® Ce 6" 6 "G5" 77 at oie
EXC Gro etsds 6 AG Be abies) Vest ips) ieee ie Ceop ae
The quick brown fox jumped over the lazy red dog

OEM_FIXED_FONT (DOS character set)


SS ee Oats Coe ed ON ee ee eee ee
8 8g 6764 OF eS IE (a ret ds Ses Hf ne
401 oP Os: Get 68 Bit gal CENn fille bits Skee Oe rake y,
2 ¢ Se avel ee ee St es Be Gy 2 ¢ Ja Pity tee = : 4
3° \.07_ 2 2 3 4 5 6 ? 8 9 = 3 < = > ?
y |e A Bee Ged Pek F G H I J K L MN N OQ
5.1-P Oe PRS El eee UAW eh ek et Ze le 61a
rad es a: chet etd ve. °f.. cg, UhS Zin ae ok ()o eae ce
7 |p q ¥ 8 t usy Ww x y z < } > ae Oa
@ |G Uomvé. Bed) 9a) Chis DED le FED AA Orie ak
Ql €) er. Bi5 86s 1606. ba a ace Ch oth ed
Ata oh 6 a AON eS ee oe in eee «< »
B) 8 ee Oe a a il a OE ara
G| © “al ard ORES eta) aeall eae fren ree tae en
ES en oe ee ee eee ee|e et pee or pt
Ela 6 F HR FE oo p t+ & B RR 6&6 © BS E€
FL eae BS epee! Sao, See ie Ph ea eg
The quick brown fox jumped over the lazy red dog

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

Happily, in this last category, Windows should prove especially popular


because it is easier to create, display, and interpret a new Windows software
font than firmware fonts.

The ANSI Character Set and Windows Fonts


The extended character set used by Windows and Windows programs is called
the "ANSI character set" and is supported by several fonts distributed with
Windows as shown in Figure 3-5. By default, applications use the proportio-
nal-spaced SYSTEM_FONT for window captions, messages, menus, and
dialog boxes as well as all other text output unless another font is explicitly
selected. As you can see, the SYSTEM_FONT has the advantage of provid-
ing smooth, readable text using less space than a fixed-width font such as
SYSTEM_FIXED_FONT or OEM_FIXED_FONT.

Figure 3-5: Six Fonts

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

SYSTEM_FIXED_FONT is the same font as the previous Windows version’s


SYSTEM_FONT, and is roughly the same as the OEM_FIXED_FONT, except
that it supports the ANSI character set rather than the extended ASCII charac-
ter set. The ANSI _FIXED_FONT is another fixed-width font that has essenti-
ally the same characters spacing as does the SYSTEM_FIXED_FONT, but is a
98 BORLAND C++ 3.0 PROGRAMMING, Second Edition

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.

Supporting the ANSI Character Set


While still limited to variations on the Roman alphabet, the ANSI character
set provided by Windows has established a departure from the chauvanisticly
English-only character sets that were standard on earlier systems. This broad-
ening of languages, however, has also invalidated a number of assumptions
and a few standard C functions.
First, when a WM_CHAR message is received by a Windows application,
the wParam variable may quite legitimately contain values above 7Fh (127). It
would bea prime mistake to "trim" the high-bit or to assume that higher values
are invalid. While many word processor programs have established propriet-
ary notions about using the high-bit on character values, this is another
mistake that will not be valid in the Windows environment. In the past, this
has also prevented many such editors from being adapted in other countries.
The familiar C functions, toupper and tolower, are no longer valid because they
operate correctly only for characters in the 41h..5Ah and 61h..7Ah range. They
are not valid for characters in the (European) COh..FFh range (see Figure 3-4).
Instead, Windows supplies two string-oriented functions, AnsiUpper and
AnsiLower, both requiring null-terminated strings:

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):

chU="AnsitLower © CLESTR) CLONG) .CB ine Che:

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:

Phsilppensurt C psStr, antlen ),-

AnsiUpperBuff and AnsiLowerBuff return a word value of the converted


string length.
Other conversions include the AnsiToOem, AnsiToOemBuff, OemToAnsi, and
OemToAnsiBuff functions for conversions between the ANSI character sets and
the extended-ASCII (OEM) character sets. These functions are principally
useful in converting non-English string arguments or text strings between
ANSI and OEM formats. Applications would include output to printers that
do not support the ANSI character arrangements, non-English file/directory
names, or converted textual data between DOS and Windows formats. Also,
in several non-European languages (Japanese is a primary example) character
sets require two bytes for some characters. The AnsiNext and AnsiPrev
functions provide an alternative to conventional C pointer arithmetic for
scanning a string. AnsiNext and AnsiPrev accept a far pointer to a string and,
after consulting the keyboard/language driver, return a far pointer that has
been properly incremented or decremented for 2-byte character codes.

Generating ANSI (Extended-ASCll) Characters


While the familiar English-language keyboard does not provide keys for
direct entry of character codes above 7Fh (127), these character codes can be
generated by using the Alt-Keypad combination and by entering the appropri-
ate 3-digit decimal value. Windows provides this same facility in two forms:
first, using ASCII character codes that are converted to ANSI codes and,
second, entering ANSI codes directly. In both cases, of course, the resulting
character is the ANSI value, not OEM ASCII value.
The first form duplicates the DOS entry format but processes the entered
value through the OemToAnsi function before generating the WM_CHAR mess-
age. Holding down the Alt key and entering 142 on the keypad (8Eh—ASCII)
is processed through the OemToAnsi function to generate a WM_CHAR
message with the character value 196 (C4h). This will display the ANSI
character “A”.
100 BORLAND C++ 3.0 PROGRAMMING, Second Edition

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

| (SSS SS5 SSS63s9 SSS Sess Sse /7//


ef, KeyCodes.C if
// C++: Windows Keyboard Test //
/ SSS SS SSSS2 SSeS SSeSoSSssseeee////

#include <windows.h>

REGT rect:

short cxChr,..cy.GhnpeckWin, cyWin-?

void ShowKey(€ HWND- hwnd, Wintec iType, BOOL EndRpt,


char *szMsg, WORD’ wParam, LONG LParam )
{
static char *szFormat([2] =
§ MBoUaS AOAr Z3d *Z02Xh"
a AIC hC AG IC ay
uZ=155s %Z02Xh VAC eX | AO2AW.. | eae
char szBuffC80J]; PAINTSTRUCT ps;
HDC hdc; short step;
WOR DEEGInICinite mm Chi Siccalny- mG Nirsaalgisr

if€ EndRpt ) step = 3*cyChr/2; else step = cyChr;


ChCnt = LOWORDC LParam );
ChScan = HIWORD(C LParam ) & OxOOFF;
ChFlags = HIWORD( LParam ) & OxFFOO;
ScrollWindow( hwnd, O, -step, &rect, &rect );
hdc = BeginPaint( hwnd, &ps );
SelectObject( hdc, GetStockObject( OEM_FIXED_FONT ) );
ifc ! Type)
TextOut( hdc, cxChr, cyWin - step, szBuff,
wsprintf( szBuff, szFormatLiTyped,
(LPSTR) szMsg, // message constant iif
wParam, // key code value //
GhiGnite, // repeat char count Hi
ChScan, // keyboard scan code //
CBYTE)( ChFklags & Ox0100 27 "V) 2!
' »iy

CBYTLEMG (ChElags S0x200017 ty" =:


' ys

CBYTE).G ChFlags & Oxs000 7 1): ™t i!


),
CBYTENGEChELags @ Oxs000 7 "7" 2 "1"
» » YE
else
Textoutccehac,. ¢xcGhr, cyWin - step, szBuff,
102 BORLAND C++ 3.0 PROGRAMMING, Second Edition

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 );
}

long FAR PASCAL WndProc( HWND hwnd, WORD message,


WORD wParam, LONG lParam )
{
Hi PRE Se Pieter: es SvEte 4 URS = Transition key state
//
SiGaitniCusCihiaieesiZiGial pulse
"Message Code Key Char Cnt Scan
CE XGt ALE Pree Suaaes
static char szUnLnL[] =

" ee

HDC hidicr PAINTSTRUCT pis7


Ee XouM EaReaG tm

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

InvalidateRect( hwnd, NULL, TRUE );


hdc = BeginPaint ( hwnd, &ps );
SelectObject( niGicy, GetStockObjectCOEM_FIXED_FONT)

SetBkMode( hdc, TRANSPARENT );


pe xe ONUtaGminidice, GoxGiniey ppmesiz Galpite, (sizeof sz€apt)-1

TextOut( hdc, couG hire, d- SraWimkgy,- (sizeof szUnLn)-1

SetBkMode( hdc, OPAQUE ); je restore default mode //


EndPaint ¢ hwnd, MS NF
Mert Cite Gu Ome

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 )
);
}

int PASCAL WinMain( HANDLE hInstance,


HANDLE hPrevIiInstance,
EP Sark LpszCmdParam,
int nCmdShow )
{
static char szAppNameL] = "Keyboard Codes";
HWND hwnd;
MSG msg;
WNDCLASS wc;

Ti Lo MnP RAWVUiMGReeliMea 2

wce.style = CS_HREDRAW | CS_VREDRAW;


we.lpfnWndProc = WndProc;
we.cbClsExtra = Q;
we.cbWndExtra vai ()E=
we.hInstance = hInstance;
WiC an con = oad Coni Gen UE Eyal Dilee APP IG Alert Oi Nis
we.hCursor = LoadCursor( NULL, IDC_ARROW );
we.hbrBackground = GetStockObject( WHITE_BRUSH );
we.lpszMenuName = NULL;
we.lpszClassName = szAppName;
RegisterClass( &we );
}
hwnd = CreateWindow(€ szAppName, "Keyboard Event Messages",
WS_OVERLAPPEDWINDOW,
CWZRUSEDERAULT, CW2USEDERAUBT
CW_USEDEFAULT, CW_USEDEFAULT,
NUCCY NULL, hinstance, NULL) =);
ShowWindow ( hwnd, nCmdShow );
UpdateWindow( hwnd );
while€ GetMessage( &msg, NULL, 0, O ) )
{
TranslateMessage( &msg );
Chapter 3: Keyboard, Caret, and Scrollbars 105

DispatchMessage( &msg );
3
return( msg.wParam );
}

PTS SSS SSS SS SSS SSS SS SSS SS SSSSSSSSSSSsssas;


i‘ KeyCodes.DEF module definition file ;
PSS SSS SSS HHS HSS SSS SSS SSS SSS SSSSSSSSSSs==;

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

ff Sos oesecSe SSeS soeooSaesoeSy/i/


LY Edi tomc //
// Ct+ Windows Editor Demo //
i f/ SSeS SS] ==SSSeocesSsensse/ /

Hinclude <windows.h>

Pde
ta nes Buds elakt x4.) 4¥) > = Co pBur fen ity * cxBu tit 1+ 2x2)

Long FAR PASCAL WndProc( HWND hwnd, WORD message,


WORD wParam, LONG LParam )
{
Sitiaicel Cm hide a DID Untmaike lems NIUIL IES:
Siwayiei~G wine cxCiniines cy Cinine, cxWin, cyWin,
COB Ushahe me Cly/B Ultima XeGalniesGy ny, CialigeLta.
WATE bere. Gee ar
HDC hdc;
PAINT STRUCT ps,
TEXTMETRIC tm;

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

XiCaline tae— sc BIUih tales break;


case VK_PRIOR:
yCaret = 0; break;
case VK_NEXT:
yCaret = cyBuff - 1; break;
GialSieyn V KeaneElFaliee
xCaret = max( O, xCaret-1 ); break;
case VK_RIGHT:
xCanet = min ® cxButf—1, xCaret+1 )> break;
case VK_UP:
yCaret = max( QO, YCArPAsveou Ye loreel<-
case VK_DOWN:
yCanete=GnintcyBuft=1, jyCaret#l = break:
Graysie) eV Kee DENS Ese:
for(€ x=xCaret; x<cxBuff-1; x++ )
. Buffer(x,yCaret) = Buffer(x+1,yCaret);
BUstaielty Gamc xsB Uihataanlemey] Gray) Ga alle =
HideCaret( hwnd );
hdc = GetDCC hwnd );
SelectObject( hdc,
Gets tockObijjeicit Gs Si EMm@nLX EDs FONT =>):
TextOut( hdc, xCaret*cxChr, yCaret*cyChr,
&Buffer( xCaret, yCaret ),
CxXiBiul
tif—=x Caines! >)
ShowCaret( hwnd );
ReleaseDC( hwnd, hdc );
break;
}
SetGainet PosiG xCaret: « sicocGhiney.Gameitrue cy Chir)
return( O ); // end cursor / keypad keys //

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)

TextOut( hdc, xCaret*cxChr, yCaret*cyChr,


SButferG xCaret, yCaret: Ja) oi-
ShowCaret( hwnd );
ReleaseDC( hwnd, hdc );
if G++ x Carnet ==" cxBut ft >
{
xCaret = O;
if(€ ++yCaret == cyBuff ) yCaret = 0;
}
break;
pa ae;
SetCaretPos€ xCaret * exChr, yCaret * cyChr );
ne turn Gsm:

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

int PASCAL WinMain(€ HANDLE hInstance,


HANDLE hPreviInstance,
LIPS Ul lpszCmdParam,
int nCmdShow )

static char szAppNameL] Hse)


CeO Pr’ f
HWND hwnd;
MSG msg;
WNDCLASS WC;

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;

Editor.DEF module definition file -


PTS SSS SSS SSS SSS SSS SHS HHS SSH SSH SSH HHS S=F;7

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

The Mouse in Windows

As you must certainly realize, MS/Windows is heavily mouse oriented even


though, unlike the keyboard, the mouse remains an optional device. Most
Windows programs can be controlled from the keyboard and, whenever
practical, your own applications should provide at least a minimal keyboard
interface. This is not to suggest in any way, shape, or fashion that the mouse
should be ignored! Even if the mouse is not essential it is still, in many if not
all cases, the most convenient interface in Windows as well as many non-
Windows applications.

Mouse Types, Yesterday, and Today


Originally, mouse devices were single-button (left only) pointing devices. This
form is still found today on Apple computers, but is virtually obsolete on
contemporary MSDOS/Windows systems. Even so, some sources suggest that
single button mice should still be considered the minimal standard. Observing
this restriction is rather like writing applications to conform to the 25x40 text-only
video standards because, today, virtually all mice, Apple systems excepted, have
two buttons (left /right) while many even support three (left/ right/middle).
While Windows supports two- and three-button mice, support is also provided
as mouse emulation for joysticks or lightpens which are necessarily treated as
single-button mice. Common usage continues to emphasize the left mouse
button, even when a three-button mouse is installed, although Windows has

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.

Is There a Mouse in the House?


Mouse-critical applications especially need to test the system and insure that
a mouse is present. This can be done by using the GetSystemMetrics function:

11) GerSysvemMetricsC-ShL MOUSEPRESENT© a)

If a mouse is installed (and working, of course), GetSystemMetrics will return


TRUE (non-zero). If a mouse is not installed, a FALSE (zero) result is reported.
The Mouse3.C program, demonstrated later, shows one response indicating
the absence of a mouse by presenting a dialog box, and then issues a
WM_QUIT message. The way in which your application should respond to
the absence of a mouse depends on the application. Unfortunately, no method
is provided by Windows to determine the number of buttons present on an
installed mouse.

The Mouse Cursor


As explained in Chapter 3, under Windows, the text cursor is now known as
the "caret" while the mouse cursor is simply called the cursor. While the mouse
cursor actually points at a single pixel, the cursor proper is a bit-mapped
image that is tracked across the display preserving the background image.
Within the cursor image, one pixel serves as the "hot spot" and is the actual
pointer location.
Chapter 4: The Mouse in Windows 113

Windows provides eleven predefined cursor images, but, by default, uses


the slanted arrow (IDC_ARROW). Applications, however, may define any of
the standard cursors as their own defaults, may change cursors according to
tasks or window areas, or may define their own custom cursors. This last topic
will be reserved for Part 2 where the Resource Workshop makes cursor
creation convenient.

Mouse Actions Three types of interaction are defined for mice:


= Clicking—pressing (and releasing) a mouse button
= Double-clicking—clicking a mouse button twice in quick succession
a Dragging—moving the mouse while holding a button down
Some applications also use a drag-and-release interaction that involves
clicking on an object and then using a second click to release the object. This
permits dragging the object without holding down the button. This form is
also popular for drawing programs and reduces "mouse-wrist" injuries (mus-
cle and tendon strains caused by holding and dragging a mouse in tasks
requiring fine positioning control).

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.

Table 4-1: Mouse Button Messages


Button Released Double-clicked
Pressed
Left WM_LBUTTONDOWN WM_LBUTTONUP WM_LBUTTONDBLCK
Right WM_RBUTTONDOWN WM_RBUTTONUP WM_RBUTTONDBLCK
Middle WM_MBUTTONDOWN WM_MBUTTONUP WM_MBUTTONDBLCK

A WM_xBUTTONDOWN or WM_xBUTTONUP message is issued only


once when the mouse button is pressed or released, and a
WM_xBUTTONDBLCK message is issued in response to a rapid double click
114 BORLAND C++ 3.0 PROGRAMMING, Second Edition

(down-up-down). However, no button messages of any kind are issued by


holding a mouse button down.
All working mice, whether they have one, two, or three buttons (or lightpen
or joystick devices), generate WM_LBUTTONxxxx messages.
WH_RBUTTONxxx x messages are generated by two- or three-button mice
while WH_MBUTTONxxxx messages are generated only by three-button
mice.

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:

we.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;

If CS_DBLCLKS is not included in the window style (or child-window style)


specification, a double-click is received as four separate messages:
WM...DOWN, WM...UP, WM...DOWN, and WM...UP. When CS_DBLCLKS is
included, a double-click is received as WM...DOWN, WM...UP,
WM...DBLCLK, and WM...UP with the double-click message replacing the
second button-down message.
Caution: If itis necessary to double click in order to performa very different
task from a single click message, your message processing could become quite
complex because a single button-down message will always be received before
the double-click message.
An example of compatible single and double-click handling is found in the
Windows File Manager. A single click on a subdirectory listing changes
directories, but a double-click calls a new directory window that displays the
selected directory. The result is that accidental entries perform essentially, if
not exactly, the same task.

Mouse Movement Messages


The tenth mouse message, WM_MOUSEMOVE, is issued every time the
mouse moves within the client window. Movement is not reported per screen
Chapter 4: The Mouse in Windows 115

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.

Additional Information in Mouse Messages


In addition to the mouse event message itself, every mouse message provides
complete mouse position and button status information in the /Param and
wParam variables. At the time the mouse event message is issued, the mouse
position is contained in the /Param variable. This can be extracted as x-coordin-
ate /y-coordinate values using the LOWORD and HIWORD macros defined
in Windows.H:

xCord LOWORD( LParam );


yCord HIWORD(C LParam );

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:

if( wParam & MK_LBUTTON ) // left button down af


if( wParam & MK_MBUTTON ) // middle button down //
if( wParam & MK_RBUTTON ) SAG Cinta DIU tatroinmacOlwiniv Gl/ay,
if€( wParam & MK_SHIFT ) // Shift key down Hi)
if( wParam & MK_CONTROL ) // Ctrl key down fa,
116 BORLAND C++ 3.0 PROGRAMMING, Second Edition

Mouse Events in Windows


Mouse events in Windows can be a bit different than those in other
environments. First, because the Windows environment is shared, mouse
button events are not always paired; a button down event might occur in one
window with only the release event reported to the application—or vice versa.
Even if the application occupies the entire screen, the application’s caption
bar, control buttons, and frame each constitute separate windows. Only those
events that occur within the client window area are reported to the
application’s client window message procedure.
Second, a window procedure can capture the mouse and continue to
receive all mouse messages even when the mouse is outside the client window
area.
Third, if a system-model message or dialog box is active, no other applica-
tion window can receive any mouse messages. System-model message and
dialog boxes prohibit switching to any other window or application until
exited. Awareness of these special circumstances aside, normally there are no
restrictions or extraordinary provisions required for handling Windows
mouse event messages.

Mouse1: Mouse Tracking


The Mousel.C program demonstrates how WM_MOUSEMOVE messages are
generated by plotting a single pixel at the reported coordinates of each mess-
age received. Plotting is toggled on/off by pressing the left mouse button. For
this purpose, only two mouse messages are handled: WM_MOUSEMOVE and
WM_LBUTTONDOWN:

switch( message )
{
case WM_LBUTTONDOWN:
fPlaninit= eh eablinktemcomel
MessageBeep(0Q);
return(Q);

The WM_LBUTTONDOWN message toggles fPaint, a static boolean vari-


able, by XORing it with one, thus enabling and disabling the WM_MOUSE-
MOVE response. The WM_MOUSEMOVE response is about as simple and as
fast as possible, and passes the mouse position reported to SetPixel to plot a
single screen pixel as a black dot:
Chapter 4: The Mouse in Windows 117

case WM_MOUSEMOVE:
iC Le an fit...)
{
hdc = GetDCC( hwnd );
SetPixel( hdc, LOWORD( LParam ),
HIWORD(C LParam ), OL );
ReleaseDC( hwnd, hdc );
m
return(0);

This example is enough to demonstrate tracking the mouse movement


through WM_MOUSEMOVE messages. Notice particularly, however, that
rapid mouse movements are reported as widely spaced dots, while slow
movements may or may not generate a completely continuous line.
Complete listings for Mouse1.C appear at the end of this chapter.

Mouse2: Mouse Cursors


The Mouse2 demo creates ten child windows within the application’s client
window area; each appearing as a simple outline with a white background.
These client windows are secondary to the main purpose of this demo which
is to show ten predefined mouse cursor shapes. As the mouse is moved, each
of these client windows tracks the WM_MOUSEMOVE event messages to load
a different mouse cursor image. Using client windows requires some additio-
nal provisions that did not appear in previous examples, beginning in the
WinMain procedure, in which a second child window class is declared:

if€ ! hPrevInstance )
{

RegisterClass( &wce ); // register parent class //


// define child window class //
we.lpfnWndProc ChildWndProc;
we.cbWndExtra sizeof ( WORD );

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.

Creating and Sizing Child Windows


The actual child windows are declared by a static array in the WndProc
procedure:

static HWND hwndChild([51C2];

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.

Cursor Operations in ChildWndProc


The ChildWndProc responds to WM_MOUSEMOVE messages and sets a new
cursor image when the mouse is moved from one child window to another. In
a previous example, the WM_SETFOCUS and WM_KILLFOCUS messages
were used to create, show, hide, and destroy the text caret, but child windows
120 BORLAND C++ 3.0 PROGRAMMING, Second Edition

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 =

The LoadCursor function is called with two parameters beginning with an


hInstance parameter indicating the .EXE file that contains the cursor resource.
Normally, for a custom cursor, this would be the same hInstance variable that
Windows uses to identify the application. Or, as in this example, the variable
is NULL because the cursor is a predefined resource.
The second parameter is the resource identifier. For the predefined cursor
images, this is actually a macro defined as MAKEINTRESOURCK(...) which
contains an ID value for the specified cursor image. The IDC_xxxx macros are
listed in Appendix A and defined in Windows.H. The custom cursors topic
will be covered further in Part 2.
The SetCursor function uses the hCursor handle returned by LoadCursor to
make this the new active cursor. While consideration might suggest that this
code would result in slow operation that repeatedly reloads the cursor image
each time the WM_MOUSEMOVE message was received, this is not actually
the case. First, LoadCursor loads the cursor resource into memory only if it has
not been loaded previously. Otherwise, it retrieves a handle to the existing
Chapter 4: The Mouse in Windows 121

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.

The Default Cursor


While the default cursor image will reappear anytime the mouse cursor moves
outside child window areas, provisions have also been made for the
WM_LBUTTONDOWN to load the default cursor. This is identified by
IDC_ARROW:
case WM_LBUTTONDOWN:
Siete Guilnsion Guload Guirsioim @aNUIEy 1)DICAeARIRO Wei s-
return(Q);

Any subsequent mouse movement results in the child window’s cursor


being reloaded. This type of handling can be used to load one cursor as a
mouse button is held down, and another cursor restored when the button is
released. A counterpart to this type of response is shown next.

Hiding the Mouse Cursor


In examples in Chapter 3, the text caret (cursor) was hidden before a screen
paint operation was executed and then revealed again afterwards. For the
mouse cursor, this appears less important (that is, appears to be handled
automatically) but there may still be other occasions when an application may
prefer to conceal the mouse cursor. For this purpose, the ShowCursor function
is called with a boolean argument; FALSE (zero) to hide the mouse cursor, or
TRUE (non-zero) to reveal it. In Mouse2.C, the right mouse button conceals
the cursor when down, showing the cursor when the button is released:
122 BORLAND C++ 3.0 PROGRAMMING, Second Edition

case WM_RBUTTONDOWN: ShowCursor(0); return(0);


case WM_RBUTTONUP: ShowCugsoriG) > return(0O);

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:

while€ ShowCursor(Q) >= O );


WiMtg (es GameSi NOW] GIUINIESiO) Il Gil) a <=

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.

Mouse3: Hit Testing


The Mouse3 example program demonstrates hit testing by creating a cruci-
form grid, as shown Figure 4-1, composed of squares within a single client
window. The actual grid is a 7x7 array, but four grid elements from each
corner of the array have been flagged as invalid and will not be included in
paint operations. The remaining grid elements each have a corresponding
boolean fState flag which is initially FALSE.
To demonstrate mouse hit testing, the location of each WM_L-BUTTON-
DOWN message is tested against the grid coordinates for a match and a
corresponding fState flag is inverted, toggling the grid state on or off. Mouse
hits that fall outside of the grid area or correspond to a grid area and are
flagged as invalid trigger a beep message. Then, when WM _PAINT is called,
Chapter 4: The Mouse in Windows 123

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.

Figure 4-1: A Cruciform Grid for Mouse Hit Testing

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

Table 4-2: Non-Client-Window Mouse Button Messages


Button Released Double-clicked
Pressed
Left WM_NCLBUTTONDOWN WM_NCLBUTTONUP WM_NCLBUTTONDBLCK
Right WM_NCRBUTTONDOWN WM_NCRBUTTONUP WM_NCRBUTTONDBLCK
Middle WM_NCMBUTTONDOWN WM_NCMBUTTONUP WM_NCMBUTTONDBLCK

These nine button messages correspond in function to the client-window


mouse button messages discussed earlier. The WM_NCMOUSEMOVE mess-
age corresponds to WM_MOUSEMOVE and only reports mouse movement
outside of the client window area. These ten mouse messages, however, do
not correspond to their earlier counterparts in their accompanying informa-
tion variables.

Non-Client Area Mouse Parameters


The wParam variable that accompanies a non-client area mouse button or
movement message returns a WinWhere() Area Code that indicates the non-
client area at which the message was generated instead of containing the
button and keyboard shift state information that accompanies client area
messages. Constants for these areas as defined in Windows.H are listed in
Appendix A. All of these 26 area codes begin with HT (Hit Test) and include
such designations as HTERROR (-2), HTTRANSPARENT (-1, corresponding
to a window covered by another window), HTNOWHERE (0, not on any
window), HTCLIENT (1, client window), or HTHSCROLL (6, horizontal
scrollbar).
The /Param variable that accompanies a non-client area mouse button or
movement message does, as with the client area messages, still contain
coordinate information except that now the x and y coordinates are screen
relative instead of client-window relative. The 0,0 origin point for the
coordinates reported begins at the upper-left corner of the screen and
increases down and right, as within a client window.

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

The address variable IpPoint is a long pointer to an instance of the POINT


record structure that contains the integer values for the x and y coordinates.
Remember, coordinates for a point lying to the left or above the client window
origin will contain negative values if converted to client window coordinates.
Likewise, since application windows can be moved "off-screen" to the left and
up, client window coordinates converted to screen window coordinates might
also contain negative values. The MAKEPOINT macro can be used to convert
[Param values to a POINT structure.

The Hit-Test Message


The WM_NCHITTEST message is a special case and, strictly speaking, is not
a non-client area message despite the NC designation. Instead, this message
is generated before any other mouse messages, client area and non-client area,
and is sent to the window that contains the cursor (or a window using
GetCapture to capture the mouse input) every time the mouse is moved.
The wParam value that accompanies WM_NCHITTEST is not used. The
lParam value contains the x and y cursor position values as screen
coordinates. Normally, applications pass this message to Windows through
the DefWindowProc function where Windows will generate all other mouse
messages based on the mouse coordinates and mouse button information.
When the resulting message generated is a client area message, the screen
coordinates are converted to client-window coordinates for the new message.
Also, intercepting WM_NCHITTEST messages, e.g.,

case WM_NCHITTEST: return( (CLONG) HTNOWHERE );

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.

A Final Mouse Message


The 20-second mouse message is defined as WM_MOUSEACTIVATE and
occurs when the cursor is in an inactive window and any mouse button is
pressed. The parent receives this message only if the child passes it to the
DefWindowProc function. In this event, the wParam variable contains a handle
126 BORLAND C++ 3.0 PROGRAMMING, Second Edition

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.

Duplicated Mouse Messages


Windows.H includes two duplicate mouse message definitions:
WM_MOUSEFIRST, corresponding to WM_MOUSEMOVE, and
WM_MOUSELAST, corresponding to WM_MBUTTONDBLCLK. These
duplicate messages are included in Appendix A but do not provide different
results. Reasons for their inclusion are uncertain.

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>

Long FAR PASCAL WndProc(€ HWND hwnd, WORD message,


Chapter 4: The Mouse in Windows 127,

WORD wParam, LONG LParam_ )

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 ) );
}

int PASCAL WinMain(€ HANDLE hInstance,


HANDLE hPrevInstance,
LIPS WAR lpszCmdParam,
int nCmdShow )
{
static char szAppNameLJ] = "Mousei";
HWND hwnd;
MSG msg;
WNDCLASS WC;
if( ! hPrevInstance )
i
we.style = CS_HREDRAW | CS_VREDRAW;
128 BORLAND C++ 3.0 PROGRAMMING, Second Edition

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 );
}

SS SS SS SS SSS SS SS SS SS SSS SSS Se See

f, Mouse). DEF module definition tite. —>


[i SS SS SS SSS SS SS SSS SSS SSS SSS Seas

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

char szChildClassC] = "Mouse2_Child";

Long FAR PASCAL ChildWndProc( HWND hwnd, WORD msg,


WORD wParam, LONG LParam )
{
HDC hidics-
PAINTSTRUCT DST;
RECs meic te
HCURSOR HGURsS on,

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);

SSeS SS SS SSS SSS Sea SS SSS SS SS eS SSS aS SS eaee a ////


// option = omit WM_PAINT for plain gray background //
ifif or include to show child window outlines //

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 );
}

Long FAR PASCAL WndProc( HWND hwnd, WORD message,


WORD wParam, LONG LParam _ )
{
static HWND hwndChild(C51C2];
short CxWialiny, chy Wil nee wexXe ay >

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 ) );
}

int PASCAL WinMain( HANDLE hInstance,


HANDLE hPreviInstance,
ERS TR LpszCmdParam,
int nCmdShow )

static char szAppNameL] "Mouse2";


HWND hwnd;
MSG mSQ,
WNDCLASS WC;

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

//*** new elements for child window class ***//


we.lpfnWndProc ChildWndProc;
we.cbWndExtra sizeof( WORD );
we.hIcon = NULL;
we.hCursor = NULL; // essential!!! hed
we.lpszClassName = szChildClass;
RegisterClass( &we );
}
hwnd = CreateWindow( szAppName, "Mouse2: StocmCuirsioinsmes,
WS_OVERLAPPEDWINDOW,
CWLUSEDEFAULT, CW_USEDEFRAULAL
CW_USEDEFAULT, CWESUSEDEFAUET,
NULL, NULL, hinstance,~ NULL De
ShowWindow( hwnd, nCmdShow );
UpdateWindow( hwnd );
while€ GetMessage( &msg, NULL, 0, O ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
return(€ msg.wParam );
}

Be SS to

; Mouse2.DEF module definition file =;

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>

#define xPos( x ) (€ (x) * cxGrid + cxOff )


Adefine yPos€ y ) (_ Cy) * cy¢rid st cyot 1 >
Chapter 4: The Mouse in Windows 133

Long FAR PASCAL WndProc( HWND hwnd, WORD message,


WORD wParam, LONG LParam )
{
static BOOL, cfstatel74Ib74I> fVala
dl 7ialivd-
StacicasnontucxGnid, «cyGrid,ficxwWanjweyWing cxOft, cyOft:
HDC hidic?- POINT point;
RE Gi mectr PAINTSTRUCT DSi7
short >
real A

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
}

int PASCAL WinMain(€ HANDLE hInstance,


HANDLE hPreviInstance,
LPSTR lpszCmdParam,
int nCmdShow )
{
static char szAppNameL] = "Mouse3";
HWND hwnd;
MSG msg;
WNDCLASS WC;

if€ ! hPrevinstance )
{
wce.style = CS_HREDRAW | CS_VREDRAW;
we.lpfnWndProc = WndProc;
we.cbClsExtra = (8)
wce.cbWndExtra = 0;
Chapter 4: The Mouse in Windows 135

wc. hInstance = hInstance;


wc = |) COLT =,Loadicon€, NULL, IDI- APPLICATION )->;
WiCs. hCursor =m SOald Gui hs Os GaN Ue ee liDiGaARIRIOW mr
wc. hbrBackground = GetStockObject( LTGRAY_BRUSH );
wc. lpszMenuName NULL;
we. lpszClassName = szAppName;
Reg isterClass( &we );
}
hwnd = CreateWindow( szAppName, "Mouse3: Mouse Hit Test",
WS_OVERLAPPEDWINDOW,
CW UISIE DIE ATU IIE Teen GlWie IS EIDiEy ANU alieg
CWSRUSIEDE RA UIT, a GWanUISIE DER AU,
NUE aeN Ui nlsnisita mGer- muNii ys
ShowWi ndow ¢ hwnd, nCmdShow );
Update Window hwnd );
while(¢ GetMessage( &msg, NULL, 0, O ) )
{
Tra nslateMessage( &msg );
Dis patchMessage( &msg );
}
return ( msg.wParam );
}

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

_-
=
_

ces 2508? e9 (a bresiterwats


c@ eentapaoedtnala <5 vr eced Thee! ©
ad wrel sour? yuoum S4eten divas @:
ewen Feeny d)-9e6epuongetddworw, HAL)
qtTA ipoie.
CrelOeedaeyt
5 sium’
l a.
Puree. seeoPeqiza = ame
: a
rE tre oa
{ mete

= SO8To Sts" e8 HANS tobe KY Pn eteeate aah es


: Ets 745; Spevoenssaswive: ou) on"
@ “Weveesta a3 £-Te5ars erews v2
yafaua\saabiays i 19 ima sve 7
6h ° SAM gesGetrntly ~ 110 ~ ate —_ : aa
id Ake | £4 6-—9602 Sait (Mayatocts
= » S'S) So OweW UGS Ra) Bed eb ae
Vv" oY Qe gUere’ Sneed limeapentunes Bre
— ~~

~ ; ‘a l*oree ‘Sipe anhae abe


-_
__evr q¢* GPRD Rad oe aye
: : rs oo Fe( ge 3
avs aa’ A hansWel beg | care

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 -

2 | 4.e< > : ® = pen : 7 gs 4


WASES 4027 PET petukr 6° Relea “ERT earnaee
Twi Guy 3401799
- 6 ‘ ny ; _ ae 253.8) "entre

re ‘ Pie J SPASZVOR ORG 3909


—— = 2-@ Vihesyre 12g sna"

: eset
cers
ae oye ASA /

t 4 ve
Chapter 5

Child Windows and Control Elements

In Chapter 4, child windows were demonstrated with provisions to show


different mouse cursors in each. Another example created an array of squares
and responded to mouse clicks by showing crossed diagonals in the appropri-
ate box. Both examples could have been created with or without using child
windows.
One of the principal uses of child windows is "child window controls".
These are child windows that process mouse (and keyboard) messages and
notify the parent window when the child window’s state has changed. This
can be accomplished without the need for an elaborate ChildWndProc, as
demonstrated previously, if you use a number of predefined window classes.

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.

=» § BS_PUSHBUTTON—control button containing a given text label,


which sends a message to its parent window whenever clicked (by a
mouse or arrow key selection followed by pressing the Enter key).
Chapter 5: Child Windows and Control Elements 139

» BS_DEFPUSHBUTTON—same as BS_PUSHBUTTON but with a bold


border representing the default user response.
= =BS_PUSHBOX—displays a label only until selected (receives the input
focus), then displays an outline which is the same as
BS_PUSHBUTTON, but with the label highlighted. The outline
remains until another pushbutton or control is selected and the input
focus is lost.

Figure 5-1: Button Control Styles

Push Button Gro Check Box Group Radio Button Group


UL Check Box

a Auto Check Box © Radio Button

o Three State @® Auto Radio Button

Auto Three State

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

BS_RADIOBUTTON—displays a bold border when clicked. Set condi-


tion is not displayed automatically but must be set by the owner.
BS_AUTORADIOBUTTON—same as BS_RADIOBUTTON, except that
once a button is checked, a BM_CLICKED message notifies the applica-
tion and checkmarks are removed from any other radio buttons in the
group. The set or clear condition is displayed automatically.

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.

BS_CHECKBOX—displays a bold border when the button is checked.


State must be set by the owner and is not displayed automatically.
BS_AUTOCHECKBOX—same as BS_CHECKBOX, except that the
button state is automatically toggled when selected.
BS_3STATE—same as BS_CHECKBOxX, except that three states can be
toggled: clear, checked, or grayed. State must be set by the owner. The
grayed state is typically used to show that a check box has been
disabled.
BS_AUTO3STATE—same as BS_3STATE, except that the button
automatically toggles its own state when clicked.

Specials

BS_GROUPBOX—designates a rectangle that groups other buttons.


Any text or label is displayed in the rectangle’s upper-left corner. The
groupbox size and position must be specified appropriately to enclose
the desired buttons. The groupbox does not respond directly to any
mouse events; it does not return any WM_COMMAND messages.
BS_LEFTTEXT—causes text to appear on the left side of the radio
button or check-box button. Used with BS_CHECKBOX,
BS_RADIOBUTTON, or BS_3STATE, but not with BS_PUSHBUTTON
(not illustrated).
Chapter 5: Child Windows and Control Elements 141

m =BS_OWNERDRAW—designates an owner-draw button. The parent


window is notified when the button is clicked. Notification includes
requests to paint, invert, and disable the button. No default image or
label is displayed (not illustrated).

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.

Creating the Buttons


Individual buttons are created using the CreateWindow function in response
to the WM_CREATE message in WndProc. In this example, three groups of
buttons have been created to group the types using BM_GROUPBOX child
windows.
Each button, including the group boxes, are created by their own Create Window
function calls using the following parameters:
142 BORLAND C++ 3.0 PROGRAMMING, Second Edition

2 IpClassName—points to an ASCIIZ character string naming the


window class. In these examples, "button" is used, but this must be
either one of the predefined class names or a registered custom class.
An error in this field does not cause an error to appear. It prevents the
erroneous class object from being displayed.
= IpWindowText—points to an ASCIIZ character string and provides a
label.
= dwStyle—a double word value specifying a combination of window
styles and control styles. In the example program, these are
WS_CHILD!WS_VISIBLE | BS_buttonstyle.
= X—an integer value specifying the initial x-position of the button class.
The X and Y coordinates are relative to the parent window origin
(hWndParent) whether the parent is the application client window or a
groupbox child window.
= Y—an integer value specifying the initial y-position of the button class.
ms nWidth—an integer value specifying the width (in device units) of the
control button.
= nHeight—an integer value specifying the height (in device units) of the
control button.
a hWndParent—identifies the parent or owner of the window being
created.
a =hMenu—a menu or child window identifier (HMENU). In this example
it is a unique integer identifying each control button.
= hInstance—identifies the module instance associated with the child
window control. In this example, the lParam variable contains a poin-
ter to a structure of type CREATESTRUCT which contains a member
of hInstance. The LPCREATESTRUCT macro is used for type casting.
Alternatively, in WinMain, a global variable hInst could be assigned as
hInst = hInstance; and is subsequently used as the argument. Or
Get Window Word(...) could be used to retrieve an instance reference as
needed.
= /pParam—a pointer to any extra parameters that are passed as a LPSTR
pointer. In this example, the field is another CreateWindow function or
NULL to terminate. For an example, the following code creates a
groupbox and two child window control buttons using second and
Chapter 5: Child Windows and Control Elements 143

third Create Window functions as [pParam arguments before terminating


the chain with NULL:

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;

In this example, the groupbox window is created as a child of the applica-


tion client window (hwnd) before a loop is used to create four autoradio
buttons using the groupbox (hwndAPB[O]) as the parent.

forme Vat. dire Wee


if
WSDrinth,« SzBufti,, “AUtoRadtouhid’) “10 )) -
hwndAPBLild =
CreateWindow
Cee DIUnt tonne ae SIZB Untate eAIUILG Olontrya ere
Pee COG hithae DIV Sit elDi Caezaenle- ae Wak) thyme 6 HiemEginiGe,
MN VRBCOal Wed; Invmsen, NWILIL vy,
}

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.

Button Control Communications: to and from


The Button1.C program demonstrates how submessages are received from
child window button controls as the wParam and IParam variables that
accompany the WM_COMMAND event message. The wParam variable con-
tains the control button ID (child window ID) that was assigned as a parameter
in the CreateWindow function call. The [Param variable contains two values:
the child window handle in the low word value and the notification code in
the high word. The child window handle value is returned from Windows by
the CreateWindow function call. The notification code, HIWORD(IParam), is a
submessage code used by the child window to inform the parent window
Chapter 5: Child Windows and Control Elements 145

precisely what event has occurred. The values of these submessage codes are
shown in Table 5-1.

Table 5-1: Control Button Notification Codes


Code Constant Value Code Constant Value
BN_CLICKED 0 BN_UNHILITE 3
BN_PAINT 1 BN_DISABLE 4
BN_HILITE 2 BN_DOUBLECLICKED 5

In the example program Buttonl only two submessage codes will be


returned: 0 or 5. The second, BM_DOUBLECLICKED, can only be returned by
the Bs_RADIOBUTTON style controls, but not by BB_AUTORADIOBUTTON.
Control notification codes 1..4 are only returned by custom control buttons
styled as BS_USERBUTTON, which are not illustrated in the present example
program.

Sending Messages to Control Buttons


The Button2 example program demonstrates four groupboxes with sets of
child window button controls. Unlike Button1.C, in this example, the
groupboxes are actually parents to the controls they enclose. As your own
experimentation will show, setting an autoradio button in one group will not
affect the settings in the other. This example shows several other aspects of
control button programming that involve messages sent to the control buttons
by the application.
There are five button-specific messages defined in Windows.H, each
beginning with the prefix "BM_" for "button message".

Table 5-2: Button Control Messages


Code Constant Value Code Constant Value
BM_GETCHECK © WM_USER+0 BM_SETSTATE WM_USER+3
BM_SETCHECK WM_USER+1 BM_SETSTYLE WM_USER+4
BM_GETSTATE WM_USER+2

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.

Figure 5-2: Grouped Button Controls

PushButtons CheckBoxes RadioButtons 1

C] Check #1 @ Radio #1

Check #2 @ Radio #2

EJ Check #3 @® Radio #3 C] Radio #3

OC Check #4 © Radio #4 Radio #4 ©

For the radio buttons, however, the BM_SETCHECK message has


established an anomalous state of affairs. Three radiobuttons in the group are
checked, where as in normal operations—as you can discover first hand—
clicking on one button would turn off any other button in the group that was
set. Remember, normal behavior for radio buttons is to have only one in a
group turned on at a time. But, likewise, one button is a group and is always
on. This is when the BM_SETCHECK message would be used correctly. The
BM_SETCHECK message was sent as:

sendMessage< hwnd... 9 BM CSE CHECK, .s Olan)


Chapter 5: Child Windows and Control Elements 147

Or, to clear the checkstate of a button, another message could be sent as:

sendMessageG hwnd.44) °° BM SETCHECK, 0, OLS):

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:

SendMessage( hwndPBL3], BM_SETSTYLE,


BiSttiiy
Gems me Bisa DER RUS HBU TRON anil e=

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:

SendMessage( hwndARB2031,.BM_SETSTYLE, CheckStyle, OL );

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

Using BM_GETSTATE and BM_GETCHECK Messages


The BM_GETSTATE and BM_GETCHECK messages are sent as information
requests to specific button controls. They then return the state or check status
of the button; TRUE if the button is depressed or checked, or FALSE Ih itis
normal. For the pushbutton controls, which do not retain checked (on/off)
information, the BM_GETCHECK message is not relevant. The status of a
checkbox control could be toggled using get and set messages for checkboxes,
for example:

SendMessage( hwnd..., BM_SETCHECK,


(WORD) ! SendMessage( hwnd..., BM_GETCHECK,
as (OE Oy IIE. ONE

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 );

Button (Window) Labels


The text labels displayed within a pushbutton beside checkboxes and/or radio
buttons or as window captions can be changed at any time by calling the
Set WindowText function:

SetWindowText( hwnd, lLpszString );

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:

nLen = GetWindowText( hwnd, lpszBuff, nMaxLen );

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:

nLen = GetWindowTextLength( hwnd );


Pari

Introducing the Resource Workshop

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

And, for a dialog box:

101 DIALOG DISCARDABLE LOADONCALL PURE MOVEABLE


LO, 907 210, ©COMSAYVEE OWS: PORUP® |) Ws=CAP LON
BEGIN
CONTROLS 22 SE Din POWSsaiC
HT UDa Ly WwSavi S TBE ieWSerhABisa0 Ran)
OXS' OF 1:05 2 eal One
CONTROL Se STAD LCIE, WS CHIL Det WS VES PBEE S| WSeGROUP:,
ioe GO, 16
CONTROL ROK" 1) "BUTTON. WS CHILD | SWSeVISTBICEM]
WSaGABS TOPS [eOXA a S2y) 50 20327004
CONTROL “&Cancel" 2, "BUTTON", WS_CHILD | WS_.VISIBLE |
WS_TABSTOP, 99, 50, 32, 14
END

Using Borland C++, however, you are not required to learn and write
resource scripts.

Developing Resources—Not Source Code


The Resource Workshop distributed with Borland C++ 3.0 contains a variety
of resource tools for creating and/or editing keyboard accelerators, bitmaps,
cursors, icons, dialog boxes, menus, and string resources.
Part 2: Introducing the Resource Workshop 151

More important, while all of these resources could be created by other


utilities or by writing script files, independently constructed resources,
particularly script files, must be compiled and linked before they can be tested.
But, using the Resource Workshop editors, these user-interface resources can
be created and tested directly. In an interactive development process, these
resources eliminate the tediously slow edit-compile-link-test-edit cycle. And,
most important, interactive development changes the emphasis from writing
source code (scripts) to directly creating the application.
At the same time, because the Resource Workshop creates these resources
directly in binary format, compile-and-link time for applications using the
resources is also reduced.
Last, but far from least, the Resource Workshop is not limited to creating
new applications but is also used to access and edit resources in existing
applications. This feature is not practical, or often even possible, in the con-
ventional development cycle. While this last feature may sound interesting,
but somewhat frivolous, its practicality and usefulness will be discussed later.

Resource Workshop Versus Resource Toolkit


When Borland C++ version 2.0 first introduced its Windows compiler, the
distribution package also included the Whitewater Resource Toolkit—a utility
package that has since been replaced by the Borland Resource Workshop.
C++ version 3.0 has not, however, rendered the Whitewater Resource
Toolkit obsolete but has simply replaced it with a different resource editor
package with slightly different capabilities. And, if you already have the
Whitewater Resource Toolkit, you may find it as useful as the Resource
Workshop, although in a slightly different way.
And, in fact, the Whitewater Resource Toolkit includes a few features that
the Resource Workshop lacks—such as the ability to open two different
resource source files (.RES, .EXE, etc.) and to copy individual resources
between the two: sources—which is, occasionally, a very useful resource in
itself.
If needed, the Whitewater Resource Toolkit is available separately from the
Whitewater Group (708-328-3800 or fax 708-328-9386) and is described in
greater detail in Borland C++ 2.0 Programming.
For the moment, however, we will concentrate on the Resource Workshop
distributed with Borland C++ version 3.0.
152 BORLAND C++ 3.0 PROGRAMMING, Second Edition

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

Application Resources and Resource Files

Windows applications are composed of a variety of elements; textual menus;


combined graphic and text dialog displays; and purely graphic bitmaps, icons,
and cursors. Individual applications may contain large numbers of each of
these as multiple menus and dialog boxes, scores of bitmaps, and a dozen
cursor images that represent different operational modes.
Instead of being explicitly coded within the application code, resources are
elements that are loaded by the application as needed. This process reduces
the memory demands on the application and makes these resources editable
and reusable.
In general, resources describe visual characteristics of program elements,
including text strings, but do not define functional elements of the application.
For example, they do not describe how the application responds to controls
or menu selections.
Note: The actions/responses of visual elements such as button controls are
controlled by Windows, not by the application. Because these resources are
separate from the application’s execute code, the user-interactive elements
defined by the resources can be changed without access to the source code.
For example, the Solitaire program distributed with Windows 3.0 contains
75 bitmaps, including a full deck of 52 card faces with a choice of a dozen
backs, while the Resource Workshop provided with Borland C++ uses 2
hot-key accelerators, 2 cursors, 50 bitmaps, 38 dialogs, 5 icons, and 9 menus,
as well as a large number of string elements. In either application, defining all

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.

Keyboard Resources: Accelerators


An accelerator is a key or key combination used as an alternative to choosing
a menu item that invokes a command such as pressing Shift-Ins instead of
pulling down an Edit menu and selecting the Paste option. Using the editor,
accelerator keys are defined as substitutions for defined-menu options and
can be subsequently identified in the menu as alternative invocations.

Image Resources: Bitmaps, Cursors and Icons


The Resource Workshop supports three types of resource images: bitmaps
(.BMP), cursors (.CUR), and icons (.ICO). The simplest image type is the
bitmap image, which is a pixel image of any size using 2, 16, or 256 colors.
Individual bitmap images may be as large as in the full screen, as in the
CHESS.BMP desktop image; medium, as in the tilable desktop images or the
Solitaire card deck; or small, as used in the REVERSI game or in icons and
cursors. Unlike cursor and icon images, however, bitmap images consist only
of a foreground image, and overwrite any background display.
A cursor is also a bitmap image, but is a specialized type of image that is
confined to a specific 32x32 pixel size and is used to select graphic elements
or to locate an insertion position. Cursor resources contain two 32x32 pixel
images. One describes the cursor image and the second describes the image’s
interaction with the existing background. For example, cursors can be parti-
ally or largely transparent or may interact with background colors. Also, each
cursor image contains a single "hot spot" location, which is a pixel coordinate
within the cursor image defining the exact screen (pixel) location of the cursor.
Icons, the third type of bitmap, are used to select or activate an application.
Like cursor bitmaps, icon images can define a combination of foreground and
background colors, permitting the icon appearance to change as it is moved
across existing images. Also, like cursor bitmaps, icons are normally 32x32
pixels using a palette of 16 or 256 colors.
The bitmap editor is a sophisticated paint program providing a variety of
tools for creating and editing bitmap, cursor, and icon images. And, while
essentially the same bitmap editor is used for all three forms of image, the
Chapter 6: Application Resources and Resource Files 155

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.

Editable vs. Untouchable


Most of the resource items in an application, including all of the preceding
resource types, can be edited, viewed, or copied for use by other applications,
as well as renamed or deleted. There are, however, a few resources that can
be copied, renamed, or deleted, but cannot be edited or revised. These include
156 BORLAND C++ 3.0 PROGRAMMING, Second Edition

fonts used by the application to display text, raw data resources (.RC data
files), and user-defined resources (included in the executable file).

Files and File Types


The Resource Workshop can create or edit most resource files used by
Windows, including executable files that contain resources. Editable file types
are shown in Table 6-1.

Table 6-1: Resource Workshop File Types


File Type Description
EXE executable executable program code containing
application resources and compiled program code
RES resource compiled (binary) resource file
DLL executable executable (dynamic link library) module
containing application resources and compiled program code
H source code header file containing symbolic names for defined resources
ICO resource icon resource file
CUR resource cursor resource file
BMP resource bitmap resource file
DLG resource script dialog box resource script containing a single
dialog box resource definition in text format
RC resource script ASCII resource script containing a list of all of an
application’s defined resources
DRV Windows device drivers
FON font libraries
FNT typeface font

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.

Table 6-2: Uneditable Resource Types


File Type Description
DAT resource raw data resources
Chapter 6: Application Resources and Resource Files 157

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.

Dynamic Link Libraries


Dynamic link libraries (.DLL) are executable modules that may contain both
Windows application functions (compiled source code) and application
resources. A .DLL (or "dill") is similar in construction to a runtime library
except that it is not linked to the application during the compile process.
Instead, a .DLL is linked during execution whenever another module calls a
function contained in the .DLL.
In brief, .DLLs have two principal strengths. First, .DLL libraries can be
accessed by more than one application without being duplicated in each
application. Second, routines in .DLL libraries can be modified without
recompiling the programs, using the library. The .DLL résources, like .EXE
resources, can be modified without recompiling the dynamic link library or
the applications using the library.

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

A typical application may require several hundred such identifiers. The


previous example, Resource Workshop, has 2 cursors, 50 bitmaps, and 5 icons,
all with corresponding numerical identifiers. It has nine menus with multiple
menu options and a hoard of strings, which all have identifiers. And, there are
38 dialog windows with every dialog box and control element within the
dialog requiring identifiers. Some of them, such as the icon identifiers, are not
normally used by the application directly, but most, including the dialog box,
menu, and string elements, are used.
The actual number of identifiers required is immaterial. The point here is
that remembering these numbers is easy for the computer but quite impracti-
cal (as well as pointless) for the programmer. Instead, header files (.H) are
used to assign symbolic names to these identifying numbers, allowing you,
the programmer, to work with names instead of attempting to maintain a
confusing list of reference numbers. The compiler, of course, translates these
symbolic names to their corresponding number values (and, eventually, to
addresses or offsets).
Maintaining such a header file is not a difficult task because the Resource
Workshop, through the relevant editors, creates and maintains the symbolic
references required for the .H file as shown in the following example:
1D NB ESOL
RON. <-H a 1fe19 11.9.9 1nd B34 Gee),
#define Sol_Main_Menu Ox3E9
#define Sol_Main_Quit Ox3EB
#define Sol_Main_New Ox3EA
H#Hdefine Sol_Inst Ox3EC
#define Sol_Inst_Object Ox3ED
#define Sol_Inst_Moves Ox3ED
#define Sol_Inst_About xe Sl5 |

In the .H example above, the numerical constants (identifiers) appear in


hexidecimal format but can also be generated, if desired, in decimal format.
Naturally, to the computer, the difference is immaterial.

The Resource Manager


Figure 6-1 shows the Resource Workshop’s opening screen and primary menu
together with a new project-type selection dialog. The New project option is
selected to create a new resource file while Open project, shown in Figure 6-2,
permits selection of an existing resource project.
Chapter 6: Application Resources and Resource Files 159

Figure 6-1: The Resource Workshop

2.
a.

a.
4
2.
2.
2
ge.
e
eB
a
2.
4
2
2.
Z.
=.
2.
a.

Create a new project

The Resource Workshop is designed for mouse operation, activating


features by clicking the button or by pressing the key that corresponds to the
underscored letter in each button’s caption; in other words, pressing N to
create a new project or P to set preferences.
Following the handling patterns established for Windows applications, the
Tab key can also be used to cycle through buttons and/or fields, with the
current selection shown by a heavy border or other visual identifier. The selected
button or field can be activated by pressing the Enter or spacebar keys.

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

Figure 6-2: Opening A Project

=
Z2
a.
a
a «(RC resource script
a |DLG resource script
s CUR cursor image
HA

{ICO icon image


BMP device-independent bitmap
a FNT font
eae
= unpak.e4RES resource Object
gt
|
FON font Library
Z
DRY windows device driver
2

iC

Operation of the resource file selection dialog is not only self-explanatory


but also offers an excellent example of a well-designed file selection mechan-
ism—and one that could well be emulated by other applications.

Resource Workshop Preferences


The third option on the initial file menu is the Preferences option, shown in
Figure 6-3. Two of the preference options are preset as defaults: the Undo
levels at 10 and the Text editor as NOTEPAD.EXE.
The Undo levels determine the number of changes—in any of the editors—
which can be automatically undone. A higher level can set, but will increase,
memory requirements.
The default editor, NOTEPAD.EXE, is a Windows-supplied editor that can
be changed to any compatible editor desired. A minor caution: since the test
files to be edited will almost always need to be ‘’plain ASCII’ (or ‘plain
ANSI”), formatting editors such as Word Perfect or Microsoft Word should
not be used.
Chapter 6: Application Resources and Resource Files 161

The Include path option permits specification of a path to be searched for


files containing identifiers, such as C header (.H) files or Include files (.I). Any
Include path option selected is saved in WIN.INI as a default path and will be
active anytime the Resource Workshop is called.

Figure 6-3: Preferences Option

“ Make backups when | files

The Multi-save option allows changes to be saved in a second format—either


.EXE or .RES—in addition to the original file format. For example, an .EXE file
that has already been compiled and tested could be modified using the
Resource Workshop, but the changes could be saved to an .RES file as well.
Remember, however, that the original file format will always be saved.
And the final option—which is ON by default, saves a backup version
whenever changes are made to any resource file. The backup version has the
same name and extension as the original resource file, except that the first
character of the extension is changed to a tilde (~). that is, DEMO.RES will be
backed up as DEMO.~ES while DEMO.EXE’s backup will appear as
DEMO.~XE.
162 BORLAND C++ 3.0 PROGRAMMING, Second Edition

Creating A New Resource Element


Resource elements can be added to either an existing resource project or toa
newly created resource project quite simply: select Resource from the main
menu, then choose New. In return, the New resource dialog, shown in Figure
6-4, will appear with a list of standard resource types.
The New resource dialog offers several options but will, most commonly,
consist simply of selecting the desired type from the resource list presented.

Figure 6-4: New Resource Dialog

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

|Create a new resource

Once the type is selected—either by double-clicking on the resource type


or by selecting the OK button—a new resource element is added to the project
list (using a default type name such as BITMAP_1), and the appropriate
resource editor is called to create the new element.
Chapter 6: Application Resources and Resource Files 163

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.

Editing An Existing Resource Element


Existing resource elements can be edited in several fashions. The simplest
option is to double-click on the resource element in the resource list (far right
list box, Figure 6-4), which will call the appropriate resource editor, loading
the indicated resource element.
Alternately, the Edit or View options can be selected from the Resource menu.
In all of these cases, the Resource Workshop will load a default resource
editor appropriate to the resource type. As will be shown later, these are
interactive editors that permit the desired resource object to be created or
modified graphically. But, there is also an option in the Resource menu to edit
resource elements in text format; the Edit as text option. The actual results,
however, depend on the resource type. In text format, dialog and menu
resources, for example, appear as .RC resource scripts, while bitmaps and
icons are presented in hexidecimal format.
While dialog and menu resources can be edited as .RC scripts, hexidecimal text
format is not particularly practical for bitmaps and icons. Also, it is important to
note, bitmaps in text format may easily exceed available memory limits.

Save and Rename Options


The Save resource as option allows saving a resource element as an individual
element for later import into a different resource file. Three formats are
supported: .RC and .DLG resource scripts and .RES resource object format (as
a compiled resource).
164 BORLAND C++ 3.0 PROGRAMMING, Second Edition

Note: if you are heavily involved in swapping resource elements between


projects, the Whitewater Resource Toolkit supplied with BC++ version 2.0
provides a marked degree of convenience both in transporting resource ele-
ments and, at the same time, renaming these elements as desired.
In the Resource Workshop, however, the Rename option does provide a
quick and convenient means of tagging resource elements with mnemonic
labels in place of the default labels supplied when elements are generated
using the New option.

Resource Memory Options


The Resource / Memory options menu item summons the dialog box shown in
Figure 6-5 on the following page. The dialog box identifies the resource
element currently highlighted in the resource list and offers four options
determining how the individual resource element will be treated by Windows.
All four options are set on by default and, individually, are as follows:

= Load on call—The resource element is not loaded into memory when


the application is called but is loaded only when actually needed.
Note: Disk caching may supersede this setting, but this remains the
preferred setting for all resource elements and helps to minimize
memory usage.
= Moveable—the resource element can be relocated in memory as
necessary. That is, the resource element does not require a fixed mem-
ory address. This option should be changed only if absolutely necess-
ary.
# Discardable—The resource element, once loaded, can be discarded
from memory when/and if Windows determines that memory space
is required for other uses. Except in very unusual circumstances, all
resource elements should be discardable.
= Pure—This prevents the resource element from being modified in
memory (that is, during execution of the application).

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

Figure 6-5: Resource Memory Options

Edit... ae 4 o i .. - : :: “f : 2 a BITMAP
Edit as text... oes GINI_LOGO
View... e

“| Save resource as... |


| Rename...
Memory options...

¥ 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.

Resource Workshop Edit Options


The Edit options offered by the Resource Workshop menu, as shown in Figure
6-6, are fairly self-explanatory.

Figure 6-6: The Resource Workshop Edit Options

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.

Resource Workshop View Options


The View menu controls how the resource object list (in the right-hand
window) is displayed; the View options are shown in Figure 6-7. The By type
option is on by default, showing a resource list arranged by types with
individual resource names indented below the appropriate type identifiers.

Figure 6-7: The Resource Workshop View Options

Show unused types

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

Resource Workshop Window Options


While working with a resource project, it’s quite possible to have a small host
of subwindows opened within the Resource Workshop. When this happens,
the Window options menu, shown in Figure 6-8, provides options to Tile or
Cascade the several windows, or to switch between windows by selecting
from the list of open resources at the bottom of the menu.

Figure 6-8: The Resource Workshop Tile or Cascade Window

File Edit Resource View Badung


Tile

However, because each of these subwindows does require some maintain-


ance as well as a measure of additional memory, closing unneeded resource
elements may improve performance—particularly if memory is limited and
Windows is using a swap file.

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

Bitmaps, Cursors, Icons, and Fonts

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.

Editing Graphics Images


Figure 7-1 shows the Resource Workshop with a bitmap image (Ribbons.BMP)
loaded as actual size. While the bitmap shown is limited to 16 colors, for purposes
of illustration, both the 16 and 256 color palettes are shown at the upper and
lower left of the window. The editor toolbar appears (vertically) at the right.

169
170 BORLAND C++ 3.0 PROGRAMMING, Second Edition

Figure 7-1: Editing a Bitmap Image

a Eee =
ze

= File Edit Resource View Text Options Bitmap Window Help

320 X 240 Colors: 16

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

However, referring to the two color selections as ‘foreground’ and


“background” is also a slight misnomer. In actual fact, the ‘‘foreground” color
is simply the color used when the left button is pressed for any drawing
operation (except the eraser), while the “background” color is used when the
right button is pressed. The eraser tool, of course, simply reverses this order.
While Windows 3.0 is nominally limited to supporting displays using
monochrome—CGA, EGA, and 16-color VGA modes—contemporary SVGA
video cards support 256 colors at resolutions up to 1024x760 (some higher).
And, while Windows does not directly support these modes, most SVGA
manufacturers supply Windows-compatible video drivers that support one or
more higher resolutions, including a 256-color palette.
Therefore, if an appropriate SVGA video card is present and the correct
driver installed (see manufacturer’s instructions), the second 256-color palette
(Figure 7-1, lower left) will also be available, assuming this palette resolution
has been selected for either a bitmap or icon.
As shown in Figure 7-1, for the 256 color palette, the letters FG and BG are too
large to be fully displayed over two selected color bars and only a partial F or B
(truncated at both top and bottom) is visible. Some assistance for 256-color
palettes is supplied in that the foreground color is shown in the block at the
bottom of the toolbar, although, unfortunately, the background color is not
shown. The background behind the line, brush, and airbrush samples at the
bottom of the toolbar remains white regardless of the background color selection.
Alternatively, the pattern sample, if not solid, does show the background color.

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

Figure 7-2: Customizing Palette Colors

|
tii

== File Edit Resour View Text Options ini Window


Hide palette
Hide toolbox
Size and attributes...

Edit-toreground color...
Edit background color...

However, the color customization option is important for SVGA palettes,


where a total of 16,777,216 hues are theoretically possible, even though any
specific palette is limited to 256 individual hues. Still, each palette entry can
be adjusted to any of the 16 million possible color hues.
If a palette has been customized, the custom palette is stored with the
resource object (bitmap or icon) and will be in effect when that object is active.
Alternately, if an image is created using a 256-color palette but is subsequently
displayed on a 16-color system, the detailed color information will not be
visible. Instead, the individual palette colors will be mapped to the standard
EGA/VGA palette colors.
Note: In the default SVGA palette, the first 16 entries correspond to the
standard 16-color EGA/VGA palette hues.

Testing SVGA and VGA Colors


If you are developing applications for both 16-and 256-color displays, you
may want to provide a convenient means for changing your Windows system
configuration between SVGA and conventional VGA resolutions. The
Chapter 7: Bitmaps, Cursors, Icons,and Fonts 173

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

But, for SVGA operation, the display specification is replaced, as shown,


following (WIN_INI.256):

[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:

display.drv=TRIDENT TVGA 800x600 256-color


display.drv=TRIDENT TVGA 1024x768 256-color

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

The Graphics Editor Tools


In Figure 7-1, the graphics editor toolbar appears at the far right with a selection
of 16 tools supported by all three bitmap image formats (bitmaps, icons, and
cursors). Each of these tools can be selected by clicking on the tool icon with the
mouse. In most cases, the mouse cursor is changed to reflect the selected task.

Left Versus Right Mouse Buttons


While roughly 86 percent of the population is right-handed, this still leaves
14 percent of you who are left-handed. Happily, Windows has made pro-
visions for southpaws in the Control Panel/Mouse utility for swapping the
operations of the left and right mouse buttons.
While this provision is both laudable and convenient, it would also be
rather tedious for me, while describing the operations of the mouse buttons
in the drawings, to repeatedly remind readers that the left and right buttons
may be swapped and that the instructions should be reversed accordingly. So,
for the one-seventh of you who are left-handed, please invert the instructions
in this chapter to match the configuration of your mouse instruments as
necessary.
With this disclaimer completed, the various tools and their uses are as
follows:

The Arrow tool is used to select a rectangular area within an image.


Selection is made by positioning the arrow cursor at one corner of
the area desired, pressing the left mouse button, and dragging the
cursor until the selection box includes the appropriate region. After
releasing the mouse button, the commands in the Edit menu (see Figure 6-6)
can be used to Cut, Copy, Delete, or Duplicate the selected area.
= The Cut and Copy commands place the selected image area in the
Windows Clipboard, where it can be retrieved for use in another
bitmap within the present project or in an entirely different project.
(Remember, the clipboard contents are lost if Windows is exited.)
= The Delete command (duplicated by the Del key) deletes the selected
area.
# The Duplicate command positions a duplicate of the selected area in
the upper-left corner of the image. This image can be repositioned as
desired using the mouse or cursor keys. Alternately, you can duplicate
Chapter 7: Bitmaps, Cursors, Icons,and Fonts 175

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.

Figure 7-3: View And Zoom Options

Z LEZ 7 \y
2

= File Resource Butem Text Options Bitmap Window Help


Edit Zoomin Ctrl+Z
Zoom out Ctri+0
Actual size Ctrl+A

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 Paintbrush tool is used to draw freeform areas within an image.


The Paintbrush can vary both in form and size (select Options/ Brush
shape) and pattern (click on the pattern sample at the base of the
toolbar or select Options/Pattern). If a pattern other than solid is
selected, the current background color will be used together with the present
foreground color during drawing. If the right mouse button is pressed, the
foreground and background colors will be reversed (see Figure 7-4).

. 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

= File Edit Resource

Pattern...
Brush shape...
Airbrush shape...
Pen Style...

Draw on both images

' Grid on zoomed windows

¥ Save with default device colors

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

Figure 7-5: Text and Font Options

source Worksho LTMAP 7 BITMAP 1


File Edit Resource View [Roam Options Bitmap Window Help
V
Align left Ee = zs =
Align center
Align right
Face name

| Modem
Roman
Script
Symbol
System

ABCDEFGHWKLMNOPQRSTUVWXYZ
abcdefghiklmnoparstuywxy2

| The Rectangles tools (Hollow or Filled) are used to draw rectangles


by pressing the left mouse button to anchor one corner, then dragg-
ing the rectangle to the desired size. Solid rectangles are filled using
the current drawing color. Hollow rectangles may be filled with a
contrasting color by using the Bucket tool.

The Rounded Rectangles tools (Hollow or Filled) are used to draw


rectangles with rounded corners by pressing the left mouse button
to anchor one corner, then dragging the rectangle to the desired size.
Solid shapes are filled using the current drawing color. Hollow shapes
may be filled with a contrasting color by using the Bucket tool.

The Circles tools (Hollow or Filled) are used to draw circles by


pressing the left mouse button to anchor one corner of an invisible
rectangle surrounding the circle, then dragging the surrounding
rectangle to create a circle of the desired size. Solid circles are filled
using the current drawing color. Hollow circles may be filled with
a contrasting color by using the Bucket tool.
Chapter 7: Bitmaps, Cursors, Icons, and Fonts 179

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

Figure 7-6: Editing a Cursor

File Edit Cursor


LESeQeS SS Hide palette
Hide toolbox

Set hot spot... ‘OR_1


3 Test
4 Set transparent color...

Transparent

Horizontal [x)-
Vertical [p}:

Set hot spat

However, in other cases, such as with a simple vertical bar using an


“inverted’”’ border or even drawing the entire cursor using “inverted” color
may be the only method of ensuring visibility.
Chapter 7: Bitmaps, Cursors, Icons, and Fonts 181

The Cursor Hotspot


Every cursor requires a hotspot—a single pixel within the cursor image (or
even outside the actual cursor). Table 7-1 shows the Icon and Cursor
Resolutions. In the Resource Workshop, the hotspot coordinates are assigned
numerically, beginning by calling the Cursor/Set hotspot... menu option. And,
in response, the Set hotspot dialog box appears, permitting two numerical
entries specifying the hotspot coordinates relative to the upper-left corner of
the cursor image. (Note: The upper left pixel is located at the 0,0 coordinate,
not 1,1.) Unfortunately, requiring a numerical entry rather than simply point-
ing to the appropriate hotspot location is a shortcoming in the Resource
Workshop, while other utilities, the Whitewater Resource Toolkit being one
example, provide a more reasonable and instinctive handling for this task.
Some of the annoyance can be avoided, however, by positioning one of the
graphics cursors (such as the Pencil tool) over the desired grid location and
noting the coordinates displayed at the bottom of the window. In Figure 7-6,
for example, the coordinates are listed as Pen x: 8 y: 9.

Table 7-1: Icon and Cursor Resolutions

Device Ico Icon Icon Cursor Cursor


Name Colors Width Height Width Height
SVGA 256 32 32 32 Moe.
4-plane 16 32 32 oz a2
3-plane 8 32 32 82 32
Monochrome y 32 Be 32 32
CGA 2 ep 16 32 32
Also, negative coordinates or positive coordinates lying outside of the actual
mouse cursor image are accepted as valid by DOS graphics mouse drivers. The
Resource Workshop and other Windows utilities, however, have generally
avoided the potential conflicts inherent in such offset hotspots by the simple
expedient of refusing coordinates that do not fall within the valid (0..31) range.

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.

Figure 7-7: Two Icons Used By FileView.C

See8ae
z Bae2ee
eee
ee

Two Icons for FileView.C


The first two resource items for FileView.C, appearing in Chapter 13, will be a
pair of icons as shown in Figure 7-7. The primary application icon is shown on
the right while a second icon, which will be used by one of the dialog boxes,
Chapter 7: Bitmaps, Cursors, Icons, and Fonts 183

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.

Figure 7-8: Font Editor

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

The second major difference is that a font resource always consists of a


group of images rather thana single resource element. The Font Size informa-
tion dialog box, shown in Figure 7-9, is used to set two important sets of
characteristics: first, the character range covered by the font and, second, the
character size.

Figure 7-9: Font Size Information Dialog Box

Size Character
|First
_ Height Last
Average width | Default
Maxuginm wiciih Break

Stretch current characters

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

In addition to the physical characteristics of a font (the size and ANSI


character assignments), the Font header information dialog box, shown in
Figure 7-10, provides other essential (and optional) information required by
custom fonts. The font name is simply an optional reference to identify a
specific font. The device field is used to specify a physical device, if desired,
and informs an application that the font in question is for use only with the
named device. Last, the copyright information is simply a copyright statement
inserted in the font header.

Figure 7-10: Font Header Information Dialog Box

_ Copyright (c) Copyright Reality Engineers 1991 -- All rights reserv

Font version — L Attributes Sizes


IN
NN
UMMM
|+
MMIII 2.00 Italic _ Horz res

«3.00 _ Underline _ Mert res


ADER
WARN
REY
| - Strikeout | Points
foe eee Int. leading [ao |
| «RASTER
AMMAN
“Weight Ext leading [0 |
| Family [os] ~— Ascent ore)
Char set or |

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

characters, strikeout characters, or variable pitch characters), font weight (400


normal or 700 boldface), and family and character set (0 = ANSI, 2 = Windows,
255 = OEM or machine-specific).
The Size fields describe font spacing and device (display or printer pixel
spacings).
Fonts and font operations are described further in Chapters 15 and 20.

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

The Dialog Box Editor

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

The Dialog Box Editor


The Resource Workshop Dialog Box editor provides two resource palettes, the
Tools palette and the Alignment palette. Both are shown in Figure 8-1 and
described in detail.
The Tools palette consists of 28 icons representing dialog element types or
features. The Alignment palette consists of eight icons offering provisions to
adjust the alignment and arrangement of controls within the dialog box. By
default, the two palettes are located at the top right of the Dialog Box editor,
immediately below the menu bar. The Tools palette is to the right and the
Alignment palette to the left.
Note that both palettes are, themselves, dialog boxes consisting of arrays of
icons and, because the dialog box being edited can be overlaid by the Tool and
Alignment boxes, these can be dragged to different positions or, using the
Options menu, can be hidden entirely if desired.
The Dialog Box editor shown on the following page in Figure 8-1 opens with
a blank dialog box, which can be resized by dragging on the outline (which
also shows that the dialog box is the currently active edit resource) or moved
as desired. Other features, including the style of the dialog box and the
caption, can be revised by calling the menu option Control/Style. The default
Captioned dialog box (upper dialog box, Figtire 8-1) can be changed to an
Uncaptioned dialog box (lower dialog box, Figure 8-1) by changing the frame
style (Figure 8-3).

The Tools Menu


All of the individual tools appearing on the Tools menu will be described in
greater detail presently. For the moment, however, a brief introduction is in
order.
The Tools menu consists of three parts beginning, in the left column, with
tools for alignment, ordering, grouping, and testing dialog boxes and controls.
The two center columns consist of standard dialog-box control types,
duplicating the control types listed on the Control menu (following).
And, last, the right column provides access to seven new (or custom)
dialog-box control types, which are provided by the Borland compiler but are
not standard in Windows.
Chapter 8: The Dialog Box Editor 189

Figure 8-1: Captioned and Uncaptioned Dialog Boxes

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

Dialog Box Styles


The first element in any dialog box is simply the dialog box itself—that is, the
style and general appearance of the dialog box. In Figure 8-1, two dialog boxes
appear within the dialog box editor: the first with a standard captioned frame
and system menu, and the second with an uncaptioned frame and no visible
system menu.

Figure 8-2: Dialog Control Options

File Edit Resource Align Options Window Help

Push button
Radio button

Horizontal scroll bar


Vertical scroll bar

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

Figure 8-3: The Style Options Dialog Box

Edit — ‘Resource Control Align Options Ww


Style...

Caption [DIALOG_1 Dislog style


» i, Vv, «
a
| | Thick frame
_ Vertical scroll

ae Se Horizontal scroll
a Popup | Dialog frame Minimize box
Child Border _ Maximize box

Overlapped + Caption | Absolute align


- Iconic popup |. No Border _ System modal
Local edit
Font Modal frame
No idle messages
_Chp children
Clip siblings
Visible

Ge
Modify control style "Modify, Absolute Grid «: 18 y:18 cx 142 oy: 92

Diaiog (Window) Types


Four dialog window types are supported, but only one type can be chosen for
any window. The dialog types are defined as:

= Popup—Most dialog boxes are popup window types, appearing only


when called by an application in response to a menu option or other
operation (default).
=m Child—The dialog box is defined as a child of another window.
192 BORLAND C++ 3.0 PROGRAMMING, Second Edition

= Overlapped—This is a popup window that can be overlapped by other


dialog windows. Normally, only the main window in an application
will be defined as Overlapped.
= Iconic popup—This is a popup window that is displayed as an icon until
selected.

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:

= Dialog Frame—The dialog box displays a double border without a


caption bar.
= Border—The dialog box displays a thin, single border without a caption
bar.
=" Caption—(default) The dialog box displays a caption bar and a thin,
single border.
= No Border—The dialog box displays neither border nor title bar.

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.

System menu—includes a System menu box at the left extreme of the


title bar. The System menu will not be shown if the dialog box is not
Captioned. Alternately, if the dialog box is defined as a child window,
selecting System menu creates a Close box rather than a Control menu.
Thick frame—creates a thick frame surrounding the dialog box.
Vertical scroll—adds a vertical scroll bar at the right of the dialog box.
Horizontal scroll—adds a horizontal scroll bar at the bottom of the
dialog box.
Maximize box—adds a maximize button at the right of the caption bar.
This option is active only if the dialog box is captioned.
Minimize box—adds a minimize button at the right of the caption bar.
This option is active only if the dialog box is captioned.
Chapter 8: The Dialog Box Editor 193

= Absolute align—positions the dialog box relative to the screen rather


than relative to the parent window.
= = System modal—makes the dialog box system modal—that is, the user
cannot switch to any other application until the present dialog box is
cleared. This option is used particularly for warning dialogs, queries,
and any other urgent or immediate message boxes.
= Local edit—edit text controls used by this dialog box are allocated from
the application’s local heap. This option is always used if the applica-
tion will be using EM_SETHANDLE or EM_GETHANDLE messages.
= Modal frame—frames the dialog box with a dialog frame and caption
(default style). This also permits users to reposition the dialog box
window.
= No idle messages—suppresses WM_ENTERIDLE messages to the
application’s main window. This is valid only if the dialog box is
modal.
= Clip children—used to protect the client window areas belonging to any
child windows, restricting drawing to the parent window area.
®# Clip siblings—valid only if the current dialog box is defined as a child
window. This option restricts drawing to the client window, protect-
ing any siblings of the window.
= Visible—Creates a window that is initially visible (overlapping and
popup windows).

Dialog Box Fonts


Dialog boxes use the system font by default but, through the Fonts option,
may be changed to other typefaces, sizes or styles, as shown in Figure 8-4 on
the following page.

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

Figure 8-4: Font Selection

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.

Dialog Box Control Elements


Dialog box control elements are defined by the programmer to provide
information displays and to define controls permitting user interactions. The
Dialog Box Tool palette provides three types of control elements: pushbuttons,
checkboxes, and radio buttons.
Chapter 8: The Dialog Box Editor 195

Buttons (aka: pushbuttons) are the simplest form of dialog box


controls and are pressed or clicked with the mouse to select an
action. Nominally, pushbuttons contain a text label (caption) iden-
tifying the purpose or selection.
Pushbutton controls execute an immediate response when activated
(clicked), but do not maintain on/off status information. Either standard,
default, or owner-draw pushbuttons can be selected by double-clicking on the
toolbar icon.
A standard pushbutton must be selected before it can be activated, either
by clicking with the mouse or by Tabbing until the button is highlighted. It is
then activated by pressing the Enter or Spacebar key.
The default pushbutton appears with a heavy border that indicates it is the
default selection and will be activated immediately by pressing the Enter or
Spacebar key. Only one default pushbutton should be defined for a dialog box.
Owner-draw pushbuttons require support for button appearance and oper-
ation within the application, instead of being handled by Windows. With
owner-draw buttons, the parent window is notified when a button is selected
and receives requests to paint, invert, or disable the button. This requires
support within the application to handle these functions. For more informa-
tion on owner-draw buttons, refer to Microsoft’s Software Development Kit.
The second, custom button type shown above is supplied by the Resource
Workshop.
Radio buttons and auto-radio buttons are used to select one or
more choices from a list of options. Visually, radio buttons are
toggled onselection (showing the presence or absence ofa dotinside
the button). Nominally, labels adjacent to each button identify the
optionorselection.
Radio and auto-radio buttons differ only in their handling, with
auto-radio buttons automatically resetting their state and the state of other
buttons in the same group when a selection is made.
Radio buttons are always defined as logical groups, as demonstrated in
Chapter 6. Only one radio button in a group can be selected at any time.
Customarily, a default radio button is set within the application, again as
shown in Chapter 6.
196 BORLAND C++ 3.0 PROGRAMMING, Second Edition

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.

Checkboxes, like radio buttons, are identified by text labels to the


right or left of the button images. Unlike radio buttons, however,
checkbox lists permit selection of none, one, or multiple items, and
each checkbox resets its own image by displaying an X or a checkm-
ark when selected. A second click on a checkbox cancels the selec-
tion, resetting the display.
Checkboxes provide multiple and independent selections, allowing each to
be selected (checked) or turned off (cleared) without affecting other
checkboxes. For example, a checkbox to enable/disable sound effect prompts
would be completely independent of a second checkbox that selects a timed
save operation, and a third that enables/disables backup files. Checkboxes
can be enclosed to appear as visual groups, with or without group captions,
using the Group Box tools.

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

Figure 8-5: Edit Field Option Selection

+ 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.

Static Text is normally used to display information, offer error pro-


mpts, ask questions, explain the consequences of proposed actions
or, frequently, provide labels for edit fields. Three format options
are supplied: left-justified, right-justified, and centered text.
A default label—"Text"—is supplied automatically. A new label is entered
via the Control/Style option from the menu bar, which calls the dialog
selection shown in Figure 8-6 ont the following page.
Caution: Static text fields are limited to 255 characters, and multiple static
text fields may conceal all or part of each other if their positions overlap.
198 BORLAND C++ 3.0 PROGRAMMING, Second Edition

Figure 8-6: Static Text Display Options

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.

Other Dialog Controls


The remaining control features are used to customize the appearance of a
dialog box, to create range sliders, to add icons or bitmaps, or simply to
visually group any variety of more conventional controls.

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.

Two Frame controls also provide a means of grouping or visually


associating other features. But, unlike the Group Box controls, they
do not impose any functional groupings or display captions. While
one control is shown as white and the other black, either can assume
three states: white, gray, or black.

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 final dialog control element is the Custom dialog control.


Unlike other control elements, however, the custom control ele-
ment depends entirely on the application to handle the appropri-
ate screen display issue and respond to messages, and handle any
other interactions.
For more details on custom controls and the programming considerations
required, refer to the Microsoft Software Development Kit.

The Controls Menu


The Control tools (along the left side of the Tools menu) offer a variety of
options specific to individual control elements within the dialog box, includ-
ing attributes, tabstops, and logical groups. To use the Controls menu, begin
by selecting the desired control element by clicking with the mouse.

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.

The Duplicate tool is used to create multiple identical copies of an


existing control element. Selecting Duplicate calls the Duplicate
Control dialog shown in Figure 8-7 on the following page.
In the Duplicate Control dialog box, four options are presented to
specify the number of rows and columns and the spacing between control
elements for each.
For example, to create a column of five buttons, you would begin by
creating one button in the dialog box, then selecting Duplicate and specifying
five rows in one.column with a row spacing of 2.

All control elements added to a dialog box are, by default, defined


as tabstops. Pressing the tab key will step through the individual
controls so that subsequent controls could, if desired, be activated by
pressing the Enter key or the Spacebar. But, you can change which
controls are defined as tabstops by using the Tab Stop tool, thus controlling
which units can be selected in this fashion.
202 BORLAND C++ 3.0 PROGRAMMING, Second Edition

Figure 8-7: Duplicate Control Dialog

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.

Grouping is used to define functional relations for sets of controls


as, for example, in a group of radio buttons. Using grouping, only
the first (or default) button in the group would be defined as a
tabstop. With this done, the tab key would take the user to this
element of the group, but subsequent movement within the group
would be accomplished using only the arrow keys. (Mouse operations are not
affected by this grouping.)
Note: Grouping does not physically relocate controls. If relocation is necess-
ary, it must be done manually by dragging and aligning the control elements
as appropriate.
Before creating a group, you might begin by selecting the Tab Set tool and
setting tabstops for only one control element in each control group. (that is,
clear all excess tabstops).
Chapter 8: The Dialog Box Editor 203

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.

Figure 8-8: Tab Stops and 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, Positioning, and Sizing Control Elements


While controls can be positioned and sized by using the mouse or by entering
position and size values directly in the Align/Size dialog box, three separate
options are offered for sizing and aligning both individual controls and groups
of controls. These are the alignment toolbar (see Figure 8-1 and following), the
alignment dialog box (Figure 8-9), and the size dialog box (Figure 8-10).
Of these three, the alignment tool bar is readily at hand. It consists of the
eight alignment options described next. All of the following tools align groups
of controls or align individual controls using a sizing frame.
Before describing the actions of the several tools, however, an explanation
of a sizing frame is in order. As an example, assume that you have created
several button controls within a dialog box and that these buttons are intended
to be aligned in a single column. To align these buttons, you begin by clicking
(selecting) one of the buttons, then, while holding down the shift key (either
right or left shift), you click on the remaining buttons belonging to the group.
As the first button is selected, a double outline will appear around it. And,
as each subsequent button is added to the group, the rectangular outline will
extend to enclose the entire group. This outline is called a sizing frame and
can be resized (dragged using the mouse) or repositioned as desired. Most
Chapter 8: The Dialog Box Editor 205

important, the sizing frame is used as a reference by the alignment tools


described below and the size and alignment dialog boxes.
Note: Alignment and sizing selections are independent of any tabstops or
control element functional groupings.

Figure 8-9: Align controls

Horizontal alignment =—=———s‘Veettical alignment


* Nochange = Ctrl + No change
_lelisides : |Tops
. Centers H : : oe |
~ Right awdee AR. _ Bottoms

- Space equally Strch = Space equally


— Center in dialog ‘D a Center in dialog

Figure 8-10: Size Controls

Horizontal size == —=s—s«Cettical size

Shrink to smallest. = Shrink to smallest


_» Growtolargest =——>_— Grow to largest
| Width of size box -—=—Height of size box
| Widthofdialog =——Ss Height of dialog
- Entervalues _. Enter values a
206 BORLAND C++ 3.0 PROGRAMMING, Second Edition

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.

Following alignment, the sizing frame is shrunk to fit the

rad repositioned controls. The horizontal and vertical double arrows


(left) center controls within the sizing frame. Again, in both cases,
the sizing frame is shrunk to fit the repositioned controls.
The bracketed double arrows (right) move both the enclosed controls and
the sizing frame, centering the sizing frame relative to the dialog box, but
leaving the position of the enclosed controls unchanged (relative to the sizing
frame). In this case, the sizing frame is not shrunk.
Caution: Applying any of the vertical alignment tools to a column of
controls, or the horizontal alignment tools to a row of controls, will result in
the control elements being stacked. The elements will attempt to occupy the
same position within the dialog box, with only one completely visible. The
remaining controls will be wholey or partially obscured. Order can be
restored, however, by individually selecting and separating the several
controls.

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

When horizontal spacing is selected, the left-most control will be left-


aligned with the sizing box, the right-most control will be right-aligned, and
any remaining controls will be positioned equally between these two limits.
Vertical spacing is accomplished in the same way.

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.

Dialog Boxes with Menus


Dialog box menus are not displayed in the Dialog Box editor but must be
created, tested, and revised using the Menu editor. At the same time, the client
area in the Dialog box appears larger vertically than it actually is because
space is reserved at the top of the client window for the menu bar, which is
not displayed.

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

A second alternative, after interactively creating a dialog box or other


resource, is to save the resource as an .RS script, then edit the resource script
and create the .H header file at that time.
Editing and creating header files will be discussed further in Chapter 12.

Three Dialogs for the FileView Application


The FileView program requires three dialog boxes: About, File Type, and
Open File. These three dialog boxes are illustrated and explained below, but
need to be created as dialog box resources in FileView.RES for later use by the
FileView.C application (the subject of Chapter 13).

The About Dialog


The About dialog shown in Figure 8-11, consists of a captioned dialog box
without a system menu but with a title bar reading About FileView. Three
dialog elements are shown inside the dialog box: a centered text line, an icon
box, and a single button.

Figure 8-11: A Simple Dialog Box with an Icon

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

The File Type Dialog


The File Type dialog box uses an uncaptioned dialog box and displays the
second icon, FILETYPE, to the left of the title text. The icon box is created in
the same way as that in the About dialog except that the icon name is entered
in the Item Attributes dialog. The File Type dialog box appears in Figure 8-12
on the left, with a duplicate box on the right showing the ID constants for each
control element.

Figure 8-12: The Select File Type Dialog Box

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

Instead, groupings are established by setting the group attributes or by


clicking on the controls to be grouped (while holding the Shift key down), and
then selecting Control/Group Together.
The box on the right in Figure 8-12 shows the ID constants defined in the
VIEWFILE.H header file. The ID values assigned to the auto-radio buttons will
be defined in the Fileview.H header file with values from 201 through 214. The
values for IDOK and IDCANCEL are predefined in Windows.H.
Note: Header files can be created using an ASCII editor, but may produce
compiler problems if the file is terminated with an EOF (0x1A) character. If
problems arise, simply load the .H file using the BC editor and then save it
again. This will remove the offending EOF tag.

The File Selection Dialog


The File Selection dialog is the last of the three dialogs for the FileView example.
It is also the most complicated and most important of the three because it is the
dialog where file names are entered or selected from the list box for display in
the main application window (which is not created as a dialog box).
The File Selection dialog shown on the following page in Figure 8-13
consists of two edit boxes (with text labels), one list box (unlabeled), three
buttons, and one icon box (displaying the FileView icon). The ID constant
values for each of these control elements are shown at the right. They are the
values (or constants) that will be used by the application to enter and retrieve
information from these dialog elements.
The first edit field, labeled File Spec: and identified as IDD_FNAME, is
shown here with a default text display. This default text, however, will
immediately be overwritten by the application when the dialog box is created
and, therefore, is actually irrelevant.
The second edit field, labeled Drv/Path: and identified as IDD_FPATH, does
not show a default text display and will also be filled in by the application
with the default drive/path when the dialog box is created. The list box,
identified as IDD_FLIST, will be filled by the application with a list of file
names that match the file specification shown in IDD_FNAME in the directory
shown in IDD_FPATH. If the list is longer than the list box, a vertical scrollbar
is generated and maintained automatically.
Chapter 8: The Dialog Box Editor 211

Figure 8-13: The File Selection Dialog Box

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.

ID Constants and Values


The values and ID constants required for the three dialog boxes are shown in
Table 8-1.

Table 8-1: Dialog Constants/Values


Constant Values Constant Value
IDD_FNAME 16 IDD_DLG 2061
FPATH iW, DD) IDLE 207
IDD_FLIST 18 IDD_ EXE 208
IDM_TYPE 104 OID) Jal 209
IDD_BMP 201 LDDIPAL 210
212 BORLAND C++ 3.0 PROGRAMMING, Second Edition

Table 8-1: Dialog Constants/Values (cont.)


Constant Values Constant Value
IDD_COM 202 IDD_RC 211
IDD_CUR 203 IDD_RES 212
IDD_DAT 204 ID DERG 213
IDD_DBS 205 IDD_ANY 214

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

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.

Using the Menu Editor


The Menu Editor consists of four parts (see Figure 9-1 on the following page):
the editor’s menu bar (top), the sample or working menu bar (immediately
below the editor’s menu bar), the options window (left), and the menu script
(right).
Initially, when you are creating a new menu, a dummy menu appears,
consisting of two items: a “Popup” main menu item and one menu item,
‘“Item’’. These default example items, of course, can be removed or revised as
desired.
Figure 9-1 shows a sample menu that begins with the popup menu item,
‘Edit’, which is a menu item that calls a pull-down (or popup) menu of further
options. The pull-down menu called by ‘Edit’ consists of five items: ‘Cut,’

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.’

Figure 9-1: The Menu Editor

File Edit Resource Menu Yiew Window Help

Edit Print File

~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__

_-* Menu item © Menu bar break MENUITEM "'Célear clipboard\t"L"

Separator Menu break —End Popup__


MENUITEM 9"'&Print’
7 Help break MENUITEM “BFile"
i — End Menu__
Initial state
+ Enabled
Disabled _. Checked
Grayed

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

Figure 9-2: Menu Options

Resource View Window Help


New pop-up Ctrl+P
New menuitem Ins
New separator Ctrl+S
New file pop-up
New edit pop-up
New help pop-up
Check duplicates

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.

Figure 9-3: Three Stock Menu Selections

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

The final menu option—‘Check duplicates’—simply runs a check on the


present menu to look for duplicate items.

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.

Menu Size Limits


In theoretical terms, the only limits on the size of a menu are those set by your
own preferences. Many levels of pull-down menus are both practical and
possible, as are long primary menu lists. As an example, Figure 9-4 shows a
primary menu bax with a total of 26 elements stretching across three lines.

Figure 9-4: A Very Long Primary Menu

| 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.

Customizing Menu Items


Individual menu entries can be as simple as a single word, a brief phrase, or
a combination of either of these with a hot-key assignment (that is, a control-
key assignment).
Enter the menu text that you want to appear on the actual menu in the Item
Text field—exactly as it should appear on the menu. Entries may consist of
any alphanumeric characters, including the extended ANSI (international)
characters. However, characters in the range 00h..1Fh should not be used.
By established convention, most menu items include one underlined, letter
in their text labels, which indicates a hot-character for selection. And, the
ampersand character (&) is a special-purpose character used to indicate and
underscore the activation character for each menu entry.
An Item Text entry specified as ‘Select &File’’ appears in the menu as
“Select File,” and "F" becomes the activation key for the entry. By default,
Windows uses the first letter in each item as the activation key but does not
identify the activation key unless the ampersand is used to specify the key
character. Of course, if a character other than the first is identified by the
ampersand, this character becomes the default activation key.
Also, if more than one ampersand occurs in a menu definition, only the last
ampersand is recognized.
The tab character is supplied by the character pair "\t" (a combination
which should be familiar to C programmers) and is commonly used to identify
accelerator keys for menu options. The right tab character aligns the items
following the tab. However, tabbed entries should not be included in primary
menu items, only in the pull-down submenus. Also, accelerator keys
designated in menu entries must still be defined using the Accelerator editor
(see Chapter 10).
In addition to the tab character, a right-justify character is also available
and is entered as ‘\a’. This causes all following text to be justified full right.
For example, the accelerator key definition commonly used with an Edit/Cut
option is the shifted delete key, which would be entered in the menu element
as ““&Cut\Shift+Del.”
218 BORLAND C++ 3.0 PROGRAMMING, Second Edition

Caution: While entries up to 255 characters are permitted by the editor, it


does nothing for the menu’s capacity to display such strings. Carriage return
and line feed characters do not enable the display of multi-line menu entries.

The Item ID Field


The Item ID is the message value returned by a menu item, identifying the
menu option selected. Values assigned must be unsigned integers in the range
0..65,535. Each newly created item (row) has an initial value of 0. [ID numbers
for menu items in each window class must be unique, even though the same
ID numbers can be used within another window class. A series of ID numbers
defined in the Menu editor for menu items can be duplicated by string table
IDs without conflict, but cannot be duplicated for elements in a dialog box
using the menu.
Menu items that call popup menus (submenus) do not return values and,
therefore, all items with submenus can have the same value-field entry (that
is, zero).
To the right of the Item ID entry box, a second, greyed-out box shows the
previous value (if any) assigned to a menu item. This prior value is retained
only while editing a menu element and is updated as soon as the menu is saved
or editing moves to another element.
The Menu Type block (immediately below the Item ID field) contains
three radio buttons and indicates the menu element type. These cannot be
selected, however, and cannot be used to change the type definitions of
existing elements.

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.

Figure 9-5 A Menu With Separators

Clear clipboard *L
*S Find “FE
Replace *R

Initial State Options


The initial state options consist of three radio buttons—Enabled, Disabled,
and Greyed—and one checkbox—Checked. While only one selection can be
made from the radio button options, the checkbox option can be combined
with any of the radio button options.
The Enabled option is the default and makes the menu element selectable.
The Disabled option renders the menu element unselectable but does not
change the appearance of the menu entry.
The Greyed option both disables the menu element and greys the displayed
text, indicating that the menu entry is not currently selectable.
The Checked option places a checkmark next to the command. This is
normally provided for a menu option that is used as a toggle, and indicates
that the initial state of the selection is ON.

Creating a Menu for FileView


The FileView application, discussed in Chapter 13, uses a relatively simple
menu consisting of one pull-down menu with four entries. Figure 9-6 on the
following page shows the Menu editor with the FileView menu in the menu
test window at the top. In the illustration, the header file is opened showing
the symbolic constant names defined in FileView.H, as well as the menu item
values. This menu will be expected in the FileView.RES resource file, witha
menu name the same as the application name: FILEVIEW.
220 BORLAND C++ 3.0 PROGRAMMING, Second Edition

Figure 9-6: The FileView Menu Resource

tem text —
'|&%Open...\t*O

lean

Item type | Break befoe 2


+ No break SEL eLese cesaseo es
FILEVIEW
_ * Menu item Menu bar break POPUP "sFile"
| Separator » Menu break MENUITEM "SOpen...\t°0" Lr
. Help break MENUITEM "8 Type. SOT"
MENUITEM SEPARATOR
MENUITEM “RA bout" ho
Initial state
MENUITEM VE ait tO" Lee]
| « Enabled
—_End Popup
Disabled | Checked —__End Menu__
Grayed

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

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.

Defining Accelerator Keys


Accelerator keys are defined in two parts: first, as individual keys or as a
combination of an individual key with the Ctrl, Alt and/or Shift keys; second,

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.

Virtual versus ASCII Keys


Virtually all (the pun is almost unavoidable) of the keys on the keyboard—
either standard or enhanced—can be defined as accelerator keys, using the
Virtual key definitions provided in Windows.H (see also Appendix A).
However, not all virtual keys have ASCII equivalents, and there are a few
ASCII keys that do not have corresponding virtual key definitions (such as the
exclamation point: !). There are even a few keys, such as the Scroll Lock key,
that are neither ASCII nor virtual. Further, if you examine the virtual key
definitions in Windows.H, you'll discover that there are definitions that do
not correspond to anything found on contemporary keyboards (such as
F13..F16), or which refer to other devices such as the mouse (VK_MBUTTON).
In general, an ASCII key refers to any of the alphanumerical keys that
produce displayable screen results—including, of course, the punctuation
keys and the space bar. |
Obversely, virtual key definitions—which begin with the prefix VK_—refer
principally to the function, arrow, and keypad keys. Thus, the F1 key is
identified as VK_F1, the PageUp key is defined as VK_PRIOR, and the up
arrow key is defined as VK_UP.
However, the alpha keys A..Z and the numeric keys 0..9 also have virtual
key definitions as VK_A..VK_Z and VK_0..VK_9, even though, upper- and
lower-case characters are not differentiated as accelerator keys whether
defined as virtual or as ASCII keys.
Chapter 10: The Accelerator Editor 223

Entering Accelerator Key Definitions


Accelerator keys can be entered in several ways. If the virtual key format is
used, the virtual key definition can be typed directly in the Key edit box.
Or, if you prefer, you can make ASCII format entries from the keyboard,
but with a slight restriction. The character A can not be entered unless C’s
string format is used (even though this is a char value rather than a string). In
other words, A is entered as "A" and Ctrl-A is entered as "A."
Note, however, that the caret (4) cannot be used with virtual key
definitions. Instead, for virtual key definitions, the Control check box (see
Modifiers and Conditions following) can be used.
And, as a third alternative, the actual key code can be entered (in decimal
format). This is not particularly recommended, however, since the resulting
display is not readily understandable (unless you are accustomed to reading
scan codes in decimal). Still, Ctrl-C can be entered as 03 or simply as 3.
A fourth format is selecting the Accelerator entry from the main menu and
then choosing Key values. When you make this selection, a prompt appears
instructing you to press the desired key or key combination and to exit by
pressing either Alt-Esc or a mouse key.
But, while this form does work in most cases, it is not entirely reliable as
there does appear to bea bug that can lock the system entirely. Yes—entirely—
not just the Resource Workshop but your entire Windows system. Thus, the
mouse may still move ... and the keyboard clicks ... but nothing else happens.
A power-down cold reboot is the only solution. CAUTION! Use this feature
with care!
Or, better yet, stick with the first two entry formats.

Modifiers and Conditions


As mentioned previously, accelerator key definitions can be combined with
the Ctrl, Shift, and Alt keys. Immediately below the Key type selection, the
Modifier selection offers four check boxes.
The first three of these—Alt, Shift, and Control—can be combined with all
virtual key definitions. However, if you choose ASCII format, only the Alt
checkbox will remain selectable and the remaining two options will be
greyed-out.
224 BORLAND C++ 3.0 PROGRAMMING, Second Edition

Note: While combinations such as Ctrl-Alt-Shift-F12 are valid, a combina-


tion like this is difficult to enter unless you happen to be an octopus. Some
restraint is suggested.
The fourth check box is labeled ‘Invert menu item.’ It simply instructs
Windows to invert (highlighted or reverse video) the menu selection—a
format that is followed in most Windows menus and that is enabled by default.

The Accelerator Menu


The Accelerator pull-down menu (on the main menu bar) offers three options.
The first option, New item, is duplicated in action by the insert key and
simply creates a new accelerator definition with an initial key value of 0 (null)
and a command (message) value incremented one beyond the highest prev-
ious value used. Both values, of course, should be defined appropriately.
The second option, Key value, was discussed previously as an option for
defining accelerator keys. This option, however, is not recommended.
The third option, Check dup keys, provides a test for duplicate key
definitions, which may not be obvious if both virtual and ASCII definitions
have been used.
Note: Duplicate message values are not flagged—only key definitions are.
Using more than one accelerator key to initiate the same function may be
unusual but is not necessarily invalid.
Incidentally, the order in which accelerator keys are defined is irrelevant,
and definitions may be ordered however you prefer.

Accelerators for FileView


While the FileView application does not have an extensive need for accelerator
keys, three accelerator key definitions are supplied as shown on the following
page in Figure 10-1. The mneumonic definitions, identified below, should be
defined in Fileview.H (see Chapter 12—The Header Editor).
The three accelerator key definitions used are the following:

# § 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

A single accelerator table (containing these three key definitions) will be


expected in FileView.RES with the accelerator table name FILEVIEW.

Figure 10-1: The Accelerator Editor

| File Edit Resource Window


Bee me SESE New item we
Key value ee ee MENU
FILEVIEW
22! DIALOG
ie S| +||| pout
|
FILEVIEW
[lll FILETYPE
VK 101 i OPENFILE
VK_O 103 STRINGTABLE
VK_T 104 1
ACCELERATORS
FILEVIEW

CO e ae

Alt ‘Shift ’ Control


¥ Invert menu item

[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
-

at a“ nite geS wonli4ast rises vidoes <=


tw 11 tunsanller <nlataioww
al? ewe

| , = 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

Editing String Resources

The String editor is a distinct departure from conventional programming in


that it groups strings used by applications as string resources instead of
having them scattered through the application’s source code. Many compilers
have typically gathered such scattered data together in the compiled program,
usually toward the end of the .EXE file, along with other static data elements.
Thus, the String Editor provides a welcome alternative by defining strings
directly as resources separate from the execution program.
This resource grouping of strings provides two advantages. First, string
data is not automatically maintained in memory as with conventional .EXE
programs, and second, as in resource form, string data can be edited, trans-
lated, or customized without recompiling the application.

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

The String Editor


The string table is displayed by the String editor in a three-column format,
shown in Figure 11-1.
By intention ID and as described in the Resource Workshop User’s Guide
the ID Source column is for entry of string ID constants.
However, as you can see in Figure 11-1, only integer values are displayed
because the Resource Workshop does not provide any facility for opening a
header (.H) definition file and, most important, does not perform as described
in the User’s Guide. See Chapter 12 for more on header files and how header
definitions can be included despite this shortcoming.
The second column—ID Value—is intended to show the numerical value
corresponding to the missing string ID constants. As the String Table editor
presently stands, the value shown here will automatically correspond to the
value entered in the ID Source column.

Figure 11-1: Stringtable definitions

New item
Delete item

| Change item Enter Bene


| Restore item Esc |:
Chapter 11: Editing String Resources 229

The third column—String—is precisely that—the string table entry to be


displayed by the application.

Limits and Restrictions


String table resources are limited to a total of 64 Kbytes and, at the same time,
the ID Source or ID Value fields are limited to integers in the range 0..65,534.
Obviously, if all of these values were used, each string could be a maximum
of one character long which is; not very practical.
Individual strings, however, can be assigned any ID Value desired with the
obvious restriction that ID values cannot be duplicated.

The String Text Field


Strings are entered without quotation marks since quotation marks will be
treated as part of the string. The entry field will scroll to permit entry of up to
255 characters, and the entry field will beep when more than 255 characters
are entered. The excess characters do not appear.
Also, strings are normally displayed as a single line, but a line feed can be
inserted using octal format as\012. Thus, the string entry, "This is the first
line\012and this is the second," will display as:
This is the first line
and this is the second

String Table Resource Names and String Groupings


Unlike other resources, string resources are not identified by labels in the
resource table (upper-right window in Figure 11-1). Instead, string groups are
identified by the first string value in each group of 16. Thus, while the string
table illustrated contains strings numbered from one through 40 with a single
string numbered 100, the resource list shows string table resources identified
Ass iml6ro2, ana 100.
The reason for this is simple. Under Windows, string resources are loaded
into memory as groups of 16 consecutively numbered strings. Thus, the
strings numbered 1..15 form one group, 16..31 form a second and 32..40 form
a third, while the string bearing the ID Value 100 is a group in itself.
Note: The rwcdemo.res file illustrated is distributed with the Resource
Toolbox.
230 BORLAND C++ 3.0 PROGRAMMING, Second Edition

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.

String Resource Attributes


Resource/Memory options (on the menu bar) permits setting resource
attributes. However, unless there are strong reasons to the contrary, string
resource should always be loaded on demand, be moveable, and be discardable.

A String table for FileView


The FileView application (Chapter 13) has virtually no use for a string table.
This is because the application title that appears in the caption bar at the top
of the main window is the only string that is not already present in a resource
component (and, therefore, editable directly). The String table editor appears
with a single entry in Figure 11-2.
Figure 11-2: A String table for Fileview

File Edit ‘Stringtable Window Help

: 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

Header Files With The Resource Workshop

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.

Figure 12-1: Using Windows Notepad To Edit A Header File

Stringtable Yin r Search elp


/* Di\BC\FILEVIEW.H 2/8/1991 22:12
ftdefine IDS NAHE 1
———=s | define IDOK 1
cn A EMIEead H#define IDCANCEL 2
| #define IDD_FNAHE 16
| t#define IDD_FPATH 17
| #define IDD_FLIST 18
| tdefine IDM_QUIT 101
| #define IDM ABOUT 162
| tdefine IDM OPEN 163
| H#define IDM _TYPE 104
| #tdefine IDD_BHP 201
| define IDD_COM 202
| #define IDD_CUR 203
ftdefine IDD_DAT 204
| define IDD_DBS 285
| #define IDD_DLG 206
| tdefine IDD_DLL 207
| tdefine IDD_EXE 268
ttdefine IDD_H 209
| tdefine IDD_PAL 210
| #define IDD_RC 211
| H#define IDD_RES 212
| tdefine IDD_TXT 213
| #tdefine

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

Header File Requirements


In general, header files should be familiar to all C/C++ programmers. The use
of header files to define mnemonic for Windows resources is simply an
extension of conventional practices. There are, however, a few considerations
to keep in mind.
Since the symbols included in the header file become global constants when
the application is compiled, the symbols (strings) must be unique within each
application. The symbolic values, however, need only be unique within their
resource types because Windows interpets these ID values within the context
of the resource presently being used in the application. Thus, in the example
provided for the FileView application, the value 1 is duplicated for both the
IDS_NAME entry in the stringtable and the button message IDOK.
Also, while ordering the entries in the header file is more convenient for the
programmer, it is not required by the compiler. Thus, entries may be grouped
in any fashion desired, comments may be included, and comments or blank
lines may be used to increase readability.
Note: Bitmap, cursor, and icon resources do not require Header entries.
tt
>

‘/
Pf.

SS
Chapter 13

Putting It All Together:


The FileView Application

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

dialog boxes that are about2.obj


button2.exe
(3)

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.

Preventing Compiler Warning Messages


Many Windows application functions, when compiled, cause the compiler to
generate warning messages that are actually irrelevant. Usually, they result
from arguments that have been passed to the functions, such as Windows
message parameters, that are not used by the function. Because of this, you
may notice that in the source listing, the following code line precedes several
functions: #pragma argsused. This pragma command prevents the compiler
from generating an unused parameter message for the function immediately
following it. The pragma directive applies only to a single function and must
precede the function declaration.

Loading Resources in WinMain


Several of the resource elements defined in ViewFile.RES are loaded during
the execution of WinMain when the first instance of the application is created.
Others will be loaded when individual dialog boxes are created and, when the
dialog boxes are finished, will be released from memory. The Accelerator
resource is also loaded within the WinMain procedure but not during the
instance initialization.
ViewFile.C uses only one string from the Stringtable resource: the applica-
tion title. This string is retrieved in WinMain during the initialization of the
application’s first instance:

LoadString( hInstance, IDS_NAME, (LPSTR) szAppName, 10 );

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

we.lpszMenuName = (LPSTR) szAppName;

The icon resource also receives a load instruction:

we.hIcon = LoadIcon( hInstance, szAppName );

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:

aaa Geuley;p Cama) D)Dat


A Nee LD Dae MPs
Stropy« szrileExt, szFileTypeLiFiletypel 0;

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.

Loading the Keyboard Accelerator


When the Accelerator resource is used, it requires a few special provisions,
beginning with a Load instruction:

hAccel = LoadAccelerators( hInst, szAppName );

The LoadAccelerators function returns a handle to the Accelerator table.


Different Accelerator tables can be loaded to provide services that correspond
238 BORLAND C++ 3.0 PROGRAMMING, Second Edition

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.

Creating the Dialogs


Three dialog boxes are used in the FileView application. They are called
through the WndProc function in response to WM_COMMAND messages
with wParams of IDM_ABOUT,IDM_TYPE or IDM_OPEN. The IDM_ABOUT
message is handled directly:
lpProc = MakeProcInstance( About, hInst );
DialogBox€ hinst, “ABOUT”, hwnd,,ipProc., 2;
FreeProcInstance( lLpProc );

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 );

The provisions within CallFileTypeDlg are essentially the same as those


used for the ABOUT dialog. They create the dialog and then discard it when
finished. Any return value is simply ignored because the real operations of
this dialog consist of setting the global variable iFileType.
Chapter 13: Putting It All Together 239

In the third case, in response to an IDM_OPEN message, the task is some-


what more complex because an initial search path, file specification, and file
type are required before calling the CallFileOpenDlg subprocedure. A result-
ing file path and filename will be expected on return.
After the subprocedure returns the file specification, this case statement
also has the task of opening the indicated file and displaying the results. This
portion of the task, however, is essentially conventional programming. The
Windows-specific provisions will be found, in part, in the CallFileOpenDlg
procedure, but principally in the exported FileOpenDlgProc procedure. This
will be discussed later. However, initializing these dialogs is, in some instan-
ces, critical.

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:

CheckRadioButton( hDlg, IDD_BMP, IDD_ANY,


IDD_BMP+iFileType );

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:

SendDlgItemMessage( hDlg, IDD_FNAME, EM_LIMITTEXT,


cio AOE 2)
240 BORLAND C++ 3.0 PROGRAMMING, Second Edition

The gist of the message is to "limit the length of text input”


(EM_LIMITTEXT) with a set limit of 80 characters. This is a reasonable length
for a file specification. It does not provide a display in the edit box, but does
set a control on what can be entered.
The SendDlgItemMessage function can be used in a variety of fashions,
depending upon the control element addressed and the message type. This is
equivalent to obtaining a handle to the control element and then calling the
SendMessage function.
The next task fills both the list box and the file path edit box in a single step,
using the DigDirList function:

Dig DHits atsct Ga hiDILGiamsiz Eislels piecr- als D)Dar lerlisnie,


TeDIDEeE PA Hepes Wilke WerAU titi mee

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.

Table 13-1: DOS File Attributes

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

Note 1: If LB_DIR is set, messages generated by DlgDirList are sent to the


application’s queue; otherwise, messages are sent directly to the dialog
function.
Note 2: The exclusive bit lists only files of the type(s) specified; otherwise, —
normal files are listed as well.
The final task during initialization is to write the file specification to the
filename edit box by calling SetDlgItemText:

SetDlgItemText( hDlg, IDD_FNAME, szFileSpec );

While Windows provides a rather powerful and comprehensive function


for directory lists, other types of lists are also needed and can be created using
the SendMessage function:

SemdiMes sage: GunhD Wg BasAD


DSi RNG esO)- esGEPISiRs esizS
cm ings

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

/ / saat tatETTSSS SSS S sees eee eS eae Ses sesessssssssaa=//

ifif FileView.C Lf
// demonstrates Resource Workshop interactions //
/ /aaSzEBEBSStasSesasssssesessesssesesssssssssssa=//

#include <windows.h>
#include <dir.h>
#Hinclude <IS) tCOree
n>
Hinclude "fileview.h"

#define wFileAttr 0Ox4010 // file attribute flag

HANDLE hInst, // hInstance of application


hAccel, // handle of accelerator
OrgFont, // handle of original font
NewFont; // handle of new fixed font
HWND hWndMain; // hwnd of main window
LOGFONT FontLog; iM On ters. GUC Une
int Cina. Clary, Chip // character size
WndX, WndY, // client window size
VESIC In MiarXe me tioiG rl MialXe, (/ SC TOO Gmalnige:s
ViSicmuUROS: sahisicG tUuRosr, / (mac Ulin © Nites Gino)a EeplOlSaseslOinsS
Velinicy eH Linicr, // scroll increments
FilLines, A/S numb pos sel ines matinee ue
WndWidth, // ew aGst Ne 0 fanGhits pila yaoi mane
WndHeight, // max Lines on screen
iFileType; // file type
char szAppNameL[10], // application name
szFileSpecl64], Lit WER S|Pie Curt OlGa ml nelsesiallaESieialincin
S Ze elE x taleorly. // default extension
szFileNamel64], // filename
szFilePathl64], Li foaatempaten
szFNamel64]; Ld tie etionsd.irs pileayy,
WORD wStatus; // status of search
ei ile aly lets // handle of open file
LONG FaluSize, LP eSVZe Of fave
HDisplay; // display width (LONG)

char SZ Pile ly pet. ed P=. ta ape MPo. 1k COMCat C Ulpune.


A ye DBS yee DE Gis k= Dieae a were Ki el ee
oe ETA lohiag's) taCie pore, Wale COA pee Weel bine ransSLaeae =

Py HSSSSeSSeeneases Aiviimieq
et prototypes ===========S====//

int PASCAL WinMain(€ HANDLE hInstance,


HANDLE hPreviInstance,
IPSS LR LpszCmdLine,
Chapter 13: Putting It All Together 243

Dali cmdShow );

BOOL FAR PASCAL HWNDAbout(


hDlg, WORD msg,
WORD wParam, LONG lLParam );
Long FAR PASCAL WndProc( HWND hwnd, WORD msg,
WORD wParam, LONG lLParam);
ext ern BOOL FAR PASCAL FileOpenDlgProc(
HWND hDlg, WORD msg, WORD wParam, LONG LParam );
ext ern BOOL FAR PASCAL FileTypeDlgProc(
HWND hDlg, WORD msg, WORD wParam, LONG LParam );

===
= = 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() { }

/ / SSSSeSSS3S5 S552 SSS 222 S222] SSS] ] So SSoSSeS=]]S5e SSeS / //


// SetScrlRange: sets vertical and horizontal scroll //
// ranges based on number of Lines in //
// file and width of display If
ifif returns: nothing, sets global values I if
/ (See Sesess2ooSe 2c SoS o Sse] SSS SSS CS eSesseSeeseee//

void SetScrlRange( HWND hwnd )


{
Vocmenax -—maxG O05 Fi Lines: ky Jes. CintDe Whdy- / Chry Dd: >3
VScrilpos = mink VSerlPos, VSerlMax”);
HScrlMax = max€ 0, WndWidth - WndX / Chrx );
HScruPposs= ming HScrliPos,HScr.UMax. );
GetScrollRange« hwnd, SB_VERT, 0, VScrlMax, FALSE );
SetScrotllPos hininicy mes DmaVAEIRele ViSTCInAUIO'Ss, aan Ea ee
SetsScrollRange( hwnd, SBLHORZ, O, HScrlMax, FALSE );
SetScrollPos( hiwinidiaes BeshiOlRiZ., HSicmuros wal RUE:
WndHeight = min€ FilLines, WndY / ChryY );
244 BORLAND C++ 3.0 PROGRAMMING, Second Edition

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

char *FormatLine( char *szBuff, LPSTR lLpMem,


int jiLen, Chiapas eniOst sia

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. //

void PaintFile€ HWND hwnd )


{
PAINTS eRwU Cola pise
HDC hide?
LONG GEbwOltiss
int Aspe iho2 Gea tS een Pal ere Pio Ss
char Sz Biintay balecoee.
HANDLE Me Untiae,
LP-STR LoSuril, WoSierpea-

BeginP anni taGeehiwinids-sG@isPJPACENiT


SieRiIU Gilmseeipisias-
hdc = Distance
Select Object( hdc, NewFont ); // use fixed font in DC
na @ flea UEainess
{
Tha Ge hata een (Olprein Gs Sizak Niaimierm lO) EamRIEVA)D ym ene amei—a—a |)
{
iFilSz = WndHeight * HDisplay; // buffer for data
hBuff = GlobalAlLloc(€ GMEM_MOVEABLE,
@DIWOIRID) ea Fail GSizaen=
1 GIn Butte
“8
tpstrl = Globallockks hBu't f* or (i ee\cOCKD net
7G LpSeri »
{ [aime tae Ontah Sie itm) Nmntalaeen COMES: ralicat
EaWOlfss = GEONGD ViSicm UPS = SHiDarsipialy,;
Teo sltseek (hres. CR iMO ts Oke = 11)
{
hUXCS 72 can tere acer r Weel t reli) str dL Size D>>
TC ACCS 2a ee le)
{
GRioism = sO).
lpsitr2es = postr
for(€ i=VScrlPos;
1<CVScrlPos+wndieight) >: i++.)
246 BORLAND C++ 3.0 PROGRAMMING, Second Edition

{
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 );
}

// CallFileOpenDlg: invokes FileOpenDlgProc to get Veli


Ha name of file to open Itii
// parameters: szhuilespecine |— a hited wes path Ifff
Ve szFileExtIn > indie Vale Goce I if
df Creturned) szFilePathOut - selected path Wi,
fel Creturned) szFileNameOut - selected file If i
// Returns | RUE files s elected ah,

int CallFileOpenDlg( HANDLE hInst, HWND hwnd,


char *szFileSpecIin, GihiaipeeSiz bagluele xecelinpe
char *szFilePathOut,
char *szFileNameOut )
af
FARPROC Upiemoic
int iReturn;

strcpy<* szfilespec,--szFalespecin) = // save init file spec


strepyt szFileExt, saeitebxt ine:
LpProc =
MakeProciInstance€ FileOpenDlgProc, hinst );
TReturn = DialogBox( hInst, “OPENFILE", hwnd,
LPP GG 6
Free PiRocin sitvainice! GalpiPiroce
Chapter 13: Putting It All Together 247

StrcpysesztitePathout>sszFilePath=)+ // ret new filepath


strcepy( szFileNameOut, szFileName ); // ret new filename
returntseaReturnzoy
}

// CallFileTypeDlg: invokes FileTypeDlgProc Meff


Hhif returns RUE ah types selected ii

int CallFileTypeDlg(€ HANDLE hInst, HWND hwnd )


{
FARPROC Wp Proce
int ijReturn;

Up Pnocs—
MakeProcInstance( FileTypeDlgProc, hInst );
iRecurn |= DialogBoxC ininst,;. “FILETYPE”, hwnd;
LolPRa@ MW,
FreeProcInstance( lLpProc );
return( iReturn );
}

te export procedures ==================//

{jf SSS SS] SS SS itp Bf hd hh petty

Vey, About: About dialog procedure //


jf ff 2ze2 2S 32 SS SSS SS SS SSSS] SSS SSS SSeS See //h/
SSeSSSseSesoqeocesss

#pragma argsused // prevents unused pa rameter message //

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 //

Long FAR PASCAL WndProc( HWND hwnd, WORD msg,


WORD wParam, LONG LParam )
{
static char szTmpFilePathl64];
SrtvartninGanc hialiemaS!Zalem Piaieiesxatyeorele=
static char szTmpFileSpecl64];
static char szTmpFileNamel64];

FARPROC UpiP noc L/S pone tom dula wogm box


char Sralgwip ib 2ial 5 // temp buffer
int lee
FICE lal
t (bs

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

Hime = LOWORD(LParam) - HScrlPos; break;


case SB_THUMBTRACK:
HInc = LOWORD(LParam) - HScrlPos; break;
detautt: Hine -= 0;
}
Hine = max€ —HScrlPos,
min@oHine Hs cruMaxt =) HSerl Pos ) ))>
1 Tune »
a
Ser llPOSG se litines
ScrolLtwindow¢ hwnd, “—Chrx*Hine, -0,eNULL> NULL. =
Sie Coico MMP Os Gawd eSB mHiOlRIZe mn SIGN POsy- mal
U Ems
UpdateWindow( hwnd );
See Dera.
case WM_KEYDOWN:
// translate keydown msg to horiz/vert messages
switch(€ wParam )
{
case VK_HOME:
SendMessage(Chwnd,WM_HSCROLL,SB_TOP,OL);
SendMessage(hwnd,WM_VSCROLL,SB_TOP,OL);
break;
case VK_END:
SendMessage(hwnd,WM_HSCROLL,SB_BOTTOM,OL);
SendMessage(hwnd,wWwM_VSCROLL,SB_BOTTOM,OL);
break;
case VK_PRIOR:
SendMessage(hwnd,WM_VSCROLL,SB_PAGEUP,OL);
break;
case VK_NEXT:
SendMessage(hwnd,WwM_VSCROLL,SB_PAGEDOWN,OL);
break;
case VK_UP:
SendMessage(hwnd,WM_VSCROLL,SB_LINEUP,OL);
break;
case VK_DOWN:
SendMessage(hwnd,WM_VSCROLL,SB_LINEDOWN,OL);
break;
casieaVike FE ri
SendMessage(hwnd,WM_HSCROLL,SB_PAGEUP,OL);
break;
case VK_RIGHT:
SendMessage(hwnd,WM_HSCROLL,SB_PAGEDOWN,OL);
break;
+} break;
case WM_PAINT:
252. BORLAND C++ 3.0 PROGRAMMING, Second Edition

PaintFile( hwnd ); break;


case WM_CLOSE:
DestroyWindow( hwnd ); break;
case WM_DESTROY:
CloseFV(); PostQuitMessage(0); break;
case WM_QUERYENDSESSION:
closer Vi return CmecloONG)] TRUE);
default: return(DefWindowProc(hwnd,msg,wParam, lParam) );
}
Pavia e WHE ws
}

// FileOpenDlgProc - get the name of a file to open //

BOOL FAR PASCAL FileOpenDlgProc( HWND hDlg, WORD msg,


WORD wParam, LONG LParam )
{
SitdaicalCac Mal: OrgPathl64];
char clWarsitiCinare:
int nLen;
Sieipuew warlsllls TennOn

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

SetDlgItemText( hDlg, IDD_FNAME,


szFileSpec );
MessageBeep(Q);
}
break;
CralSicun yD Die le leonie=
switch(€ HIWORD(LParam) )
{
case LBN_SELCHANGE:
if€ DlgDirSelect( hDlg, szFileName,
DD) LIS 2
strcat( szFileName, szFileSpec );
SetDlgItemText( hDlg, IDD_FNAME,
szFileName );
break;
case LBN _DBLCLK:
if€ DlgDirSelect( hDlg, szFileName,
DD PLUEST QW 2»
{
strcat( szFileName, szFileSpec );
DlgDirList(€ hDlg, szFileName,
IDD_FLIST, IDD_FPATH, wFileAttr );
SetDlgItemText( hDlg, IDD_FNAME,
S Zab Ge.s pieicums-
}
else
{
SetDlgItemText( hDlg, IDD_FNAME,
szFileName );
SendMessage( hDlg, WM_COMMAND,
TDOKY OL)
+} break;
eb meak-s
case IDD_FNAME:
if€ HIWORD(LParam) == EN_CHANGE )
{
EnableWindow( GetDlgItem( hDlg, IDOK ),
(BOOL) SendMessage( LOWORD(LParam),
Hi GEV TEX TLeNG
Wil, Of, WIL 2 MF
FP
break:
case IDOK:
GetDlgItemText( hDlg, IDD_FNAME,
szibaibeName, sol) |):
nien = strient szFiteName );
cLastChar = *AnsiPrev( szFileName,
szFileName + nLen );
254 BORLAND C++ 3.0 PROGRAMMING, Second Edition

if @teast Char s==7 NU Merlaclast Char ==" "2" 5)


strcat( szFileName, szFileSpec );
if ¢ “strchre ‘szFvleNnames oh* vey 4) |
strchr( szFileName, '?' ) )
{
1fC Digdirlist© hDtg? =szEaleName,
IDDSFEIST PS FIDDEFRATH? MwrileAttur ) )
{
strepy( szFileSpec, szFileName );
SetDlgItemText( hDlg, IDD_FNAME,
szFileSpec );
}
else MessageBeep(0Q);
break;
}
‘lef Gee DAG IDatin) eens eta ounhh)DilQU we Size hmaleeiNiaimier,
IDD_FLISTP EDDSEPATH, wrileAttr ) »)
{
strcepy( szFileSpec, szFileName );
SetDlgItemText( hDlg, IDD_FNAME,
SZ FateGeroipierc mmy>
break;
}
szFileNameCnLen] '\O';=
// szFileName not search path
if Gehan
sit CGusiz FulueName:uscnt alent OF Oma)
{
sStmeatGiszrileName, eszrileext. )-
due FomediiuPswe¢ SvzikileNane, Sialenmio, @ 2) »
{ Li it lesa Oeste xalrsre
MessageBeep(0Q); break;
iy
GetDigitemText © hDlg, LDDS2RPATH,
SiZhaGelPal thy arom e
S GUDi Gusizen
Ge Pacinma
LEC ss7rivePathietrienCszkubvePath.) —Ule= wee)
StecatCaszhibePa th pu 4 wVa sn
strepy( szFileName, fileinfo.ff_name );
// return filename
einer’ Ope iPatel- 2). // reset directory
EnddDialogG@ hDtg,> TRUE) > // end dialog box
break;
case IDCANCEL:
chdir(€ OrgPath ); // reset directory
EndDialogC hDUg? FALSE) = // end dialog box
break;
Chapter 13: Putting It All Together 255

default: rewwrimn€ PAILSIE Y-


De Dinero:
default: return( FALSE );
}
met urn TRUEST Oe.

// FileTypeDlgProc - get file type to open ih

#pragma argsused // prevents unused parameter message //

BOOL FAR PASCAL FileTypeDlgProc( HWND hDlg, WORD msg,


WORD wParam, LONG LParam )
{
int OrgFileType;

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
}

// WinMain - ViewFile main laf


a

#pragma argsused // prevents unused parameter message //

int PASCAL WinMain(CHANDLE hInstance, HANDLE hPrevInstance,


LPSTR Eps 2 cmid lanier, aed t cmdShow )
{
MSG msg;
HDC hidiey;
WNDCLASS wc;
TEXTMETRIC tm;
IL Gue Un Pimewslinis tranicems) hi WM Wey (oP THSeGANCGE.s EMIS TS Tvirst
{
FoaldS timing Ga hins tance en LD SaNAIME,
CEPSTROE szAppiNamer 10m):
we.lpszClassName = szAppName;
we.hInstance = hInstance;
we.lpfnWndProc = WndProc;
WiGH-an) GUiiSiOls = (Lonccursor¢ MUILK, WDC _ARIROW 2
we.hIcon = LoadIcon( hInstance, szAppName );
we.lpszMenuName = CLPSTR) szAppName;
we.-hbrBackground = GetStockObject( WHITE_BRUSH );
we.style = CS_HREDRAW | CS_VREDRAW;
WCcecDGlisEx tira a Oe
wce.cbWndExtra = 0;
RegisterClass(&wc);
}
else GetInstanceData( hPrevinstance,
(PSTR) szAppName, 10 );
iFileType = IDD_ANY - IDD_BMP; LINING C138 ot: bemty pe
strepyC szrileExt, szFilelypebiPiletypein))
th setudetault extension
hInst = hInstance; // global: save for use by window procs
hWndMain =
CreateWindow( szAppName, szAppName,
WS_OVERLAPPEDWINDOW | WS_HSCROLL | WSaee VS) CiRtO) Lalare
CW_USEDEFAULT, OF CWlUSEDEEAUIETY oF
NUCL, NULL hinstance mNUEL
Chapter 13: Putting It All Together 257

hdc = GetDC( hWndMain );


FontLog.lfHeight =) 6;
Fontloge UtwWidth = 6;
FontLog.lfEscapement = O;
FontLog.LfOrientation = 0:
FontLog.lfWeight = FW NORMAL;
ROMG eo Gren lati letalc = RAILS Ey
FontLog.lfUnderLline = AES ES
FontLog.lfStrikeOut = RAISES
FontLog.lLfCharSet = ANSI_CHARSET;
BOM Ogre OU tine Caisson — SO UiienD IE RAW EaleeeiRiE Gel Se
FOMELOG. lLyrClipPrRacision = CLP MaPAUE IPR CES 5
FontLog.lfQuality = DIB PYNW Ei) CW ANIL oe 17 5
FO Mit Oren Gite PaleteG NVA ClFsallt Cyant— en FeloXiDae C Hin | isreeD ONT GARIE:
strcpy( FontLog.lfFaceName, "System" );
NewFont = CreateFontIndirect( (CLPLOGFONT) &FontLog );
OrgFont = SelectObject( hdc, NewFont );
GetiextMethriacs @ hdic, & tim)
ChrX = tm.tmAveCharWidth;
ChrY = tm.tmHeight + tm.tmExternalLeading;
ChrV = tm.tmHeight;
ReleaseDC( hWndMain, hdc );
ShowWindow( hWndMain, cmdShow );
UpdateWindow( hWndMain );
hAiccel = LoadAcceleratorns © hilnst, YRIEEVIEWS >>
while€ GetMessage( &msg, NULL, O, O ) )
{
if€ !TranslateAccelerator( hWndMain, hAccel, &msg ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
Few:
return(msg.wParam);
u

PSS SS SSeS SSSSsSso5 1

: FileView.DEF =;
FS SS SS SS SS SSS =

NAME FileView

DESCRIPTION 'Windows FileView Program'

STUB TWINSTUB EXE!


258 BORLAND C++ 3.0 PROGRAMMING, Second Edition

CODE PRELOAD MOVEABLE DISCARDABLE


DATA PRELOAD MOMEABISES MU Ete r rE

BME UW\PIE WINDOWS

SEGMENTS
_RES PRELOAD MOVEABLE DISCARDABLE

mle /iPS it 74(2 128


SHACKS AE 8192

EXPORTS
WndProc
About
FileOpenDlgProc
FileTypeDlgProc

I if
//
el,

#define IDS_NAME string table ID Le

#define IDD_FNAME edit and UnieseG DIO ae DIS Ld


#define IDD_FPATH
ACeu aCe aD) Dee riesleSai

#define IDM_QUIT tba menu item IDs ae


#define IDM_ABOUT
#define IDM_OPEN
#define IDM_TYPE

#define IDD_BMP Loh radio button IDs tes,


#define IDD_COM
#define IDD_CUR
#define IDD_DAT
#define IDD_DBS
#define IDD_DLG
#define DIDI E
#define TD DaE Xie
#define IDD_H
#define IDD_PAL
#define IDD_RC
#define TNDIDEERIESS
H#define IDD_TXT
#define IDD_ANY
Chapter 14

Message Box Dialogs

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.

Example One—Message Box Dialogs


The message dialog box function (MessageBox) is called with four parameters,
beginning with an HWND parameter that indicates the window owning the
message box. The second parameter, an ASCIIZ (null-terminated) string,
contains the message to display. This message will be wrapped to fit the
message box, but Windows will also size the message box to fit the message.

259
260 BORLAND C++ 3.0 PROGRAMMING, Second Edition

When a system-modal message box is created to indicate low memory,


strings passed as text and caption parameters should not be taken from a
resource file because an attempt to load the resource may fail.
The third parameter is also an ASCIIZ string that provides the dialog box
caption. If the caption parameter is NULL, the default caption "Error" is used.
The fourth parameter is a WORD value that specifies the icon, button, and
mode settings as shown in Table 14-1.
Table 14-1: Message Box Types
Icons
Symbolic Constant Value Meaning
MB_ICONSTOP 0x0010 stop sign icon
MB_ICONHAND same as MB_ICONSTOP
MB_ICONQUESTION 0x0020 question-mark icon
MB_ICONEXCLAMATION 0x0030 exclamation-point icon
MB_ICONINFORMATION 0x0040 displays a lowercase 7 in a circle
MB_ICONASTERISK same as MB_ICONINFORMATION
Icons cannot be combined. Only one icon can be specified for a message box. If two icons are com-
bined (using the OR operator), either one or no icon is displayed (that is MB_ICONSTOP |
MB_ICONQUESTION would be interpeted as MB_ICONEXCLAMATION).

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

Table 14-1: Message Box Types (cont.)


Mode Settings
Symbolic Constant Value Meaning
MB_APPLMODAL 0x0000 (default)
The user must respond to the message box before continuing work in the window identified by the
hWndParent parameter. However, the user can move to the windows of other applications and work
in those windows. MB_APPLMODAL is the default if neither MB_SYSTEMMODAL nor
MB_TASKMODAL is specified.

Symbolic Constant Value Meaning


MB_SYSTEMMODAL 0x100 0
All applications are suspended until the user responds to the message box. Unless the application
specifies MB_ICONHAND, the message box does not become modal until after it is created. Con-
sequently, the parent window and other windows continue to receive messages resulting from its
activation. System-modal message boxes are used to notify the user of serious, potentially damaging
errors, such as running out of memory, that require immediate attention.

Symbolic Constant Value Meaning


MB_TASKMODAL 0x200 0
This is the same as MB_APPMODAL except that all the top-level windows belonging to the curr-
ent task are disabled if the hWnd~Owner parameter is NULL. This flag should be used when the call-
ing application or library does not have a window handle available but still needs to prevent input to
other windows in the current application without suspending other applications.

When an application calls MessageBox specifying the MB_ICONSTOP and


MB_SYSTEMMODAL flags, Windows will display the resulting message box
regardless of available memory. It limits the length of the message box text to
one line. If a message box is created when a dialog box is present, use the
handle of the dialog box as the hWndParent parameter. The hWndParent para-
meter should not identify a child window such as a dialog box control. The
return value specifies the outcome of the function, returning zero if there is
insufficient memory to create the message box. Otherwise, the value returned
will be as shown in Table 14-2.

Table 14-2: Dialog Box Command IDs


Symbol Value Meaning
IDOK 1 OK button pressed
IDCANCEL 2 Cancel button pressed
IDABORT 3 Abort button pressed
262 BORLAND C++ 3.0 PROGRAMMING, Second Edition

Table 14-2: Dialog Box Command IDs (cont.)


Symbol Value Meaning
IDRETRY - Retry button pressed
IDIGNORE 5 Ignore button pressed
IDYES 6 Yes button pressed
IDNO ie No button pressed

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:

Tre WaSsa@gesox. ntinel., VE: Hfyoliceiome™n~ VEIT? -


MB_ICONQUESTION | MB_YESNOCANCEL
eM Bae DER BiUMsO)N ca ae—— sl Diae ome

Or, use a switch..case statement to test all possible return values:

switch< MessageBox hwnd, “Exit application?” |) ZExi1t 2717


MB_ICONQUESTION | MB_YESNOCANCEL
LC MBSDEFSUTTON 2. 00)
{ case IDYES:
case IDNO:
GarsiGme liDIGANIGEaaa cnae 3

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

Figure 14-1: Message Boxes

Message Boxes
Asterisk Icon [Information]
Exclamation Icon
Hand Icon (Stop)
: i
Asterisk or Information icon mes sage box with
Question Icon
OK button only

Exclamation icon message box with OK/Cancel


buttons

Hand or Stop icon message box with


Abort/Retry/Ignore buttons

Question icon message box with Yes/No/Cancel


buttons

Example Two—Borland Windows Custom Controls


The second example—using Borland Windows Custom Control message box
dialogs—is very similar to the first but unlike the preceding example does
require a resource file (MsgBox_2.RES), which is prepared using the Borland
Resource Workshop.
The resource file for MsgBox_2 consists of nine elements: one menu, four
icons, and four dialogs.
The first element, the menu (now named MsgBox_2), is almost the same as
the one created for MsgBox_1 with the exception of a change in the text labels
on the pull-down menu, shown in Figure 14-2.
Second, there are four icons—one for the MsgBox_2 application itself (again
titled MsgBox_2) and three used in the dialog box examples to replace the
question, exclamation, i, and stop icons. These last three are titled, quite
piesaicly, 1/72, and 3.7
264 BORLAND C++ 3.0 PROGRAMMING, Second Edition

Figure 14-2: Custom Message Boxes

ECT est Quit


Caution or Query
Halt everything 4 This dialog box replaces the
Information only asterisk icon dialog but is still
Yes } No} Cancel for information only!

This dialog box is a query :


requesting confirmation or ___©
cancellation. |

This dialog box asks the


familiar Abort } Retry / Ignore

This dialog box is a query


requesting a response as
Yes, No or Cancel

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

Bice EmOGalnisneamice) Camp Pole) =


me CUE Gums

In essence, this handling should be familiar from previous examples as


should the exported InfoProc procedure:

BOOED AIRS RAS GAULe mslongo


ealy OlG Gas HIWINDim niDalegi, WORD msg,
WORD wParam, LONG lParam )
{
switch( msg )
{
case WM_INITDIALOG: Patri G TRUE 2»;
case WM _COMMAND:
switch(€ wParam )
{
Cla Sic DOKe- a En GiDirallorg Gan hiDae Gy, sel) Ri) Eme
Pea wir i IWRUIE WS
diehivalt ates merc UlGin) Gaal R UIE) r=
} }
eng
Ul Gun Allsi Ee
}

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

And the second is the inclusion of the BWCC.LIB in your application


project. (BWCC.LIB is located in the \BORLANDC\LIB directory.)
Happily, with these two minor additions, the MsgBox_2 application will
execute by itself, but, without these, you may find yourself facing a more than
minor mystery.
Further information on the Borland Windows Custom Control style can be
found in the document file BWCCSTYL.RW (in \BORLANDC\DOC).

ji / SSeS ses e]SSe=] 2a a= = SSS as/ //

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"

Long FAR PASCAL WndProc(€ HWND hwnd, WORD message,


WORD wParam, LONG LParam )
{
switch( message )
f
case WM_COMMAND:
switch(€ wParam )
{
case IDM_ASTERISK:
MessageBox( hwnd, “Asterisk or Information",
» tcon message box with OK. button only”,
SN Beli CcOIN AVSalis Rel) Kees
MBOICONASTERISK || MB2OK ):
rSeeUrma¢ i DM.
case IDM_EXCLAMATION:
MessageBox(€ hwnd, "Exclamation icon message"
"box with OK/Cancel buttons",
"MB_ICONEXCLAMATION",
MB_ICONEXCLAMATION | MB_OKCANCEL );
PEgUMPmM¢e 4 De
Chapter 14: Message Box Dialogs 267

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

int PASCAL WinMain(€ HANDLE hInstance,


HANDLE hPrevIiInstance,
LPSTR LpszCmdParam, int nCmdShow )
{
static char szAppNameLJ] = "MSGBOX_1";
HWND hwnd;
MSG msg;
WNDCLASS WC;

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

we.hCursor =, LoadCursor(“NULEA IDG lARROW 2-¢


we.hbrBackground = GetStockObject( WHITE_BRUSH );
we.style = SiH RUEIDIRVA\ Wil fae G:Sam VRSEIDIRVA\WH,
RegisterClass( &wec );
}
hwnd = CreateWindow(
szAppName,
"Demonstrating Message Boxes",
WS_OVERLAPPEDWINDOW,
GCWRUSEDE RAUL CW US EDERAULE Te,
CWRUSIEDE FAU Ale eG Wee UISIE DIEsRAWiEale,
NUL SNUUEAlbins tance, NULLI E>:
ShowWindow( hwnd, nCmdShow );
UpdateWindow( hwnd );
while(€ GetMessage( &msg, NULL, O, O ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
return( msg.wParam );
}

(pia SS SSS SI SSS SS SSS SS SS SSS SSS S SSS SS SSSoSl= -,

MsgBox_1.DEF module definition file 2


7 SSS SS SS SSS SS SS SS SS SS SSS SSS SS SSS SSS SS saa.

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

/ (SSS SSeS Ssese=seosoesssoeSesesesaocesce / //

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

/ / SSSSSSS]] S45 ]=SSSSsSS=sSeee// //

// 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-;

BOOL FAR PASCAL InfoProc(€ HWND hDlg, WORD msg,


WORD wParam, LONG LParam )
r
switch( msg )
{
case WM_INITDIALOG: PawuUPime WRU M-
case WM_COMMAND:
switch( wParam )
{
ClalSieielhDIO Ke aE nid Daranlolgia nD lSgy walk UiEMEs-
PeetemG WiRUNS 5
GOrAWUics PEWUPMNC WikWis 0
} y;
Revuuinn GurAeS Ea.) y.
}

BOOL FAR PASCAL QueryProc( HWND hDlg, WORD msg,


WORD wParam, LONG LParam )

switch( msg )
{
case WM_INITDIALOG: PetwPiNnG WRU YF
case WM_COMMAND:
switch( wParam )
iC
case IDOK:
ClalSicue DIG AUNIG
Ee) = Er clDiialloig) Gua) Dalagp-amelaR, UEau =
neturn© TRUE 2
default: return TRUE~ ;
y }
revurnice FALSE. Dy
}

BOOL FAR PASCAL AbortProc( HWND hDlg, WORD msg,


WORD wParam, LONG LParam )
fc
270 BORLAND C++ 3.0 PROGRAMMING, Second Edition

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
}

BOOL FAR PASCAL ChoiceProc( HWND hDlg, WORD msg,


WORD wParam, LONG LParam )
{
SiWitatcintG =InSiGua
{
case WM_INITDIALOG: RecUmniGen RUE)
case WM_COMMAND:
switch( wParam )
t
Calsicmr uD NEos
case IDNO:
Gaisiey LD GANICEIS sa EndiDairallogiGehiDilg 7asi RU Ea.
Reuunn GanRUES))-
Havaviles Pecwieme wiRWie YD -
} }
Pew TFYAILSIS 2)
}

Long FAR PASCAL WndProc( HWND hwnd, WORD message,


WORD wParam, LONG LParam )
{
FARPROC Up Pino.

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

int PASCAL WinMain( HANDLE hInstance, HANDLE hPreviInstance,


[EPISoER LpszCmdParam, int nCmdShow )
{
static char szAppName[] = "“MSGBOX_2";
HWND hwnd;
MSG msg;
WNDCLASS WC;

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///

#define IDM_QUERY 101


#define 1DM_ABORT 102
#define IDM_INFO 103
#define ISD/ManG HOM GE 104
#Hdefine IDM_QUIT 106
= Aal Ring
el
> 0"
=xl areiprss
2
es ae err so
= @ EPR” 6 EG eSurveneee
oc@
9 BPa? oir (St rae eng a emehiok
ons a Gee
y a ie © Ces@iw
12 CPO RAs he: hi ert was » ioe anhy
L@ 6-..a) @ of 346400) Gywwad

‘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

“000 20 n' « Canereenen


7 ys 2 canis

an | ‘ - . ‘-
weed © i] -
aa» | . ‘ ri ee
7

— sug :
ou ° ‘ar Soe a
¥
ti :

= —
ee S

= . =
‘ -
= =

> ———
(Pigieire 5

Graphics Programming In Windows

The Graphics Device Interface is a bridge between an application and one or


more output devices, whether video display, printer or plotter. For example,
video displays vary greatly in size, resolution, and color capabilities. For a text
(DOS) display, however, any application could assume that the display was
80 characters wide and 25 lines vertical, without worrying about the pixel size
of individual characters or making any provisions for mapping text differently
for a 200 pixel vertical resolution or a 480 pixel vertical resolution.
In effect, DOS and ROM (BIOS) services combine to provide "translation"
services between the virtual world manipulated by the application and the
physical world of the video monitor. In Windows’ graphic environment, the
GDI provides these same "translation" services for applications, allowing the
applications to operate in a "virtual" environment and mapping the
application’s output to the physical hardware display, whether a CRT or a
hard-copy device.
Thus far, the GDI has been used only in a limited respect to display text
information with a brief mention of fonts and text alignment. It is now time
to look at the GDI from a more general standpoint before, in subsequent
chapters, manipulating colors, images, and graphic/text combinations.
The GDI does far more than mapping output images froma virtual display
to a real display, and one of the first items which we will look at is how the
GDI permits Windows to determine, from the output drivers, what hardware

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)

Introducing Graphics Device Interface

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.

Accessing The Device Context


Access to the device context has already been demonstrated in preceding
examples, using the BeginPaint and the GetDC functions and using the EndPaint
and ReleaseDC functions to release the device context when finished. What you
should remember, however, is that any application should not attempt to hold
on to a device context beyond the immediate operation, particularly since
Windows has only five device context handles to share among all executing
applications.
Instead, a device context should always be released or deleted as soon as
the immediate output operation is finished. Of course, after releasing the
handle, the device context is no longer valid. .
When calling BeginPaint, a second parameter was included as:

hdc = BeginPaint( hwnd, &ps );

The EndPaint function also includes the ps parameter:

EndPaint( hwnd, &ps );

The ps parameter is a structure variable of type PAINTSTRUCT, and con-


tains a rectangle structure indicating the invalidate region within the window
client area and, specifying the region to be updated. Thus, with BeginPaint,
drawing operations are limited to the indicated region. The ps parameter (and
BeginPaint) are used in response to WM_PAINT messages when only a portion
of a screen needs to be updated. The GetDC and GetWindowDC are also used
to retrieve a handle to the device context. The GetDC function is used for
operations on the entire client window area and released using the ReleaseDC
function. Get WindowDC provides access not just to the client window, but to
the entire window—including the caption bar, menu, scrollbars, and frame as
well as the client window area. It does not, however, provide access outside
of the application window. Again, the ReleaseDC function is used when
finished.
Chapter 15: Introducing Graphics Device Interface 279

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:

ndce=—sCcreateDCce "DISPLAY! , NULL, NULL, NULL )>

The second function, CreateIC, is used to obtain an information context rather


than a device context. The difference is that an information context, unlike a
device context, does not provide a handle for output operations, but does
provide a handle that can be used to obtain information about the output
device.

Selecting The Device


The first step in accessing device information is to determine which device
information will be requested about.
For the video display—the CRT—information is accessible by creating an
information context using the default specification, "DISPLAY", as:

fecinvoe= Create!Ct “U1orLAY™ > NULL NUELO NULL

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:

GetProfilestring<« “WINDOWS”, {DEVICEY.> °";> szP nn


SHIEZAC
LOM @Si2)eal ee

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]

devace=PCl / HP LaserJet ,HPPCE EP T 1):

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.

Device Context Information


Device context information is data about the resolution and capacities of a
hardware output device such as a CRT video display, a printer, a plotter, a camera
device, or even a metafile (a file consisting of GDI calls in binary format).
The device context contains a variety of information that can be accessed
through the 28 identifiers defined in Windows.INC. Several of these identifiers
return word values composed of bit-flags for further breakdown. Indexes and
identifiers are listed in Table 15-1, and demonstrated in the Devices.C demo
application appearing at the end of this chapter.

Table 15-1: GDI Information Indexes


Index Meaning
DRIVERVERSION version number; as 0x300 for version 3.0
TECHNOLOGY device technology
Value Meaning
DTLPLOTTER Vector plotter
DT_RASDISPLAY Raster display
DT_RASPRINTER Raster printer
DT_RASCAMERA Raster camera
DT_CHARSTREAM Character stream
DT_METAFILE Metafile
DT_DISPFILE Display file
Chapter 15: Introducing Graphics Device Interface 281

Table 15-1: GDI Information Indexes (cont.)


Index Meaning
HORZSIZE physical display width (in millimeters)
VERTSIZE physical display height (in millimeters)
HORZRES display width (in pixels)
VERTRES display height (in raster lines)
LOGPIXELSX pixels / logical inch (width)
LOGPIXELSY pixels / logical inch (height)
BITSPIXEL adjacent color bits for each pixel
PLANES color planes
NUMBRUSHES device-specific brushes
NUMPENS device-specific pens
NUMFONTS device-specific fonts
NUMCOLORS entries in the device color table
ASPECTX relative width of device pixel for line drawing
ASPECTY relative height of device pixel for line drawing
ASPECTXY diagonal width of device pixel for line drawing
PDEVICESIZE size of PDEVICE internal data structure
GLIRECARS flag indicating clipping capabilities (1=True / 0=False)
The next three indexes are valid only if RC_PALETTE bit is set in RASTERCAPS
index and available only if driver version is 3.0 or higher.
Index Meaning
SIZEPALETTE entries in the system palette
NUMRESERVED reserved entries in the system palette
COLORRES actual color resolution in bits per pixel
RASTERCAPS bitmask indicating raster capabilities
Capability Meaning
RC_BANDING requires banding support
RC_BITBLT capable of transferring bitmaps
RC_BITMAP64 bitmaps larger than 64K supported
RC_DI_BITMAP SetDIBits and GetDIBits supported
RC_DIBTOQDEV SetDIBitsToDevice supported
RC_FLOODFILL flood fills supported
RC _GDI20 OUTPUT Windows 2.0 features supported
RC_PALETTE palette-based device
RC SCALING scaling supported
RC_STRETCHBLT StretchBlt supported
RC_STRETCHDIB StretchDIBits supported
282 BORLAND C++ 3.0 PROGRAMMING, Second Edition

Table 15-1: GDI Information Indexes (cont.)


Index Meaning
CURVECAPS bitmask indicating curve capabilities
Capability Meaning
CEZCIRCLES circles
CGSPIE pie wedges
CC_CHORD chord arcs
CG@SELLIPSES ellipses
CC_WIDE wide borders
CGiSTY LED. styled borders
CC_WIDESTYLED wide and styled borders
CC_INTERIORS interiors
high byte is 0

LINECAPS bitmask indicating line capabilities


Capability Meaning
LGLPOLYLINE polylines
LC_MARKER polyline
LC_POLYMARKER polymarkers
bit 3 reserved
LC_WIDE wide lines
E@sSTYLED styled lines
LC_WIDESTYLED wide and styled lines
LC_INTERIORS interiors
high byte is 0

POLYGONALCAPS bitmask indicating polygonal capabilities


Capability Meaning
PCO FOLYGON alternate fill polygon
PC_RECTANGLE rectangle
PC_TRAPEZOID winding number fill polygon
PC_SCANLINE scanline
PC_WIDE wide borders
PCZSTYLED styled borders
PC_WIDESTYLED wide and styled borders
PC_INTERIORS interiors
high byte is 0
Chapter 15: Introducing Graphics Device Interface 283

Table 15-1: GDI Information Indexes(cont.)


Index Meaning
TEXTCAPS bitmask indicating text capabilities:
Capability Meaning
TC_OP_ CHARACTER character output precision
LESOPISTROKE stroke output precision
TC_CP_ STROKE stroke clip precision
TCR 90 90-degree character rotation
TC CR ANY any character rotation
TC_SF X_YINDEP scaling independent of X and Y
TC_SA_DOUBLE doubled character for scaling
TC_SA INTEGER integer multiples for scaling
TC_SA_CONTIN any multiples for exact scaling
TC_ EA DOUBLE double-weight characters
TC IA_ABLE italicizing
TC UA ABLE underlining
TC SO_ABLE strikeouts
TC_RA ABLE raster fonts
TC_VA_ABLE vector fonts
high-bit reserved -- must be returned zero

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

Figure 15-1: CRT Device Information (VGA)

Device

Width: 268 (mm) HOR2ZSI2ZE


Height: 156 (mm) VERTSIZE
Width: 646 (pixels) HORZRES
Height: 486 (pixel/raster lines) VERTRES
Pixels per inch: 96 (horz) LOGPIXELSX
Pixels per inch: 96 (vert) LOGPIXELSY
Color: 1 (bits/pixel) BITSPIXEL
Color planes: PLANES
Device brushes: NUMBRUSHES
Device pens: NUMPENS
Device markers: NUMMARKERS
Device fonts: NUMFONTS
Device colors: 16 NUMCOLORS
Pixel aspect: 36 (horz) ASPECTX
Pixel aspect: 36 (vert) ASPECTY
Pixel aspect: 51 (diag) ASPECTXY
Device structure: 35 (size) PDEVICESI2E
Clipping flag: 4 (T/F) CLIPCAPS
Palette entries: n/a SIZEPALETTE
Palette entries: n/a NUMRESERVED
Color resolution: n/a COLORRES

Figure 15-2: HP LaserJet


Device Information

Device Capabilities

Width: 263 (mm) HOR2ZSIZE


Height: 266 (mm) VERTSIZE
Width: 2468 (pixels) HORZRES
Height: 3158 (pixel/raster lines) VERTRES
Pixels per inch: 366 (horz) LOGPIXELSX
Pixels per inch: 366 (vert) LOGPIXELSY
Color: 1 (bits/pixel) BITSPIXEL
Color planes: 1 PLANES
Device brushes: Ue? NUMBRUSHES
Device pens: 2 NUMPENS
Device markers: 8 NUMMARKERS
Device fonts: NUMFONTS
Device colors: NUHCOLORS
Pixel aspect: (horz) ASPECTX
Pixel aspect: (vert) ASPECTY
Pixel aspect: (diag) ASPECTXY
Device structure: (size) PDEVICESIZE
Clipping flag: (T/F) CLIPCAPS
Palette entries: SIZEPALETTE
Palette entries: NUMRESERVED
Color resolution: COLORRES
Chapter 15: Introducing Graphics Device Interface 285

Table 15-2: Video Device Resolutions

GetDeviceCaps CGA EGA VGA 8514/A


HORSIZE ((in) mm) (9.44) 240 (9.44) 240 (8.19) (11.02) 28
VERTSIZE ((in) mm) (7.09) 180 (1.89) 175 (6.14) 156 (8.27) 210
HORZREZ (pixels) 640 640 640 1024
VERTREZ (pixels) 200 350 480 760
ASPECTX (horiz) 5 38 36 14
ASPECTZ (vert) 12 48 36 14
(aspect ratio x/y) 0.416 0.791 1.000 1.000
ASPECTXY (diagonal) GS) 1S Feo) >i (19.8) 19
LOGPIXELSX (pixels/inch) 96 96 96 120
LOGPIXELSY (pixels/inch) 48 1? 96 120

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

Figure 15-3: An Application Using Logical Inches

3
fe

[File Edit Search Character Paragraph Document Help


H4
H

This 1s a monospace (fixed width) font displayed at 10 CPI


(characters per inch), corresponding to a standard Courier
typewriter font. The ruler above is in ‘logical’ inches
which are approximately 1.57 real inches (on screen}. This
‘fiction’ allows the screen display to be larger {and easie
to read) than a true 12-pt (10 CPI) font display.*

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

Table 15-3: Windows Mapping Modes


Default Origin
Mapping Mode Value Logical Unit — x-axis y-axis

MM_TEXT 1 pixel left top


MM_LOMETRIC 2 0.1 mm left bottom
MM_HIMETRIC 3 0.01 mm left bottom
MM_LOENGLISH 4 0.01 inch left bottom
MM_HIENGLISH 5 0.001 inch left bottom
MM_TWIPS 6 "/14g0 inch left bottom
MM_ISOTROPIC 7 variable (x=y) variable variable
MM_ANISOTROPIC 8 variable (x!=y) variable variable

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.

Setting (Getting) Mapping Modes


The various mapping modes can be set or retrieved using the SetMapMode and
GetMapMode functions:

SetMapMode( hdc, nMapMode );


nMapMode = GetMapMode( hdc );

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

Screen (Window) Origins


All mapping modes under Windows have variable origin points which can be
selected using the SetViewportOrg or SetWindowOrg functions to specify a
desired 0,0 point.
The SetViewportOrg function sets the viewport origin of the specified device
context and is declared as:

DWORD SetViewportOrg( HDC hdc, int xcoord, int ycoord )

The SetWindowOrg function sets the window origin of the specified device
context and is declared as:

DWORD SetWindowOrg( HDC hdc, int xcoord, int ycoord )

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:

SetViewportorgGuhdc,wexclient /) 2 9cyClientv/i2) Di;


or
SetwundowoOrg@ nde, cxcirent / 2, icyClient /M2cr) >

Either of these statements would establish client area coordinates as shown


at the left in Figure 15-4, or, using both, would establish client window
coordinates as shown at the right in Figure 15-4.
290 BORLAND C++ 3.0 PROGRAMMING, Second Edition

Figure 15-4: Client Area Coordinates

Using Mapping Modes With Physical Units


While the text mode uses coordinates expressed in screen units, all other
mapping modes supported by Windows express logical coordinates in terms
of physical measurement units, that is, English, metric and point (TWIPS)
measurements. However, as you can see in Table 15-4, all of these modes use
units which are higher in resolution than the video display.

Table 15-4: Comparative Device Resolutions


Device Logical Size Unit Inches Millimeters
EGA Video (H/V) 640/350 pixels 0.014800/0.0197 0.3750/0.5000
VGA Video (H/V) 640/480 pixels 0.012795 0.3250
MM_LOENGLISH 0.01 0.010000 0.2540
MM_LOMETRIC 0.1 0.003940 0.1000
LaserJet Printer 0.003333 0.0846
MM_HIENGLISH 0.001000 0.0254
MM_TWIPS pt 0.000694 0.0176
Typesetter dpi 0.000500 0.0127
MM_HIMETRIC 0 mm 0.000394 0.0100

Further, in the case of MM_HIENGLISH, MM_TWIPS and MM_HIMETRIC,


the mapping resolution is still higher than laserjet resolution. Of course, there
are still higher output resolution devices such as 2000 DPI typesetters, but
these are hardly commonplace and are still exceeded by the MM_HIMETRIC
resolution. If images calculated in these modes exceed display resolutions,
Chapter 15: Introducing Graphics Device Interface 291

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.

Variable Mapping Modes


Windows also provides the MM_ISOTROPIC and MM_ANISOTROPIC map-
ping modes which, unlike the previous modes, permit changing the viewport
and window extents, the scaling factors used by Windows to translate logical
coordinates to device coordinates. The difference between these two is that
MM_ISOTROPIC uses the same scale for both the x and y axis while
MM_ANISOTROPIC permits two different scales to be used for the x and y axis.

SetWindowExt and SetViewportExt


The SetWindowExt and SetViewportExt functions are only valid with the
ISOTROPIC and ANISOTROPIC mapping modes, and are ignored when the
TEXT, METRIC, ENGLISH, or TWIPS modes are in effect.
The SetWindowExt function determines the size of the window in logical
units. In Isotropic mapping, the x and y axis extents remain equal even though
two extent parameters are still required when setting the range. In Anisotropic
mapping, the two extent parameters may specify different ranges for the x and
y axis extents.
292 BORLAND C++ 3.0 PROGRAMMING, Second Edition

The SetViewportExt function assigns x and y viewport extents that define


how much the GDI must stretch or compress units in the logical coordinate
system to fit units in the device coordinate system. Again, in Isotropic map-
ping, the x and y axis extents remain equal while, in Anisotropic mapping,
different ranges may be specified for the x and y axis extents.
As an example, if the window x-extent is two and the viewport x-extent 1s
four, the GDI maps two logical units (along the x-axis) to four device units. In
similar fashion, if the window y-extent is two and the viewport y-extent is one,
the GDI maps two logical units to one device unit along the y-axis.
However, since the viewpoint y-extent is negative and the window y-extent
positive, the vertical y-axis coordinates are reversed, and the positive y-axis
points in the logical coordinate system are mapped to the negative y-axis in
the device coordinate system. If both are positive or both are negative, positive
logical coordinates are mapped to positive device coordinates and vice versa.
Note: When Isotropic mode is set, an application must call the Set WindowExt
function before calling the SetViewportExt function.
Both of these functions are illustrated in the MODES.C program,below.

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.

Figure 15-5: Three Mapping Modes

UpRight: (1637,582)

Center: (159,89) Center: (518,291)

__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.

Figure 15-6: Menu for the Devices 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

int GxeGinine weCryaG Nila

void ListBasic( HDC hdc, FHDIGuhiDiGelinGhOne


{
Sutra nen eC meSatan UIC: Camm Time in) else & Canales S3ZClOlnesate =
Ginlalipee Siz Disicipr- char *szNotes; y AMO EA =
i RIOIRIZSSeleZace, MRNOUR ZS if 21S 5 Wal Clatal nase Crinum)7
VIEIRGOnle-aEa, WABI S if7215p Miate@ ine” Cin)”,
HiOIRIZARIESr TALON 76 Rie SO PS Cheln ” ys pa GDEIEXCeuLES Dues
VERTRES, "VERTRES", "Height",
"Cpixel/raster lines)",
OIG PAGE sxe, “TE OGIPIMIEILS MY “PU veals fer winely”’-
eon Oil Z- au
OIG PAL XCE IE Siie, MLO ILS Al - MPaUvals Meir wine’
ae Cole ta) baa
BITSPIXEL, HBL TSPIXE Ly "Color", "Cbits/pixel)",
FAAINIES Se, POILINNES GOOD alniersmar. poo
NUMBRUSHES, "NUMBRUSHES", "Device brushes", ort
NUMPENS, "NUMPENS", "Device pens", 6
NUMMARKERS, "NUMMARKERS", "Device markers", ee
NUMFONTS, "NUMFONTS", CUD Vilaciemm tlOl G:Siue, as
NUMCOLORS, NIU IMIGIOMROIRe Siu, DIC WaECeCamC OMAOliSuuer, paar
AISIEJE Gilexe, MINS IPIEG Ik Mariel AS ace, MC Nie e -
AVSiRJE Galaye, MIASIPIEG TC PaleXcGsU mals Dectmus mm Vien Ge) ae
ASPECT XY, MINSIP EC LL - Pixel aspect: 7 Gd irag). ,
PIDIE WAN GEySily
Zee ey DIES VG ESS lyZsEee ees DIC VIUGIC nS Gly UlGataUl
lI Clan
Wie sq ze)! |
CELRCARS, MME TL ENE IIPS "CI/lFI, Tlippiing rlag™>»
Uf Following three indexes valid only if device driver
if sets the RC PALETTE bit in the RASTERCAPS, andex and
j/eeavaimapve only at idrivereversion 1s 3.0 son ehigher.
SELERALEGIE ea SLLEPALE bees «Palette ventries©» oe
NUMRESERVED, "NUMRESERVED", "Palette entries",
"Creserved)",
COLORRES, BC ORORRE Sse, LCO-Or shes Ol Ut Onis, we
3;
char. szeutTteso0d;
int ip mbines

forme 10s i<sizeotvinto)4sizeot(intolOI); 14+ 9


296 BORLAND C++ 3.0 PROGRAMMING, Second Edition

if GuGeint Obiaendlid xt =eS CEPA LEMne eo let


C antolidentdx ==" NUMRESERVED.). |.
C intolidsnidx == COLORRES > )
TextoOune hace. exci Cyc” Ci iy pe scout,
Sorwimier¢ Seawsri. “Wires naa AC OS lee Asa
Tn OLA die siz Dis Cpa.) ei OG ds 6. 1s es,
else
rex eoucc Ndcs cxCnr,. cy Chis CTH es Ze tiw.,
Sprint ( SszBUrat = 12S 9 6. Olen CU San ena Siar,
TAO lenSIZ1 DISC) Dy,
GetDeviceCaps© hDcintoe,, Intobad.nldxs
Thiol iS ZNO eGSy set Otel 1S:2 COS tee,
}

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

iMac i, j = GetDeviceCaps( hDcInfo, DRIVERVERSION );

Textout Cehdey ex Chir cy Ghr> es2butt,


SpPRIntte szButt > supnivern Version: «42da702d",
HEBiD wLOBY TEC i) a) p>

TextouUeeCendc,ecxcht, cy Chiise.iSzButt >»


SprinicerG ois, IgereMMLOMsO!G Yaseee/ASiaee
sziechl GetDeviceCaps(€ hDcInfo, TECHNOLOGY ).1 ) );
TextoucCahdc,wexChr, cyChr<4a>7eszButt,
SID ileal Cana GueSiZa
6 Ubtiata
BRaSte@recapabi-hi ties CRASTERCAPS)" ) )::
for(€ i=0; i<sizeof(raster)/sizeof(raster[L0]); i++ )
if€ € rasterlLil.nMask == RC_BANDING ) ||
C rasterfiid.nMask> ==) RC_PALETTE )~)
LextoutCehdes pexcChet4yeacyChr*Ci4+6)> oszBurt t-
SPEINTEGRSZBUTT, (i EAcIyZ=S54s 9] -7555
GetDeviceCaps( hDcInfo, RASTERCAPS ) &
PESO PIE a atiwasle 4 Ue ea YU.
PasterBisdeszDescp ,enasterliduszMask ) -)::
else
ie xct OU)tas hidics, mc XC Nine 47 me cy Chins Gitiopr-mEs 2B Untete,
Sorimer« szamry, “ACA Supports Z=25S || 4S"
GetDeviceCaps( hDcInfo, RASTERCAPS ) &
ars jte nm bet eun Mials: kee? eXGbe se eet
nals) tein nprersiz DISiC Di mehals tei aelnerSiz Mais: Kae ee
}

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

"Integer multiple scaling",


i Game Atm GOIN EL NG, HE S/N COR BEN
"Any multiples tor exact =scaling. ,
Theale AwDOUBIEE- IC E/N DOWIEMLIEY’ ,
"Double-weight characters",
THEN _ AM SsIETS ME tN I NBILIS My LEN ta EGSa,
Gan WAM AS Er EWN /MBILIE 5 Undies linn Gi,
Gas OMA BSE, FUE SO_INILIE™ - Sep WKGOUES” >
1 Cap eAme AS eee. TGR AA BIE me, Ra Sit eG nameOlnitsSiar,
ip Gan VASE AB Ev, PIE NWN WSL [eB a VIG Cet Olnmmnt OlniteSum pee
Sitiaieal CMEC NainueS ZB Ulta eo Ole
int tle

re xcciOuit Garhidicy- mc xiC hieamcty Ginitn-aeS:Z BiUbhatie


Sprintrc szBuft, -=+Lext ‘capabilities *CLEXTGARS) 2)
for, 1=03 i<sizeotCtextd /sizeot (text hOl) >] 31473)
pe xc OU tC Gunicicy mec 0G Nlhem ¥.G Dien Gc SD) mee 216 Oltat ee
Sorimer Srpmiir, Merced Zoos || 4st,
GetDeviceCaps( hDcInfo, TEXTCAPS ) &
texctliadenMask ee xX ee
EEDECT d+ SHMSCa, COXUILITA. SeWeASie » YF
}

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

Tex toutt “hdc, *cxChr, cy Chr es zeutn,


sprintt( szBuff, “Curve capabilities CCURVECAPS)".) Dy
for€ i=0; i<sizeof(curves)/sizeof(curves(l0]); i++ )
Textout nde, exChreacyCherCi4s). szBuane
sprintf <€ szButt, *Ch2cea Supporese4=e5 smn ou
GetDeviceCaps( hDcInfo, CURVECAPS ) &
cucvesttdtnMask7 2. 2X... = eae
curveslid2szDscpys cunvest isa szmMas kaa):
Chapter 15: Introducing Graphics Device Interface 299

woldslastlbines CeHDeGahde| HDC hDelnfo-.-)


{
Static BITS “inesLir=
{ [Cae ORAL INIEP ALG. POL NLitNs LUpiolley, Ualinie:s ua,
LC_MARKER, PEG MUN TRESS IR "markers",
LC_POLYMARKER, MIL POL AAMARIKIER - "polymarkers",
i CaeWEL DEF vECLWALDEMs "wide Lines",
Caro SEDs, AEE Si yk - "styled Lines",
Caw DIE Sapa De MYL MEST YEE I
"wide and styled Lines",
Coe lGNGTGe Rel OIRsSe, oo Cee N ape REINO IR!S mur, Eo IMP Wop Ss oY is
Sita tucume Nain s 2b Un tlnoO
ay
int ie

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
}

ViOuICmn aes: Golly, Guar iD GunCice-aaH|D) Gash DicelinitOm


{
SitvaltaGis Bilal ompiolley [en] ae
if PiGaaP.OlevaGiOING, nee Come Ol SVaGOINGae,
"alternate fill polygon",
PiGR RIE CIPAINIGIINER, OE Ue Gu /Aelbie” - "rectangle",
RiGee RARE ZsOn De POG RAT PS Z2(0) JEib)?”
"winding number fill polygon",
PiGaeoG AIN LN Ev H PiGaes CANIEL NiEw LS 7Crai naa ealine:Sinee,
PGW DEF bale Camel DiEan, aaWnIGLe mm Oma Cle Smaue,
RiGaeSieyIREDr, MG Syke p” - "styled borders",
PiG@WehD E-SilpleeiDr MG DES PY Le)
"wide and styled polygons",
PAGaehINGEsReOlRton, FAOC MIN WIE LORS Sp TTVMEGr TORS” Be
Statice char SzbutseEso0d;7
i (aie “ys

TextoutG hdc y~scxChnmcyGhryaszBu fit 7


sprintt< szButf,;
YPPolygonal capaba Witt és CPOLYGONALCAPS).” .), dx;
Tomer heO- i <sizeotopoly »
7sizeot (polylLOl); i++ >
300 BORLAND C++ 3.0 PROGRAMMING, Second Edition

Tex t0unG hide wscxChroacy Chine Cit5) >) s2bUin,


SprAuncik SZbUTHy scum SUPDOnt Si. 4—2i.6 8 ase,
GetDeviceCaps( hDcInfo, POLYGONALCAPS
poly Ea nMasike ? AUX. 9 ee Do,
polyLisi-szDsep,* pol yEtieszMaskey >,
}

Long FAR PASCAL WndProc( HWND hwnd, WORD msg,


WORD wParam, LONG LParam )
{
SiGiantalnc lin GainiGuir Die Ve—— hDIMmG Rien Culm nhhOm—aeleD MaeByAtoinGy.
char SZPrnoOAdl-
char SZ DEV 1Cel aS ha Vel> pues Siz OU Ute
HDC Hicicy-wenicicalnnmor:
HMENU hMenu;
PALIN omiR U.Calis psy,
PE XSRME WRC tm;

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

CheckMenultem( hMenu, nCurInfo, MF_CHECKED );


InvalidateRect( hwnd, NULL, TRUE );
esc uUien G OnE:
+} break;

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

int PASCAL WinMain(€ HANDLE hInstance, HANDLE hPrevIinstance,


EPSTR lLpszCmdParam, int nCmdShow )
{
static char szAppNameL] = "DEVICES";
HWND hwnd;
MSG msg;
WNDCLASS WC;

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

DESCRIPTION “DUSIRILAVS DiEWicts (AIPA


EI piles”
Ee PV PIE WINDOWS
STUB PANT LINES TEU) 6 1S
CODE PRELOAD MOVEABLE DISCARDABLE
DATA PRELOAD MOVEABLE MULTIPLE
HEAPSIZE 1024
SRIPALGIKe SNL rae 8192
EXPORTS WndProc
[eee
Se] Se Seeee/ /
if DEVICES sah ifif
f/f SSS Ss SS] 5323 S5///

#define IDM_CRT 101


#define IDM_PRN 102
#define IDM_BASIC ONS
#define IDM_OTHER 104
#define IDM_CURVE 105
#define TD/IMEIE NIE 106
#define TDIMasPiOley, 1)(07%
#Hdefine IDM_TEXT 108

j/ fPoesesessseesa=seseneee//
ia Modes.C Lf
// C++ Windows Modes //
/ PSS saopaSeeSss=Ssessee/ /

Hinclude <windows.h>
#include <string.h>
#Hinclude "modes.h"

HANDLE Dens ite


a Jayie XViewAspect, YViewAspect, XWinAspect, YWinAspect;
304 BORLAND C++ 3.0 PROGRAMMING, Second Edition

#pragma argsused

BOOL FAR PASCAL AspectProc( HWND hdlg, WORD msg,


WORD wParam, LONG LParam )
{
CihialiseSiZ4AtS
ple cata edn Oely=

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, )':

Long FAR PASCAL WndProc(€ HWND hwnd, WORD msg,


WORD wParam, LONG LParam )
{
static wit. ncurorg =-LTDM UPLEFT;
nCurMode = IDM_TEXT;
static char *szCurModeL] =
Ue hexteis LOW INE rs.c > “sha ohame era. ct,
‘Lom leincikisin’, “hha tinglisine, “iiss? -
peleS/OuG
O Pilic ue meee AN 1S Ont ieOlpal Chae.)
+isi7 CurOirglis. =
(ofUpper ihett’> “Lowers bef ti “Centered” +>
eG NBUtth x Lefty, eck 1 ght.) «Center:
YiikOp=yiDOtton way Gen Gel, sO mg DG,
CXUGINee mercy, Cilnine=
char szBuffl641i;>
HDC Nidice ah dic Linshor
HMENU hMenu;
DWORD WinSize;
RECT rec te
POINT Pity;
PAVING ES ei R.U Cilmene Diss
imEXoRM En Ree tm;
FARPROC EDP moc

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;

Gasieel D Mal siOmROIR


AL C=
case IDM_ANISOTROPIC:
EnableMenultem( hMenu, IDM_ASPECT,
MiFae NAB ics ae
DrawMenuBar( hwnd );
nCurMode = wParam;
nCurOrg = IDM_CENTER;
break;

case SLD MRUPEEF ie nCurOrg ED IMSUIPISES cate


case 1 DMEDNEEFTi: nCurOrg IDM_DNLEFT;
case IDM_CENTER: nCurOrg TaD MeGiE NERD:
}
InvalidateRect( hwnd, NULL, TRUE );
CheckMenultem( hMenu, nCurMode, MF_CHECKED
CheckMenultem(€ hMenu, nCurOrg, MF_CHECKED
SoriumMmEere Suburi, YAS : Say
szCurModeCnCurMode-IDM_TEXT],
szCurOrglCnCur0rg=IDMLUPLEFTI) Ds
SetWindowText( hwnd, szBuff );
Rec uirnniGU>s-
Chapter 15: Introducing Graphics Device Interface 307

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 );

SetMapMode( hdc, nCurMode - IDM_TEXT + 1 );


SleltiWiinidioiwiOlnG) Guhidicy-an 0) Om)»
switch ( nCurMode )
{
case EDIMaa es ORO ANGE
case IDM_ANISOTROPIC:
SetWindowExt( hdc, XWinAspect, YWinAspect );
if€ XViewAspect && YViewAspect )
SetViewportExt( hdc, XViewAspect,
YViewAspect );
else SetViewportExt( hdc, XWinAspect,
YWinAspect );
break;
}
GetClientRect( hwnd, &rect );
DPtoLP( hidicy GEOINT mr cimelciG:, mcmnD a
switch ( ACURORG »
{
case IDM_UPLEFT:
SieltiWitinidiow OlmgiG@ehid/ cy, 0). Ol»:
break;
case IDM_DNLEFT:
SetWindowOrg( hdc, 0, -rect.bottom );
break;
case IDM_CENTER:
SetWindowOrg( hdc,
—Crnectelied
t+mec te ha gihto./-2.,
—(rect.toptrect.bottom)/2 );
break;
}
GetClientRect( hwnd, &rect );
DPtoLP(¢ hac ACEP POMNIDarS rect, 2. J.
308 BORLAND C++ 3.0 PROGRAMMING, Second Edition

xLeft =sMeiC.tre Wests


xRight = rect.right;
yTop = Rect. Gop,
yBottom = rect.bottom;
RCenten b=" Cixh VON tetaXLe tte we ec,
yCenter = © yiop.+.yBottome) 7 2;

RestoreDC( hdc, OrgDC );


GetClientRect( hwnd, &rect );
DIP;GoleP Gehidic a CEP POL Nimans Le Cicer cme
nButf = sprintt< szBuTt espe tt=.4 (4d 7a 0
xett> yiLop
Text0Oute hdc; rect eft, orect. toppeszeburticenburti.),

MIM = SoPiimer¢€ Sziurr.,. “Dilbevrwes Geel, ~6ely -


ILE A Seieeom 2) 5
eX COUT Galhidicuuah eGiuenWielt tr-mmine Cit r DiOMGEtOMeIG yaGiniInes
SB Unie en D Un haeDsy

Dr? & Sprimere Salsuir. “Camrers sel, 4el) 7,


xCenter, yCenter );
fextout ( hd¢e, Crect=right—rect.Uef t—nBuf f*cxChr)/2-
(rect bottom=rect.top)/2,
SAuiiir., miawag MF

HBUTteraSPranttiG szButt “UpRight«s(7d 70d.


MERIC Nite meYalLOl
pp E-
TextOutC hdc, rect..nignt— (nButi?) Cx Chr). rec tm co p-
S ZB Ul teen Usha as

nButt- = sprint? € szButt,> <“DnRight< C4d-7d)4,


XGRaIG) NitemeysB1O) Gato mea
TextOut€ hdc, rect.right—GnBuff*cxchr),
rect. bottom=cyChr, =szbutt. nButh )-

EndPaint( hwnd, &ps );


return(Q);

case WM_DESTROY:
PostQuitMessage(0);
return(Q);
}
return(€ DefWindowProc( hwnd, msg, wParam, lParam ) );
}

#pragma argsused
Chapter 15: Introducing Graphics Device Interface 309

int PASCAL WinMain( HANDLE hInstance, HANDLE hPrevinstance,


EPRSuR lLpszCmdParam, int nCmdShow )
{
static char szAppNameLJ] = "MODES";
HWND hwnd;
MSG mMSQg;
WNDCLASS WC;

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

DESCRIPTION "WINDOWS MODES"


EXE Rie WINDOWS
STUB WeNiSsiUIB
a EX Eee
CODE PRELOAD MOVEABLE DISCARDABLE
DATA PRELOAD MOVEABLE MULTIPLE
HEAPSIZE 1024
STACKSIZE 8192
EXPORTS WndProc
AspectProc

}/PSeassesesescecsesesesase/ //
iif D:\BC\MODES.H Ifi
jf SeSSeoeSSaeseseeesse///

#define IDM_TEXT 201


#define IDM_LOMETRIC 202
#define IDM_HIMETRIC 205
#define IDM_LOENGLISH 204
H#define IDM_HIENGLISH 205
#define IDM_TWIPS 206
#define TED Nelo OssROlPs Isc 207
#define IDM_ANISOTROPIC 208
#define SOE URE EL ay 301
#define IDM_DNLEFT 302
#define IDM_CENTER 50S
#define IDM_ASPECT 401
#define 1DD_XVIEWASPECT 501
#define 1DD_YVIEWASPECT 502
#define LDD_XWINASPECT 503
#define LDD_YWINASPECT 504
jf [Be SeSs S32 SS SSSeSeSseces// /

ii MODES OUtalog "Script. 77


/ / SSeS s= SSS se5e2]Sseeses
/ //

ASPECT DIALOG DISCARDABLE LOADONCALL PURE MOVEABLE


10,S47 164, fe
STYLE WS POPUP: || WS_CAPTION | Ox80OL
CAPTION "Viewport / Window Aspects"
BEGIN
CONTROL MODES OF AL Cee, s Woeec HED WS_VISIBLE
OXS eee ae oe LO panko
CONTROL URAXVS Extent” (0 2.) Sa A Car eW'S-.c HED WS_VISIBLE
Ox2lyi 3 S64 Geet
CONTROL PAX IS Extent) 03S VAT Clos aW Se Chil
(sD WS_VISIBLE
OxZ, 5) AGG eae ee
CONTROL "Viewport" OF: STATICS WSeGH LISD WS_VISIBLE
Chapter 15: Introducing Graphics Device Interface 311

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// /

MODES MENU LOADONCALL MOVEABLE PURE DISCARDABLE


BEGIN
POPUP "&Modes"
BEGIN
Menultem se ee OEXa tae Z\OM
ib aeCHIENEID
Menultem "&LoMetric", 202
Menultem "&HiMetric", 203
Menultem MLM@lE Mel tt Sin” - 204
MenuIltem "Hi&English", 205
Menultem Ueliee Wilt PISaLe, 206
Menultem "&Ilsometric", 207
MenuIltem "&Anisometric", 208
END
POPUP "&0Origin"
BEGIN
Menultem "&Upper Left", 301, CHECKED
MenuItem. "&Center", 303
Menultem "&Lower Left", 302
END
MenuIltem "&Aspect", 401
END
= a

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
:

va hay Shen gscon ‘vt Dae


\ reer oie ae BY arb ehare 4 -

a penne: 2in Seuss2SRENA TE OD Leadewhhe un 9


: 7 : ‘aedronee wap
4

(i ."*t2eTs”- ast che


soe’ ; “2 Fagen nem
ba » astazgertngr ews
e0S"* .. eeblendass~. weele
oe) ' “Avlinesatn” estius
us e ,enlvale

~205* °“" sto? eaos te"


SUS. "Si ssemssinee

— ‘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

Colors in the Windows environment were discussed in part when we talked


about device capabilities and how color information was organized by differ-
ent device types. This previous discussion, however, was principally intro-
ductory and did not cover a number of topics that are integral to developing
graphics in Windows. In this chapter, we will look at how color and drawing
information are handled under Windows, beginning with how various
devices handle color information.
For some devices, color information is organized by color planes, with each
plane representing a specific primary color (red, blue, green), and each bit
within a plane representing a single pixel. In this arrangement, a pixel’s color
is determined by a combination of the corresponding bits from the several
color planes. In other cases, devices use a single color plane with adjacent bits
representing the pixel color.
The GetDeviceCaps function is used to query the color planes and bits per
pixel, as well as the number of colors supported :

nColorPlanes GetDeviceCaps( hdc, PLANES );


nBitsPerPixel Gert Die ValnGe) Galpisi Ga nidicy sa BrllhSi
Pl eles E
nColors GetDeviceCaps( hdc, NUMCOLORS );

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

Table 16-1: Device Color Capabilities


Device VGA 8514A LaserJet Plotter
nColorPlanes 4 1 1 1
nBitsPerPixel 1 8 1 1
(calculated colors ) 16 256 Ds 2
nColors 16 20 2 8

In theory, the device color capabilities (nColors) could be calculated as:


gnColorPlanes * nBitsPerPixel

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.

Windows Colors vs Device Colors


The device color limitations are not necessarily limitations on colors that can
be used by an application, because Windows will map colors or use dithering
(patterns mixing pixels of pure colors) to represent colors which are not
directly supported by the device. Thus, on a laserjet printer or a monochrome
video, for example, video colors will be mapped to shades of gray. Or, on a
VGA which supports on 16 "pure" colors, all other colors are dithered, using
two or more supported colors in patterns of mixed pixels.
One good example of dithered colors is found in the Bitmap editor in
Whitewater. The editor supplies a palette of 28 colors consisting of 16 "pure"
colors on VGA, and 12 dithered colors. Further examples are shown in Figure
16-2, where the Colors.C program is used to demonstrate the full range of
Windows colors, and in Figure 16-1 which shows how colors are dithered to
supply hues and tones not directly supported by the display device.
Chapter 16: Colors in Windows 315

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.

Table 16-2: Comparing Color Specifications


Color Windows DOS (CGA)
Simin Lend
Black 0 0 0 0 0 0
Blue 0 0 25)5) 0 0 il
Green 0 ae (0) 0 1 0
Cyan 0 290) L200 0 1 1
Red DMs 0 1 0 0
Magenta 5 5a) NS, 1 0 1
Yellow DSS P35) {Y) 1 1 0
White MES) PA5\3) PAS) 1 1 1

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.

Figure 16-1: Four Dithered Colors (enlarged pixel maps)

Red (166) Green (175)

Blue (256) RGBC166,175,256)

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.

Figure 16-2: Creating Colors

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:

DeleteObject( GetClassWord( hwnd, GCW_HBRBACKGROUND )


SetClassWord( hwnd, GCW_HBRBACKGROUND,
CreateSolidBrush(
RGBiGe CV ale OMe GVial leblele GV alllcel a)a eee
InvalidateRect( hwnd, NULL, TRUE );

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

hBrushlil = CreateSolidBrush( RGBcolor );


UnrealizeObject( hBrushlil );
return(€ (CDWORD) hBrushLlil );
}
break;

This operation is executed in response to the WM_CTLCOLOR message that


is sent by Windows to the parent window of a predefined control or message
box when the control or message box is about to be drawn (or redrawn).
The wParam value is a handle to display context for the child window and
the low-order word ( LOWORD(IParam) ) contains the child window handle
while the high-order word ( HIWORD(IParam) ) identifies the control object
type as shown in Table 16-3.

Table 16-3: Control Type Identifiers


Value Control Type
CGIECOLORSBIN Button control
CTLCOLOR_DLG Dialog box
CTLCOLOR_EDIT Edit control
CTECOLEORSEISTBOX List-box control
CTLCOLOR_MSGBOX Message box
CTLCOLOR_SCROLLBAR Scroll-bar control
GIECOLORSSPATIEC Static control

As demonstrated here, this CTLCOLOR_xxxx value is useful, when an


application needs to intercept redisplay instructions for specific control ele-
ments. Alternately, if no specific response is provided to the
WM_CTRLCOLOR message, as in all previous examples, the DefWindowProc
function selects the default system colors.

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.

Destroying Brushes When Finished


During the execution of this example program, a number of brushes have been
created and destroyed, but when operations are finished (that is, when the
WM_DESTROY message is received), provisions are needed to destroy these
brushes:

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

Using Dithered Colors


While dithered colors extend the range of available colors beyond the default
palettes, there are some limitations on how dithered colors can be used.
As explained, dithered colors are based on an eight-by-eight pixel array to
simulate color hues which are not directly supported. As such, dithered colors
can be used for area fill, but cannot be used for pixel drawing operations such
as SetPixel, or for line drawing operations such as LineTo or LineRel.
Instead, for pixel or line operations, the GetNearestColor function can be
used to return the closest pure color supported. GetNearestColor is called as:

rgbPureColor = GetNearestColor( hdc, rgbDitherredColor );

In normal circumstances, of course, Windows will handle this conversion


automatically. If necessary, an application can query the device context (hdc)
for the nearest pure color value.

Colors and Drawing Modes


In conventional DOS graphics, a drawn line simply replaced or wrote over
existing screen pixels with a drawing color. In Windows, however, multiple
drawing modes are supported in which, again using a line for an example, the
line image is combined with the background (existing) image in a variety of
fashions.
This type of operation is referred to as a bitwise boolean operation or, in
Windows, as a "raster operation", commonly abbreviated as "ROP". Because
line operations involve two pixel patterns, the line and the screen display,
these are referred to as "ROP2" operations and, in Windows.H, sixteen ROP2
operations are defined as shown in Table 16-4.

Table 16-4: Binary Raster Operations


Boolean
Mode Constant Operation* Image Resulting
R2_COPYPEN iP pen overwrites screen (default)
R2_NOP S screen not affected
R2_NOTCOPYPEN al pen inverted
R2_NOT ~S screen inverted
R2_MASKPEN P&S pen ANDed with screen
R2_MASKNOTPEN ~P&S inverted pen ANDed with screen

*(S = Screen P= Pen)


322 BORLAND C++ 3.0 PROGRAMMING, Second Edition

Table 16-4: Binary Raster Operations (cont.)


Boolean
Mode Constant Operation” Image Resulting
R2_MASKPENNOT R&-S pen ANDed with inverted screen
R2_NOTMASKPEN ~(P&S) pen ANDed with screen, result inverted
R2_MERGEPEN Paes pen ORed with screen
R2_MERGENOTPEN oP AS inverted pen ORed with screen
R2_MERGEPENNOT Pele-S * pen ORed with inverted screen
R2_NOTMERGEPEN ~(P 1S) pen ORed with screen, result inverted
R2_XORPEN prs S pen XORed with screen
R2_NOTXORPEN me) 0) pen XORed with screen, result inverted
R2_BLACK 0 black line (drawing color ignored)
R2_WHITE i] white line (drawing color ignored)
*(S = Screen P= Pen)

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

Figure 16-3: Binary Raster Operations

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)

25% Séx% 75% 160%! Blue Magenta Wh ite


Grays Green Red Yellow

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>

#define CHD ie ovleyaleie WS Chit |) WS War Sa [sible

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

char SiZiB Ui ten OMr=

Long FAR PASCAL KeybdProc( HWND hwnd, WORD msg,


WORD wParam, LONG LParam )
if
int i = GetWindowWord( hwnd, GWW_ID );

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 ) );
}

Long FAR PASCAL WndProc( HWND hwnd, WORD msg,


WORD wParam, LONG LParam )
{
static HBRUSH hBrushisd-
HDC hdc;
Long RGBcolor;
nant I, ocxwind, cy Wood) cy Chri 1c xc ery size
TE Xai Exe elec tim):

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

int PASCAL WinMain( HANDLE hInst, HANDLE hPrevIinst,


IPSS IP LpCmd, NBG nCmdShow )
£
Sit aca Game hain szAppNameL] = "COLORS";
statuenmcehanr Sw Cole@rlaljalieal S WRedia ee Gree nme. Bite +;
FARPROC LpKeybdProc;
HWND hwnd;
MSG msg;
int re
WNDCLASS WC;
Chapter 16: Colors in Windows 327

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

ShowWindow ( hwnd, nCmdShow );


UpdateWindow( hwnd );
while€ GetMessage( &msgue NULL IGRI: )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
return(€ msg.wParam );
}

Pas SS SSS Sasa 7

: GOROIRS
=) DIE Fase
pas SSS SS S35 55> 5

NAME COLORS

DESCRIPTION “Gmeat ang Colors.


BX EAL YP E WINDOWS
STUB AW LINES 1 WB 6 ECS
CODE PRELOAD MOVEABLE DISCARDABLE
DATA PRELOAD MOVEABLE MULTIPLE
HEAPSIZE 1024
SWAGCK SL 4E 8192
EXPORTS WndProc
KeybdProc

if f/fSoS plese seS=eHe==eeeeees


// //
Le PenDraw.C i if
i if C++ Windows Drawing LA
/ f/fSasso =S ees Ceneoseeesea// /

#Hinclude <windows.h>
#include S18 1 TA} ole
#include "“pendraw.h"

Long FAR PASCAL WndProc(€ HWND hwnd, WORD msg,


WORD wParam, LONG LParam )
{
Sitrartelrcamc O) OR RIES peas) Gor Sol lon m=
{ RGBC OF OF ORD (e/a BilealcKk
RGB O, OFZ Die) // Blue
RGB¢ OF eadinl; Osa, // Green
RGB (¢ UA WApiay C2e%S) 9) // Cyan
Chapter 16: Colors in Windows 329

RIGBIG=ZpiD:, oF OFDe, // Red Lf


RGBiGeZpip:, OF 425to¥ eS // Magenta //
RIGIBIGR Zp ipi aceon, OF Hi VOlow Wi
RGBG 22i5'9:,42.0.5 ,ecoD oh HP: // White a
SitahtaliGammeninkt nColor = IDM_BLACK;
HDC nicics-
HMENU hMenu;
HBRUSH hBrush;
HPEN hPen;
PAINTSTRUCT pise
Re Cai meict::
int ees en MOdier-eeH Uiniktisue— OOP aVIUInintts a —amlpoe>

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

// Stock Brush Backgrounds //


for’: 1=0;" 4<5> 1-+3->)
“fl
SOURGECEC SPeEGe., vs, Op, Weasd, WUMies 5
FaWwURect G@ hdc, cimect, sGcets toc kOjbijie
ct: @mil)))):
ip

// Color Bar Backgrounds //


FOR ay = 17) €7<6 Fyre.)
{
AB MUS aeGielartiels
olla Bisuisin Gua Goll omens peel ey
SetReact.Gesrect) je srias, 0, “j *5o+iF VuUnits >
FibtRectC hdc, Skreet, hBrush );
330 BORLAND C++ 3.0 PROGRAMMING, Second Edition

// Line Draw Modes //


for(€ nMode=R2_BLACK; nMode<=R2_WHITE; nMode++ )
{
hPen = CreatePen( PS_SOLID, 1,
LpColor£CnColor-IDM_BLACK]I );
SetROP2( hdc, nMode );
SelectObject( hdc, hPen );
MoveTo( hdc, 1, nMode );
LineTo(€ hdc, HUnits-1, nMode );
}

EndPaint( hwnd, &ps );


DeleteObject( hPen );
return(Q);

case WM_DESTROY:
PostQuitMessage(Q);
return(0Q);
}
return( DefWindowProc( hwnd, msg, wParam, lParam ) );
}

#pragma argsused

int PASCAL WinMain(€ HANDLE hInstance, HANDLE hPreviInstance,


ERSiER lpszCmdParam, int nCmdShow )
{
static char szAppNameL] = "PENDRAW";
HWND hwnd;
MSG mSQ,
WNDCLASS WC;

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

DESCRIPTION "DRAWING WITH PENS"


EX(Esieya re WINDOWS
STUB PUNTERSWE 5 Ee
CODE PRELOAD MOVEABLE DISCARDABLE
DATA PRELOAD MOVEABLE MULTIPLE
HEAPSIZE 1024
STACKSIZE 8192
EXPORTS WndProc

f/f SHSSssesessesae
//

// PenDraw.H “//
/ jf SSeS Tee =]>so=e8// //

#Hdefine IDM_BLACK 300


#define IDM_BLUE 301
#Hdefine IDM_GREEN 302
#define IDM_CYAN 5105
#define IDM_RED 304
#Hdefine IDM_MAGENTA S05
#define IDM_YELLOW 306
Hdefine IDM_WHITE 307

i f[faeSseclsseoSSSseesoseseasea/ //
332, BORLAND C++ 3.0 PROGRAMMING, Second Edition

PENDRAW MENU LOADONCALL MOVEABLE PURE DISCARDABLE


BEGIN
POPUP & Golo
BEGIN
Menultem "&Black", 300, CHECKED
MenuIltem "B&lue", 301
Menultem "&Green", S102
Menultem "&Cyan", 303
Menultem "&Red", 304
Menultem "&Magenta", 305
Menultem "&Yellow", 306
Menultem "&White", SiO
END
END
Chapter 17

Drawing Shapes and Figures

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.

Table 17-1: Standard Figures


Function Shape
Rectangle rectangle with squared corners
RoundRect rectangle with rounded corners
Polygon multisided figure
PolyPolygon multiple multisided figures
Arc an open curve
Chord an arc with the endpoints connected by a chord
Ellipse ellipse or circle
Pie pie wedge based on ellipse

Five of these eight figure functions will be demonstrated in the PenDraw2.C


program; the Polygon and PolyPolygon functions will be covered later.
However, before creating figures, there are also six line (pen) styles defined.

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:

hPen = CreatePen( nPenStyle,


nPenWidth,
RiGBiGolSonmE
SelectObject( hdc, hPen );

Or these can be combined in a single step as:

hPen = SelectObject( hdc,


CreatePen( nPenStyle,
nPenWidth,
RiGiBiC Olona E-

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.

Table 17-2: Redefined Pen (Line) Styles


Style ID Line Type
PS_SOLID —_—-
PS_DASH ----
BOAO ee Som ev ies
PS_DASHDOT =.
PS_DASH2DOT =...
RSeNiWIae none
PS_INSIDEFRAME (see note following)

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

Table 17-3: Predefined Hatch Fill Styles


Hatch ID Hatch Type
HS_HORIZ horizontal lines
HS_VERT vertical lines
HS: FDIAG forward diagonal
HS_BDIAG backslash diagonal
HS_HCROSS horizontal crosshatch
HS_DCROSS diagonal crosshatch

A hatched brush is created in the same fashion as a logical pen and is subject
to the same restrictions:

heicush. = Selecuobyec tiGshid cy,


CreateHatchBrush( nHatchStyle,
RGBicolior >) dy:

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

RectiangLex hdcys xUL,VyUL, xR, yUR J):

Or, to create a square, a special case of Rectangle can be used as:

RectangveGihdc, xUL VUE,


CUE ee mane eX ERS ayy Lande,
YUL temenc yLR=yuUEsexLR=xUL ) des

The RoundRect function is not demonstrated in PenDraw.C, but operates in


the same fashion as Rectangle except for the addition of two parameters
defining the elliptical curves for the four corners, as:

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).

Figure 17-1: Corners Using RoundRect

x=2y

Ellipses are drawn much like rectangles, requiring only corner coordinates
for a rectangle enclosing the ellipse, thus:

EGidipse@indc,. “ULe- YUL. xXER, AyLER ©);

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

EEG apsieG shidic,, <1)ey) eee lf circular ellipse fd


XU) eect Ge XoLe Re XU Lee yLR=yUL ),
YUE eeeeemniniGery Ry, Ul xLR-xUL ) ye

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.

Figure 17-2: Three Elliptical Figures

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.

AUC nde KUL yULS xR YuURG XP len ey pa ep eey Doar.


ChordC hdc, xUL, YUE, xERY yURG@=xXp is -yYoOl> “pe, ype.
Pie( ndc ye xUL oyu XoR, §y ER, xpi, ypl> xXbpe, ype. Do:

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

Figure 17-3: Arc, Chord, and Pie Figures

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

Later, in PieChart.C, conventional trigonometry will be used to calculate


points which do lie on the arc, since this is easier than calculating appropriate
points which do not, but the emphasis here is on the fact that the start and end
points are not required to lie on the arc. Even if the mechanisms are different,
the end results are the same.
The PenDraw2.C program (a complete listingof which appears at the end of
this chapter) demonstrates the Rectangle, Ellipse, Arc, Chord and Pie functions
together with pen (line) and hatch-fill styles as shown in Figure 17-5.
Chapter 17: Drawing Shapes and Figures 341

Figure 17-5: The PenDraw2.C Application

Line Style FillStyle Color Figure

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:

Tere iO sjsAS jeesp 2


{
TextOut( hdc, (j+1t)*70+20, 2*MaxVal+20, szBuft,
Sprint? CeszBucl,.- 705 eee ais ames,
hPen = SelectObject( hdc, CreatePen( PS_SOLID, 1,
LoCOl@reE yea) » Ws
hBrush = SelectObject( hdc,
CreateSolidBrush( lLpColorljtld ) a;
Relic
an giver Guhidicima Girly sap 2*MaxVal+20,
Cpe10* 70415, 2*¥MaxVail+ts )-;

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

Figure 17-6: The BarGraph Demo

#1989 19909 1991

“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

After this is done, TotVal[i] no longer describes the magnitude of a category.


Instead, it can be used to calculate a percentage of the total and therefore, can
calculate a rotation point around the circle:

LOCK 1 =O 21 SS 7) Let)
{

hPen = SelectObject<( hdc, CreatePenC PS SOLID, 1,


ED COLOT LMsse pula.
hBrush = SelectObject( hdc, CreateSolidBrush( lLpColorlil )
);
PieC hdc, -Radius, Radius, Radius; —~Radius,
344. BORLAND C++ 3.0 PROGRAMMING, Second Edition

Since the mapping mode is centered, the enclosing rectangle is defined by


two pairs of points: (-Radius,Radius) and (Radius,-Radius).
The start and end point coordinates for each pie section are calculated as
points on the circumference :

Goinitp an Gaia GldtSmcenGIOlS) Gamerz Ino: Vail a)


PoeeWaelieeal d
(Gisnicp ae Gekia Chit Sameer Sti Gamalae LO} CAV cane aii
LOEWE WIE oY DY,
Giinitp Gan Ral Giiith Sie ClOrSiGaee liz T.0,t.
Via abt
O.t Vids lSella aeee
Caine C FREICIURS tS Symi iParz POW E UIE Wal J
ey,
tte,
Ftc,
eR,
Sra
a Por Wak Es al 2) 2 Ms

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:

Polygonec-hdc? tpPointss neoTnts.

The /pPoints parameter is a pointer to an array of POINT, while nPoints is


the number of points in the array.
The Polygon function creates a figure by connecting successive pairs of
points with straight lines, closing the polygon, if necessary, by connecting the
final vertex with the first vertex.
The PolyPolygon function creates a series of closed polygons and is called
as:

PolyPolygon(€ hdc, lLpPoints, lLpPolyCounts, nPolygons );


Chapter 17: Drawing Shapes and Figures 345

Figure 17-7: The PieGraph Demo

Annual Report

Like the Polygon function, the /pPoints parameter is a pointer to an array of


POINT containing coordinates for the position of each vertex. The new para-
meter, [pPolyCounts, is a pointer to an array of integers which specify how
many points are in each successive polygon. And, again, nPolygons is the total
number of polygons—not points—in the /pPoints array.
Unlike the Polygon function, the PolyPolygon vertex arrays must be closed,
that is, the last vertex in each array must be the same as the first vertex—
because the PolyPolygon function will not automatically close a polygon. Also,
multiple polygons are permitted to overlap but are not required to do so. The
Polygon function is demonstrated in PenDraw3.C, following. PolyPolygon is not
demonstrated.

Polygon Fill Modes


In the previous examples, the figures created were simple closed outlines with
continuous interiors that required no special handling to fill. With polygons,
however, as will be demonstrated, the interiors of a closed figure may or may
not be continuous, and, if not, require a different approach for filling.
346 BORLAND C++ 3.0 PROGRAMMING, Second Edition

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:

Ton T= j=0 7. tt ow = eS) Ae // seven points //


{
ae lE Ab E Tl 58 Giimtee es in P27 7e ee. OOM a er OP
oe E ANE Whal 37 Clint). Cncos Cay Pil'2/ fees hil OO) x
Chapter 17: Drawing Shapes and Figures 347

Figure 17-8: PenDraw3—Polygons and Poly Fill Modes

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:

static POINT pt] =


CeO OUP — 5)2 n 5 OF 121015) ats Ol. —ipi, S0—16167, |oO
=O, WOO, We. WO “KS, “0, See OSA Cow, Seer
ta, =22pn WS, OB, Gin SOUR W1O-p WOO) sas
StiaitacC aml MeCeE DO) syle lea— mete Or Oe Mae:

The brief array poly declares the number of points in each polygon. With
these declarations, PolyPolygon could be called as:

PolvPolygonG Ndcywpts poly,.cizeot (poly). 7/ VsizeofCint)));

Since PolyPolygon requires closed polygons, instead of the five points


calculated for Polygon, six points are needed for the five-pointed star, with the
final coordinate pair the same as the first point. Likewise, the seven-pointed
figure requires eight points for its definition.
348 BORLAND C++ 3.0 PROGRAMMING, Second Edition

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.

(PSSe Secs] oSSSesoseesceses// i/


LEN PenDraw2.C Mek
// C++ Windows Drawing //
jf SS] SS >See SSSeceSsee—Se/4/

#Hinclude windows.h
Hinclude "pendraw2.h"

long FAR PASCAL WndProc( HWND hwnd, WORD msg,


WORD wParam, LONG lParam )
{
static, COLORREF cColori=en RGB. Oo sO a0).
LpColorLl8] =
/* Black * / { RGB OF OF One
/* Blue * / RGB OF OF e255Q 0"
/* Green * / RGB OF. 2555 ONDE
/* Cyan */ RGB ¢ 0, 205) 205: Ds
/* Red */ RiGBIG=S Zp: OF (8).
/* Magenta */ RGB 2557, O39. 25: 5-4) =>
/* Yellow */ RGBiG325 5s 25 or, OAs
/* White * / RGB 255.625 )% 255) _> eee
S tiaitic int cxWnd, cyWna, <UL). YUL, XRBo oyRe,
Xpdi> yp; expe; ype, XPS, YDS,
nFigure = IDM_RECT,
nHatch = IDM_HORIZ,
nColor = IDM_BLACK,
nPen =e DIMieSiO ape
HDC hdc;
HMENU hMenu;
PAL NilpomeRiU
|Cilmmn pissy
Chapter 17: Drawing Shapes and Figures 349

RE Gan INCiG te,


i faye Ly
switch( msg )
{
case WM_COMMAND:
hMenu = GetMenu( hwnd );
switch( wParam )
{
case IDM_BLACK: case IDM_BLUE:
case IDM_GREEN: case IDM_CYAN:
case IDM_RED: case IDM_MAGENTA:
case IDM_YELLOW: case IDM_WHITE:
CheckMenuItem(€ hMenu, nColor, MF_UNCHECKED
);
nColor = wParam;
GCoome— a Upico Gon niGoll op D/MaBiIlsAIG Kall
CheckMenuIltem(€ hMenu, nColor, MF_CHECKED );
break;
case IDM_ARC: case IDM_CHORD:
case IDM_CIRCLE: Garsenel DiNma Ete Ege SEs:
case IDM_PIE: case sl DMERE Ci:
case IDM_SQUARE:
CheckMenultem( hMenu, nFigure, MF_UNCHECKED );
nFigure = wParam;
CheckMenultem( hMenu, nFigure, MF_CHECKED );
break;
case IDM_SOLID: case IDM_DASH:
case IDM_DOT: case IDM_DASHDOT:
case IDM_DASH2DOT: case IDM_NULL:
case IDM_INSIDEFRAME:
CheckMenuIltem( hMenu, nPen, MF_UNCHECKED );
nPen = wParam;
CheckMenuIltem(€ hMenu, nPen, MF_CHECKED );
break;
case IDM_HORIZ: case IDM_VERT:
case IDM_FDIAG: case IDM_BDIAG:
case IDM_HCROSS: case IDM_DCROSS:
CheckMenultem( hMenu, nHatch, MF_UNCHECKED );
nHatch = wParam;
CheckMenulIltem( hMenu, nHatch, MF_CHECKED );
break;
}
InvalwvoateRectC hwnd, NUL, CFAESE =);
return(Q);

case WM_SIZE:
350 BORLAND C++ 3.0 PROGRAMMING, Second Edition

cxWnd = LOWORD( LParam );


cyWnd = HIWORD(C LParam );
xRB = cxWnd - ( xUL = cxWnd / 6 );
yRB = cyWnd - (¢ yUL = cyWnd / 6 );
xp1 = yp2 = 0;
xpo = C xp2. = exWnd =) laos
XPom=—Gayp l= cy Wind As cr
return(0);

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

xUL + min€ xRB=xUL, yRB-yUL ),


YUL + minGeyRB=yULPeXRB=xUL) 1);
break;
}
EndPa int G *hwnd, &ps );
return(0);

case WM_DESTROY:
PostQuitMessage ODF.
return(0Q);
}
return(€ DefWindowProc ( hwnd, msg, wParam, lParam ) );
}

#pragma argsused

int PASCAL WinMain( HAND LE hInstance,


HAND LE hPreviInstance,
LIPS R LpszCmdParam, int nCmdShow )
Ht
static char szAppName C] = "PENDRAW2";
HWND hwnd;
MSG msg;
WNDCLASS WC;

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

DESIGR GRA LON "DRAWING WITH PENS"


EXEinyee WINDOWS
STUB SeWrlNicinUIB
ene xuces
CODE PRELOAD MOVEABLE DISCARDABLE
DATA PRELOAD MOVEABLE MULTIPLE
Me iNie SALA le 1024
SHIPAICINiSHeZ 8192
EXPORTS WndProc

HMUf PENDRAW2.H iif


ji [PS lSSsoe Seles se2se/ //

#define IDM_SOLID 100


#define IDM_DASH 101
#define IDM_DOT 102
#define IDM_DASHDOT LOS
#define IDM_DASH2DOT 104
#define IDM_NULL 105
#define IDM_INSIDEFRAME 106

#define IDM_HORIZ 200


#define IDM_VERT 201
#define IDM_FDIAG 202
#define IDM_BDIAG 203
#define IDM_HCROSS 204
#define IDM_DCROSS 205

#define IDM_BLACK 300


#define IDM_BLUE 301
#define IDM_GREEN 302
#define IDM_CYAN 0%
#define IDM_RED 304
#define IDM_MAGENTA 305
Chapter 17: Drawing Shapes and Figures 353

#define IDM_YELLOW 306


#define IDM_WHITE 307

#define IDM_RECT 401


#define IDM_SQUARE 402
#define IDM_ARC 403
#define IDM_CHORD 404
#define IDM_PIE 405
#define ID al EIR IL PSE 406
#define ID Maer GEE 407

/{PseSeese]ss]52SSSS55///

if PenDraw2.RC id
// menu script iif
f/f SS SSS SSeS eesees///

PENDRAW2 MENU LOADONCALL MOVEABLE PURE DISCARDABLE


BEGIN
ROPRUIPMS acl nemesit
y, lel
BEGIN
MenuIltem "&Solid", TOW Pe CniECKED
Menultem "&Dash", 101
Menultem "D&ot", 102
MenulIltem "Dash Dot", Os
Menultem "Dash Dot Dot", 104
MenuIltem "&Null", 105
Menultem "&Inside Frame", 106
END

POPUP "Fill &Style"


BEGIN
Menultem "&Horizontal", AC OF eG HEGKE D
MenuIltem "&Vertical", 2(0)4
Menultem "&Forward Diagonal", 202
Menultem "&Backward Diagonal", 20)
MenuIltem "&Crosshatch", 204
Menultem "&Diagonal Crosshatch", 205
END

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

MenuIltem "&Magenta", 305


MenuIltem "&Yellow", 306
Menultem "&White", S04
END

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>

static int Accountsl[4]C8] =


te URIS stipe Kictan Alin OCS tPaa. Ute Sie
Wray CalA SUA. aes Ales tia FA aly
(HAS Ob Ets SNA Alin SA Sie. Alte.
LOO 260, 604. leu Onno pal tel came
Yearst4) =) 1988, 1959 71990) 3199 193:
static char *AccTypesl[9] =
t "NOtofas CAcsry” . “Repts:, =.Govat.,
Peas eg onl eS ae au P allMiteressue Ml SCM os

Long FAR PASCAL WndProc( HWND hwnd, WORD msg,


WORD wParam, LONG LParam )
{
Sitiactaice GOMORRIEE Lpcolor£8] =
1 (RGBCG.S07, 504750 // DarkGray es
RGB Ce S006 ao 0) cO0 // Blue Mai
RGB¢ S05 ZO, So // Green AL
RGEC "507e 200520007 Lim@Cyan Yall
RGBIC 2007 a0, 6> 0a // Red //
RGBC -200¢e250;, 2000, // Magenta as
RGB C 2007s 20072 On), WH
we // Yellow
weyrwoewwewo ae,
Chapter 17: Drawing Shapes and Figures 355

RGB CGi2Z007 e200, 200") }:>- 77 aightGray //


static: Oants ©cxWind, cyWnd?
HDC hac, .OIngDGe
HPEN hPen;
HMENU hMenu;
HBRUSH hBrush;
PAINTSTRUCT Dist,
Reescall meicit>
iia i, j, MaxVal = 0;
char Sralsiwiy
wile 4 (al 2

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

int PASCAL WinMain(€ HANDLE hInst, HANDLE hPrevInst,


LPSTR lpszCmdParam, int nCmdShow )
{
static char szAppNameLJ = "BARGRAPH";
HWND hwnd;
MSG msg;
WNDCLASS WiGe>

ma Yo InP RaW iSie 2


{
wce.hInstance = hInst;
we.lpfnWndProc = WndProc;
wics.cbCilsEx tina ei (0)
we.cbWndExtra = 0;
we.lpszClassName = szAppName;
wce.hIcon = LoadIcon( hInst, szAppName );
we.lpszMenuName = (LPSTR) szAppName;
we.hCursor ="LoadCursorG- NULL, EDC.ARROW ):
we.hbrBackground = GetStockObject( WHITE_BRUSH );
we.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass( &wc );
hy
hwnd = CreateWindow( szAppName, "Acme Bargraph",
WS_OVERLAPPEDWINDOW,
CWLUSEDERAULT, -CWLUSEDERAULT,
CWlLUSEDERAUBT A CWLUSEDE FAULT,
NWIKIE, NWIE, Iniiimsite, INUILIL WF
ShowWindow( hwnd, nCmdShow );
Chapter 17: Drawing Shapes and Figures 357

Update Window( hwnd );


while(¢ GetMessage( &msg,"NULELY OF70 ). )d
{
ligiea nslateMessage( &msg );
Dis patchMessage( &msg );
}
return ( msg.wParam );

5 BARGRAPH.DEF ;
PPS SS SS SSS SSS SS 55%

NAME BARGRAPH

DESCRIPTel ON "DRAWING BUSINESS GRAPHS"


M1 1 APIS WINDOWS
STUB eW LLNS hU Bie Xie
CODE PRELOAD MOVEABLE DISCARDABLE
DATA PRELOAD MOVEABLE MULTIPLE
DIEVAVRAS
pleZ4E 1024
SHIPAUCHKG
oly Za 8192
EXPORTS WndProc

ff /PRSSSS SS SSeS ee esseseees / //

Hf PieGraph.C //
// C++ Windows Drawing if
ff = ee

#include <windows.h>
#Hinclude <stdio.h>
#include <math.h>
#include "niegraph.h"

#define Piles celtaOy ee ae yO,

Seeyewe ain t AccountsL[4][8] =


at OS, Ses ss sie Cel ay “ep eee
Zax Mp SO, 2a, “tin 2a, Co. il-
Asher eeOO cir. UA llLee OO toe pam lates
NOG a OU MOG wirepal Owe Woy medi pemhei. vy
YearPsitsd |S * IOS, O39, 990, 004) 2-
Year = 0;
static char *AccTypesL[9] =
£ MiOroOr’, “WeSRry pA “INEDPS’ 5 “HOWE
MIM aGeu a ei es) yn ont > wal Ml SC ep Ih.
358 BORLAND C++ 3.0 PROGRAMMING, Second Edition

Long FAR PASCAL WndProc( HWND hwnd, WORD msg,


WORD wParam, LONG LParam )
{
Sita caiGmac OOJRIRIENE LpColorl8] =
{oe RGB GanlO|\O)- ea) OOF ma O Oma DarkGray
RGB ¢ 3}(0) - 50), 20) 0, Blue
RGBC S10), (0)(0), Dim, Green
RGB(¢ OR O0Feec UUM Fr Cyan
RGBC 200, DIO DIO MDE, Red
RGBC( 200, 3)(0),, 2ls)(0) 2) Magenta
RGBIGe 2002 010r 5@) 2), Yellow
RGBiG=Z00 e200 S200 s- LightGray
Static int cxWnd, cyWnd;
HDC nidicy Ong DIG,
HPEN hPen;
HMENU hMenu;
HBRUSH hBrush;
PANTiSiER
UG apse
REG rect;
fave ln te REICIMS, WeeWallleeals
char SZ Buia
SsleOrlE

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

TotVallCit1]*° =" TotVallild +) AccountslYearilid:;


Radius#=n 1507
hdc = BeginPaint( hwnd, &ps );
OrgDC = SaveDC( hdc );
SetMapMode( hidice eeMaies Ol RO/P iGaap ss
SetWindowExt ¢ hidics, 400, 400 )
SetViewportExt( hdc, cxWnd, -cyWnd )
SetViewportOrg( hdc, cxWnd/2, cyWnd/2 )
GetClientRect( hwnd, &rect );
Pexccoute hdc, 320—CexWwndy/i2)>" 160, szBuftt,
Sip halinieats GaeS 2 Buti tae ee ae ale MG CLOmD ae
FOR TSW dWsstse Wakes
{
ee Xa OUite Ga Nicdicy- mz Ul Geox Wind /12pEp Gomi arc Ob mms 2 BUat
Spirimcy7¢ sziBwirr, 29", NGe7oesiidal .) >,
hPen = SelectObject( hdc,
CreatereniGePiSas. Oley eale-. UpiGolkom—=n=ls > = al
hBrush = SelectObject( hdc,
Ciera) tie S01 Usd Bin US NiGaau DIG. One Olean sae)amy=
Rectangle( hdc, -(cxWnd/2), CS ip ae Oe
—CerMme/( 2st, Chad )ee = i) 2)
Pie€ hdc, =-Radius, Radius, Radius, —=Radius,
Gant ee Gee Ra cit UiSee aC O1SiG™ Pale mccmn vont ama rere)
ff VouWa ict ) »
Ginktp a Cm alGh US eesti Can rly cee ee Ont ape lets)
/SeT OMe ana orl) meee
Gime) © Racdiwse ~ Cos e Pie «© poOENWEIE iss
/ SOc alse ae,
Coline) (¢ IRECIIOS <2 Sime (Rie Wore Welle gee
ff WouVe Wiel 2. 9 25
DeleteObject( hPen );
DeleteObject( hBrush );
}
RestoreDC( hdc, OrgDC );
EndPaint (hwnd, Sps);
Recunn GOs

case WM_DESTROY:
PostQuitMessage(Q);
he turniGoD):
}
return( DefWindowProc( hwnd, msg, wParam, lParam ) );
}

#pragma argsused
360 BORLAND C++ 3.0 PROGRAMMING, Second Edition

int PASCAL Win Main(€ HANDLE hInst, HANDLE hPrevIinst,


EE Sie LpszCmdParam, int nCmdShow )
{
Sitraltenc acinar szAppNameL] = "PIEGRAPH";
HWND hwnd;
MSG msg;
WNDCLASS WC;

vus - InizrPew, Inst )


{
W Geen linisat ance = hInst;
we.lpfnw ndProc = WndProc;
WiGHenG DIGIUS Extra = 0;
wce.cbWnd Extra = 0;
Wwice Up szic lassName = szAppName;
we.hIcon = LoadIcon( hInst, szAppName );
we.lpszM enuName = CLPSTR) szAppName;
we.hCurs or =— FoardGuite Sion Gas NIU eel DIGaEAUR ROW aer=
we.hbrBa ckground = GetStockObject( WHITE_BRUSH );
we.style = CS_HREDRAW | CS_VREDRAW;
Register CWarsisiGs Sin cap
}
hwnd = Crea teWindow(
szAppName, "Acme Piegraph",
WS_OVERLAPPEDWINDOW,
CWRUSE DER AUT eC Wee US EIDET A Ulan
CiW meUISTES DIES ErANU a liypmen. Wiamm EST Es! EsRiv) arte
NUDES NUE eS painste N UE)
ShowWindow( hwnd, nCmdShow );
UpdateWindo wC hwnd );
while€ GetM essage( &msg, NULL, O, O ) )
{
Translat eMessage( &msg );
Dispatch Message( &msg );
i;
return( msg -wParam );

fe tT

; PAE IGIRIAIR/ Ary D)Ear é


(p= SES SIS SSS SSS]S 5 7

NAME PIEGRAPH

DESCRIPTION "DRAWING BUSINESS GRAPHS"


SMe IP APE WINDOWS
STUB “WINSTUB.EXE"
CODE PRELOAD MOVEABLE DISCARDABLE
Chapter 17: Drawing Shapes and Figures 361

DATA PRELOAD MOVEABLE MULTIPLE


ME AIP SIE 72|e aUiZ4
STACKSIZE 8192
EXPORTS WndProc

| {/Sesesss=s]Sees///
// PIEGRAPH.H itif
/ (Sees Ssseseses //i/

#define Y1991 1991


#define Y1990 1990
#define Y1989 1989
#Hdefine Y1988 1988

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"

Hoerine rue © 2.0 8 Saas »


// radians in 360 degrees //

SEQEIG POM fre ealle ale

Long FAR PASCAL WndProc( HWND hwnd, WORD msg,


WORD wParam, LONG lParam )
if
static a faye cxWnd, cyWnd, nFillMode = IDM_ALTERNATE;
HDC hidicr
HPEN hPen;
HMENU hMenu;
RPALNT Sim RU Gi ps),
int les Bbye

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

oe lE 4)AE Ghal a x CIN Sine aePl2/7 See T008 sale OF


pel 1 INE a al aSy Cinto GC icos © JA Ph2/ 7 ete OOM
}
return(Q);

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

int PASCAL WinMain(€ HANDLE hlInst, HANDLE hPrevIinst,


ER Sieh lpszCmdParam, int nCmdShow )
{
static char szAppNameLJ] = "PENDRAW3";
HWND hwnd;
MSG msg;
WNDCLASS Wice:

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

DESCRIPTION "DRAWING WITH PENS"


EAE Taare WINDOWS
STUB WON STRUIB = EX Ex:
CODE PRELOAD MOVEABLE DISCARDABLE
DATA PRELOAD MOVEABLE MULTIPLE
HEAP ST ZE 1024
STACKSIZE 8192
EXPORTS WndProc

j / HSeSeses2eeseeee
// //
if PENDRAW3.H ah
(jf SeSesSsesees][oen/ j/

#define IDM_ALTERNATE 1
#define IDM_WINDING 2
Chapter 18

Brushes, Bitmaps, BLTs, and DIBs

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.

Figure 18-1: Three 8x8 Bitmap Patterns

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):

HBA tmalpse= Chea teBalt ma piGoresor anle, lems GiliRD msWib.rHiC


Kes mn

The CreateBitmap function creates a device-dependent memory bitmap


with the width and height specified by the first two parameters. The third
parameter sets the number of color planes in the bitmap (each plane has
nWidth /nHeight /nBitCount bits) while the fourth parameter sets the number
of color bits per display pixel.
For VGA color monitors please see the"Devices" program in Chapter 15,
four color planes are supported with one color bit per pixel (per plane).
Changing these parameters, however, would require a change in the bit
pattern which has been defined for monochrome. Of course, monochrome is
compatible with all devices.
The final parameter required is a pointer to an array of bytes containing the
initial bitmap bit values. If NULL, the bitmap remains uninitialized.
With the bitmap handle, CreatePatternBrush can be called to create a brush
using this pattern:

hBrush = CreatePatternBrush( hBitmap );


selectObject( hde, hBrush )
Chapter 18: Brushes, Bitmaps, BLTs, and DIBs 367

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:

DeleteObject¢ hBrush )>


DeleteObject¢ hBitmap D>

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];

Tarte AIS! CAV ous Wat Miaiianieemeie


{

if( ! hPreviInst )
{ Pee }
368 BORLAND C++ 3.0 PROGRAMMING, Second Edition

hBitMapLO] LoadBitmap ( hebnsitiee BRILCKS.4 );


hBitMapLl1] LoadBitmap( helimiSate mee CHAE INIOms we
hBitMapL2] LoadBitmap( hinst,, .DDAMOND = 23
hBitMapLl3] LoadBitmap( hinst,° Steines =e

return( msg.wParam );

Figure 18-2: A 24x24 Bitmap Pattern (CHAINS)

This 24x24 bitmap, if


used as ‘wallpaper’, will
produce a screen of inter-
locking rings.
As a patterned brush,
however, the results are
not quite as expected.

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.

Table 18-1: Predefined Windows Bitmaps


OBM_BTNCORNERS OBM_LFARROW OBM_RGARROW
OBM_BTSIZE OBM_LFARROWD OBM_RGARROWD
OBM_CHECK OBM_MNARROW OBM_SIZE
OBM_CHECKBOXES OBM_REDUCE OBM_UPARROW
Chapter 18: Brushes, Bitmaps, BLTs, and DIBs 369

Table 18-1: Predefined Windows Bitmaps (cont.)


OBM_CLOSE OBM_REDUCED OBM_UPARROWD
OBM_COMBO OBM_RESTORE OBM_ZOOM
OBM_DNARROW OBM_RESTORED OBM_ZOOMD
OBM_DNARROWD OBM_OLD_CLOSE! OBM_OLD_REDUCE
OBM_OLD_UPARROW OBM_OLD_DNARROW OBM_OLD_RESTORE
OBM_OLD_ZOOM OBM_OLD_LFARROW OBM_OLD_RGARROW

: Bitmap names beginning with OBM_OLD._... represent bitmaps used by Windows versions prior
to 3.0.

The [pBitmapName parameter can also be a value created using the


MAKEINTRESOURCE macro with the bitmap ID as the low-order word,
while the high-order word must be zero. Both custom and predefined bitmap
handles should be deleted (use DeleteObject) when no longer needed.
Loading the bitmaps, in either case, is only the first step, because for the
PenDraw4 demo, a brush still needs to be created and selected:

hBrush = CreatePatternBrush( hBitMapL nBitMap J] );


Sieh erc.c Olbijlerc
tiCas hidick-s en Bimuismee) >

When finished, DeleteObject is called to cancel the brush handle:

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

Figure 18-3: Four Brushes Using Bitmaps

Using Larger Bitmaps


Earlier, a bitmap used for brushwork had a minimum size of eight pixels by
eight pixels and three of the bitmap images used here were 8x8. The fourth
bitmap was 24 by 24, and as a tiled image such as wallpaper in Windows,
would produce the pattern shown in the left half of Figure 18-4.
When used as a brush pattern, however, the resulting pattern appears as
shown in the right half of Figure 18-4. (Note: The two halves of Figure 18-4
are not to the same scale.) What’s happening here?
Quite simple. While bitmaps larger than 8x8 can be used for patterned
brushes, only the upper left 8x8 bits are actually used. The remainder of the
bitmap image is ignored. And that is exactly what has happened here: The
upper left 64 bits are used to create a repeating brush pattern, and the remainer
is ignored.

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

Figure 18-4: Intended VS Actual Bitmap Results

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

hBitmap = CreateBitmap( cwWidth, cyHeight,


nptanes, NBIttSsPpixel, UpB its 2,
hBitmap = CreateBitmapIndirect( &bitmap );
hBitmap = CreateCompatibleBitmap( hdc, cxWidth, cyHeight );
hBitmap = CreateDiscardableBitmap( hdc, cxWidth, cyHeight );

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.

Table 18-2: BITMAP Structure

Field Type Description


bmType int 0
bmWidth int width in pixels
bmHeight int height in pixels
bmWidthBytes int width of bitmap in bytes (even values only)
bmPlanes BYTE number of color planes
bmBitsPixel BMT number of color bits / pixel
bmBits LPSTR far pointer to array of bits

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:

SetBitmapBits(€ hBitmap, dwCount, LpBits );


Chapter 18: Brushes, Bitmaps, BLTs, and DIBs 373

Or an image can be retrieved from an existing bitmap using the GetBitmap-


Bits function:

GetBaitmapBits C hBitmap, dwcount, UpBits >

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:

GetObject( hBitmap, sizeof(BITMAP), CLPSTR) &bm );

dwCount can be calculated 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:

elit ee aca de ot) 0) 04 0.000800) Ox= sor FSO


Peete Oe Oe) On dete On OO OOO) Ons CAS 0
fe BOO O00. 0 00)0 50 09 = A230
Om Or e060 019070 80010070) = 9453.0
rOmtOm de O00 th OOL0 O80 0" 0KH=a88030
On dae Oi eeCenO et OO OMOM Or 20494-5830
(Oe 1 Om Orn ate O66 £000 AO e000 e0s =) A2aiSO
lene OO se Oe Oe Ol. O07 0 t= C1 280
eerie TO Oe OOO? OO = Ere 8O
374. BORLAND C++ 3.0 PROGRAMMING, Second Edition

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:

Sita tice Bb ll MARG Dini=: Oe Oo peo cee ly ees sy

and the data stored in a BYTE array as:

$tatic BYTE CheckBoxl) = € OXFF, Oxs0, 0x1 0xS07 0xAzc,


0x80, °0x94, 0x80> 0x88, “0xs0,7 0x94, 0x60"
OxA2> 0x80. 0x61 we 0NS OF ZOE ree oxcors

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.

The simplest method of creating a bitmap from this data is:

NiBaatimalpe —wC ela tie Ba timialp Gur 9s, mov mele mene eC NeIC;KIB
0 Xam

The CreateBitmapIndirect function can also be used as:

bm.bmBits = (CLPSTR) CheckBox;


hBitmap = CreateBitmapIndirect( &bm );

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:

hBitmap = CreateBitmapIndirect( &bm );


SetBitmapBits( hBitmap, (DWORD) sizeof(CheckBox), CheckBox ee

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

FE bet honk eke PFO) .0.00) SPE aR FEES F000


ome ace 204 Fa) O00 EFeeewee ver FO, 00
ee teres 2 cab eer Un 00> = tebe 22-2, FO 00
Reeceec tec he ce ec 0) 0. Oe he: Pepe ber OF 00
Recwe cure tae wee eotret0! 0) Og henec. Fe ce wh0. 00
feecage. (Foe, Prize SFO) (0s Ogee ee Ee? Pa 2 2 ee O00
neemibrecescr 2..F ferme Or Oe=Kb be ah cin2 ee bo er Om 00
ehececercs 2-20 Foy 0-0 tee Re mc cecer OF eh Om0.0
eegcen k oP eb tb 0.0! OSS rari ak Pe Fibs iF ee OmO0

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 >>

For an EGA/VGA device, this bitmap can be interpreted as a marked


checkbox image in white against a dark green background with each four bits
representing the appropriate palette color for one pixel.
What about an IBM8514/A, which interprets each eight bits as the color
value for one pixel? Not only will the image be quite different, but the colors
will be different as well. The solution to this quandary is found in the device-
independent bitmap format.

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

Figure 18-5: DIB Bitmap File Dump (BRICKS.BMP)

76 66 66 6H 28 44

The DIB Header


The bitmap file begins with a file header containing information about the
structure of the file itself (Table 18-3) in the first 14 bytes.

Table 18-3: Example of DIB File Format


Field Size Data Value Description
bfType WORD 42 4D "BM" for bitmap
bfSize DWORD 96 00 00 00 96h total size of file
reserved] WORD 00 00 0 h set to 0
reserved2 WORD 00 00 Oh set to 0
bfOffBits DWORD 76 00 00 00 76h offset to bitmap image from
first of file

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.

The BITMAPINFOHEADER Section


The file header information is followed by second header defined by the
BITMAPINFOHEADER structure (defined in Windows.H, see "Structures For
Defining DIBs" in Appendix A). This header contains information about the
size and dimensions of the bitmap image, color organization, and data com-
pression. Additional information may be included about device horizontal
and vertical resolution, and about any special color information which is
contained in the bitmap file.
Chapter 18: Brushes, Bitmaps, BLTs, and DIBs 377

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.

Table 18-4: Example of BITMAPINFOHEADER Structure


Field Size Data Value Description
biSize DWORD 28 00 00 00 28h size of BBTMAPINFOHEADER
biWidth DWORD 08 00 00 00 8h bitmap pixel width
biHeight DWORD 08 00 00 00 8h bitmap pixel height
biPlanes WORD 01 00 th color planes (always 1)
biBitCount WORD 04 00 4h color bits per pixel (1, 4, 8, 24)
biCompression DWORD 00 00 00 00 Oh compression scheme (0 = none)
biSizelmage DWORD 20 00 00 00 20h bitmap image size (used only
if compression is set)
biXPelsPerMeter DWORD 00 00 00 00 Oh horizontal resolution (pixels
/meter)
biYPelsPerMeter DWORD 00 00 00 00 Oh vertical resolution (pixels
/meter)
biClrUsed DWORD 00 00 00 00 Oh number of colors used inimage
biClrImportant DWORD 00 00 00 00 Oh number of important colors

If data compression is used, the data compression scheme is identified


together with the actual size of the uncompressed bitmap (in bytes) for a
redundancy check in decompressing (see Appendix A, "Constants For The
biCompression Field").
The biClrUsed and biClrImportant may contain additional information about
custom colors or multiple color palettes. Zero values in these last four fields
simply indicate default values.

The Bitmap Color Table


Following the BTTMAPINFOHEADER structure, and enclosed by an outline
in Figure 18-5, is the bitmap color table. This consists of a series of RGEBQUAD
structures. In the order read, the first byte is blue intensity, the second green,
the third red, and the fourth byte of each is set to zero.
378 BORLAND C++ 3.0 PROGRAMMING, Second Edition

The number of RGBQUAD structures is set by the biBitCount field. For a


setting of one color bit, two RGBQUAD values are required (foreground and
background), for four color bits, 16 RGBQUAD values are needed (as here),
and for eight color bits, 256 RGBQUAD values. If the biClrUsed field is non-
zero, then the biClrUsed field reports the number of RGBQUAD structures in
the color table.
Table 18-5 shows the default color values for a VGA 16-color palette.

Table 18-5: Color Values From BitMap


Palette Rebquad Color Value Approximate
Entry Data R G B Color
0 00 00 00 00 00 00 00 Black
1 00 00 80 00 80 00 00 Dark Red
fa 00 80 00 00 00 80 00 Dark Green
S 00 80 80 00 80 80 00 Gold Green
4 80 00 00 00 00 00 80 Dark Blue
5 80 00 80 00 80 00 80 Purple
6 80 80 00 00 00 80 80 Blue Gray
i 80 80 80 00 80 80 80 Dark Gray
8 CO CO CO 00 CO CO CO Light Gray
2 00 00 FF 00 FF 00 00 Light Red
10 00 FF 00 00 00 jel 00 Light Green
i 00 FF FF 00 FRE FF 00 Yellow
12 FF 00 00 00 00 00 EE Light Blue
13 FF 00 FF 00 EE 00 BP Magenta
14 FF FF 00 00 00 FF FF Cyan
NS PREP PF 00 FF PP FE White

The Bitmap Image


The final portion of the bitmap file is the bitmap image itself. The arrange-
ment of this portion of the file is partially dependent on the number of colors
used, as reported by the biBitCount field, but there are two other elements in
the organization of the bitmap image which are constant:

= 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

= Second, unlike the original bitmap format for device-independent


bitmaps, the array of pixels begins with the bottom row of the image,
not the top.

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:

SOP S50 S0sthr0S) 0S U0 8i0F &

and would break down as:

ne UIA Se mealos Vis Ui li heaa


OS On 00M O00 11.503
O02 030571" °0-0..0 LT 0S
G0 07007 1200-070 // 08
(0d Ba Dia ilela | SFE
VeRO 0s00:6'0 105.0 L180
00.00 20..0.,0 1/80
jeOee O00 04.0 . 0 if 30

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:

Stealth eee ecdee de ta Tt Ol ie 1 8B 86s SSeS


Meier tele wo died tbl Ot (1 sOO soo eos) oo

and is decoded 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

For a 256-color bitmap, each pixel is represented by a byte value indexing


the 256 entries in the color table.
Last, for a 24-color-bit-per pixel bitmap, unless the biClrUsed field is non-
zero, instead of a 16 million entry color table, each pixel is represented by a
three- byte RGBcolor value. If the biClrUsed field is non-zero, then a color table
appears and pixels are indexed to the table.

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 Dimension Functions


Windows also supplies two bitmap functions dealing with dimensions:
SetBitmapDimension and GetBitmapDimension. These two functions do not,
however, have any connection with the pixel dimensions of bitmaps (which
can not be changed once defined), but provide a means of setting or retrieving
bitmap dimensions in logical units in MM_LOMETRIC mapping mode. These
dimensions are not used by the GDI, but can be used by cooperating
applications to assist in scaling bitmaps that are exchanged via the clipboard,
DDE, or other means. These functions are called as:

SetBitmapDimension( hBitmap, xUnits, yUnits );


dwBMSize = GetBitmapDimension( hBitmap );

The xUnits size is found in the low word of dwBMSize; the yUnits size in the
high word.

Creating and Using Device-Independent Bitmaps


The theory of device-independent bitmaps is all very well, but, in practice, the
important consideration is being able to create and display a bitmap. Ideally,
Chapter 18: Brushes, Bitmaps, BLTs, and DIBs 381

you expect a simple function to be introduced with a name and format


something like:

DrawBitmap( hwnd, lLpBitmapName, xPos, yPos );

However, even though bitmaps are an important and integral part of


Windows’ GDI, there are no basic functions provided to display and manipul-
ate bitmap images. Instead, Windows provides a series of bitmap primitives
which can and will be used to create several of the missing high-level bitmap
handlers.

Step One: A Global Instance Handle


Earlier, using bitmaps for brush patterns, several bitmaps were loaded in the
WinMain procedure so that these could be used later to create logical brushes.
The LoadBitmap instruction(s) appeared in WinMain for the simple reason that
this was the only portion of the program where the /Inst application instance
handle was available (hInst is passed by Windows to WinMain as a calling
parameter when the application instance is created). While loading all bitmaps
at the start of the program is possible, it is not the most practical approach, or
the most desireable. Therefore, to give us more flexibility, a global handle is
declared as:

HANDLE niGeanisse.-

and is assigned in WinMain as:

hGInst = hInst;

The alternative, of course, would be to pass hInst as a calling parameter to


any subprocedures which might need to call DrawBitmap, but, as a method of
saving the overhead of a 16-bit global variable, this would be a foolish
approach. With hGInst available as a global instance handle, the LoadBitmap
function can be relegated to its proper place within our DrawBitmap function.

Step Two: Loading The Bitmap


Later, several other bitmap handlers will be demonstrated, but to begin, the
basic form simply displays a bitmap at the coordinates specified, and is called
382 BORLAND C++ 3.0 PROGRAMMING, Second Edition

with four parameters: The window handle, the bitmap name, and the x and y
window coordinates for position.

BOOL DrawBitmap( HWND hwnd, LPSTR LpName,


bine “POS, IN VWROG 2»
{

DrawBitmap is also given the capacity of returning a boolean result to report


success or failure, but, as with most C functions, this return value can be used
or ignored as desired. Also, for local use, a couple of variables will be needed:

HDC hdc, hdcMem;


BITMAP bm; -
HBITMAP hBitmap;

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€ !(€ hBitmap = LoadBitmap( hGInst, lLpName ) ) )


return(€ FALSE );

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.

Step 3: Device Contexts


In Windows, bitmaps can not be drawn directly to the screen’s device context.
Instead, a separate device context is created in memory using the hdcMem
declared earlier as a local variable. At the same time, the application’s existing
device context cannot be ignored. Therefore, the first step is to retrieve a
handle to the application’s device context:

hdc = GetDC( hwnd );


hdcMem = CreateCompatibleDC( hdc );

CreateCompatibleDC is called to create a memory device context compati-


ble with the hdc device context. A memory device context is a block of memory
representing a display surface, and, as with bitmaps, can be used to prepare
an image in memory before copying the image to the actual surface of the
Chapter 18: Brushes, Bitmaps, BLTs, and DIBs 383

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:

SelectObject( hdcMem, hBitmap );


SetMapMode( hdcMem, GetMapMode( hdc ) );

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).

Step 4: Bitmap Information And Mapping Coordinates


Most of the necessary information can be transferred by calling the GetObject
function to fill the buffer (bm) with the data defining the logical object, the
selected bitmap. For a bitmap, GetObject returns the width, height, and color
format information. The actual bits of the bitmap image will be retrieved
separately:

GetObject( hBitmap, sizeof(BITMAP), CLPSTR) &bm );

There is still one very important task remaining, because the bitmap image
information has not yet been retrieved.

Step 5: Bit Block Transfers


The BitBlt function (pronounced “‘bit-blit’’”), together with the PatBlt and
StretchBlt functions, comprises Windows’ pixel manipulation power oper-
ations. Technically, BitBlt stands for ““bit-block transfer’’ but more is involved
than simply copying bits from one memory location to another. Instead, the
bit transfer involves a choice of raster (logical) operation between three sets
of bits (or pixels). While not the simplest of the three, the BitBlt operation is
384. BORLAND C++ 3.0 PROGRAMMING, Second Edition

the present operation of choice, and completes the task of writing the bitmap
image to the client window:

BitBlt( hdc, xPos, yPos, bm.bmWidth, bm. bmHeight,


hdchem, Us" 05, SRECCOPY =») 7

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).

Table 18-6: Raster Operations Codes


Code Operation* Description
BLACKNESS 0 turns all output black
DSTINVERT ~D inverts destination
MERGECOPY P&S ANDs pattern and source bitmap
MERGEPAINT ~$ 1D ORs inverted source bitmap with destination
NOTSRCCOPY ~S copies inverted source bitmap to destination
NOTSRCERASE (S71) Dy) ORs destination and source bitmap, then inverts
result
PATCOPY P copies pattern to destination
PATINVERT PAD XORs pattern and destination
PATPAINT Pes 7D ORs inverted source bitmap with pattern, then
OR s result with destination
SRCAND S&D ANDs destination and source bitmaps
DRECOPY S copies source bitmap to destination
SRCERASE S&~D ANDs source bitmap with inverted destination

*(P) Pattern (S) Source Bitmap (D) Destination


Chapter 18: Brushes, Bitmaps, BLTs, and DIBs 385

Table 18-6: Raster Operations Codes (cont.)


Code Operation* Description
SRCINVERT SD XORs destination with source bitmaps
SRCPAINT le ORs destination with source bitmap
WHITENESS 1 turns all output white

*(P) Pattern (S) Source Bitmap (D) Destination

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:

ReleaseDC( hwnd, hdc );


DeleteDC( hdcMem );
DeleteObject( hBitmap );
rewwrPme WWE Ms

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 brief, StretchBlt moves a bitmap from a source rectangle to a destination


rectangle, stretching or compressing the bitmap as necessary to fit the
dimensions of the destination rectangle.
Calling StretchBlt is very similar to calling BitBlt, but with two differences
as shown here:

BitBlt( hdc, xPos, yPos, bm.bmWidth, bm.bmHeight,


hadcMem, xOrg, yoOrg, SRCCOPY )>
Stretcheltee hdc, xPos, yPos, xWidth,> yHeitgnhe,
hdcMem, xOrg, yOrg, xOWidth, yOWidth, SRCCOPY);

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:

SetStretchBltMode( hdc, nStretchMode );


Chapter 18: Brushes, Bitmaps, BLTs, and DIBs 387

Table 18-7: Stretch Modes

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.

BLACKONWHITE and WHITEONBLACK modes are typically used to


preserve foreground pixels in monochrome bitmaps. COLORONCOLOR
mode is typically used to preserve color in color bitmaps. The return value
reports the previous stretching mode.

Demonstrating Bitmap Operations:PenDraw5


The PenDraw5 program demonstrates both the BitBlt and StretchBlt bitmap
functions, showing these in several different contexts even though only the
two basic functions are used. PenDraw5 requires five bitmaps, four 16x16
pixel bitmaps, and one 40x70 pixel bitmap as illustrated in Figure 18-6 on page
388. However, any bitmaps desired can be substituted as long as the appropri-
ate name changes are made in the .RES resource file and in the source code.
The DrawBitmap function discussed in this chapter draws the designated
bitmap and positions the upper-left corner of the bitmap at the coordinates
specified. All five bitmaps are drawn with the four smaller bitmaps placed at
the corners of a rectangle within the client window, and the fifth bitmap
located at the center.
The DrawCenBitmap function operates in the same fashion as DrawBitmap
except that the bitmaps are centered at the coordinates specified.
The LineGraph function uses a brief array of data to position a series of
bitmaps in the form of a simple line graph display.
The StretchBitmap function stretches a bitmap to fit a specified rectangle
while the StretchBitmap2Client function stretches a bitmap to fill the entire
client window.
Last, the MoveBitmap function tracks mouse movement with a bitmap
when the left mouse button is down. Note, however, that bitmaps are not
particularly well suited to this type of operation, and this is demonstrated
more as a curiosity than as a serious suggestion.
388 BORLAND C++ 3.0 PROGRAMMING, Second Edition

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.

Figure 18-6: Five Bitmaps For PenDraw5

eaealent tcl pate]


EEE EE &
EES Se 5 ror .
BEEBsEs 88 Pe eeee ee eee
BEES Ss «Ss Sonat eats
SEEEEEEEEES = SB sa
a5 Ge Gene & 8
EE Se Se 68 = Ee
aaa oe
ee San = GB
gE 8 Eee = 8
EES Ee fo 8
- a5 Ga.
ae 5
ane
rT
Ean
SEng8
=EEe
S #SEEEe
S8eec55s
PEE
EEeeeEe=
Eee =EEe
=EeS =
Pry
ase
322
aa
Se
a
@

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

Recommended reading includes the Windows 3.0 Programming Primer by


Alan Southerton (published by Addison Wesley) as a source for further
bitmap operation examples as well as other interesting subroutines. One
caution: the Programming Primer was written using Microsoft C rather than
Borland C++, and some examples may require minor modifications.

| /{ SSSSS Ses 2223 =S222Ss=s5=2/ //


// PenDraw4.C //
Ifif C++ Windows Drawing //
if PPS 2[Se SSS 2S2 = =SSeS 2222 2s2/ //

#Hinclude <windows.h>
Hinclude <stdio.h>
#Hinclude "pendraw4.h"

static HBITMAP hBitMapL4];


Sitaitics PONE fon a ee
6 140, 100, =Ssa-, —80, =205, S00, 15, SO, =W6G,
=O 100, GA 1005 I55, “YO, S24 Gan 2Or,
tS, -=22, Way Oy Cts “90-7 110, 100 2:
static int svete © COA CG awe

Long FAR PASCAL WndProc( HWND hwnd, WORD msg,


WORD wParam, LONG LParam )
xl
Sitart ac 1 alae cxWnd, cyWnd, nBitMap = IDM_BRICK;
HDC hidice
HMENU hMenu;
HBRUSH hBrush;
PAINTSTRUCT ps;
7}(MAE ee

Swit Chasis me,


{
case WM_COMMAND:
hMenu = GetMenu( hwnd );
switch( wParam )
{
case IDM_BRICK: case IDM_CHAINS:
case IDM_DIAMOND: case IDM_STRIPE:
CheckMenulIltem( hMenu, nBitMap, MF_UNCHECKED a;
nBitMap = wParam;
CheckMenultem( hMenu, nBitMap, MF_CHECKED Ly
break;
390 BORLAND C++ 3.0 PROGRAMMING, Second Edition

InvalidateRect© hwnd, NULLJ. TRUE»;


return(0);

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

int PASCAL WinMain(€ HANDLE hInst, HANDLE hPrevInst,


LIP STR LpszCmdParam, int nCmdShow )
{
static char szAppName[] = "PENDRAW4";
HWND hwnd;
MSG msg;
WNDCLASS WC;

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 );

hBitMapLO] = LoadBitmap( hInst, "BRICKS" Dee


hBitMapL[1] = LoadBitmap( hInst, "CHAINS" yee
hBitMapL2] = LoadBitmap( hInst, "DIAMOND" );
hBitMapLl3] = Foald Bi timiaip Gaanhlnisste,) suaSulyRiliPiEsS am)»
hwnd = CreateWindow( szAppName,
"Pen Draw 4: Bitmapped Brushes",
WS_OVERLAPPEDWINDOW,
CWRUSIE DER AUT en G Wee USE DIE AIUete,
CW RUISIE DEI RAWIIE Tepe G Wee USE DIE RAIUILE Ie,
NU. INU, Initinswe > INWILIL 2d.
ShowWindow ( hwnd, nCmdShow );
UpdateWindow( hwnd );
while€ GetMessage( &msg, NUE EO Ol) ae
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
return( msg.wParam );
}

7 PENDRAW4.DEF :

NAME PENDRAW4

DESCRIPTION "DRAWING WITH BITMAPS"


EXCE lame WINDOWS
STUB "WINSTUB.EXE"
GO DIE PRELOAD MOVEABLE DISCARDABLE
DATA PRELOAD MOVEABLE MULTIPLE
We IMP SH 74(2 1024
STACKS TZE 8192
EXPORTS WndProc
392 BORLAND C++ 3.0 PROGRAMMING, Second Edition

|}fessase]sase5es/ /
Hal PENDRAW4.H WU
/ fesse se seaSeseces/ //

#define IDM_BRICK 100


#define IDM_CHAINS 101
#define IDM_DIAMOND 102
#define TDIMae Sse E TOS

| essa se=sS2Ses25=5e/ //

// PenDraw4.RC KY
// application menu //
/) (SS SseeSeSeeseseesss/ /

PENDRAW4 MENU LOADONCALL MOVEABLE PURE DISCARDABLE


BEGIN
POPUP "&Bitmaps"
BEGIN
Menultem "&Bricks\t(8x8)", TOO GHEGKED
MenuIltem "&Chain\t(24x24)", 101
Menultem "&Diamonds\t(8x8)", 102
Menultem "&Stripes\t(8x8)", 103
END
END

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"

HANDLE helnst; /jeegivoibarl instance handle as


int BitmapOp = IDM_DrawBM;
BOOL bMoveBitmap = FALSE;
HBITMAP hBitmap;
POINT ptBitmap;

static tits EineDatabi-=—)€ 1005" 150 .4:135077 160> ee 155,


1255) 140, 110 pai20 (Sey 140,
ESO onloi Ae oO L207 100 };
HCURSOR SwitchCursor( LPSTR LpName )
{
Chapter 18: Brushes, Bitmaps, BLTs, and DIBs 393

HCURSOR HiGUunpSIonn:

MGUiaS Ole LoadCursor( NULL, LpName );


SetCursor ( hCursor i
return( DIGUiSsiOlae

BOOL DrawBitmap( HWND hwnd, LPSTR lLpName,


li Gee Ose, iMe WIPOS 2
{
HDC hidicy, hdcMem;
BITMAP bm;
HBITMAP hBitmap;

if¢!¢€ hBitmap = LoadBitmap ( hGInst, lLpName ) ) )


Pevuyrme FALSE 0-
hdc = GetDCC hwnd );
hdcMem = CreateCompatibleDC( hdc ye
SelectObject( hdcMem, HIBaumialom se:
SetMapMode( hdcMem, GetMapMode( hdc ) );
GetObject( hBitmap, sizeof(BITMAP), GEPSTRDS Sb e-
Bosc taG@uhidicy wm xboss my POs, bm. bmWidth, bm. bmHeight,
hdcMem, O, O, SIRIGCOR Va e-
ReleaseDC( hwnd, hdc 7
DeleteDC( hdcMem );
DeleteObject( hBitmap ye
PEeURMG IRM ws

BOOL DrawCenBitmap( HWND hwnd, LPSTR lLpName,


lini GeexXcROlse, Anicaey LOIS
i
HDC hidics, hdcMem;
BITMAP bm;
HBITMAP hBitmap;

if€( !(© hBitmap = LoadBitma p¢ hGInst, lpName ) ) )


me euinniG eA SEs:
hidicn =s- Gert DIG Gehiwinids ))-
hdcMem = CreateCompatibleD CiGehidic he
SelectObject( hdcMem, hBit map );
SetMapMode( hdcMem, GetMap Mode(€ hdc ) );
GetObject( hBitmap, sizeof Cy HYNIP D5 (CLIPS) talon) 25
xPos -= bm. bmWidth/2; // center bitmap LY
yPos bm. bmHeight/2; // on coordinates //
Bite lve ndicy. xPo's -ey Pos, b m.bmWidth, bm.bmHeight,
394. BORLAND C++ 3.0 PROGRAMMING, Second Edition

hdeMem, 0,0, -SRCCOPY .);


ReleaseDC( hwnd, hdc );
DeleteDC( hdcMem );
DeleteObject( hBitmap );
pee Wein TARWSs

void LineGraph( HWND hwnd, LPSTR LpName )


{
int le

for@ 1=0;7 i<sizeofCbineDatian >: ++)


DrawCenBitmap( hwnd, lLpName, i*25+20, LineDataLlil );
}

BOOL StretchBitmap( HWND hwnd, LPSTR LpName, RECT rect )


{
HDC hdc, hdcMem;
HBITMAP hBitmap;
BITMAP bm;

if( !'( hBitmap = LoadBitmap( hGInst, lLpName ) ) )


Rew Ui nim RAS Emr
hdc = GetDCC( hwnd );
hdcMem = CreateCompatibleDC(¢ hdc );
SelectObject(€ hdcMem, hBitmap );
SetMapMode( hdcMem, GetMapModeChdc) );
GetObject( hBitmap, sizeof(BITMAP), (LPSTR) &bm );
Sitiment.c nisilitiGahiGic pane cities Lekiaty mm Ine 1G Cra tOlDE
Recon 1ght,.recesbot tom,
hdcMem, O, O, bm.bmWidth, bm.bmHeight, SRCCOPY);
DeleteDC( hdcMem );
ReleaseDC( hwnd, hdc );
DeleteObject( hBitmap );
er Uintah Ems:

BOOL StretchBitMap2Client( HWND hwnd, LPSTR LpName )


d
HDC hdc, hdcMem;
HBITMAP hBitmap;
BITMAP bm;
RECT Rie,

if€ !€ hBitmap = LoadBitmap( hGInst, lLpName ) ) )


me CUI GAG Ea)
Chapter 18: Brushes, Bitmaps, BLTs, and DIBs 395

hdc = GetDCC hwnd );


hdcMem = CreateCompatibleDC( hdc );
SelectObject( hdcMem, hBitmap );
SetMapMode( hdcMem, GetMapModeChdc) );
GetObject( hBitmap, sizeof(BITMAP), CLPSTR). &bm );
GetClientRect( hwnd, &rect );
SUretcnbDltG hdc, rect. left, smects top,
MCIG ie maIe EG nit smn e Calm D Ostut om
hdcMem, O, O, bm. bmWidth, bm. bmHeight,
SRIC CORN:
DeleteDC( hdcMem );
ReleaseDC( hwnd, hdc );
DeleteObject( hBitmap );
Re Un GaaliRUiEm os

void MoveBitmap( HWND hwnd, HBITMAP hBitmap, POINT pt )

HDC hdc, hdcMem;


BITMAP bm;
hdc = GetDCC hwnd );
hdcMem = CreateCompatibleDC( hdc );
SelectObject( hdcMem, hBitmap );
SetMapMode( hdcMem, GetMapModeChdc) );
GetObject( hBitmap, sizeof(BITMAP), (LPSTR) &bm );
BacgeilGia hidicy anpit ex. itnys,- ee Dime bm Wikd st Nemo Omi enlace,
niciciMieims,w Oy weOp-meS kiCiCO)PaYuu a
ReleaseDC( hwnd, hdc );
DeleteDC( hdcMem );

Long FAR PASCAL WndProc( HWND hwnd, WORD msg,


WORD wParam, LONG LParam )
{
static int cxStep, cyStep;
HDC hdc;
HMENU hMenu;
REG Recut,
P/AVGNGc
ous Riv Cale pisi,

switche msg ?)
(i
case WM_COMMAND:
hMenu = GetMenu( hwnd );
switch( wParam )
396 BORLAND C++ 3.0 PROGRAMMING, Second Edition

case IDM_DrawBM: case IDM_CenterBM:


case IDM_StretchBM: Calsicue UD ME Sitimertic
hid CUskeini tie
case IDM _BMTrackMouse: case IDM_BMLineGraph:
CheckMenuIltem( hMenu, BitmapOp,
MES UN GHECKEDE Ds,
BitmapOp = wParam;
CheckMenulIltem( hMenu, BitmapOp, MF_CHECKED );
break;

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

if€ !€( wParam && MK_LBUTTON ) ) break;


case WM_LBUTTONDOWN:
if€ BitmapOp == IDM_BMTrackMouse )
{
InvalidateRect( hwnd, NULL, TRUE );
ptBitmap = MAKEPOINT(C LParam );
}
return(0);

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

int PASCAL WinMain( HANDLE hInst, HANDLE hPrevInst,


(SESuuk lpszCmdParam, int nCmdShow )
{
static char szAppNameL] = "PENDRAWS5";
HWND hwnd;
MSG msg;
WNDCLASS WC;

Cen Pine velins te


Chapter 18: Brushes, Bitmaps, BLTs, and DIBs 399

we.hInst ance = hInst;


we.lpfnw ndProc = WndProc;
wick. cbDIG ls Extra = 0;
we.cbWnd Extra =O):
WiGemDSIZ1G lassName = szAppName;
we.hiIcon = LoadIcon( hInst, szAppName );
we.lpszM enuName = (LPSTR) szAppName;
we.hCurs or = Load curs ome NUE ES DIG ARROW =»):
we.hbrBa ckground GetStockObject( WHITE_BRUSH );
we.style = CS_HREDRAW | CS_VREDRAW;
Register CilGalsis Gua Cum
ay
hGInst = hl nist: // provides global handle to hInst //
hwnd = Crea teWindow( szAppName,
"Pen Draw 5: Bitmap Images",
WS_OVERLAPPEDWINDOW,
CWS EDIE AU ETe- as GiWae UISEIDIEAUN Eie,
CWHUS EDIE FAUITe- = CWaUIS E DENA.
NULL, NWILL. InemSis. WWNLIL dN¢
ShowWindow( hwnd, nCmdShow );
UpdateWindo wC hwnd );
while(€ GetM eisisralgie|Gara MSigh- NIU Oyen Oe)
{
Translat eMessage( &msg );
Dispatch Message( &msg );
}
return( msg -wParam );

NAME PENDRAW5

DES CREP LOIN "DRAWING WITH BITMAPS"


EXE TY, PE WINDOWS
STUB SWOUNSW UBS EXE
CODE PRELOAD MOVEABLE DISCARDABLE
DATA PRELOAD MOVEABLE MULTIPLE
ler Ss)Ab7412 1024
STACKSIZE 8192
EXPORTS WndProc
400 BORLAND C++ 3.0 PROGRAMMING, Second Edition

Pp SeSeeseesssesseses
/ //
// PENDRAWS.H //
// fPseeSsaeassaSesseeee/ /

#define IDM_DrawBM 601


#define IDM_CenterBM 602
#define IDM_StretchBM 603
#define IDM_Stretch2Client 604
#define IDM_BMTrackMouse 605
#define IDM_BMLineGraph 606

f/f SeSsasesessaesesoss/ /
Leh PenDraw5.RC Lh
//e*menuSstructure’ //
f /fPSSeee sos] SeSseeosses
////

PENDRAW5 MENU LOADONCALL MOVEABLE PURE DISCARDABLE


BEGIN
POPUP "&@Bitmap Operations"
BEGIN
Menultem "&Draw Bitmap", oOber CHECKED
Menultem "&Center Bitmap", 602
Menultem L& See ne tic he eBuktima ples, 603
Menultem ‘St&retch Bitmap toeclvent: 604
Menultem oPSolntwal
GiKee MOUS eum Gala nG Kae 605
Menultem "&Bitmap Line Graph", 606
END
END
Chapter 19

Metafile Operations

Metafiles, mentioned briefly in Chapter 18, are a collection of GDI functions


encoded in a binary format and, as such, can be replayed just like a tape
recording, recreating the original image. This may sound very nice, but it is
not particularly useful. So, what good are metafiles?
First, metafiles provide a means of sharing pictures between
applications, either via the clipboard or through disk files. Second,
metafiles provide a means of recording a calculated graphic so that it can
be repeated without necessarily repeating the calculations. Third, metafiles
often require much less space to store than bitmapped images. For example,
a 150-byte metafile can easily replace a 3,970-byte image file. Fourth,
metafiles are less device-dependent than bitmaps.
Do any of these sound like possibilities?
Granted, metafiles are not miracle solutions which will immediately cure
all of your programming problems, but they do offer additional possibilities
and potentials.

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:

SevectObiect<« hdc, GetstockObject( IlbiGRAY DRUSHe =);


Ellipse ¢, hac, —100,..—-1007>100, 100 97
SelectObjectG-hdc, GetStockObject< DKGRAY_BRUSH ). )>
SetPolyFillMode( hdc, ALTERNATE );
Roly Gomi nidice- =ap.tr name

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:

static HANDLE hMetafile;


HDC hdc, hdcMeta;

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:

nPen = "CreaterenC PS NULL Ay OL.


SelectObject(€ hdcMeta, hPen );
SelectObject(€ hdcMeta, GetStockObject( LTGRAY_BRUSH ) 7
Ellipsex <hdecMetay—~100p I= 10054 100 100 a)>
Chapter 19: Metafile Operations 403

SelectObject( hdcMeta, GetStockObject( DKGRAY_BRUSH ) );


SetPolyFillMode( hdcMeta, ALTERNATE );
Polygon hdcMeta, pt, ¢ .)>

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:

hMetaFile = CloseMetaFile( hdcMeta );

In this instance, the CloseMetaFile instruction also returns a handle to the


metafile which will be needed later to replay the metafile. There are, of course,
alternative methods of creating a handle to an existing metafile, and these will
be discussed presently.
After closing the metafile, just as during normal screen paint operations,
it’s time to clean up by deleting any logical objects which were created for use
during this process—in this case, the hPen object:

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

First, however, a few familiar instructions are needed, beginning with a


BeginPaint instruction to return a device context handle and mapping mode
and extent instructions:

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:

for(€ i=07 1<37 itt )


{
SetWindowOrgC hde,- —200—-Ci*3500) —500).)-
PlayMetaFile€ hdc, hMetaFile );
SetWindowOrg( hdc, -500, -200-(i*300) );
PlayMetaFile(€ hdc, hMetaFile );
u

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:

EndPaint( hwnd, &ps );


PEE Wein G9) 5

The metafile operations just discussed are demonstrated in the PenDraw6


program at the end of this chapter, and should adequately illustrate the
basic principles of metafile operations, creating the screen display shown
in Figure 19-1.

Figure 19-1: Metafile Images Produced By PenDraw6


406 BORLAND C++ 3.0 PROGRAMMING, Second Edition

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.

Writing Metafiles To Disk


In the preceding example, the metafile created was a memory file. Metafiles
can also be written to disk, requiring only one small change in form:

hdcMeta = CreateMetaFile( "D:\\METAFILE.WMF" );

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:

hdcMeta = CreateMetaFile( (LPSTR) szMetaFileName );

where szMetaFileName is a null-terminated string specifying the filename


and, as desired, drive and path.

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:

GetTempFileName( cDrive, lLpPrefixStr, wUnique,


LpTempFileName );

or, more appropriately, as:

GetTempFileName( O, "MTF", O, CLPSTR) szMetaFileName );

On return, the temporary filename is in the szMetaFileName variable.


The GetTempFileName function creates a temporary file with a filename
which begins with the tilde character (~), followed by an optional prefix (up
Chapter 19: Metafile Operations 407

to three letters), and completed with a unique four-character hexadecimal


value created from the wUnique parameter. If wUnique is zero, a number will
be generated.
The cDrive parameter is an integer value for the suggested drive for the
temporary file. If cDrive is passed as zero, the default (current) drive is
assumed.
The szMetaFileName is an empty string which will receive the
drive/path/filename specification for the temporary file. A buffer length of
144 characters is suggested to provide enough room for all possible path
specifications, and presumably to allow extra space for the doubled backslash
characters ("\\") required by C strings, since DOS pathnames are not per-
mitted to exceed 67 characters (excluding drive and file names).
An example of the returned drive/path/filename might be:
D:\WINDOWS\TEMP\~MFT2E12.TMP.
Other than the format and location returned, the tempfile is no different
than any other disk file.
However, large numbers of temporary files do tend to clog a hard disk, a
fault which is present in many existing Windows applications. The bad news
is that your only real recourse is to check the \Windows\Temp directory
periodically and erase the fossil files. The good news is that unlike .BAK files,
these are all gathered ina single location where they are easy to find and wipe.
For your own applications, one small provision can prevent this annoyance:
Erase temporary files when no longer needed. This can be accomplished with
a very minor revision, 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.

Accessing Disk MetaFiles


One of the reasons for creating a metafile is so that another application can
access the information contained, or so that the application can access its own
metafile(s) created at some previous time. To do so, a means is needed to
408 BORLAND C++ 3.0 PROGRAMMING, Second Edition

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:

hMetaFile = GetMetaFile( (LPSTR) szMetaFileName );

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.

Figure 19-2: Metafile Contents

000006) 8§6—41 46 69 66 «26 63 4B 66 6266 68 G3 6a (12 68 88 88 G-o-- -- ---


666616 66 66 68 66 66 66 FA 42 65 46 61 66 48 46 88 48 ------@9-G-----
866626 «§6966 66 64 66 «266 86 2D 61 «266 66 87 66 «28 88 FC 82 --¢
060036 46 66 CH CH CH 46 66 66 44 66 66 66 2D 61 81 28 --L
660048 67 66 66 66 18 44 64 46 64 66 9C FF 9C FF 67 48 e---Ted-d-£ £ +-
6668656 66 66 FC 62 46 66 46 46 46 46 06 66 64 66 48 88 --"@- -@@@---¢---
666668 2D 61 62 66 64 66 66 66 G6 41 61 64H 12 86 B86 48 —G6-¢- --@05-f---
666676 24 63 67 66 66 66 64 64 2B 64 AG FF 2B2 FF 3E 68 S@+---d-+-2
066688 61 66 EA FF 9F FF EA FF 4E 66 3E 66 ODS FF AG FF a-n f 2 N->
6888980 63 66 66 66 66 48 @-----

In general, the metafile begins with an 18-byte record header described by


the METAHEADERR structure, and is followed by a series of METARECORD
records, each consisting of a minimum of three WORD values.
Chapter 19: Metafile Operations 409

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:

Es ala pisies Gaaniare Mert arm —n10] OF — 400 OOP mn OOM es

appears in the metafile as a seven-word record which, in hexadecimal format,


reads 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:

00000007 0418 0064 0064 Fnore aR OG

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):

0001 0009 0300 0000004B 0003 00000012 0000


METAHEADER
hpenw=Createren« -PSONULES S172 0L Oy
00000008 O2FA 0005 0001 2000 0000 0000
410 BORLAND C++ 3.0 PROGRAMMING, Second Edition

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)

Notice that the SelectObject (GetStockObject... instructions are broken down


as two separate meta-instructions, while the final parameter for each of the
three SelectObject instructions is a sequential object identifier. Also, the Poly-
gon meta-instruction includes the complete polygon data point information.
And finally, there is one omission to point out, the fact that no DeletePen
instruction appears in the metafile, even though it did appear in the original
instructions. Where the original instruction was CreatePen, in the metafile this
has become CreatePenIndirect, both voiding the need for a delete instruction
and voiding any requirement to restore the original pen, brush or other
drawing objects.
Happily, of course, you are not personally required to be able to read
metafile instruction in order to use metafiles, but occasionally, it may help to
be familiar with the metafile organization.
Chapter 19: Metafile Operations 411

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:

= Functions treating the metafile device context as if it were an actual


device context. These include CreateCompatibleBitmap, Create-
CompatibleDC, CreateDiscardableBitmap, DeleteDC, PlayMetaFile and
ReleaseDC.
ms All functions beginning Get..., such as GetDeviceCaps or GetTextMetrics.
All information in the metafile is preset and can not accommodate
information returned by such functions.
= Any other functions designed to return information to the program such
as DPtoLP, LPtoDP, etc. Macros, on the other hand, are permissible since
these are evaluated at compile time.
Functions requiring handles to brushes such as: FillRect and FrameRect.
= Some of the more complex functions including Drawicon, GrayString
and SetBrushOrg.
412 BORLAND C++ 3.0 PROGRAMMING, Second Edition

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.

Saving and Restoring The Device Context


When a metafile is played back, operations begin with the device context
attributes that were already in effect at the time of the PlayMetaFile instruc-
tion. However, the metafile is free to change the drawing mode, mapping
mode, text colors, etc., but when the metafile is finished, these changes remain
in effect for the device context. Therefore, to retain the original device context,
two options are possible:
First, before the PlayMetaFile instruction is executed, the present device
context can be saved as:
SaveDC( hdc );

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

Htif PenDraw6.C ifUf


Wil C++ Windows Drawing Ihif
fio creating a metafile //

#include <windows.h>
#include <stdio.h>
Hinclude <math.h>

feGTrING IPU2Z © 240) * 6947s » // radians in 360 degrees //

long FAR PASCAL WndProc( HWND hwnd, WORD msg,


WORD wParam, LONG LParam )
{
Sit citalCmalinat cxWnd, cyWnd;
static HANDLE hMetaFile;
SUaCTC (PONY feels vale
HDC hdc, hdcMeta;
HPEN hPen;
PAINTSTRUCT Dist
int ie Ie

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

int PASCAL WinMain( HANDLE hInst, HANDLE hPrevInst,


LPSTR lLpszCmdParam, int nCmdShow )
{
static char szAppName[] = "PENDRAW6";
HWND hwnd;
MSG msg;
WNDCLASS wicy

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

we.hCursor = LoadCursor( NULL, IDC_ARROW );


wce.-hbrBackground = GetStockObject( WHITE_BRUSH );
we.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass( &we );
i
hwnd = CreateWindow(
szAppName,
"Pen Draw 6: MetaFile Images",
WS_OVERLAPPEDWINDOW,
CWRUISIED ElAW le eG WaeUISEDIE AU eae.
CWRUSE DEF AU Eie-anG WasU)SiE
DIE AUT
NWR NWEL, heise, NWILIL 5
ShowWindow( hwnd, nCmdShow );
UpdateWindow( hwnd );
while€ GetMessage( &msg, NULL, O, O ) )
if
Translat eMessage( &msg );
DispatchMessage( &msg );
}
return(€ msg.wParam );
}

5 PENDRAW6.DEF

NAME PENDRAW6

DESIGCR LPL iN "DRAWING WITH PENS"


EeXtealeyarse WINDOWS
STUB WINS TUB SEXES
CODE PRELOAD MOVEABLE DISCARDABLE
DATA PRELOAD MOVEABLE MULTIPLE
HEVA|P:SAGZ:E 1024
STACKSIZE 8192
EXPORTS WndProc

ji / Sse sesSeoSeese
i //

a, PENDRAW6.H Mfif
/ fSeSSsSeessesessee/ //

// empty file -- nothing required //

/ fSesSsssosesesees/ /

Hfif PENDRAW6.RC //
f/f fPoscesssSssssece//

// empty file -- no menu defined //


416 BORLAND C++ 3.0 PROGRAMMING, Second Edition

/ 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>

Holo We IPue © B25 3, aS 2

Long FAR PASCAL WndProc(€ HWND hwnd, WORD msg,


WORD wParam, LONG lParam )
{

Sitatalc char szMetaFileNameL67]; // add Wy)

switch( msg )
c
case WM_CREATE:

GetTempFileName( QO, MEST OG,


CEPSTRD szMetaFileName ); // add //
hdcMeta = CreateMetaFile(
C(LPSTR) szMetaFileName ); // revise tok

GemeunMiGoD

case WM_SIZE: // no changes //

case WM_PAINT: // no changes Lh

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

Graphic Typefaces And Styles

Chapters 2 and 3 have already introduced Windows’ basic text-graphic fea-


tures as well as some aspects of fonts, font selection and the differences
between ANSI and ASCII fonts, and functions supporting string conversion
between the two forms. Thus far, virtually every application demonstrated
has used some text, if only for the caption bar at the top of the window. But
now it’s time to look at the more advanced elements of text manipulation,
including font sizes and styles, justifying text, using text in different device
contexts, and the stock fonts supplied with Windows. However, before dis-
cussing fonts and typefaces, there are a few aspects of text output remaining
to be introduced.

The TextOut and SetTextAlign Functions


Most text output thus far has used the general format:

Lexcpoutceunde, xpos, yros, UpStr, nCount » 3

or, more conveniently, the format:

exo Ota nidic mEexX-OIS anys OISs ee Siz BUmtate,


MED til twig cars'2 Buchs: mec: tb so

where the wsprintf function is incorporated to provide both convenience in


calculating the length (nCount) parameter, and in creating formatted strings.

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:

SetTextAlign( hdc, wFlags );

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.

Table 20-1: Text Alignment Flags


Flag ID Meaning

Vertical Alignment At yPos


TACTOP aligns the top of the bounding rectangle
TA_BASELINE aligns the baseline of the chosen font
TA_BOTTOM aligns the bottom of the bounding rectangle

Horizontal Alignment At xPos


TA_CENTER aligns the horizontal center of the bounding
rectangle (Note: current position is not affected.)
TA_LEFT aligns the left side of the bounding rectangle
TA_RIGHT aligns the right side of the bounding rectangle

Current Position Control


TA_NOUPDATECP current position is not updated after TextOut or ExtTextOut calls
TA_UPDATECP current position is updated after each TextOut or ExtTextOut call

The default flags are TA_LEFT, TA_TOP, and TA_NOUPDATECP.


SetTextAlign returns a word value reporting the previous text alignment
settings with the horizontal alignment in the low byte and the vertical align-
ment in the high byte. The previous current position flag is not reported.
Chapter 20: Graphic Typefaces and Styles 419

The ExtTextOut Function


The ExtTextOut function uses a RECT specification to write a character string
within the region specified. The rectangular region can be set as opaque
(ETO_OPAQUE) to be filled with the current background color, and/or can
be set as a clipping region (ETO_CLIPPED). ExtTextOut also includes a provi-
sion to control spacing between characters. By default, the current position is not
used or updated by ExtTextOut. However, applications can call SetTextAlign
with wFlags set to TA_UPDATECP, permitting Windows to use and update
the current position each time ExtTextOut is called. However, when
TA_UPDATECP is set, Windows ignores the X and Y parameters on subse-
quent ExtTextOut calls. Further details on ExtTextOut parameters and features
can be found by using the Help function.

The TabbedTextOut Function


The TabbedTextOut function provides an alternative to TextOut, permitting the
output string to be tabbed according to column positions specified in the
IpnTabStopPositions field. The TabbedTextOut function is called as:

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

nTabOrigin allows an application to call TabbedTextOut several times for a


single line. When TabbedTextOut is called repeatedly with nTabOrigin set to
the same value each time, all tabs are expanded relative to nTabOrigin.
TabbedTextOut returns a DWORD value with the height in the high word and
the width in the low word.

The DrawText Function


The DrawText function draws formatted text in the rectangle specified by the
IpRect parameter. Flags in the wFormat instruction can be set to expand tabs,
justify text to the right, left, or center of the rectangle area, or write text as
multiple lines.
DrawText uses the device context’s selected font, text color, and background
color. Unless DT_NOCLIP is used, DrawText clips the text displayed to the
given rectangle. Also, all formatting is assumed to have multiple lines unless
the DT_SINGLELINE format is given. DrawText is called as:

Dirawhext Gvhidic alps tr sncColmty a UpReiciG a WirOinma tems:

IpString is a pointer (LPSTR) to a string or string buffer. Note that if the


nCount parameter will be passed as -1, this must be a null-terminated (ASCIIZ)
string.
The nCount parameter is an integer value specifying the number of bytes in
the string. If nCount is -1, lpString is assumed to be a long pointer to a
null-terminated (ASCIIZ) string, and the character count is computed
automatically.
IpRect is a pointer to a RECT data structure containing the rectangle (in
logical coordinates) within which the text is to be formatted. If the selected
font is too large for the specified rectangle, DrawText does not attempt to
substitute a smaller font; depending on the clipflag, only part of the text will
be printed.
wFormat is a WORD value containing flag values which set the method
of formatting the text. This may be any combination of values shown in
Table 20-2.
Chapter 20: Graphic Typefaces and Styles 421

Table 20-2: DrawText Formats

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)

Automatic Rectangle Calculation


DISCALCRECT when set, DrawText calculates width and height of the rectangle
according to the text displayed.

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

Modifying The Device Context


While the choice of output function and flag parameters controls many
features of how text is displayed, the active device context also governs
several aspects of the display, including not only foreground and background
colors, but also how the text display pixels are combined with existing back-
grounds.
Using the default device context, the foreground (text) color is black, but it
can be changed as desired using the SetTextColor function as:

Sleltane
> &Gol Cols Gun CiGr-mmING| D1GOMOD E

or

SetTextColor( hdc, GetSysColor( COLOR_WINDOWTEXT ) );

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:

SetBkMode( hdc, nMode ); // OPAQUE or TRANSPARENT //

In TRANSPARENT mode, the text is written only as the foreground pixels


and the existing background pixels remain unchanged. The background color
(white, by default) can be changed using the SetBkColor function as:

SetBkCotor’ “hdc? irobGol or

Or,

SetBkColor(€ hdc, GetSysColor( COLOR_WINDOW ) );

Again, if the rgbColor value results in a dithered color, it is converted to the


nearest pure color supported by the output device.
Chapter 20: Graphic Typefaces and Styles 423

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:

Table 20-3: Stock Logical Fonts


Constant Value Char Set Typeface*
OEM_FIXED_ FONT 10 OEM Terminal (DOS font)
ANSI_FIXED_FONT 11 ANSI Courier (typewriter)
ANSI VAR_FONT 12 ANSI Helv (Helvetica)
SYSTEM_FONT ils) ANSI System
DEVICE_DEFAULT_FONT 14 varies, device _ varies, device dependent
dependent
SYSTEM_FIXED_FONT 16 ANSI Courier (Windows 2.0
compatible)
* may vary, depending on system and output device

The DEVICE _DEFAULT_FONT, is dependent on the output device’s capabil-


ity. For a CRT output device, such as a VGA video system, this will probably be
the Courier font, but for a printer such as an Epson, this will bea typeface internal
to the printer. The advantage of using a device font is that output is faster, since
Windows does not need to operate the device in graphics mode.
Selecting any of these stock fonts is quite simple: accomplished as:
hEont =e Ge tstockObject«. StockFon
tl Di 2;
SellectObject( hdc, hFont )-;

The same thing can be accomplished in a single step as:


SelectObject( hdc, GetStockObject( StockFontID ) );
424 BORLAND C++ 3.0 PROGRAMMING, Second Edition

At this time, both the SelectObject and GetStockObject functions should be


quite familiar, since they have been used repeatedly in previous chapters to
select logical objects such as bitmaps, brushes, and pens into the device
context. The drawback to using GetStockObject, however, is that this gives you
very little control over the size, spacing, and typeface selection. Instead of
GetStockObject, such control requires using custom fonts.

A Brief History of Typefaces


Having (almost) cut my teeth on a pi-stick, a device used by printers in the
days before electronic typesetting, to me the word "typeface" still conjures up
memories of trays of small compartments containing individual metal
characters in various sizes and typeface designs.
Granted, much of the typesetting on the newspaper where I worked beginn-
ing at age six (sweeping floors ... but for pay) was accomplished by a huge and
intricate machine known as a linotype. However, larger type sizes, such as
those used for ads, headlines, and other features, were beyond the capacity of
the linotype, and fell to a set of nimble fingers which could choose, arrange,
and align individual characters from the appropriate tray with a speed which
might have been envied by many typists. This was the only way that different
typefaces and different type sizes could be assembled for printing.
Today, of course, these are only the memories of an old-time printer’s devil,
but please refer back to the dedication, because movable type was a techno-
logy that revolutionized the world.
Neither handset nor linotype were compatible with computers so, for a
video display, new methods of creating letters were required. The first of these
methods, still used today, was known as a bitmapped font.
In a bitmapped font, sometimes called a "raster" font, the data required
to display each character is stored as a bit image, an array of bits telling
which pixels are on and which are off, just like the bitmapped icons or the
bitmapped brushes discussed previously. So bitmapped fonts were and are
convenient, because these are easily output to a screen, or to a dot-matrix
printer or a laserjet, on a one-bit-to-one-pixel basis, no calculations
required. However, bitmapped fonts have shortcomings, as illustrated in
the left side of Figure 20-1. They are not easily sized, and when enlarged, are
grainy and generally unattractive.
Chapter 20: Graphic Typefaces and Styles 425

Figure 20-1: Bitmapped vs. Stroked (Vectored) Fonts

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

In Windows 3.0, some provision is made for increasing the weight of


enlarged characters by adding to the number of strokes comprising the charac-
ter. The results are similar to the BGI Triplex font. However, the large screen
characters remain more like outlines than solid figures. Nevertheless, as a
trade-off against slowing display speed, these results are acceptable, if not
completely ideal. Overall, stroked fonts have other advantages that far out-
weigh the few disadvantages, since both italics and boldface can be created
with results considerably superior to similar operations with bitmapped fonts.
This is not to imply that bitmapped fonts can not be italicized, simply
that the results are not always as clear as might be desired. For an example
please refer to Figure 20-3, where the Symbol font appears in both standard
and italic forms.
A third type of font is also used, in which the characters are described as
lines and curves comprising an outline image, which is drawn and then filled.
This type, however, requires considerably more computation time to draw,
and is generally used only by drafting/typesetting applications such as
CorelDraw, Ventura Publisher, or other elaborate output software/devices
requiring high-fidelity, high-quality typefaces.

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.

Table 20-4: Font Families

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.

Font Resource Files


When Windows is installed on a system, a number of .FON font files are
copied to the d:\WINDOWS\SYSTEM subdirectory. Precisely which files
are installed depends on your system configuration and hardware
(particularly the system video), but the selections will be similar to those
shown in Table 20-5.
428 BORLAND C++ 3.0 PROGRAMMING, Second Edition

Figure 20-2: Principal Fonts and Styles

ANSI, Default Pitch Font: Don't Care Typeface: Courier


ANSI, Default Pitch Font; Roman Typeface: Tms Rmn
ANSI. Default Pitch Font: Swiss ‘Typeface: Helv
ANSI, Default Pitch Font: Modern Typeface: Courier
ANSI, Default Pitch Font: Script Typeface: System
ANSI, Fixed Pitch Font: Don't Care Typeface: Courier
ANSI, Fixed Pitch Font: Roman Typeface: System
ANSI, Fixed Pitch Font: Swiss Typeface: System
ANSI, Fixed Pitch Font: Modern Typeface: Courier
ANSI, Fixed Pitch Font: Script Typeface: System

ANSI, Variable Pitch Font: Don't Care Typeface: Helv


ANSI, Variable Pitch Font: Roman ‘Typeface: Tms Rmn
ANSI. Variable Pitch Font: Swiss Typeface: Hely
ANSI, Variable Pitch Font: Modern Typeface: Courier
ANSI, Uariable Pitch Font: Script Typeface: System

QEM. Default Pitch Font: Don't Care Typeface: Roman


QEM. Default Pitch Font: Rernan Typeface: Roman
OEM. Default Pitch Font: Swiss Typeface: Roman
OEM, Default Pitch Font: Madern Typeface: Medern
GEPR, Safad! Pach Jord: Jonigd Sypafacs: Jeonigh
OEM. Fixed Pitch Font: Don’t Care Typeface: Terminal
OEM, Fixed Pitch Font: Roman Typeface: Terminal
OEM. Fixed Pitch Font: Swiss Typeface: Terminal
OEM, Fixed Pitch Font: Modern Typeface: Terminal
OEM. Fixed Pitch Font: Script Typeface: Terminal

OEM. Variable Piteh Font: Don't Care ‘Typeface: Roman


OEM. Variable Pitch Font: Rernan Typeface: Roman
OEM. Variable Piteh Font: Swiss Typeface: Roman
OEM, Variable Pitch Font: Modern Typeface: Modern
GER, Varabh (Puch Jord: Doripl Sypafacs: Sorigl

Table 20-5: Windows .FON Typeface/Font Files


Filename Family Typeface Charset Type Size
VGAFIX DONTCARE System ANSI Bitmap 5776
VGAOEM MODERN Terminal OEM Bitmap 5584
VGASYS SWISS System ANSI Bitmap 6368

MODERN MODERN Modern OEM Stroked 9728


ROMAN ROMAN Roman OEM Stroked 14336
SGRUPA SCRIPT Script OEM Stroked ikeyeyl2
Chapter 20: Graphic Typefaces and Styles 429

Table 20-5: Windows .FON Typeface/Font Files (cont.)


COURE MODERN Courier ANSI Bitmap 21360
HELVE SWISS Helv ANSI Bitmap 59696
TMSRE ROMAN Tms Rmn ANSI Bitmap 53520
SYMBOLE DECORATIVE Symbol SYMBOL Bitmap 56912

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.

Table 20-6: Device Font Identifiers


Device ID Aspect Ratio _—_Pixels/Log Inch
Horz Vert
CGA video A 200 96 48
EGA video B 133 96 72
Okidata Printers Cc 83 60 72
Epson (or IBM) Printers D 167 120 72
VGA video E 100 96 96
8514/A video F 100 120 120

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

The Symbol Font


Figure 20-3 shows a sixth typeface, Symbol, which is simply a Greek character
set from the catch-all Decorative family. This family can include any type of
symbol set, such as Zapf Dingbats or other special fonts.
The Symbol font is a bitmapped font, and so provides a good example of
the effective distortion resulting from generating an italic version of a bitmap-
ped font. With a stroked font, some distortion may still exist, but certainly to
a much lesser degree and, in general, the distortion is not visually obvious.

Figure 20-3: The Symbol Character Set

ABXAE @®THISKAMNOTIOPZ TY chia?


ABNAL DP!AIGAANMINGHOSS FY¥CHEEL
aSyosdymp kAuvor GooTmaue yo
LE YAEL IO AA LISI LITE CTE BS
Using Logical (Custom) Fonts
Logical fonts, while based on the same font files, provide us with the flexibility
that is lacking in the stock font objects. For example, using a custom logical
font, an application is able to change the characteristics of the font, including
height, width, italic and bold faces (weight), and to select explicit typefaces or
styles which are not supported as stock logical font objects.
The appearance of a custom logical font is controlled by the fourteen fields
in the LOGFONT structure defined in Windows.H:

LOGFONT ¢€ struct tagLOGFONT )


Gaiinat: LfHeight; int LfWidth;
int LfEscapement; i iaie LfOrientation;
int LfWeight;
Bais Eee felataulenicr
Biya Eee UimGieln leiumer: BYTE: Et Strakedutc.
Bere UtiGialmSients-
BYTE UfOutPrecisions. .BYTE sic lipePrecis
aon
Bynes stat QiuianGgnGys-
BYTE LfPitchAndFamily;
Chapter 20: Graphic Typefaces and Styles 431

BYTE lfFaceNameL LF_FACESIZE J];

Character Size: IfHeight and IfWidth


The /fHeight field defines the height of the font in logical units, including the
internal leading but not the external leading. Since the point size of the font is
the font height less the internal leading, this value specifies line spacing rather
than absolute font size. Negative values of [fHeight are treated as absolute
values, setting the desired ascent size (font size) instead of the line spacing.
A value of zero sets a default size for the font.
The /fWidth value sets the character width in logical units. Normally, a zero
value would be used to allow Windows to match the font width to the height,
but specific values can be set to create condensed or expanded fonts. However,
when non-zero values are used, particularly with bitmapped fonts, Windows
may select a font designed for an aspect ratio different from the device context
where the font is selected.
To review the font height and width characteristics, refer to Chapter Two,
Figure 2-1, "Windows’ Font Metrics".

Character Orientation: IfEscapement and IfOrientation


The /fEscapement value is an angle expressed in '/joth degree increments,
beginning with 0° at horizontal, setting the angle at which the string is written.
The /fOrientation value is also an angle expressed in '/yoth degree
increments, again beginning with 0° at horizontal, but now setting the angle
of the individual characters. Table 20-7 displays the text orientation for both
If Escapement and If Orientation

Table 20-7: Text Orientation

Value Degrees IfEscapement IfOrientation


String Orientation Character Orientation
0 0° left to right (default) normal (default)
900 90° vertical, rising rotated 90° counterclockwise
1800 180° right to left inverted
2700 2/0" vertical, falling rotated 90° clockwise
432 BORLAND C++ 3.0 PROGRAMMING, Second Edition

Caution: Neither the escapement nor the orientation features work


particularly well on screen, though these capabilities may be improved at a
future time. Also, before using either feature with an output device (printer,
plotter, etc), check the TEXTCAPS index with GetDeviceCaps to determine if
the device is capable of handling character rotation. (See the Devices.C pro-
gram in Chapter 15.)

Boldface Fonts: IfWeight


The IfWeight integer presently sets normal or boldface with two values
recommended: 400 for Normal, or 700 for Boldface. In actual fact, any value
from 0.550 is normal, and any greater value is boldface. Future versions of
Windows, however, are expected to support a wider range of font weights, so
their constants are already included in Windows.H as:

Table 20-8: Font Weights


Value Font Weight ID
0 FW_DONTCARE
100 FW_THIN
200 FW_EXTRALIGHT or FW_ULTRALIGHT
300 FW_LIGHT
400 FW_NORMAL or FW_REGULAR
500 FW_MEDIUM
600 FW_SEMIBOLD or FW_DEMIBOLD
700 FW_BOLD
800 FW_EXTRABOLD or FW_ULTRABOLD
900 FW_HEAVY or FW_BLACK

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

Underlines and StrikeOuts


The /fUnderline and IfStrikeOut fields are byte values used as flags. When
IfUnderline or IfStrikeOut are non-zero, underlining or strikeout bars are syn-
thesized for GDI fonts. For other output devices, check the TC_UA_ABLE and
TC_SO_ABLE bits in the TEXTCAPS returned by GetDeviceCaps (Chapter 15).

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.

Table 20-9: Character Sets

Value Character Set ID


0 ANSI_CHARSET
2: SYMBOL_CHARSET
128 SHIFTJIS_CHARSET (Japanese Kanji)
205 OEM_CHARSET

Caution: If an application uses a font with an unknown character set, no


attempt should be made to translate or interpret strings which will be
rendered with that font. Instead, the strings should be passed directly to the
output device driver.
Note: Kanji (Japanese) fonts are not distributed with U.S. and European
versions of Windows.

Font Matching: IfOutPrecision


The [fOutPrecison byte is not implemented by Windows 3.0 (see [fQuality) but
will be used to instruct Windows on matching desired font characteristics and
sizes with available fonts. Windows.H defines four identifiers as:

Table 20-10: Logical Font Precision


Value Precision ID
0 OUT_DEFAULT_PRECIS
1 OUT_STRING_PRECIS
2 OUT_CHARACTER_PRECIS
S OUT_STROKE_ PRECIS
434 BORLAND C++ 3.0 PROGRAMMING, Second Edition

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:

Table 20-11: Clipping Precision


Value Clipping ID
0 CLIP_DEFAULT_PRECIS
i CLIP_CHARACTER PRECIS clip entire character
i CLIP_STROKE PRECIS clip individual strokes

Font Matching: IfQuality


The /fQuality byte field tells Windows how to match an actual font to the
requested font characteristics (see also [fOutPrecision). Three options are
defined in Windows.H as:

Table 20-12: Font Matching


Value Font Matching ID
0 DEFAULT QUALITY
1 DRAFT_QUALITY
Ms PROOF_QUALITY

The PROOF_QUALITY setting instructs Windows not to increase font size


to match requested character height or width. While this setting restricts fonts
to their optimum size(s), obviously the results may be smaller than requested.

Font Pitch and Family


The /fPitchAndFamily byte is a combination of two values with the least
significant two bits selecting the pitch setting, and the most significant four
bits selecting the typeface family. Pitch and family values are combined using
the OR operator. Constants for pitch and family are defined in Windows.H as:

Table 20-13: Pitch and Family Constants


Value Family ID Value Pitch ID
0x00 FF_DONTCARE 0x00 DEFAULT_PITCH
0x10 FF_ROMAN 0x01 FIXED_PITCH
Chapter 20: Graphic Typefaces and Styles 435

Table 20-13: Pitch and Family Constants (cont.)


Value Family ID Value Pitch ID
0x20 FF_SWISS 0x02 VARIABLE_PITCH
0x30 FF_MODERN
0x40 FE_-SERIPT
0x50 FF_DECORATIVE

Typeface Names
The /fFaceName field is an array of byte, defined as:

BYTE Li FaceNamel (UF _ FACESIZE. J=

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:

GetTextFace( hdc, LF_FACESIZE, szFaceName );

The constant LF_FACESIZE is defined in Windows.H with a value of 32,


the maximum number of characters permitted in a typeface name.

Creating A Logical Font


Speaking of creating a logical font is partially a misnomer, because neither the
CreateFont nor CreateFontIndirect functions actually create a new font. Instead,
both functions select the closest match available from the GDI’s pool of
physical fonts. The matching font is selected on the basis of the information
found either in the LOGFONT structure using CreateFontIndirect, or in the font
specification parameters passed as arguments with the CreateFont function. In
either case, the first step in loading a specific font is calling either CreateFont
or CreateFontIndirect to return a handle to a font of type HFONT.
Next, after retrieving a handle to a logical font, the SelectObject function is
called, and responds by selecting the real physical font which is the closest match
to the logical font requested. After a font has been selected, the size and
characteristics of the actual, physical font can be queried via the GetTextMetrics
function while the GetTextFace function can be used to return the font name.
436 BORLAND C++ 3.0 PROGRAMMING, Second Edition

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.

CreateFont and CreateFontindirect


The CreateFont function is called with fourteen parameters corresponding to
the LOGFONT fields discussed previously, as:

CreateFont( nHeight, nWidth, nEscapement, nOrientation,


nWeqdght,.c¢ Ltalaicy ac Unde i Wilner arc cstimmikKe
OU tr,
cichvanSieite-. ac OlU Uput Pinexcrsmion ance ll piRtiTechrSmoinys
cQuality, cPitchAndFamily, lpFacename );

The CreateFontIndirect function is called with a briefer parameter list. This


is only because the /pLogFont argument is a long pointer to a structure of
LOGFONT type which contains, by assumption, essentially the same informa-
tion which is passed to CreateFont as individual parameters.

CreateFontIndirect( lLpLogFont )

Both functions return a handle to a logical font with the characteristics


requested. However, when the font is selected by using the SelectObject
function, GDI’s font mapper attempts to match the logical font with an
existing physical font. If an exact match is not found, an alternative font is
returned whose characteristics match as many of the requested characteristics
as possible.
The primary fields used for font matching, in approximate order of import-
ance, are shown following:
The /fCharSet field is always used, selecting either OEM or ANSI character
sets. If stroked fonts are desired, OEM _CHARSET should always be specified,
since ANSI_CHARSET selects only raster (bitmapped) fonts.
The /fFaceName field is important in specifying exactly which typeface is
desired. This field may also be passed as NULL, leaving it to the [fPitchAndFamily
field to select the actual typeface. The [fPitchAndFamily field, as previously
discussed, is actually two fields, but if [fFaceName is not set, both the pitch and
family specifications will be treated as important.
Chapter 20: Graphic Typefaces and Styles 437

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 );

Of course, a hands-on demonstration is always worth a hundred pages of


explanation, and the best way to become familiar with the font mapping
considerations is to execute the Fonts1.C demo program (the complete listing
appears at the end of this chapter) and play with different fonts and settings
while using the preceding comments as a guideline.

The Fonts1 Demo Program


The Fonts1 demo program illustrated in Figure 20-4 provides a convenient
means of selecting different font parameter settings while watching the result-
ing output in the bottom of the client window. Note that, particularly for
larger type sizes, it may be necessary or helpful to enlarge the application to
full screen size.
Most, though not all, of the font selection parameters are included in a
modeless dialog box, either as check boxes, as radio buttons, or (for the height,
width and weight fields) as edit boxes.
Seven mapping modes are provided, including the conventional Hi and Lo
Metric, Hi and Lo English, Text, and TWIPS modes.
The seventh mode, Logical TWIPS, is a custom mode corresponding to
conventional TWIPS except that the twenty logical units per point and 72
points per real physical inch in conventional TWIPS are now converted to 1440
438 BORLAND C++ 3.0 PROGRAMMING, Second Edition

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.

Figure 20-4: Fonts and Font Variables

Mapping Mode Text Metrics


OLoMetric © Text Height 500
Character Set OHiMetic © TWIPS Ascent 338
© ANSI @ OEM © Lo English © Logical Descent 162
© SYMBOLS © Hi English TWIPS Int Lead 27
Ext Lead 54
Height Type Style Avg Width 212
: Max Width 400
Width =| 200 © Roman ® Script Weight 700
Weight © Swiss © Decorative Pitch Variable
© Modern © Don't care Type Face Script
L] Match Aspect
Char Set OEM
Ed Italic
CL] Underline Cue: fe FontName — Script
eonrewste @ Default @ Default x Aspect 2
piece Moma | 5C Draft © Fixed Y Aspect 3
© Proof © Variable Overhang 262

pT EDS EGIL GE
as red. dag. 127456 7890 |

Calculating Point Sizes In Logical TWIPS


The Logical TWIPS mode is set as:
SetMapMode( hdc, MM_ANISOTROPIC );
SetWindowExt( hdc, 1440, 1440 );
SetViewport(€ hdc, GetDeviceCaps( hdc, LOGPIXELSX ),
GetDeviceCapst hde LOGPEX ELS Yao. 7
With this provision, point-sized fonts can now be written to the screen such
that a 72-point font will be one logical inch high, not one physical inch. But
Chapter 20: Graphic Typefaces and Styles 439

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:

LmHeight = PointSize * ( tm.tmHeight +


tm.tmInternalLeading ) / tm.tmHeight

Of course, it would probably be more convenient to begin by creating a


height ratio for the font as:
fnRatio = ( tm.tmHeight + tm.tmInternalLeading ) /
tm.tmHeight;

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 szAppNameLJ = "FONTS1";


int nMapMode =e aD) Dale eexales
DWORD dwAspMatch = OL;
HWND niDimor
OG FOINTmen fs:

void SetMapModeProc( HDC hdc )


{
if( nMapMode != IDD_LOGTWIPS )
SetMapMode( hdc, nMapMode ip) Wx se (Wil. wledaap 2) £
else
tt
SetMapMode( hdc, MM_ANISOTROPIC );
SetWindowExt( hdc, 1440, 1440 );
SetViewportExt( hdc,
GetDeviceCaps( hdc, LOGPIXELSX ),
GetDeviceCaps( hdc, LOGRIXELS ¥oe)
Pees:
void ShowMetrics( HWND hDlg )
<
SiG ait aicenl E Xu MEMsRelGescm):
Static struct
f ine tbe ies
lat Gauss PalaMys i
FontDataEd =
{ DID Ra aen EeliGiilie, &tm. tmHeight,
15D Dei MeeAtoiG
EN aie. &tm. tmAscent,
TD DEM DESCENT: &tm. tmDescent,
IDD_TM_LEADINT, &tm -tmInternalLeading,
ToD DRM ae EA DIE Xa, &tm. tmExternalLeading,
IDD_TM_WIDTHAVE, &tm -tmAveCharWidth,
IDD_TM_WIDTHMAX, &tm. tmMaxCharWidth,
TDIDES MA WEL Gite, &tm -tmWeight,
IDD_TM_OVER, &tm. tmOverhang,
TDD STM DIGI TX: &tm. tmDigitizedAspectXx,
SD) D Reet Mime isGelatin &tm -tmDigitizedAspectY 7
static char *szFontsL]
(a Done tiCamers: ARMENIA - LESIWLILS: Sime,
"Modern", SAW eaNeh als "Decorative" },
*szCharsetlid = FP ELAN See SayaMis\O} leans "OEM" +;
Chapter 20: Graphic Typefaces and Styles 441

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

WORD wParam, LONG LParam )

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

teen atOlhaleiniGaltaloin ool


7
Ghee hOUGe mecuisia On OUT_DEFAULT_PRECIS;
Ute Gt. Cl piPiveicai:s aon CLIP_DEFAULT_PRECIS;
ShowMetrics( hDlg ); fi Well einPowgy wo SeEerFoOeuUS bef
case WM_SETFOCUS:
SetFocus( GetDlgItem( hDlg, IDD_HEIGHT ) );
PacWwPiMG iFAILSIE MF
case WM_COMMAND:
switch( wParam )
{
case PDD EXd. case IDD_LOMETRIC:
case TSDID eels MiEs ReleGe= Gase ew UDI DSEOE NGAI SiH
case IDD_HIENGLISH: case IDD_TWIPS:
case IDD_LOGTWIPS:
nMapMode = wParam; break;
case IDD_ASPECT: case IDD_ITALIC:
case IDD_UNDERSCORE: case IDD_STRIKEOUT:
// no response //
break;
case IDD_ANSI: a Uh halinoe tame Or break;
case IDD_SYMBOL: Cat aa at GhaieC. —mee break;
case IDD_OEM: l f sirCharset ==a2555 break;
case IDD_QUALDEFAULT: case IDD_QUALDRAFT:
case IDD_QUALPROOF:
Lf.lfQuality = CB YehE.)
(wParam — IDD_QUALDEFAULT);
break;
case IDD_PITCHDEF: case IDDUPITCHEIXED:=
case IDD_PITCHVAR:
Lf.lfPitchAndFami Ly &= Ox FOr
Lf. lfPitchAndFami Lyail= (BYTE)
Chapter 20: Graphic Typefaces and Styles 443

(wParam - IDD_PITCHDEF );
break;

case IDD_ROMAN: case LDDESWILS Ss):


case IDD_MODERN: Gasiew LD Des G Rap de
CaSiemiaD Dep EeCOl case IDD_DONTCARE:
Lf#,tftPitchAndFamily &= Ox0F:
Che CEPItchAndFamily? |=a< BYTE)
CwParam = IDD_DONTCARE << 4 );3
CalsicD Dass EneRONM
ShowMetrics( hDlg );
InvalidateRect( GetParent(hDlg), NULL, TRUE );
break;
Casies 1 DDE QUIT:
PostQuitMessage(0Q0); break;
+} break;
aera late MENG UIuh) Gus RAlLSciEmme n=
}
ReScUInni GRU Esme
}
Long FAR PASCAL WndProc(€ HWND hwnd, WORD msg,
WORD wParam, LONG LParam )
nf
Sitautiicumcialmers: 2 eX tel sa
"The quick brown fox jumps over the Lazy red dog."
BW ASAE CIO 5
HANDLE helnisste.
HDC hidicy
HFONT MROInnte
FARPROC pin Dig Rin orcs
PAINT STRUCT: ps;
RECT rect;

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

hFont = SelectObject( hdc,


CreateFontIndirect( &lf )
TexstjoutG hdc) rect.lett, rect. Dolton, sz1ex tuo
DeleteObyect @ SelectObjectC hdc, hFront ») »;
EndPaint( hwnd, &ps );
return(0Q);
case WM_DESTROY: PostQuitMessage(0); return(0O);
}
return( DefWindowProc( hwnd, msg, wParam, lParam ) );
}
#pragma argsused
int PASCAL WinMain( HANDLE hInst, HANDLE hPrevInst,
LPSTR lLpszCmdParam, int nCmdShow )
{
static char szAppNameL] = "FONTS1";
HWND hwnd;
MSG msg;
WNDCLASS we;
ite Ge eeinismelvelini Sate)
{
we.hInstance NLinisite
we.lpfnWndProc WndProc;
we.cbClsExtra OF
we.cbWndExtra OF7
we.lpszClassName szAppName;
we.lpszMenuName CLPSTR) szAppName;
wce.hIcon LoadIcon( hInst, szAppName );
we.hCursor LoadCursor(€ NULL, IDC ARROW );
we. hbrBackground GetStockObject( WHITE_BRUSH );
we.style CS_HREDRAW | CS_VREDRAW;
RegisterClass( &w U0
Wu
CDP
CUP
0)
te
TOP
Cee 7
>
hwnd = CreateWindow( szAppName,
LeoOntese ts Selecting Fonts.+,
WS_OVERLAPPEDWINDOW |
WS_CLIPCHILDREN,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
MWe MNMIEE, Iniinsie, INWIL{(L yes
ShowWindow( hwnd, nCmdShow );
UpdateWindow( hwnd );
while€ GetMessage( &msg, NULL, 0, O ) )
{
if€ hDlg == O || ! IsDialogMessage( hDlg, &msg ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
Chapter 20: Graphic Typefaces and Styles 445

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

#define TDDar olEalph ONT


#define IDD_QUIT
#define IDD_TEXT
#define IDD_LOMETRIC
#define IDD_HIMETRIC
#define IDD_LOENGLISH
#define IDD_HIENGLISH
#define IDD_TWIPS
#define IDD_LOGTWIPS
#define IDD_HEIGHT
#define IDD_WIDTH
#define IDD_WEIGHT
#define TDD SUT Arc
#define IDD_UNDERSCORE
#define IDD_STRIKEOUT
#define IDD_ASPECT
#define IDD_ANSI
#define IDD_OEM
#define IDD_SYMBOL
#define IDD_QUALDEFAULT
H#define IDD_QUALDRAFT
#define IDD_QUALPROOF
#define IDD_PITCHDEF
#define IDD_PITCHFIXED
#define IDD_PITCHVAR
#define IDD_DONTCARE
#define IDD_ROMAN —=
—=

=


=
—=
= KEFWWWGN
pS
Rm
HSH
PAH
AP
MR
FR
Vin
MOND
lO=O
NHON-O
-O
AUFWN
NO
A
446 BORLAND C++ 3.0 PROGRAMMING, Second Edition

#define IDDaSWISS 152


#define IDD_MODERN {as
#define TDIDEESIGRED Pali 154
#define IDD_DECO 1.55
#define IDD_FACENAME 160

#define IDD_TM_HEIGHT 200


#define IDD_TM_ASCENT 201
#define ToD) Dae DIE oiGE INA 202
Hdefine IDD_TM_LEADINT 203
#define 1D) Das Mae EADIE Xa 204
#define IDD_TM_WIDTHAVE 205
#define IDD_TM_WIDTHMAX 206
#define IDD_TM_WEIGHT 207
#define TDD Meee LH 208
#define IDD_TM_FAMILY 209
#define IDD_TM_CHARSET 210
#define IDD_TM_OVER 211
#define IDD_TM_DIGITX 212
#define Jed Degen DeGraley, ANS
#define IDD_TF_NAME 214
ji / SSS eSseeeeeee// //
If FONTS1.DLG Hi
jf pee SeeeeSoesseei/ //

FONTS1 DIALOG DISCARDABLE LOADONCALL PURE MOVEABLE


AP UGh pwee abe
SUWLIE WS Chak || WS War Sanya WiSmeBO)RIDIE Ra |e Obarlie
BEGIN
GOINGERIO) AN Solaoamee 2 OP-mmeee UsTeleO)N eae
WiSenG Hil Diee iW SaenVieleSeii8 (ee Or, Bp 22-5 Zon We
GOIN RIO) a O)Misael ede eee 0 iO) Nae
WiSme CHS Die ie W See Seis) [ee Ox9iE 7565) 29>. 26pele
COMWROL 8 perawtkie? Waa, PUT TON"
WSeeCH TED WiSmeVilcS
ehB SE WS_GROUP Ox9L,
VAtp Wika Behe WZ
GOIN TRO} aa EcEe cen 4a] ere UsTel
O)Niue
WS CHILD: | WS) VISIBUEN| Oxo mie cee7s Ss et
CONTRO Ea NV aiparabi
ext 142y eB Use ONE
WS CHILD |) WS VISIBUES[POx9Us 127, Sores ome te
CONTRO eo D esfal Uta 5)Op mens Uae OINiuaee
WiSae GH Das |W Sm VeleSe See WS_GROUP Oxo atom io 42, 12
COMUROL VDREREY AS, MSU IOI
WiSee G Hits Dies |e WiSeeV ele tS ae Oxo ove lene ae see
CONTROL] SProot!) 432708 CU ONw.
WiSanG HE Dias |S WiSme VETS ThBIE Ox9l tio lS Ope 4e rm te
CONTERIO Sea O Malian ieee Una O)Niue
WS_CHILD | WS_VISIBLE | WS_GROUP | Ox9L, 74, 70, S97 2
CONTROL" Seript= 154 -"- BU inONG
WSe CHE Des WSm
CL SH BSE Oxo 1135.50 7an> Ome
Chapter 20: Graphic Typefaces and Styles 447

CONTROL “SWasise. 1oe,) BUTTON’:


WOCSCHT
ED. WT WSOVISIBLE Si Ox9USi 74,1305. 393242
CONTROL "Decorative s 15557" BUT TLON?,
WoSCHULD: | WSOVISIBLE | 0x9 mas, SO Sosa 12
CONTROL Modern” 9153; BUTTON";
WS CHULD! WSAVISIBEE | Ox9LS976 5090) .39 4c
CONTROL “Dont tecare™ 150, “BUTTON”,
WSeCHILD | WSIVESTBLE | “Ox9EGG1185—90-0502. 42
CONTROL] “Loametnic™. 101 ."BUTTON'
WSSCHIED. | WS_VISITBLE | WS°GROUPRS| Ox9L7 02457105 4657 t2
CONTROL text’. 10058 “BUTTONS ;
WS CHILD | WSOVISTBILE 0% Olea? 6). OF a4 OR 40
CONTROLS HiamMet rac’ (1025) "BUTTON;
WSECHITD I WS VISTBCE |) Ox9LYS742920,..46, 12
COIN STAR) Welle See)ieee OTTO) Nae
WSECHILD JeWSeVISTBLEL | OX9L2 21262820, 7°402910
COINGARO a OmmeE MiGilnies hmaeenl Ol mn ewSUNalaOIN ieee
WSECHILED, je WS=2ViIStBLE |2 0x98 745030, 9546" 712
CONTRO) alo GaiCral anual OO;- men Unie hOIN mare
WSS CHhED ie WSeV ESTB LES | Ox9ORe ti26, s0> 40,7470
GCOMNPROL “hit EBhgiivani® IeA-~ VBWarroOnY -
WSECHILD Ja WS VISIBLE | OxX9LS ©765°407. 4665042
COMYUPROL @aKeS4 24s, MSpace.
WSecCHiL Dee WSs VISTBLEM |. 0x20p0 126, 1400832, 09
CONROE enig nitaus ieee SilANiliGuer,
WS CHILD | WS_VISIBLE | WS_GROUP, 5, 58, 30, 8
SON EOE AO SIs oh = Meharry A
WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP,
SD Oy OU a ne
CONTRO Ee ww. OCR = ero TAD Ces
WS_CHILD | WS_VISIBLE | WS_GROUP, 5, 71, 30, 8
COMROL YY Wala), Platina 4
WiSmGiHels| Dien |e WIG 10S ine |W See OLR DIE Rann EW Same PAIB Sil Oley,
SHSyrp allob ne Ope eahr
GONMTERIOISaeey Wiemtg htave —e mee S IA Nels Gna,
WSeGHIED: | WS VISIBLE | WSaGROUPR> 5S ieS2n030748
CONPROL YO We, Ebr;
WS -CHLUED | WS_VISIBEE {| WS2BORDER |TWS]TABSTOP,
eps. Gian sells” Aly
CONGPRIO) ea Mialpipminicie Mode ssnyee Until O)Niaw,
WS=]CHILDe |e WSZVISTBLES | OxX7Es "0707 ER10275856
GOIN RIO) eee Martic NeeAVSipielG:
Gamal lOs- wus BU aan OIN nay,
WSeCHITLDP LOWS. VISSE LES TMWS TABS TORS [eOx3h> 3, °947 2607 11
GOIN MARIO eeela
tral ncaa delis,meme BU Ten O)Ninue.
WSECHIED, [@WSEVISTBLER( SWS TABSTOPRS | Ox3 07
Sl OA 0.05) 0isl
GCONTROLS Under Gaines 147, 8B Ui ON 7
WS CHILD: [ WSOVISTBLE | WS TABSTOP® (7 O0x3L,
Dee Wes. 20 Opal
GO NERO eee Site ake Ol teal aleos, ee Un OlNiay,
WS. CHIED. PEWS2VISIBLE@POWS_TABSTOP [| “<Ox3L,
448 BORLAND C++ 3.0 PROGRAMMING, Second Edition

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

CONTROL Os 203,40 .STATIC,


WS_CHILD | WS_VISIBLE | WS_GROUP | Ox2L, 234, 48,
CONTROL “O" 2055) “STATIC”,
WSeCHILD! | WSeVIGIBLE |- WSUGROUP | Ox2L, 252550"
CONTRO “OS 206 STATIC,
WS_CHILD | WS_VISIBLE | WS_GROUP | Ox2L, 234, 66,
CONTROL “Of e207 STATIC’,
WS_CHILD | WS_VISIBLE | WS_GROUP | Ox2L, 234, 75,
CONTROL e208) STATIC,
WS_CHILD | WS_VISIBLE | WS_GROUP | Ox2L, 215, 84,
CONTROL 209 STATICE,
WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP | Ox
2A Sy 0 Se 340748
CONTROL 24050 "STATIC,
MSECHIEDE eWSeVTSTBEE ls WSUGROUP i|erOx2, 215.8102)
CONTROL "O" 211,. "STATIC",
WSeCHIUD: |) WSaVISIBLE | WS GROUP |. Ox2b, 9234, 138,
CONTROL 10" 212) "STATIC,
WS CHLLD |) WS VISIBLE | WS GROUP |! 0x2L, 234, 120,
CONTROL 20s. 21s STATIC.
WS ICHIED GIP WSLVISIBLE "JmWS<e GROUP. ||Ni0x2l> 254 5e29%
CONIROwe ated aaah SL AiT econ
USMCHIED | WS VISTBLE |o WS GROUP |-.Ox2L,. «2155, 111,
CONTROL "Exit" 2, "BUTTON",
WSMCH DUD WSMV Sa BCE! GWSATABST OPS) 13.6560 nerd
CONTROL "SYMBOLS" 122, "BUTTON",
WSeCHILD | Woe VISIBLE | Ox9L, 8.595 467. de
END
y LOTR. 2
wos a pag 4,
: ae 5 seals beer ’
Fer subtlabama’ a, wa Re
TSA «4° Sass fe 7 ’
‘a
atid 5 RA eve 6 enone 2 i
LL TEA Len? Same) ele y:; ny
eonyaa? de 0 MARTE ee y euee
RnR, ae ontbis "
_ i. £Ptee : 8 CPE WT oaks Or: Beg * ae :
ant Vee, > 6c) abe? om ‘were’, Ph writs >
; sa epidty A oa a ‘eit : wus, wee
@74t¥ sag / = J oy, at SPITE
Ol ptt SRE seSycy 7 SoS el: > Semis
LM Sahay Mas
f ;
Hip
SAG Pete erhe bs len ow’ rae yu e TETSCE* CFS OOS SG:
4. AY SE ae A Lf i sama, SU ij- 5 hp $4aaa ag 41
SSPE? We * 408 des / J91ATS= JP 0
Pt let Rhy AMI 9 “ea ae 7D sania om
ieRy 6o6 ree , - "AUTAcs
. ee Dts Ao, weil cyeap a=) s/h. aaie"
Ly Sear >eiyber wht “pPurTUpt:. © Dy
- pr) fi ey 4% ty Vea &iss 2S a sepals vo ay
ive e; Pe wets : Y rent; verori:
7 > ¢ Gi< ” yr). as ooo ome

+ ; » »>« = 3°
5 ry so a _
Ce - a)

1 ® : 7
i “ '
> : : - waa
7 Ke > =

f =. f
Chapter 21

The Printer Device Context

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.

Printers vs. Video


While Windows has provided a high degree of video independence, even with
device-independent printer drivers, applications using the printer must still
have some consideration for device limitations.
When dealing with screen graphics, we could count on two certainties:
First, that the video was graphics capable, and second, that the correct graph-
ics driver was installed, since, if either were not the case, Windows itself
wouldn’t execute.
With printers, however, these conditions can be hoped for but not assumed.
A printer may function quite well with Windows in text modes, but not
support graphics at all. It might have only limited graphics support or, like a
plotter, might support vectored graphics very well, but be almost useless for
both text and bitmapped images. Further, unlike video systems, printers:
=» Usea one-shot display surface which can not be rewritten or corrected,
that is, a page must be written as a single image;
are relatively slow to execute;
may run out of paper;
may be off line;
may return error reports.

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:

Initiating Printer Operations


Under Windows, printing operations involve a number of Windows modules,
including the printer device driver library (.DRV), the Windows Print Manager
(PRINTMAN.EXE), and GDI library module.
Chapter 21: The Printer Device Context 453

As a first step in printer operations, the application calls for a handle to a


printer device context using the CreateDC function. In turn, Windows loads
the printer device driver library module (if it is not presently in memory),
instructing the driver to initialize itself.
The second step in the application is to call the Escape function, handled by
the GDI module, requesting the Escape subfunction STARTDOC to initiate a
new print task. In turn, the GDI module calls the Control function in the printer
driver, telling the printer driver to execute any appropriate initialization
routines before it returns to the GDI module with an OpenJob request. And,
after this, the GDI module loads the Print Manager (spooler) program.
At this point, Windows is ready for printer operations, but remember that
the output is not being sent directly to the printer device. Instead, Windows will
create one or more temporary files to where the actual output will be held. Two
types of files are created: Metafiles with the filename format ~MFxxxx.TMP for
graphics instructions, and text files with the format ~SPLxxxx.TMP. Both types
of file are written to the subdirectory indicated by the TEMP variable (in
WIN.INI), or to the root directory of the first fixed disk if no TEMP specifica-
tion exists. When printer output is finished, these temporary files will be
erased automatically.
For text output, each page (as written by the printer) will be stored in a
separate file. For graphics metafiles, a similar format is used except that
output may be separated by bands rather than pages (see"Banding", follow-
ing). Most of this, however, is invisible to both the application and the
programmer.

Output To A Printer Device Context


Most of an application’s output to a printer device is not particularly different
from writing to the video device. The principal difference is simply the device
context handle where the operations are directed. For example, the
instructions:
fqets<eourter, sazeot ( Butter 2, Evve );
TextoutG hdcern sO, nlinetnt * nbineVert,
GERSTRO “BULLER, Strlen< Buffers): 27

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 );

with a printer device instruction as:


hdcPrn = GetPrinterDC( hwnd );

Subsequent drawing instructions require virtually no revision except for


changing hdc to hdcPrn as:
hPenw—GreatePenGseSms OllliDis es Ol yr:
SelectObject( hdcPrn, hPen );
SelectObject( hdcPrn, GetStockObject( LTGRAY_BRUSH ) );
SetMapMode( hdcPrn, MM_ISOTROPIC );
SetWindowExt ( hidickinny, ADEE - 72 74(0). O)
SetViewportExt(€ hdcPrn, cxWnd, cyWnd );
SetWindowOrg( hdcPinny sac OF, a) De
SetPolyFillMode( hdcPrn, nFillMode );
Rolsygion GanidicPin
ni =pice Olli esas)
Roilty qioniGanid.c Pipinp- am Dit lalely- mean b>

Of course, when you are finished, the device context needs to be closed, but
instead of:

EndPaint( hwnd, &ps );

the appropriate instruction is:


DeleteDC( hdcPrn );

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 Escape function allows applications to access facilities belonging to a


specific device which are not directly available through GDI functions. While
Escape is a single function, sixty-five subfunctions are defined in Windows.H,
making calling Escape rather like calling a DOS Interrupt 21h function. Only a
few of these subfunctions will be discussed here. For a complete list, see GDI
Escapes in Appendix A.
The Escape function is called with five parameters as:
Escape« hdcePrn, nEscape, nCount, lolndata;s lUpoutDatasd :

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:

Table 21-1: Spooler Error Codes


Constant Value Meaning
SP_ERROR =! general error
SP_APPABORT ao application abort
SP_USERABORT -3 job user terminated through Print Manager
SP_OUTOFDISK - -4 insufficient disk space currently available
for spooling /no more space expected to become
available
SP_OUTOFMEMORY 5 insufficient memory available for spooling

Amending The Output Example


In Chapter 17, an example was given showing how a portion of a routine could
be converted for printer output, concluding with the comment that printer-
specific instructions had been omitted. Now that you've met, however briefly,
456 BORLAND C++ 3.0 PROGRAMMING, Second Edition

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

The STARTDOC subfunction tells the printer—(or, more correctly, the


spooler)—to initiate a new document. In this case, two additional parameters
are being passed: First, the nCount parameter which specifies the size of the
szDoc array, and second, the szDoc data itself, which, for STARTDOC, is
simply a string that Print Manager will use to identify the document. Note
that this label will not be printed and is only used within Print Manager to
identify the document being processed. The value returned by the Escape
function is tested, proceeding with the drawing instructions only if the result
of the STARTDOC call is positive.
Following the drawing instructions, a second Escape function call is made,
but this time for the NEWFRAME subfunction, advancing the printer toa new
page, that is, ejecting the finished page:
1i@ Escape nclePrn, MEMRRANs, ©, NWhILA, NULL de @ 2
Escape( hdcPrn, ENDDOC, OP NIUE eae NII) =
}
DeleteDC( hdcPrn );

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.

Gaining Direct Access To The Printer


To begin, don’t!
Chapter 21: The Printer Device Context 457

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!

Banding Graphics Output


A full page of graphics on a 300 dpi laser jet may easily require a megabyte or
more of data. At the same time, many laser jet printers may have 512K of RAM
(or less). And, while using lower resolutions, dot-matrix printers which
support graphics usually lack adequate memory to hold an entire bitmapped
page. Thus, because many print devices may lack sufficient memory for full
page images, a technique called "banding" is employed, allowing a page of
graphics to be printed as a series of separately constructed rectangles which
are called bands. When printed, these bands compose a single image and do
not present any break or other differentiation between the separate bands.
They do, however, provide a means of circumventing device limitations.
Of primary importance to the programmer is understanding that an
application does not need to be responsible for banding operations. When an
output device requires banding, but the application makes no provisions for
specific banding operations, the GDI uses banding techniques transparently
by first storing the image as a metafile. It then employs banding to set clipping
regions to define the band area before replaying the metafile to the output
device driver. The application can also provide its own banding operations
using the Escape NEXTBAND subfunction.
However, if the GDI can handle this type of operation on its own, why use
explicit banding? There are two principal reasons: First, explicit banding can
increase printing speed because the application only needs to call graphics
functions affecting each specific band. This is often faster than replaying an
entire metafile for each band. Second, banding can reduce the disk space
458 BORLAND C++ 3.0 PROGRAMMING, Second Edition

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:

After calling STARTDOC, the output process begins with an Escape


NEXTBAND function call to retrieve the first rect coordinates:
hdcPrn = GetPrinterDC( hwnd );
the escape G hidicPinn ss LAR DOIG,
ST 2eOTCSHZDOEGI—, SHMOG, INWIEIL 2) ce (0) 2
{
Eisicaipe anderen Nixa BAND a Ore NUE CEPISiRD mace cstemp a:

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 );

With banding, however, the NEWFRAME subfunction should not be used,


since it will only result in a blank page being ejected after the completed page.
Instead, only the Escape ENDDOC subfunction is called as:
Escape «ana crritive ENDDOGye. O- INU Le. NULL):
be
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.

Printer Abort Procedures


While you should not tamper directly with the printer output, there are
provisions which can be made within an application to allow you some
additional control over the printing process.
The most useful example is an abort process that can interrupt data output
if or when the spooler reports an error. As you know, output is written first
to a disk file. An SP OUTOFDISK error code is the most common error to
occur, but just which error is not important.
What is important is, first, providing an exported function which Windows
can call to report an error; second, within the application, making provisions
to register the exported procedure; and third, making provisions for checking
for an error report.
The first step is creating the abort procedure, commonly called AbortProc
and defined as:
ant FAR PASCAL AbortProcG HDC hdcPrn, int nPrnvStat )
{
460 BORLAND C++ 3.0 PROGRAMMING, Second Edition

MSG msg;

while€ !bAbortPrn &&


PeekMessage( &msg, NULL, 0, O, PM_REMOVE ) )
if€ !IsDialogMessage( hAbortDlg, &msg ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
+
return( !bAbortPrn );
}
Of course, this function must also be listed in the EXPORTS section of your
.DEF file and, within the main application, you will also need two global
declarations as:
FARPROC LpAbortProc;
BOOL bAbortPrn;

The first declaration, lpAbortProc, will provide a pointer to AbortProc, while


the second, bAbortProc, provides a flag used both by AbortProc and by another
procedure to be introduced for general error conditions.
Windows includes two provisions for establishing an abort procedure. The
first is MakeProcInstance, which is called as:

LpAbortProc = MakeProcInstance( AbortProc, hInst );

The second, the Escape SETABORTPROC subfunction, registers the abort


procedure with the Print Manager:
Escape( hdcPrn, SETABORTPROC, QO,
CEP STR De Up AlbiO murinroc,mNU Els

The SETABORTPROC subfunction is called before the STARTDOC call.


Note, however, that it is not necessary to "unregister" the abort procedure
when printing is finished. This is essentially all that is necessary because after
this, it is up to the GDI to process graphics or text output instructions to build
output files, and to transfer these files to the actual device. During this process,
the GDI repeatedly calls the exported abort procedure to peek at messages
which are actually intended for the Print Manager.
The purpose of this, while difficult to explain, is to allow the Print Manager
(or the GDI if the spooler is not loaded) opportunities to transfer temporary
disk files to the output device, freeing more space for new temporary disk
Chapter 21: The Printer Device Context 461

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;

And, like AbortProc, AbortDlg is created as:

LpAbortDlg = MakeProcInstance( AbortDlg, hInst );

Figure 21-1: A User-Abort Dialog Box

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

hwnd aiUpAbomtDlq: Jeon


return NULL.
ShowWindow( hAbortDlg, SW_NORMAL );
EnableWindow( hwnd, FALSE );
return( hwndDlg );

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

(LPSTRO+ Buiter, strlenG Buffer.) OF


if( ++nLineCnt > nLinesPg )
i
nLineCnt 1;
nPrnStat Escape( hdcPrn, NEWFRAME,
OF NUE NUE Ds:
}
The only provision here that would not be found ina screen output program
is a test to reset the line number position, and to issue an Escape NEWFRAME
subfunction call when each page is filled.
The Print.C program (a complete listing appears at the end of this chapter)
is designed to read its own source file, writing 65 lines per page to the printer.
It does this very well ... with one or two exceptions:
Within Print.C, provisions are included for the program to query the printer
driver to determine the page sizes and to query the font size (of the output
device, not the screen) to adjust line spacing. But there are other provisions
which have not been made but are left as an exercise—and an object lesson—
for the reader.
First, as you should note when executing this program (at least on a laser
jet), all carriage returns in the source file are printed as characters on the
output. Second, tab characters are not expanded as spaces, but are also printed
as characters. In both cases, the result on my own laser jet for both the carriage
returns and the tabs are half-tone block characters. On a dot-matrix, of course,
the results are likely to be quite different and, as a wild guess, you might
expect to see tabs fully and correctly expanded, while the carriage returns,
depending on the printer, might result in double spacing. The best solution is
not to depend on happy circumstance, but to provide appropriate filtering. Of
course, you may also need to provide ANSI/OEM conversion, but both of
these are left for your own implementation.

Available Fonts and Device Capabilities


One problem with output to a printer or plotter device is determining avail-
able fonts. Retrieving TEXTMETRIC information—information about device
capabilities and features—has been demonstrated and used many times in this
book, beginning in Chapter 2.
While it is useful, the TEXTMETRIC information is only part of the story.
It tells us nothing about fonts which may or may not be internal to a printer
Chapter 21: The Printer Device Context 465

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:

Note that the EnumFonts function parameters include a FARPROC pointer


to another function which is known as a “callback’’ function, which will be
explained presently.
The [pFaceName parameter is a pointer toa null-terminated, ASCIIZ, charac-
ter string specifying the typeface name of the desired font(s). If lyFaceName is
NULL, EnumFonts randomly selects and enumerates one font from each avail-
able typeface. .
The /pData parameter is a pointer to application-supplied data which is
passed to the callback function along with the font information.
Last, EnumFonts returns a value which is the last value returned to EnumFonts
by the callback function. The meaning of the returned value is user-defined with
one exception: EnumFonts continues enumeration until there are no more fonts,
or until the callback function returns zero.

The Callback Function


The callback function is called by EnumFonts, but must be supplied by the
application. The /pFontFunc declaration is only a place holder, and does not
identify an actual function. Instead, the callback function defined in the
application is required to actually process the font information retrieved by
EnumFonts and passed, not to the calling application, but to the callback
function.
Three restrictions exist for callback functions: The address passed as the
IpFontFunc parameter must be created by using the MakeProcInstance function;
the function must use Pascal calling conventions and must be declared FAR;
and the function must be declared in the application’s EXPORTS list (in the
.DEF file).
The callback function is declared as:
int FAR PASCAL FontFunc(€ LPLOGFONT LpLF, LPTEXTMETRICS LpTM,
TNC Oinicalay Pier, EPSTR “GpData™)

An example of a callback function is:


466 BORLAND C++ 3.0 PROGRAMMING, Second Edition

int FAR PASCAL EnumFontTypes( LPLOGFONT lf, LPTEXTMETRIC tm,


TG MOY!pier,
ENUMDATA FAR *EnumData )
{
FONTDATA FAR *font;
if( ! GlobalReALlLloc( EnumData->hGlobal,
(DWORD) sizeof(€ FONTDATA ) *
( 1 + EnumData->nCount ),
GMEM_MOVEABLE ) )
ReveUi NG HAL SIE
font = CFONTDATA FAR *)
GlobalLock(€ EnumData->hGlobal ) + EnumData->nCount;
font->nFontType = nFontType;
TOmeEsely = tlbaps
font=>tm = *itim;
GlobalUnlock( EnumData->hGlobal );
EnumData->nCountt+t;
PEEURMG ANRUIE dF
}

In this example, the /pData parameter is declared as a FAR pointer to an


ENUMDATA structure. The actual callback function begins by dynamically
allocating memory for a copy of the data supplied by EnumFonts, then copying
the entire data structure—the tm, lf and nFontType arguments—into the mem-
ory structure. Afterwards, this data is directly available to other functions
within the application.
As an alternative, the callback function can simply create an array, either
static or dynamically allocated, of such selected data as the application may
require. How the application uses this data, and which data the application
chooses to employ, is entirely up to the application. However, to assist in
creating your own applications, a partial example of a program using two
exported callback functions, EnumFontTypes and EnumTypeFaces, appears at
the end of this chapter.
Two final comments on EnumFonts: Remember, the EnumFonts function is
also useful for retrieving Windows (GDI) font information, not just printer
font information. Also remember that the information retrieved is only what
the device driver knows about. For a laserjet, for example, EnumFonts cannot
report on fonts that are supplied by a plug-in cartridge.
Chapter 21: The Printer Device Context 467

The System Menu Entry


In most previous examples, an application menu has been used to select
operations. In this example, however, only one option is needed and, instead
of creating an application menu, the single entry required is added to the
system menu.
And this is a very simple process accomplished, first, by retrieving a handle
to the system menu and then calling AppendMenu to add a separator line and
the option label "Print File", thus:

case WM_CREATE:
hMenu = GetSystemMenu( hwnd, FALSE );
AppendMenu( hMenu, MF_SEPARATOR, O, NULL );
AppendMenu( hMenu, OQ, SC_PRINT, "&Print File" );
return(Q);

In brief, using AppendMenu is a very simple process requiring four


parameters: the menu handle, a WORD flags value specifying information
about the initial state of the menu item, a WORD item ID specifying the
command ID returned by the menu item or, if MF_POPUP is set, the handle
for a pop-up menu and, last, a LPSTR value setting the menu entry, normally
a string but can be, optionally, a bitmap or an owner-draw item.
AppendMenu can also be used with application menus but does make it more
difficult to later revise the menu (using the Resource Editors) for transport,
for example, to another language.

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

int FAR.PASCAL AbortProc( HDG: hdcPrn,. int nePrnStat )


{
MSG msg;

while€ ! bAbortPrn &&


PeekMessage( &msg, NULL, 0, O, PM_REMOVE ) )
if€ !IsDialogMessage( hAbortDlg, &msg ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
return(€ !bAbortPrn );
}
HWND CreateAbortDlg( HWND hwnd, HDiGehidickitiny,
HANDLE hinst, EPSTR hex)
Chapter 21: The Printer Device Context 469

&
HWND hwndDlg;

LpAbortDlg = MakeProcInstance( AbortDlg, hInst );


LpAbortProc = MakeProcInstance( AbortProc, hInst );
Escape hdcPrn, SETABORTPROC, O, LpAbortProc, NULL )’;
Jie Sicaipie« sndicikirnys SimiAki DOC ,1thoe exit, =O) ). < 0) )
{
Py eer ocamsitance ts LLDADOMED. gs >
BreeProc instance € UpAbortPproc )

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) -

GetTextMetrics( hdcPrn, &tm );


nLineVert tm.tmHeight + tm.tmExternalLeading;
nPageLen GetDeviceCaps( hdcPrn, VERTRES );
nLinesPg nPageLen / nLineVert - 1;
Tow COoraiLes =. fopen (a GUpszracreNname,. (ro) dt)
Pewewirm¢ IPSs 5
neinecnte =n le:
Wha let (feof GC er ess) )
470 BORLAND C++ 3.0 PROGRAMMING, Second Edition

fgetsC Butter, sizeotC Buffer X%| File),


TextOut( hdcPrn, 0, nLineCnt * nLineVert,
CEPSTRDs BUT Ler, s.trvcen.c BUt Ter.)
if( ++nLineCnt > nLinesPg )
{
nisimeGniter—— le,
nPrnStat = Escape( hdcPrn, NEWFRAME,
OF NULLS ON:
}
ete Genie hnisst:a tae<a Oe |e DIA DOln tein ni
{
Escape« hderPrn, sABORIDOC, (0) NULE A NULES
MessageBox( hwnd, "Print Aborted!",
szAppName, MB_OK | MB_ICONSTOP );
bAbortPrn = TRUE;
break;
bd
i hiGeeubAD Onc rnin
{
lat GaES Cap© Gannidici
bin nym N/EW) RIRCAIM/
Espa Oy memNU)
LeDre merNUS) Dene) ea (On
Esicalpie G@ hidic Pirin, iE NIDIDOICy Dn, NUL, MUILIL wd.
MessageBox( hwnd, "Print Completed...",
szAppName, MB_OK | MB_ICONEXCLAMATION );
}
fclose( lLpszFileName );
Pewuirin€ WRU DM;
}
BOOL SetPrnFile€ HWND hwnd, HANDLE hInst, LPSTR LpszText )
{
HDC hdc¢Pirn:

if€ 'C hdcPrn = GetPrinterDe€c hwnd » > ) returnG FALSE Ds


if€ !€ hAbortDlg = CreateAbortDlg( hwnd, hdcPrn,
nElinisite- weDISiZalke
xXacam ae) ee)

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

int PASCAL WinMain(€ HANDLE hInstance, HANDLE hPrevInstance,


[EPASauhy lLpszCmdParam, int nCmdShow )
{
HWND hwnd;
MSG msg;
WNDCLASS WC;

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

DESCRIPTION “Print Program”


EXEyiRe WINDOWS
STUB "WINSTUB.EXE"
CODE PRELOAD MOVABLE DISCARDABLE
DATA PRELOAD MOVABLE MULTIPLE
HEARS. ZE 1024
STACK SL iE 8192
EXPORTS WndProc
AbortDlg
AbortProc
{/eseesaaeoesees///
LI PRIN PODEG er,
j fSSeSeseeseses
////

PRINT_DLG DIALOG DISCARDABLE LOADONCALL PURE MOVEABLE


Ie Mb A SST CSC
STYLE WS POPUP [) WSSVISTBLE | WS CAPTION
CAPT DONS SOUutiouite. Faller Tomer ranteire,
BEGIN
CONTROL, Printing: +) Aes S Ade Gee,
WS_CHICD.-| WSLVISTBEE—| WS GROUP. | Ox2,
tet Oh 4141
CONTROL "Cancel" 2 BUTTONS 2
WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP,
(Nd fase they {hth
GON DsRiO) ee Od ED lee
WS_CHILD | WS_VISIBLE | WS_BORDER | Ox8L,
Mat Uhr et Kobedre by?
END
Chapter 21: The Printer Device Context 473

jf SSSSsSsSeeee////
(Le PRET Hsad /
fj [SSeS Sesssess ////

#define SC_PRINT 100


#define IDD_FILENAME 101
ij (SSeS S2 2S] 25 S52 3S SS SSS] SSsSaoe5eeoeess
//
ifif PARSING Hfi
// partial example showing retrieval //
if wor. printertont information. Wsings 74
L/ the Enumponts function: 2. . > % Leh
| (PSsesSsssSSsSsoSossosesesse
See sseeSeoeess /h//

typedef struct € GLOBALHANDLE hGlobal;


int nCount; +} ENUMDATA;
typedef struct f{ int nFontType;
LOGFONT Utes
TE XOMME TRICE tm; 5 FONTDATA;
#pragma argsused
int FAR PASCAL EnumTypeFaces( LPLOGFONT Lf, LPTEXTMETRIC tm,
int nFontType,
ENUMDATA FAR *EnumData )
{
LPSTR lLpFaces;
if¢€ ! GlobalReAlLloc(€ EnumData->hGlobal,
CD IWIO| RED) i ue ROAVG ENSile74 Eger
Gi EnumDa ta->n Counter,
GMEM_MOVEABLE ) )
return(€ FALSE );
lpFaces = GlobalLock(€ EnumData->hGlobal );
Ustrepy( lpFaces + EnumData->nCount * LF_FACESIZE,
lf->lfFaceName );
GlobalUnlock( EnumData->hGlobal );
EnumData->nCounttt;
me Gur nil UE mos:
}
#pragma argsused
int FAR PASCAL EnumFontTypes( LPLOGFONT lf, LPTEXTMETRIC tm,
Tce Onin y.Dier,
ENUMDATA FAR *EnumData )

FONTDATA FAR *font;

if( ! GlobalReAlLloc( EnumData->hGlobal,


(DWORD) sizeof(€ FONTDATA ) *
( 1 + EnumData->nCount ),
GMEM_MOVEABLE ) )
Mero Ulm Gu rA
LaSiEmeD se
474. BORLAND C++ 3.0 PROGRAMMING, Second Edition

font = CFONTDATA FAR *)


GlobalLock( EnumData->hGlobal ) + EnumData->nCount;
font->nFontType = nFontType;
VOUMeSeli S elbirs
font->tm = *tm;
GlobalUnlock(€ EnumData->hGlobal );
EnumData->nCount+t;
HERGni GaaleRUiEmmE-
}
HDC GetPrinterIC()
i
Chane sizZ Pehanvenr So4er-
char *szDevice, *szDriver, *szOutput;

GetProfileString( "windows", "device",


ee SrIPRIMECR A Os )-
iv, © S2beawiee = Srruek SaPrimver, “p~" Y 2 fe
GEES Z1DiealnV/
Cpe — me Satin Ole GumNIUILeller, ep aia)me Laseee
Ges ZOU tpDUt =a Sstime Ol Ge NUE epi we) awe)
return( CreateIC( szDriver, szDevice,
SZ ONU tte NIU eee) ee)
revwiriG MMII 6
}
long FAR PASCAL WndProc(€ HWND hwnd, WORD msg,
WORD wParam, LONG LParam )
{

static ENUMDATA EnumData1, EnumData2;


static FARPROC LpfnEnumTypeFaces, lLpfnEnumFontTypes;

LPSTR LpFaces;
iE Xen MEM: Relac tm;
PAINTSTRUCT pis:
int ‘le
BOOL bRetrieved;
switch( msg )
{
case WM_CREATE:

hInstance = CCLPCREATESTRUCT) LParam)->hInstance;


LpfnEnumFontTypes =
MakeProcInstance( EnumFontTypes, hInstance );
LpfnEnumTypeFaces =
MakeProcInstance( EnumTypeFaces, hInstance );

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 );
}

EndPaint( hwnd, &ps );


return(0Q);

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

one: ey-4 errr thie _


O6460611..008 (6 enete ®t iui Fears
se
Sgbartcseablasshisteset = Te resend
i) ay! 406 dda ahbbe! = beigds
ne \@'n savddas pe
= 996 Poiages peesFaraases joe? Dxybn tens
BOBBY Se 6 abn OPN 4 (a. 2 (edna desea
“3
Co Rars ( *SONr: 70 7 Smiatthiell y r
Sx
‘eo
ar "7" iaee wa’ be peteon’ Weedlvisse = cesta a
£92. oftey th, (etameunerd \UAt: bese
age aseh itil peed Zenagph aves ate g ra
@ MULEPAANLY) 400 oie
oa? abeyrtPasenuniadad: : ee
-§ setetauned: Lal’ t<ge re >. =
2, bbe the Saipy o1 sana eredese : D
Paes n¢@P Eber iatsededsd 46 eihvensAri
1s @vela idcesk Euabost | aenacdeedseans vase
PerOTAs COCO o0089 Vas! GNApGPReTRIen
: bev! Sel Rrra seay* .
1 oe a 8) é E ei
Pe hal@AgltbIszaenwany ‘ones istqua., ~— _
ne y » s ‘ ‘ : @S 7 oint

“> CVO way ead 900 Ogs


HCCI ANES IS)

eee ° :

aan ; se
®
.

EE
[@
¥ »

es
a
>

> ‘ oe ed 94%" OC@ biive 0) Sy4tenyel etae teqiis

2 nS 7 : >
75 :

ety baru opin


488 ani Te°Su a ; At. TA 3.347% nt ‘ae aaniv % a
. e ° beee CG @ ® Z7>eeoa Pre | : 7
Chapter 22

Clipboard Data Transfers

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.

The Clipboard Viewer: ClipBrd.EXE


The ClipBrd.EXE program distributed with Windows 3.0 is a clipboard viewer
that provides a means of checking (viewing) material which has been copied
to the USER clipboard facilities. As such, ClipBrd can be useful for testing your
own clipboard routines or, in other circumstances, can be used to capture the
contents of the real clipboard by saving these contents as a disk file. Remem-
ber, this is not the real clipboard. The real clipboard will be introduced in a
moment.

Using The Real Clipboard


The real clipboard is a series of features that provide a platform to transfer
material between applications. To use the clipboard, one application, the
source, must copy material to the clipboard using either one of the predefined
formats, which will be discussed presently, or using a custom format. Once

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.

How The Clipboard Functions


In brief, the Windows clipboard functions by taking control of globally
allocated memory blocks by altering memory allocation flags.
Chapter 22: Clipboard Data Transfers 479

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:

GlobalReAlloc( hMemory, NULL, GMEM_MODIFY | GMEM_DDESHARE );


At this point, the transferred memory block no longer belongs to the
original application, and can be accessed only by calling the clipboard through
the GetClipboardData function. Normally, calling GetClipboardData grants an
application temporary access to the clipboard data via a handle to the global
memory block. However, ownership of the data remains with Windows, not
with an application accessing the memory block. Therefore the clipboard data
can only be deleted by calling EmptyClipboard. There is an exception to this
which will be discussed later, but it is not recommended.

Clipboard Data Formats


Windows supports nine standard clipboard formats. These are defined in
Windows Heas. Cr oBIIMAP, CE DIB, CE DIF CERMETAFILEPICT,
CEFOENTEXT,CRAPALEIIE, CESYLK, CF TEXT and CE TIRF~ [heserare
options, not limitations, and there is nothing to prevent you from defining your
own clipboard data format(s), but for most purposes, the standard formats
should serve. (Note: Four additional formats and several special flags are defined
in Windows.H. See "Predefined Clipboard Formats" in Appendix A.)
The standard clipboard formats are defined 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

application to the clipboard, or from the clipboard to the destination applica-


tion. Once transferred, the memory block becomes the property of the
clipboard, and the application originally generating the memory block should
not attempt to access the block further. At this point, the memory block
belongs to the USER module.

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

The hMF metafile handle is a handle to the METAFILE structure introduced


in Chapter 19.
The differences between a clipboard metafile transfer and a metafile within
an application are found in the remaining three parameters which indicate the
mapping mode (mm), and the x and y extent fields (xExt and yExt) that define
the height and width of the metafile image. As with clipboard text formats,
metafile images are transferred to the clipboard as a global memory block.
Once transferred, the originating application should not attempt to use either
the global memory block or the original metafile handle.

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

block, beginning with a BITMAPINFO structure and followed by the bitmap


data. The BITMAPINFO structure is defined in Windows.H as:
struct tagBITMAPINFO
{ BITMAPINFOHEADER bmiHeader;
RGBQUAD pmicolors £11; + BITMAPINFO;

Bitmap structures were introduced in Chapter 7.


Again, once the global memory block handle has been transferred to the
clipboard, the originating application should not attempt to access either the
memory block or the bitmap handle.

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.

Special Purpose Formats


Windows 3.0 also defines three special clipboard formats, originally created
for use by specific applications. They are:

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):

BOOL TransferToClipBD€ HWND hwnd, HANDLE hMemBlock,


WORD FormatCB )
{
if€ OpenClipboard( hwnd ) )
{
EmptyClipboard();
SetClipboardData( FormatCB, hMemBlock );
CloseClipboard();
me Gin Gaal) RUIEMe > e=
}
eStC hn GamAlS|Eau p=
a

The TransferToClipBD function is used in the ClipBd.C demo program. It


begins by requesting access to (opening) the clipboard, then copies a single
memory block to the clipboard before closing the clipboard and relinquishing
Chapter 22: Clipboard Data Transfers 483

further access. TransferToClipBD is quite generic and accepts any type of


handle, but it does require the FormatCB parameter to specify the format type
used.
In other cases you may want to copy more than one memory block to the
clipboard. Note, however, that the term "memory block" does not specify a
size. The size of the block was set by the GlobalAlloc function and could contain
paragraphs of text, multiple records, or other data.
Suppose that your application needs to transfer a bitmap, a metafile and a
block of text. What then?
Actually, the process is quite simple. First, each item is copied separately
to globally allocated memory with handles to each memory block as, for
example, hBitmap, hMetafile and hText. Next, the clipboard is opened and
emptied, and each of these handles is transferred to the clipboard:
Tafa Gun ©)Plein Gilad) piblorai; GiGaahiwindi)) a)
if
EmptyClipboard();
SetClipboardData( CF_METAFILEPICT, hMetafile );
SetClipboardData( CF_BITMAP, hBitmap );
Sieht Gala Oran GlDiabtral Gas Gabemelee xls, make Xecump
CloseClipboard();
}

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.

Determining Item Availability


When using the clipboard, an important item is to be able to find out if the
clipboard contains a particular data type. Different data types require differ-
ent handling on retrieval, and applications need to know if the data type
desired is available.
One method would be to ask for data of the desired type and see if anything
was returned. This approach is neither elegant nor efficient. Instead, there are
two methods—functions—which can be used to ask about the clipboard
contents.
The first method uses the IsClipboardFormatAvailable function, which
returns a boolean result indicating whether the clipboard contains a particular
data format. [sClipboardFormatAvailable is called as:
if€ IsClipboardFormatAvailablet CF_xxtypexx ) )

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();

Retrieving Clipboard Data


Retrieving clipboard data actually consists of two different operations: First,
retrieving a handle to the clipboard data memory block, and second, doing
something with the data after retrieving the handle.
The first is quite simple. The second, which is dependent on the data type,
will be deferred for the present and illustrated later for three data types.
Retrieving a handle to a clipboard data block is quite simple, and can be
handled by a subprocedure as:
HANDLE RetrieveCB( HWND hwnd, WORD FormatCB )
{
HANDLE hCB;
if€ ! IsClipboardFormatAvailable( FormatCB ) )
return( NULL );
OpenClipboard( hwnd );
hCB = GetClipboardData( FormatCB );
CloseClipboard();
Rewiurnse Me Beye
i
In actual practice, and in the ClipBd.C demo program and the examples
derived from this demo, a slightly different format is used. Nonetheless, the
preceding offers a generic subroutine that will return an untyped handle to a
clipboard memory block. If the requested type is not available, the subprocedure
returns NULL.

The ClipBd.C Demo


The ClipBd.C program demonstrates three types of clipboard operation: text,
bitmap, and metafile. ClipBd.C uses a simple menu with two initial menu
486 BORLAND C++ 3.0 PROGRAMMING, Second Edition

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.

Text Clipboard Operations


Perhaps the simplest type of clipboard operation is text operation. The first
step is to transfer text information to the clipboard. While the text chosen is
brief but hardly original, it does serve to demonstrate the principles involved.
The mechanism of actually transferring the text to the clipboard is
accomplished by a subprocedure that is called with two parameters, a handle
to the application (hwnd) and a pointer to the text (IpTxt):

BOOL TextToClipboard(€ HWND hwnd, LPSTR LpTxt )


{

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;

The hGMem variable provides a global handle to a block of memory which


has not yet been allocated. The [pGMem variable is a long pointer which will
be used as a pointer into the memory block.
Chapter 22: Clipboard Data Transfers 487

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) >>

After copying the text information, GlobalUnlock is called to release the


memory block on hGMem, making it again moveable and relocatable.
However, if the memory block had been moved while the local text informa-
tion was being copied, the |pGMem pointer would not have remained valid.
Last, the hGMem block, the CF_TEXT flag, and the window handle are
passed to the TransferToClipBD subroutine described earlier, completing the
process of transferring a block of text to the clipboard.
Retrieving text from the clipboard is almost as simple and very similar, but
instead of a subroutine, in the ClipBd.C demo, the text retrieval operations are
included in the response to the WM_PAINT message. For retrieval, operations
begin by opening the clipboard, and continue by calling GetClipboardData to
return a handle to the memory block:
OpenClipboard( hwnd );
hTextMem = GetClipboardData( CF_TEXT );
LpText = GlobalLock( hTextMem );
488 BORLAND C++ 3.0 PROGRAMMING, Second Edition

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.

Bitmap Clipboard Transfers


For the text clipboard transfer, a subprocedure was used with the text passed
as a parameter. For the bitmap image, because screen capture is being used to
supply the bitmap image, operations begin with two point pairs, pt1 and pt2,
which describe the area to be captured.
The first step is to create a compatible device context, i.e., compatible with
the present window (screen) context:
hdcMem = CreateCompatibleDC( hdc );
hBitmap =
CreateCompatibleBitmap( hdc, abs(pt2.x),
aDISiGpiarceeny,) ae

The second step is to get a handle to a bitmap using CreateCompatibleBitmap


and the absolute value of pt2, because this point is an offset from ptl and
therefore, defines the size of the image to be captured.
Next, SelectObject is called to select the bitmap (Bitmap) into the device
context (idcMem):
if€ hBitmap )
f
SelectObject(€ hdcMem, hBitmap );

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 );

A compatible memory device context is also needed, after which the


SelectObject function selects the bitmap into the device context:
SelectObject( hdcMem, hBitmap );
SetMapMode( hdcMem, GetMapMode( 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.

Metafile Clipboard Transfers


Transferring metafiles involves a couple of complexities which simply weren’t
relevant in the discussion of text and bitmap transfers. For metafiles, not only
is the mapping mode needed under which the metafile was originally created,
but also the application replaying the metafile may need extent or size
information which is not inherent in the metafile.
With text, metric, English, and TWIPS mapping modes, mapping scale is fixed.
The MM_ISOTROPIC and MM_ANISOTROPIC mapping modes, which have
popular advantages for metafiles, also present a need for special information to
accompany the metafiles. For these reasons, the METAFILEPICT record is the
clipboard storage format, and includes a record of the mapping mode used, and
x and y extent information as well as the metafile script itself.
Before examining how this information will be used, however, transferring
a metafile to the clipboard will illustrate how the information is generated.
For convenience, a subroutine (DrawMetafile) is used in the ClipBd.C demo
program, beginning with these local variables:
{
LPNETAFILEPICT LpMFP;
GLOBALHANDLE hGMem;
HDC hdc, hdcMeta;

IpMFP provides a pointer to the METAFILEPICT structure that will be


generated, and /:GMem provides the usual global memory handle. hdcMeta, of
course, is a handle to the metafile itself.
The process of actually creating the metafile has been omitted here but
differs from previous examples only in the metafile creation declaration, as:
hdcMeta = CreateMetaFile( NULL );

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;

The allocated memory is locked, returning a pointer (IpMFP) before the


mapping mode is copied to the IpMFP->mm field.
The two extent fields are also important, and these receive the suggested
size of the metafile image in MM_HIMETRIC units:
LpMFP->xExt 200;
UpMER=>y Ext oul
it 200;
LpMFP->hMF = hMetaFile;

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
@:-

The CreateMapMode subroutine is the key to replaying the metafile and is


called with four parameters: The device context (hdc), a pointer to the
METAFILEPICT structure (IpMFP), and the client window sizes (cxWnd and
cyWnd):
BOOL CreateMapMode( HDC hdc, LPMETAFILEPICT LpMFP,
int cxWnd, int cyWnd )
{
Long LMapScale;
int nHRes, nVRes, nHSize, nVSize;

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

If the mode is neither MM_ISOTROPIC nor MM_ANISOTROPIC, then the


step above is all that is necessary, and the subroutine can return right now.
For the isotropic or anisotropic mapping modes, additional information is
needed, and is obtained by calling GetDeviceCaps for the horizontal and verti-
cal resolution, and the size of the device:
nHRes = GetDeviceCaps( hdc, HORZRES );
nVRes = GetDeviceCaps( hdc, VERTRES );
nHSize = GetDeviceCaps( hdc, HORZSIZE );
nVSize = GetDeviceCaps€ hdc, VERTSIZE );

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.) >

The calculations are relatively straightforward as shown, and once the


viewport extent is set, the application is ready to play back the metafile.
The second case is a negative value for xExt (and, presumably, for yExt),
which means that the values are a suggested aspect ratio rather than a metafile
image size:
else
1G lpr: < @ »
{
LMapScale =
Mome ©¢ qOOk 2 Clone) crxdincl <3
nSize / inHRes / —UpMEP=>xExt. ),
€ 100L * (long) cyWnd *
MHVSi2e 7 MWRAS / slijolteiPeesvisyce 2 YF
SetViewportExt( hdc,
Gini ae Ga Gloinigp a W piIMIFIP= xe xi Gee GMiaipisic.allicme>
MiReES £ MiSize 47 WOO >.
Cint) € Clong) =tpMFRP->yExt * LMapScale *
MrRas /¢ pVSizZze / WO) » dy
y

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

Other Clipboard Formats


We have covered three of the nine clipboard formats. These three are
representative of the processes involved, and using any of the remaining six
principal clipboard formats should present no special difficulty.
This does not exhaust the clipboard formats that are available. Windows
also defines private data formats as: CF_DSPTEXT, CF_DSPBITMAP, and
CF_DSPMETAFILEPICT (DSP is for "display"). These correspond in format to
the CF _ TEXT, CF BITMAP, and CF METAFILEPICT formats, but with a
difference: Applications asking for the normal formats will not access these
private formats.
The assumptions are, first, that this type of data is for exchange between
two instances of the same program, and second, that these clipboard files
contain private information as, for example, formatting and font information
used by the Windows WRITE program.
Obviously, two instances of the same application should understand their own
private formats, or two companion programs could use these private formats to
communicate. When these private formats are used, the question will arise as to
where a CF_DSPxxxx format clipboard entry originated. Did this come from
another instance of the current application, or from a companion program?
Or was it generated by an entirely different application which just happens
to be using the same format? Provisions have also been made for this
eventuality, and the originator of the clipboard contents can be obtained by
calling:
hwndCBOwner = GetClipboardOwner();

Remember, when EmptyClipboard was called preparatory to copying mater-


ial to the clipboard, this function assigned the calling application as the
clipboard owner. Regardless of who accesses the clipboard, only another
clipboard owner can copy new material into the clipboard—which means
erasing the previous material.
Of course, the hwndCBOwner parameter does not in itself tell you very
much, but it does provide a handle to find out the application’s class name,
as:
GetClassName( hwndCBOwner, &szClassName, 16 );
Chapter 22: Clipboard Data Transfers 495

With this information, szClassName can be compared to the current


application’s classname (or to a copy of a companion application’s classname)
to identify the source of the clipboard information.

Delayed Clipboard Rendering


Copying data to the clipboard frequently involves passing a copy of the data
to the clipboard while keeping the original data, and this, of course, means
expending memory on duplicate data blocks. In many circumstances this is
not particularly important, simply because the amount of memory used is
small. In the case of the ClipBd.C demo, no copy of the data is retained and
this also avoids the memory problem.
There is also a third approach which is especially appropriate when large
data blocks—bitmaps, metafiles, text or custom data—are involved: Delayed
rendering of the clipboard data.
For delayed rendering, instead of passing a global memory block handle to
the clipboard, only a format specification is passed, and the handle parameter
is passed as NULL, as:
SetClipboardData( wFormat, NULL );

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.

Owner Displayed Clipboard Data


A fourth, very private, clipboard format is also declared as:
SetClipboardData( CF_OWNERDISPLAY, NULL );
In this format, the global memory handle is passed as NULL, just as it is
with delayed rendering. But because the clipboard owner is directly responsi-
ble for the display, Windows does not send a WM_RENDERFORMAT request.
Instead, messages are sent to the clipboard owner from the clipboard viewer,
requesting the originating application to provide the actual display and,
granting the originating appication access to the destination application’s
display.
The five messages which can be sent from the destination window are:
WM_ASKCBFORMATNAME, WM_HSCROLLCLIPBOARD, WM_PAINT-
CLIPBOARD, WM_SIZECLIPBOARD, and WM_VSCROLLCLIPBOARD.
a WM_ASKCBFORMATNAME is sent by the clipboard viewer to request
a copy of the format name from the clipboard owner. Remember, the
clipboard itself contains only the CF OWNERDISPLAY format iden-
tifier. The wParam argument specifies the maximum number of bytes
to copy, and the /Param argument points to the destination buffer.
Chapter 22: Clipboard Data Transfers 497

# =WM_PAINTCLIPBOARD is sent to request repainting part or all of the


clipboard viewer’s client area. The wParam argument supplies a handle
to the destination window, while the low-order word of /Param iden-
tifies a PAINTSTRUCT data structure defining the portion of the client
area to be painted. The high-order word of /Param is not used.
To determine whether all or part of the client area requires repainting, the
clipboard owner must compare the dimensions of the drawing area specified
in the rcpaint field of the PAINTSTRUCT with the dimensions in the most
recent WM_SIZECLIPBOARD message. The PAINTSTRUCT data should be
globally locked during use and, unlocked when finished.
= WM_SIZECLIPBOARD is sent to indicate that the clipboard application
window has changed size. wParam contains a handle to the application
window, while the low-order word in [Param identifies a RECT data
structure containing the new window size. A WM_SIZECLIPBOARD
message sent with a null rectangle (0,0,0,0) as the new size indicates
that the clipboard application is about to be destroyed or minimized,
and permits the clipboard owner to free its display resources.
=» WM_HSCROLLCLIPBOARD and WM_VSCROLLCLIPBOARD
messages are sent when an event occurs in the clipboard-application’s
vertical scroll bar. wParam contains a handle to the clipboard applica-
tion window, while /Param contains the scrollbar code in the low-order
word. If the scrollbar code is SB_THUMBPOSITION, the high-order
word in [Param contains the thumb position. This is the same as
1Param’s low-order word in normal scrollbar messages. Otherwise, the
high-order word is not used.
In response to either message, the clipboard owner should use the
InvalidateRect function, or repaint as desired. Also, the scrollbar position
should be reset.

Other Uses For Private Formats


Applications can also define their own private clipboard formats, registering
a new clipboard format by calling:
wFormat = RegisterClipboardFormat( lpszFormatTitle );
498 BORLAND C++ 3.0 PROGRAMMING, Second Edition

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:

GetClipboardFormatName( wFormat, lpszBuffer, nCharCount );

The identifiers CF_PRIVATEFIRST...CF_PRIVATELAST (0x0200..0x02FF)


can also be used as a range of integer values for private format identifiers.
Note that data handles associated with formats in this range will not be freed
automatically; any data handles must be freed by the application before the
application terminates, or when a WM_DESTROYCLIPBOARD message is
received.

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.

Htif Gl pBdiesc Lal,


// C++ Windows Drawing //
// creating a metafile //

#include <windows.h>
A niGUUudes<S t diorhi>
#include <math.h>
#include <clipbd.h>
HANDLE hGInst;
HANDLE hMetaFile;
Chapter 22: Clipboard Data Transfers 499

rdeTine Pilce (F220) * 3k 14 be // radians in 360 degrees //


BOOL TransferToClipBD( HWND hwnd, HANDLE hMemBlock,
WORD FormatCB )
{
if€ OpenClipboard( hwnd ) )
{
EmptyClipboard();
SetClipboardData( FormatCB, hMemBlock );
CloseClipboard();
rPewwirmlG wikis
}
MecUirnni GaAs sicmm Es

BOOU lexthoClipboard<, HWNDe hwnd, LPSTR Txt)


“c
int tA TILams
GLOBALHANDLE hGMem;
LIP SYP LR LpGMem;
wlLen = strlenC€ Txt );
hGMem = GlobalAlloc(€ GHND, (DWORD) wLlLen + 1 );
LpGMem = GlobalLock( hGMem );
for(€ i=0; i<xwLen; i++ ) *lpGMem++ = *Txt++;
GlobalUnlock( hGMem );
return(€ TransferToClLipBD( hwnd, hGMem, CF_TEXT ) );
}
ViOmG mere alsin RierC tas WiIN| DasiniWinicl-mme OF Nils pit ll OPN rt: cme)

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//

nHRes GetDeviceCaps( hdc, HORZRES );


nVRes GetDeviceCaps( hdc, VERTRES );
nHSize GetDevicecaps< hdic,. HORZSIZE D>;
500 BORLAND C++ 3.0 PROGRAMMING, Second Edition

nVSd4zem =) GetDeviaiceCaps © hdc, VERTSIZE DF


// positive values = suggested //
ld Amage..s ize an MMgHIMERiCeuny ts 7/27
i1#( lLpMFP=>xExt > O )D
SetViewportExt( hdc,
Cint)(Clong) LpMFP->xExt * nHRes / nHSize / 100 ) 4

Cint)(Clong) LpMFP->yExt * nHRes / nHSize / 100 OE


else
// negative values = suggested //
L / ASIDE Ct hataloy nol 8S Ze //
dar@ lkyodlFPoeesessee << 1) 2
{
l\MapScale =
minG C e10 0s Clonigy sc xWinidme*
nSize /enkikese/ = — Up Mbp => x Ex t ),
CHOU CUonign = cy.Winida
MViSHIZC mee nV RVG Sme/ae DIMI RIP > yvaEexat Dime) =
SetViewportExt( hdc,
Gint) © Clong)) =—UpMER=>xExt * “UMapScale *
DIRAS / MISiwza / WOO XH,
Cint) (€ Clong) -lUpMFP=>yExt * tMapScalte *
AVUReES 7 WWStze 7 AO) » »F

// zero extent = neither size //


// nor aspect ratio suggested
else SetViewportExt( hdc, cxWnd, cyWnd );
PEwUPiMNG WWE D-
}
BOOL DrawMetafile€ HWND hwnd, int cxWnd, int cyWnd )
f
RAMI Atr alee teelcals LpMFP;
GLOBALHANDLE hGMem;
POINT aMBIE TAS
HDC hdc, hdcMeta;
HPEN hPen;
int ite: Ute
Lj acalieulartiesponint Say, /)
for ( 1=3=03) 1<07 144599 a0 j45907700 // seven points fy)
{
ele dla he Ci nito Gist sy <P mi & 1100
Pic Eaieleny, Cimt).Gvcos Cie Pili
2 ee De 1.00 Ne
Nal
}
ti se reate: ME GAG icew/a/
hdcMeta = CreateMetaFile( NULL );
SetMapMode( hdcMeta, MM_ISOTROPIC );
SetWindowExt ( hdcMeta,, LO00F. 10007 o¢-
SetViewportExt( hdcMeta, cxWnd, cyWnd );
SetWindowOrg( hd.eMetay7 500 47-500) Di
hPen = CreatePen( PSeNULLgea > Ola»)
Chapter 22: Clipboard Data Transfers 501

SelectObject(€ hdcMeta, hPen );


SelectObject( hdcMeta, GetStockObject(~LTGRAY_BRUSH ) );
EUlipseC® hdcMeta, >=1007'=1005. 1001, 1007);
SelectObject( hdcMeta, GetStockObject( DKGRAY_BRUSH ) );
SetPolyFillMode(€ hdcMeta, ALTERNATE );
PolygonG Nache ta, pt, wa 0
hMetaFile = CloseMetaFile( hdcMeta );
DeleteObject( hPen );
J/ create METAFIEERITCGh /7/
hGMem = GlobalAlloc(€ GHND, (DWORD) sizeof(METAFILEPICT) );
LpMFP = CLPMETAFILEPICT) GlobalLock( hGMem );
LpMFP->mm = MM_ISOTROPIC;
LinliiPiPae ley S P(o)o)s // suggested size in ai
LidlFPSeswiaxi S LOO) - // MM_HIMETRIC units he.
LpMFP->hMF = hMetaFile;
GlobalUnlock( hGMem );
[EGO Diy aatiOmC Uap blOraliy Cinea/,
return(€ TransferToClipBD( hwnd, hGMem,
ClrWeeP ney 2 Mw.
}
Long FAR PASCAL WndProc( HWND hwnd, WORD msg,
WORD wParam, LONG LParam )
{
static BOOL bCaptEnable, bCapturing;
Sita cece On iNil, Pitule-ee patie
Sit-alitec = nic cxWnd, cyWnd, nClipRetrieve;
char Mexcts tin woWrip
a fayie sly Me
LPM AMP Lelie ip (Croley.
LIP SSL IR LpText;
GLOBALHANDLE hGMem;
PAINTSTRUCT Diss
BITMAP bm;
HBITMAP hBitmap;
HANDLE hTextMem;
HMENU hMenu;
HDC hdc, hdcMeta, hdcMem;

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

static char szAppNameLJ] = "CLIPBD";


HWND hwnd;
MSG msg;
WNDCLASS WiGe,

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 );

NAME EL UIP Ese

DESiGRa Fal LOIN "CLIPBOARD OPERATIONS"


EM Ure WINDOWS
STUB EIEN TU} 6(EK lS
CODE PRELOAD MOVEABLE DISCARDABLE
DATA PRELOAD MOVEABLE MULTIPLE
HEAPSIZE 1024
STACKSIZE 8192
EXPORTS WndProc
506 BORLAND C++ 3.0 PROGRAMMING, Second Edition

/ fPressssceeasses////
Hi CllipBeath WH
//S2esesseseseee
//

#define IDM_PUT_BITMAP 101


#define IDM_PUT_METAFILE 102
#define IDM _PUT_TEXT 103
#define IDM _GET_BITMAP 201
#define IDM_GET_METAFILE 202
#define IDM_GET_TEXT 203

/ (/SSS2SSsesceSssesesesSeSeessea// //
Hfi/ Clap Bide RiGee mule tiallenn/a/,
}j // SSS a=aeooesoe=Seoeeee2// //

CLIPBD MENU LOADONCALL MOVEABLE PURE DISCARDABLE


BEGIN
PO RIUIP SME Draltiage Omen Gili biOlalCies
BEGIN
Menultem "Capture &Bitmap", 101
MenuItem "Write &Metafile", 102
MenuIltem "Write &Text", 103
END
0) PUP eeDrabcraee ellen Calets
pio alte Cher
BEGIN
MenuIltem "Retrieve &Bitmap", 201
Menultem "Retrieve &Metafile", 202
MenuIltem "Retrieve &Text", 203
END
END
Chapter 23

Dynamic Data Exchange (DDE)

In Chapter 22, the Clipboard was presented as a means of exchanging data


between two applications: This is only one of the data transfer facilities which
Windows 3.0 offers. Now it’s time to take a look at a second process: the
Dynamic Data Exchange process. (A third process also exists—shared mem-
ory in dynamic link libraries—but is not discussed here.)
The DDE is based on a message system. Managed by Windows, this system
permits applications to send messages either broadcast (that is, to any applica-
tion which will respond), or to post messages directly to another application.
Using global memory blocks similar to those which were introduced in Chap-
ter 22 for the clipboard, blocks of data can also be exchanged between
applications.
Unlike clipboard traffic, using Dynamic Data Exchange (DDE) methods
ensures that messages and data are passed directly between applications
without the data becoming general, and without the possibility of data becom-
ing lost if the clipboard is preempted by another application. Further, unlike
the clipboard, data traffic between two applications can be carried on without
requesting (requiring) permission from the user.

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".

# The application name is a familiar item from all the application


examples shown in this book. In DDE applications, however, the
application name will be used in a new fashion, to identify the
applications themselves within the conversation, or to identify a
specific server to which a message is addressed.
= The topic name is a new element introduced by DDE applications. All
DDE conversations involve at least one "topic", though individual
server applications may provide data for several different topics.
= The item name is an identifier within a topic, specifying a specific item
of data.

In the example programs DDE_DATA and DDE_MAIN, the DDE_DATA


is a dedicated server which supplies information on a single topic titled
“Item_Stocks". In this example program, the available data under this topic
consists of a series of item names, part numbers, and quantities, all of which
are provided as static data.
Chapter 23: Dynamic Data Exchange 509

In actual applications, of course, the types of data that might be exchanged


are not limited to text and number data but, like the clipboard, could include
bitmaps, metafiles, or custom data types. At the same time, the server could
be a database application, a spreadsheet, some other utility such as a graphics
program, or even a device-driver/data converter. Nevertheless, the static data
used here is adequate to demonstrate principles without adding unnecessary
complications.
Before we go into details about the example programs, first we will look at
the types of DDE conversations which are possible. In general, DDE con-
versations can be broken down as three types: cold link, warm link, or hot link
conversations. (Dde_Data and Dde_Main demonstrate hot link con-
versations.)

Cold Link DDE Conversations


A cold link conversation is initiated by a client application which broadcasts
a WM_DDE INITIATE message that identifies the called application and the
type of data requested. Both the application ID and topic can be specified as
NULL if any available server or subject is acceptable.
A server application supporting the requested topic sends a reply to the
client with a WM_DDE_ACK message, as shown in Figure 23-1. The
WM_DDE_ACK acknowledgment is assumed to contain a positive confirma-
tion at this time (see Table 23-1, DDE Window Messages, for details on
message structures). Otherwise the server would not reply. The response also
includes the server’s identification, which can be used for subsequent message
traffic.
After receiving the acknowledgment, the client sends a WM_DDE_REQUEST
message that includes an item identifier specifying the information desired. The
server replies witha WM_DDE_DATA message, after which the client confirms
receipt with its own WM_DDE_ACK message.At this point, the conversation is,
temporarily, complete, but the channels of communication remain open, and the
client can, as desired, send another WM_DDE_REQUEST message.
If, however, the server is out of data—that is, has no further data to
supply—the server may (as illustrated) respond with a negative
WM_DDE_ACK mes-sage. Or, if the client desires no further details, the client
can send aWM_DDE_TERMINATE message. The server will respond to this
using its own ID and repeating the same message, thus terminating the
510 BORLAND C++ 3.0 PROGRAMMING, Second Edition

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

Hot Link DDE Communications


The limitation on cold link conversations is that the server is not free to let the
client know that it (the server) has updated information available which
would be of interest to the client. The hot link conversation circumvents this
problem by permitting the server to initiate data transmissions.
As you can see in Figure 23-2, the conversation begins in the same fashion
as the cold link, that is, with a WM_DDE_INITIATE and a WM_DDE_ACK
exchange. Next, instead of a WM_DDE_ REQUEST message, the client sends
a WM_DDE_ADVISE message, again requesting information about a specific
item. Before responding with the data, the server replies with a positive
acknowledge message (WM_DDE_ACK), saying that it can supply the
requested data, or negative acknowledge message if it cannot.
The WM_DDE_ACK message is followed witha WM_DDE_DATA message
containing the actual information, and the client confirms receipt with its own
WM_DDE_ACK message. At this point, however, the server has accepted an
obligation to notify the client at any time that previously transmitted data has
changed.
Chapter 23: Dynamic Data Exchange 511

Figure 23-2: Hot Link Message Traffic

Client Server
Application Application
WM_DDE_INITIATE———> Eu
[positive] |< _wM_DDE_ACK
{positive ] <«———————_WD_DDE_ACK
{item 1 <———————WM_DDE_DATA

ss |WM_DDE_UNADY ISE————>| Litem]


_ Lpositive ] <———————_WM_DDE_ACK
___ |WM_DDE_TERMINATE———>
[conf irms] |}<———WM_DDE_ TERMINATE

Notification of a change is made by the server with another


WM_DDE_DATA message that may contain a flag requesting acknowledg-
ment, or may simply be sent without needing a receipt.
Alternatively, the client application may choose to send a
WM_DDE_UNADVISE message, cancelling any further notification of
changes. The server would confirmthis message, and, finally, the conversation
can be terminated as before.

Warm Link DDE Communications


A warm link combines features of both the cold and hot link formats and is
initiated, as always, with a WM_DDE INITIATE / WM_DDE_ACK message
pair. Figure 23-3 shows warm link message traffic.
Again, a WM_DDE_ADVISE message is sent by the client application, but
this time with a difference, because the advise message includes a deferred
update flag. This flag informs the server that the client should be notified of
changes, but defers receipt of the updated information. Thus, for a warm link,
when a change does occur, the server still sends aWM_DDE_DATA message,
but with a NULL data block. As before, the client application responds with
a WM_DDE_ACK message. Remember, however, that the changing data has
not yet been received.
512 BORLAND C++ 3.0 PROGRAMMING, Second Edition

Figure 23-3: Warm Link Message Traffic

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_ UNADY ISE——+| Liteni


[positive ] <—\ WM_DDE_ACK

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.

DDE Conversations: Atoms and Character Strings


Earlier, you were told that in order to carry on a conversation, DDE applications
require three identifiers in the form of character strings. These provide the
application name, the data topic, and the data item. However, these strings
are not passed directly between applications as a part of the message traffic.
Chapter 23: Dynamic Data Exchange 513

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;

and strings are included in the atom table as:


altem AddAtom( lLpItemStr );
aName AddAtom( lLpNameStr );

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 );

However, calling DeleteAtom does not immediately remove a string from


the atom table. Instead, calling DeleteAtom decrements the string’s reference
count by one, but the string itself is removed only when the reference count
reaches zero.
Two functions are provided to access the contents of the string table:
FindAtom and GetAtomName.
The function FindAtom searches the atom table to retrieve the ATOM
(WORD) value associated with a specific string, but does not increment the
string’s reference count. FindAtom is called as:
aIltem = FindAtom( LpItemStr );

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:

GetAtomName( altem, lLpBuff, sizeof( lpBuff ) );


514 BORLAND C++ 3.0 PROGRAMMING, Second Edition

GetAtomName retrieves a copy of the character string associated with the


altem parameter and places the string in the buffer indicated. The third
parameter specifies the maximum number of characters which can be copied,
that is, the size of the buffer. GetAtomName returns the actual number of bytes
copied to the buffer, or zero if the specified atom is not valid. Again, the
string’s reference count is not affected.

Global Access For Atom Tables


Atom tables within an application are all very well, but these are not hash
tables, and there is no guarantee that an atom code for one application’s atom
table will match another application’s atom table. Therefore, when atom codes
are to be exchanged between applications, as in DDE message traffic, a shared
atom table is needed, and for this purpose the Global...Atom functions provide
global access.
Four global atom functions are defined as GlobalAddAtom, GlobalDelete-
Atom, GlobalFindAtom and GlobalGetAtomName. Each is called in the same
fashion as their local versions, and operates in the same fashion. There is a
single exception, and the exception is the reason for using the Global...Atom.
In these cases, the atom table is not local to the application, but is stored in a
dynamic link library within Windows. It is therefore common to all Windows
applications. However, by storing string information in the global atom table,
applications can exchange application, topic, and item strings indirectly by
exchanging atom values.

Other DDE Communications Elements


All Windows DDE communications begin with one of a series of message type
identifiers which are defined in the DDE.H header file as shown in Table 23-1.
Two of these messages which have not been illustrated in preceding con-
versation are the WM_DDE_POKE message, used by a server to pass
unsolicited data to a client, and the WM_DDE_ EXECUTE message, used by a
client application to send a command string to a server application. These two,
however, are not commonly employed.
The DDE.H header file also defines the four data structures used with
various messages: DDEACK, DDEADVISE, DDEDATA, and DDEPOKE.
Chapter 23: Dynamic Data Exchange 515

Table 23-1: DDE Window Messages


Constant Definition Value
WM_DDE_FIRST 0x03E0 0x03E0
WM_DDE_INITIATE (WM_DDE_FIRST) 0x03E0
WM_DDE_TERMINATE (WM_DDE_FIRST+1) 0x03E1
WM_DDE_ADVISE (WM_DDE_FIRST+2) 0x03E2
WM_DDE_UNADVISE (WM_DDE_FIRST+3) 0x03E3
WM_DDE_ACK (WM_DDE_FIRST+4) Ox03E4
WM_DDE_DATA (WM_DDE_FIRST+5) Ox03E5
WM_DDE_REQUEST (WM_DDE_FIRST+6) 0Ox03E6
WM_DDE_POKE (WM_DDE_FIRST+7) Ox03E7
WM_DDE_EXECUTE _ (WM_DDE_FIRST+8) Ox03E8
WM_DDE_LAST (WM_DDE_FIRST+8) 0x03E8

DDEACK Message Record


The DDEACK message record is sent with WM_DDE_ACK messages in
response to WM_DDE_DATA, WM_DDE_REQUEST, WM_DDE_POKE,
WM_DDE_ADVISE, or WM_DDE_UNADVISE messages, and is contained in
the low word of /Param ( LOWORD(IParam) ). All data fields are bit fields as
shown by the DDEACK structure definition:
DDERCK. Cstruct)
{ unsigned bAppReturnCode:8,
reserved:6,
(BS eral,
fAek:13 Dy;

The DDEACK structure begins with an eight-bit (byte) bAppReturnCode,


which could be used for any privately defined message information desired
or, more commonly, passed as zero.
The fBusy and fAck fields are one-bit boolean flags. By convention fBusy, is
set to indicate that the server is otherwise occupied, while the fAck field is set
at one for a positive acknowledgment or clear (0) for a negative acknowledgment.
The six-bit reserved field is undefined, but could be used to pass custom flag
values if necessary.
516 BORLAND C++ 3.0 PROGRAMMING, Second Edition

DDEADVISE Message Record


The DDEADVISE message record is contained in the low word of the [Param
parameter ( LOWORD(lParam) ). Except for the cfFormat field, which is an
integer value, all fields are bit values as defined by the DDEADVISE structure:

DDEADVISE (struct)
{ unsigned reserved:14,
fDeferUpd:1,
tAhCKRe
Gs I;
int cfFormat; }

The DDEADVISE structure consists of three fields, beginning with the


one-bit boolean fDeferUpd (deferred update) specification. This is used in the
warm link protocol to request only notification of change. The fAckReq
(acknowledgment required) field indicates whether the sender expects a
WM_DDE_ACK response. The cfFormat field is an integer value that follows
the same conventions as the format specifications used with the clipboard.

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]; }

An old version of essentially the same structure was defined in Windows


2.0, and for compatibility it is still valid. However, it is not suggested for use
other than for support of 2.0 applications:
DDEUP = Gstmticit» <old version>
{unsigned unusedzt2-,
tpAVG Keele
fRelease:1,
fReserved:1,
fAckReq:1;
aeG cfFormat;
Bivaine Eg bilayer }
Chapter 23: Dynamic Data Exchange 517

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]; }

The DDEPOKE structure is used with the WM_DDE_POKE message only.


A Windows 2.0 version, DDELN, continues to be valid, but is supplied only
for backwards compatibility:
DIDEISNe Cs tamuict <old version>
{ unsigned unused:13,
fRelease:1,
fDeferUpd:1,
fAckReag:1;
int Cit, Fiompmianty }

Note that the DDEPOKE structure typedef’ed in earlier versions of DDE.H


did not correctly define the bit positions.

The Dde_Data and Dde_Main Programs


Theory is well and good, but a practical example of DDE communications is
always better. The Dde_Data.C and Dde_Main.C programs demonstrate hot
link DDE communications, with Dde_Data acting as a dedicated server, and
Dde_Main as the client application displaying data provided by the server.
For demonstration purposes, a brief data set has been defined in both
examples as a series of item names and part numbers but, where Dde_Data
has initial quantities defined for each item, the corresponding data in
Dde_Main has only zero values. To demonstrate hot link communications, the
server application will, at five second intervals, choose one or more data items,
randomly changing the quantity for each, and reporting, via hot link, the
changed values to the client. Each application is compiled separately, and
produces separate .EXE files, even though both use a single resource file
(DDEDEMO.RES) that provides two icons.
518 BORLAND C++ 3.0 PROGRAMMING, Second Edition

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.

Figure 23-4: DDE_MAIN and Server

Box Link
Chain Stock
Clipboards
Colors
Devices
Diamond Link
Font Stock
Pen Grids
Pie YYidgits
Printer Jaks
Round Stock
Shim Stock Pee

DDE Server Dero

The Dde_Data Server


Aside from using DDE message handling, the Dde_Data server program
differs from previous examples in several fashions, beginning in the WinMain
procedure.
Chapter 23: Dynamic Data Exchange 519

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 )
{

if@ hPrevinst ) return FALSE )->

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

wc. cbhGUSEx tira 0;


we.cbWndExtra 2 * sizeof( WORD );
we.lpszClassName szServerClass;
we.lpszMenuName NULL;
we.hIcon = NULL;
WIC CUISiOln: = NULL;
we.hbrBackground = NULL;
we.style = 0;
RegisterClass( &we );

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 );

Within the exported WndProc function, a second provision is in the form:


case WM_QUERYOPEN: return(Q);

and insures that Dde_Data remains displayed as an icon. Remember, just


because the window remains minimized and cannot be activated does not
mean that the application is not active!
The remainder of WinMain follows the same format as previous examples
with a single additional provision: Before the application exits, kill the timer
registered with Windows.
Now, if only one instance of Dde_Data can be active at one time, the same
is not true of the Dde_Main application. Multiple instances of Dde_ Main can
be served by a single instance of Dde_Data, though Dde_Data does create a
separate ServerProc for each dialog.
Chapter 23: Dynamic Data Exchange 521

DDE Message Traffic


In the Dde_Data application, most of the DDE message traffic is handled by
the exported ServerProc, while in the Dde_Main application, the WndProc
procedure handles both conventional and DDE message traffic. Where the
DDE messages are handled, however, is less important than the information
accompanying each message. The first message sent, in any dialog, is a
WM_DDE INITIATE message.

Tracking DDE Message Traffic


AWM_DDE_INITIATE message is always issued by the client window. Before
the initiate message is sent, the server application (szServer) and topic (szTopic)
are added to the global atom table, returning aApp and aTopic as the atom
identifiers. With this preparation completed, the message is sent as:
SendMessage( OxFFFF, WM_DDE_INITIATE, hwnd,
MAKELONG(C aApp, aTopic ) );

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

i#( € !LOWORD(LParam) || LOWORD(LParam) == aApp ) &&


( 'HIWORD(LParam) || HIWORD(LParam) == a

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 ) );
}

Last, a WM_DDE_ACK message is returned, addressed explicitly to the


caller using the calling processes’ handle in wParam, and now including the
server application’s window handle (hServer) with the original aApp and
aTopic atoms in !Param.
If this is not a match, that is, if this server cannot respond appropriately,
then Dde_Data calls GlobalDeleteAtom to remove the aApp and aTopic atoms:
else
Ht
GlobalDeleteAtom( aApp );
GlobalDeleteAtom( aTopic );
}
RecunnGuys-

This is how a conversation is initiated, but at this point, the conversation


is in the client’s court. The server will not act further until it receives a
WM_DDE_REQUEST or WM_DDE_ADVISE message.
Next, since the Dde_Main application is using hot link protocols, the client
responds with a series of WM_DDE_ADVISE messages specifying all of the
items on the stocklist for which information is requested:
for 1=0;791<ST.OCK2LT-EMS viit+
{
hDdeAdvise = GlobalAlloc(€ GHND | GMEM_DDESHARE,
sizeof(€ DDEADVISE ) );
LpDdeAdvise = ( DDEADVISE FAR * )
GlobalLock(€ hDdeAdvise );
LpDdeAdvise->fAckReq = TRUE;
Chapter 23: Dynamic Data Exchange 523

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 ) ) )

A PostMessage call is made to send a WM_DDE_ADVISE message for each


item with the memory block handle (hDdeAdvise) and stock number atom
(altem) in lParam. Because PostMessage is used instead of SendMessage,, an
immediate response is returned reporting the receipt of each message.
Note that this response only means that the server process has received the
message. It is not the same as the requested WM_DDE_ACK message. If the
message is not received, then the hDdeAdvise memory block is freed and the
altem atom deleted, because communications have broken down, that is, the
server is suddenly unavailable for further services.
{
GlobalFree( hDdeAdvise );
GlobalDeleteAtom( altem );
break;
}

As long as communications continue, the client process will wait up to three


seconds for a confirmation response from the server:
DdeAck.fAck = FALSE;
dwTime = GetCurrentTime();
while( GetCurrentTime() - dwTime < 3000 )
if( PeekMessage( &msg, hwnd, WM_DDE_ACK,
WM_DDE_ACK, PM_REMOVE ) )
=
GlobalDeleteAtom( HIWORD( msg.lParam ) );
DdeAck = *(€ DDEACK *) & LOWORD(msg.lParam);
if( ! DdeAck.fAck ) GlobalFree(€ hDdeAdvise );
break;
}
524 BORLAND C++ 3.0 PROGRAMMING, Second Edition

If positive acknowledgment is not received (! DdeAck.fAck), again the global


memory block containing the request is freed. For a computer, even a slow 12
MHz model, three seconds is a long time. If a response is not received within
this time, the only reasonable assumption is that something is wrong and it’s
time to quit waiting.
Normally, of course, the response will be received within a relatively short
time (a few CPU cycles, not fractions of a second), and the total time, even for
a long list of WM_DDE_ADVISE messages, should not be particularly notice-
able. Still, requests should be made only for data which is needed at the
present time, and not made willy-nilly for all possible data regardless of need.
When the server application receives each WM_DDE_ADVISE message, the
client ID is contained in wParam, with the handle for the memory block
(hDdeAdvise) in the low-order word of /Param, and the stock number atom
(altem) in the high-order word of !Param. The server begins by identifying the
item request, making sure that it does have data available and appropriate to
the request, and reporting accordingly via a WM_DDE_ACK message.
Again, since PostMessage rather than SendMessage is used, acknowledging
the receipt of the message is immediate. The server application continues by
calling its own PostDataMsg subprocedure to send the requested data via a
WM_DDE_DATA message.
The actual data transmission is handled via a subprocedure, because this
procedure will be called not only from the WM_DDE_ADVISE response, but
also in response to timer events which are used to direct Dde_Data to provide
updates of the data.
As far as the Dde_Main application is concerned, at this point the
WM_DDE_ADVISE messages have been posted, and there is nothing to do
except wait for WM_DDE_DATA messages to be returned by the server
(Dde_Data) application.
If you have been following the rather complicated maze of events which are
actually taking place here, you've already realized that, while Dde_Main is
still sending WM_DDE_ADVISE messages, Dde_Data is already responding
with WM_DDE_ACK messages as well as WM_DDE_DATA messages con-
taining the requested data. On receipt of the WM_DDE_ DATA messages,
Dde_Main is expected to return its own WM_DDE_ACK acknowldgements to
Dde_Data, which is still receiving WM_DDE_ADVISE messages. Complicated
isn’t it? And aren’t you glad that it’s up to the computer to keep track of it all?
Chapter 23: Dynamic Data Exchange 525

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

struct { char *szItemName;


char *szStkNum;
Long LQuantity; }
SiC OICIKSali a Came B alin merontiOlG Kenue "BS3", 3926,
Mee IT iMiK 4p "BL3", 246,
“Chain Se@ek” - eCSOM 1G,
“Ch dMlooOares” NCB pace
aACcOLO Sime "CL8", 358,
"Devices", "Dv1I", 13010,
“nabeiiie@ime! balms” "DL4", 150,
“Rome SeOCKk", "ESQ", 1283,
Pein GiralC: Sua, "PGA", (3380,
Polke me Wil iGiintS su, "Pw2", 350,
OP RSEnbeeamkalKSiey, "PJZ", 1325
SARTO UIC SG OlCl Keuuey "RS1", 3926,
“Siniin SUOCK™; SG in a (AEC OF,
#define NUM_ITEMS GEscize
orf Gsitioic ka / sizeof (¢ SstOiGikss OED )
typedef struct f unsigned int fAdvise:1;
unsigned int fDeferUpd:1;
unsigned int fAckReq:1;
unsigned int undefined:13;
long lLQuantity; } SHORales
HANDLE hGInst;
#pragma argsused
BOOL FAR PASCAL TimerProc( HWND hwnd, LONG lParam )
{
SendMessage( hwnd, WM_TIMER, Oa Olbae es
Rec Unni Guu mE =
}
#pragma argsused
BOOL FAR PASCAL CloseProc( HWND hwnd, LONG lParam )
{
SendMessage( hwnd, WM_CLOSE, OF 0a) =
return(€ TRUE );
}
long FAR PASCAL ServerProc( HWND hwnd, WORD msg,
WORD wParam, LONG lParam )
{
ATOM altem;
char SZlecemil OF pms z StkNumC3];
Long Sizer
DDEACK DdeAck;
DDEADVISE FAR *LpAdvise;
DDEDATA FAR *lpDdeData;
DWORD dwTlime;
GLOBALHANDLE hStkReport, hDdeData, hDdeAdvise,
Chapter 23: Dynamic Data Exchange 527

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

GlobalGetAtomName( altem, szItem,


S$1ze0f Cszitem. J
for( i=O; i<NUM_ITEMS; i++ )
ifCl stromp© szitem, stockskidsszstkNum)
break;
dae a < WWM TES >
i
hStkReport = GetWindowWord( hwnd, 2 );
LpStkReport = (CSTOCKRPT FAR *)
GloballockC HWStkReport 07
LpStkReportlil.fAdvise = TRUE;
LpStkReportlil.fDeferUpd =
lLpAdvise->fDeferUpd;
LpStkReportlild.fAckReq = LpAdvise->fAckReq;
LpStkReportlil.lQuantity =
Sat O1C KS) teen Qitrainntmluc.
yas
GlobalUnlock( hDdeAdvise );
GlobalFree( hDdeAdvise );
DdeAck.bAppReturnCode
DdeAck.reserved
DdeAck.fBusy RAISES:
DdeAck.fAck ey TRUE
Ly
wStatus = *€ WORD *) & DdeAck;
if€ !' PostMessage( hClient, WM_DDE_ACK, hwnd,
MAKELONG( wStatus, altem ) ) )
GlobalDeleteAtom( altem );
else
PostDataMsg( hwnd, hClient, FALSE,
LpStkReportLild.fAckReg,
LpStkReportlild.fDeferUpd, j
Ue
GlobalUnlock( hStkReport );
return( O );
Ady 2;
GlobalUnlock(€ hDdeAdvise );
DdeAck.bAppReturnCode Ol
DdeAck.reserved ay
DdeAck.fBusy AIRS Ee
DdeAck.fAck PAIRS Es:
wStatus = *(€ WORD *) &DdeAck;
if€ ! PostMessage( hClient, WM_DDE_ACK, hwnd,
MAKELONG( wStatus, altem ) ) )
{
GlobalFree(€ hDdeAdvise );
GlobalDeleteAtom( altem );
}
return(Q);
case WM_DDE_UNADVISE:
hClient = wParam;
cfFormat LOWORD( LParam )
altem HIWORD( LParam ) Ne
Chapter 23: Dynamic Data Exchange 529

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

wStatus = *(€ WORD *) & DdeAck;


if( ! PostMessage( hClient, WM_DDE_ACK, hwnd,
MAKELONG( wStatus, aItem ) ) )
4
GlobalFree( hDdePoke );
GlobalDeleteAtom( altem );
}
me LulpmGODr-
case WM_DDE_TERMINATE:
hClient = wParam;
PostMessage( hClient, WM_DDE_TERMINATE, hwnd, OL );
DestroyWindow( hwnd );
return(0Q);
case WM_TIMER:
hClient GetWindowWord( hwnd, O );
hStkReport GetWindowWord( hwnd, 2 );
LpStkReport CSTOCKRPT FAR *)
Gilfolbia lElRoic: ke Gaahiste Relpioin tum
for( i=O; i<NUM_ITEMS; i++ )
qf Gaal pisrekinvel pionmt: Scien Ad Valls emn»
if€ lLpStkReportLlil.lQuantity !=
SEOIC KS) Mae ea Qual natant yan)
C
iif Guan O}Sats Dial trail Sq) Gumi Winiclie-mmn) GeUaihe hihtaemmnAy
lyGiem
LpStkReportlLild.fAckRegq,
LpStkReportLlild.fDeferUpd, i ) )
break;
Ups tkRie piommst lees AOlulain: tant, ya
stocksCLil.lQuantity;
i
GlobalUnlock(€ hStkReport );
return(0);
case WM_CLOSE:
hClient = GetWindowWord( hwnd, O );
PostMessage( hClient, WM_DDE_TERMINATE, hwnd, OL );
dwlime = GetCurrentTime();
while€ GetCurrentTime() - dwTime < 3000 )
if€ PeekMessage( &mMsg, hwnd, WM_DDE_TERMINATE,
WM_DDE_TERMINATE, PM_REMOVE )
break;
DestroyWindow( hwnd );
return(Q);
case WM_DESTROY:
hStkReport = GetWindowWord( hwnd, 2 );
GlobalFree(€ hStkReport );
return(Q);
}
return(€ DefWindowProc( hwnd, msg, wParam, lParam ) NE
Chapter 23: Dynamic Data Exchange 531

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;

altem = GlobalAddAtom( stocksLiState].szStkNum );


if( ! fDeferUpd )
{
WIS Pinal Mita GuaS!Z.O]Pyn amUa Uninc mESHELOIC KeS} nie Sntrchtre lien iG Ulcinitalmthyane ie
hDdeData = GlobalAlLloc(€ GHND | GMEM_DDESHARE,
sizeof€ DDEDATA ) +
SitialeiniGas 74 ODED as
LpDdeData = (DDEDATA FAR *) GlobalLock( hDdeData );
LpDdeData->fResponse = fResponse;
lLpDdeData->fRelease eRVIESs
lLpDdeData->fAckRegq fAckReqg;
LpDdeData->cfFormat = CF_TEXT;
Lstrcepy( (LPSTR) lLpDdeData->Value, szPop );
GlobalUnlock( hDdeData );
b
else hDdeData = NULL;
if€ !PostMessage( hClient, WM_DDE_DATA, hServer,
MAKELONGC hDdeData, alItem ) ) )
{
if€ hDdeData ) GlobalFree( hDdeData );
GlobalDeleteAtom( altem );
Pes Unni Gu Ales EE
}
if€ fAckReq )
{
DdeAck.fAck = FALSE;
dwTlime = GetCurrentTime();
while€ GetCurrentTime() - dwTime < 3000 )
if€ PeekMessage( &mMsg, hServer, WM_DDE_ACK,
Chapter 23: Dynamic Data Exchange 533

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

int PASCAL WinMain(€ HANDLE hInst, HANDLE hPrevInst,


ERSiiek lpszCmdParam, int nCmdShow )
t
HWND hwnd;
MSG mMsg;
WNDCLASS WC;

17 C INiPPEWVlMStt DY Peer FAISIE 2s


Gai nis:tes—a hens)te
// register window class //
we.style 0;
we.lpfnWndProc WndProc;
we.cbClsExtra J

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

- D DIES DAA DIE is


PSS SSS SSSoesSSeSa tas

NAME DDE_DATA

DESIGR EG PilelOiNn "DDE Server Demo"


EXGE auc WINDOWS
STUB EWEN Sa UIB ieee ae
CODE PRELOAD MOVEABLE DISCARDABLE
DATA PRELOAD MOVEABLE MULTIPLE
HIBAIP Shae 1024
SupPAICKS Ze 8192
EXPORTS WndProc
ServerProc
TimerProc
CloseProc
}{/SSeSssssesessseosoes/ /
iff DDE_MAIN.C ifff
// DDE Server Demo “//
/ PPeessessoesocessseess///

#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

aTopic = GlobalAddAtom( szTopic );


SendMessage( OxFFFF, WM_DDE_INITIATE, hwnd,
MAKELONG( aApp, aTopic ) );
if€( ! hServer )
{
WinExec( szServer, SW_SHOWMINNOACTIVE );
SendMessage( OxFFFF, WM_DDE_INITIATE, hwnd,
MAKELONG( aApp, aTopic ) );
}
GlobalDeleteAtom( aApp );
GlobalDeleteAtom( aTopic );
DAliniktpra tema AS Ee
due 2 INseGrwar 2
{
MessageBox( hwnd, "Server does not respond!",
szAppName, MB_OK | MB_ICONEXCLAMATION );
Re cuUnniGODE
}
for( i=O; i<STOCK_ITEMS; i++ )
{
hDdeAdvise = GlobalAlloc(€ GHND | GMEM_DDESHARE,
sizeof DDEADVISE )»)>
lLpDdeAdvise = ( DDEADVISE FAR #* )
GlobalLock(€ hDdeAdvise );
lpDdeAdvise->fAckReg = TRUE;
lLpDdeAdvise->fDeferUpd = FALSE;
LpDdeAdvise->cfFormat = CF_TEXT;
GlobalUnlock(€ hDdeAdvise );
altem = GlobalAddAtom( stocksLild.szStkNum );
if€ ! PostMessage( hServer, WM_DDE_ADVISE, hwnd,
MAKELONGC hDdeAdvise, altem ) ) )
c
GlobalFree( hDdeAdvise );
GlobalDeleteAtom( altem );
break;
}
DdeAck.fAck = FALSE;
dwlime = GetCurrentTime();
while€ GetCurrentTime() - dwTime < 3000 )
if€ PeekMessage( &msg, hwnd, WM_DDE_ACK,
WM_DDELACK, PMZREMOVE ) )
{
GlobalDeleteAtom(€ HIWORD( msg.lParam ) );
DdeAck = *€ DDEACK *) & LOWORD(msg.lParam);
if€@!DdeAck.fAck) GlobalFree( hDdeAdvise );
break;
}
1fC°)) DdeAck-fAck J break-
while€ PeekMessage( &msg, hwnd, WhEDDESELRS i,
WM_DDE_LAST, PM_REMOVE ) )
DispatchMessage( &msg );
Chapter 23: Dynamic Data Exchange 537

}
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 -

40 1 W*%e : oy Iey - Oy sia


— @ 7 a4 wena
74 ;
bert 7 [eye
~—
. a
a a
rR 4
3 ah ae

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

Index To Constant/Message/Parameter Tables

Background modes Doll Events


biCompression Field, constants 548 ExtFloodFill style flags
Bitmap header definition 548 Font families
Boolean constants 543 Font weights
Brush, logical 549 GDI Escapes
Brush styles 99 GDI Section
Button control messages 562 GDI Logical Objects
Button control styles 562 General Purpose Definitions
CBT hook codes 554 Get /SetSystemPaletteUse(),
Catch() and Throw() 545 constants for
Class styles 557 GetClassLong() and GetClass-
Clipboard Formats, predefined Sy Word() field offsets
Clipboard Metafile Pict Struct 548 GetDeviceCaps(),
Color types oY, device parameters for
CombineRgn() styles 553 GetDriveType return values
Combo Box messages 563 Rey store? codes
Combo Box notification codes 563 GetTempFileName() flag
Combo Box return values 563 GetWindow() constants
Combo Box styles 563 GetWindowLong()/GetWindow
Control Manager Structures Word() window field offsets for
and Definitions 561 Global Memory flags/macros
Conversion function numbers 560 GlobalFlags, flags returned by
Coordinate modes Doll Hatch styles
CreateDIBitmap, constants for 903 Help Engine Section
Cursor IDs, standard 560 Hook codes
Curve Capabilities. 552 Icon IDs, standard
Device Capability Masks 551 Journaling, message structure
Device Technologies 551 Line capabilities
Dialog Box command IDs 561 Listbox Return Values /Notifica-
Dialog codes 562 tion Codes/Messages
Dialog styles 562 Listbox styles
DIB color table identifiers 548 Local Memory flags
DIBs, structures for defining 548 LocalFlags, flags returned by
DrawText() format flags 552 Font, logical
Edit Control messages 561 Logical Objects, stock
Edit Control notification codes 561 Macro Definitions
Edit Control styles 561 Mapping Modes
EnumFonts masks 550 Menu Flags for
EnumObjects(), object defs 548 Add../Check../EnableMenu
Error flags 564 Item()
Escape functions 564 Menu Item Resource Format
542 BORLAND C++ 3.0 PROGRAMMING, Second Edition

Index To Constant/Message/Parameter Tables (cont.)


Message structure 555 Size Message commands 556
MessageBox() flags 559 Spooler Error Codes 547
Metafile constants 546 Static Control constants 562
Mode/ Hardware constants 545 StretchBlt() modes 546
Mouse Messages, key state masks System Menu command values 560
for 556 Text alignment options 546
OEM Resource ordinal numbers 545 Text capabilities 552
OpenFile() flags 544 Type equivalents 543
OpenFile() structure and User Button notification codes 562
ointers 544 USER Section DoS
wner Draw control Virtual Keys, standard set 553
types/actions/state flags 558 VK, from the Keyboard Driver 560
alette Entry flags 552 VK, to Applications 560
Palettes, logical 549 WEP fSystemexit flag values 545
Parity Codes 564 WH_MSGFILTER filter proc
PeekMessage() options 558 codes 554
Pel array 549 WinHelp() commands 564
Pen, logical 549 WinWhere() area codes 556
Pen styles 551 Window Manager hook codes 554
PolyFill() modes 546 Window messages 555
Polygonal capabilities Boe Window styles 557
Raster capabilities 552 Window styles, extended 557
Raster operations, binary 546 WM_MOUSEACTIVATE,
Raster eperee aD ternary 546 return codes 556
Region oe 553 WM _SHOWWINDOW message,
Resource Types, predefined 545 identifiers 553
Screen (Window) coordinates 543 WM_SYNCTASK commands 556
Scroll Bar commands 553
Scroll Bar constants 553
Scroll Bar styles 563
SetWindowPos flags 558
SetWindowsHook() codes 554
ShowWindow() commands 553
Show Window() commands, old 553

Index To Structure Definitions

BITMAP 548 MDICREATESTRUCT 564


BITMAPCOREHEADER 548 MEASUREITEMSTRUCT 558
BITMAPCOREINFO 548 MENUITEMTEMPLATE 560
BITMAPFILEHEADER 548 MENUITEMTEMPLATEHEADER 560
BITMAPINFO 548 METAFILEPICT 549
BITMAPINFOHEADER 548 METAHEADER 549
CLIENTCREATESTRUCT 564 METARECORD 549
COMPAREITEMSTRUCT 558 MSG 555
COMSTAT 564 MULTIKEYHELP 564
CREATESTRUCT 557 OFSTRUCT 564
DCB 564 PAINTSTRUCT 557
DETETEITEMSTRUCT 558 PALETUEENTRY 549
DRAWITEMSTRUCT 558 PELARRAY 549
EVENTMSG 554 POINT 543
HANDLETABLE 548 RECT 543
KANJISTRUCT 561 RGBQUAD 548
LOGBRUSH 549 RGBTRIPLE 548
LOGFONT 550 TEXTMETRIC 549
LOGPALETTE 550 WNDCLASS 554
LOGPEN 549
Appendix A: Constants/Flags/Macros/Structures 543

General Purpose Definitions


Boolean Constants

FALSE 0 TRUE 1
Type Equivalents

FAR far NEAR near


LONG long VOID void
PASCAL pascal
BOOL int BYTE unsigned char
WORD unsigned int DWORD unsigned long
COLORREF DWORD
HANDLE WORD GLOBALHANDLE HANDLE
HBITMAP HANDLE HBRUSH HANDLE
HCURSOR HANDLE HDC HANDLE
HFONT HANDLE HICON HANDLE
HMENU HANDLE HPALETTE HANDLE
HPEN HANDLE HRGN HANDLE
HSTR HANDLE HWND HANDLE
LOCALHANDLE HANDLE
BEB yane Cpointer) Bayelsa AR LPDWORD Cpointer) DWORD
FAR
LPHANDLE Cpointer) HANDLE FAR LPINT Cpointer) int PAR
LPLONG Cpointer) long FAR EPSTER Cpointer) char FAR
LPVOID Cpointer) void FAR LPWORD Cpointer) WORD FAR
NPSTR Cpointer) char NEAR RB Nelee Cpointer) BYTE
NEAR
PDWORD Cpointer) DWORD NEAR PHANDLE (pointer) HANDLE
PINT Cpointer) int NEAR PLONG (pointer) long
NEAR
PSTR Cpointer) char NEAR PWORD Cpointer) WORD
NEAR
SPHANDLE Cpointer) HANDLE NEAR
FARPROC() Cpointer) CFAR PASCAL) int NEARPROC() Cpointer) CNEAR
PASCAL) int

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))

Screen (Window) Coordinates

RECT (€ struct tagRect )


{ iiae - Ueapies Tice tiOlDy, Time fede Wai Joferrs wey }
PRECT (pointer) RiesCan NPRECT Cpointer) RECT NEAR
EPRIECT Cpointer) RECT FAR

POINT (€ struct tagPOINT )


{ int Kos int vie ;
PPOINT (pointer) POINT NPPOINT (Cpointer) POINT NEAR
LPPOINT (pointer) POINT FAR
544 BORLAND C++ 3.0 PROGRAMMING, Second Edition

OpenFile() Structure and Pointers


OFSTRUCT ( struct tagOFSTRUCT )
(on Bail Eau
Diyatce
Sie Bia) E@eetakstexce CDiiSike,
WORD nErrCode; BYTE reservedl[4];
BYTE szPathNamel[128]; }
POFSTRUCT (pointer) OFSTRUCT NPOFSTRUCT (pointer) OFSTRUCT
NEAR
LPOFSTRUCT Cpointer) OFSTRUCT FAR

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

Global Memory Flags /Macros

GMEM_FIXED 0x0000 GMEM_DISCARDABLE 0x0100


GMEM MOVEABLE 0x0002 GMEM_NOT_BANKED 0x1000
GMEM_NOCOMPACT 0x0010 GMEM_ SHARE 0x2000
GMEM_NODISCARD 0x0020 GMEM_DDESHARE 0x2000
GMEM_ZEROINIT 0x0040 GMEM_ NOTIFY 0x4000
GMEM_MODIFY 0x0080
GMEM_ LOWER GMEM_ NOT _BANKED
GHND ( GMEM_ MOVEABLE | GMEM_ZEROINIT )
GPTR ( GMEM_FIXED | GMEM_ZEROINIT )
GlobalDiscard(¢h) GlobalReAlLloc(Ch, OL, GMEM_MOVEABLE)

Flags Returned By GlobalFlags (in addition to GAEM_DISCARDABLE)


GMEM_DISCARDED 0x4000 GMEM_ LOCKCOUNT Ox00FF
LockData(dummy ) LockSegment (OxFFFF)
UnlockData(Cdummy ) UnlockSegment (OxFFFF)

Local Memory Flags

LMEM_FIXED 0x0000 LMEM_ NODISCARD 0x0020


LMEM_ MOVEABLE 0x0002 LMEM_ZEROINIT 0x0040
LMEM_NOCOMPACT 0x0010 LMEM_ MODIFY 0x0080
LMEM_ DISCARDABLE 0x0F00
LHND == ( LMEM_MOVEABLE | LMEM_ZEROINIT )
LPTR ==S ( LMEM_FIXED | LMEM_ZEROINIT )
NONZEROLHND == ( LMEM_MOVEABLE )
NONZEROLPTR ==> ( LMEM_FIXED )
LNOTIFY_OUTOFMEM 0 LNOTIFY_DISCARD 2.
LNOTIFY MOVE 1
LocalDiscard(Ch) LocalReAlloc(h, 0, LMEM_MOVEABLE)
Appendix A: Constants/Flags/Macros/Structures 545

Flags Returned By LocalFlags


LMEM_DISCARDABLE 0x0F00 LMEM_ DISCARDED 0x4000
LMEM_ LOCKCOUNT Ox00FF
UnlockResource(h) GlobalUnlockCh)
MAKEINTRESOURCE(Ci) CLPSTR) CCDWORD) (CCWORD) Ci)))

Predefined Resource Types


RT_CURSOR MAKEINTRESOURCE(1) RT_STRING MAKEINTRESOURCE(C6)
RT_BITMAP MAKEINTRESOURCE(C2) RT_FONTDIR MAKEINTRESOURCE(7)
RT_ICON MAKEINTRESOURCE(C3) RT_FONT MAKEINTRESOURCE(C8)
RT_MENU MAKEINTRESOURCE(C4) RT_ACCELERATOR MAKEINTRESOURCE(9)
RT_DIALOG MAKEINTRESOURCE(C5) RT_RCDATA MAKEINTRESOURCE(10)
ATOM WORD
MAKEINTATOM(C7) CLPSTR)CCDWORD)
CCWORD) (i)))

Catch() and Throw()

int CATCHBUFL9]; int FAR *LPCATCHBUE ;

Mode /Hardware Constants

WF_PMODE 0x0001 WF_WIN386 0x0020


WF_CPU286 0x0002 WF_CPU086 0x0040
WEF_CPU386 0x0004 WEF_CPU186 0x0080
WF_CPU486 0x0008 WF_LARGEFRAME 0x0100
WF_STANDARD 0x0010 WF_SMALLFRAME 0x0200
WF_WIN286 0x0010 WE_80x87 0x0400
WF_ENHANCED 0x0020
WEP fSystemExit Flag Values
WEP _SYSTEM_EXIT 1 WEP_FREE DLL 0

OEM Resource Ordinal Numbers

OBM_ CLOSE 32754 OBM_CHECK 32760


OBM UPARROW 32753 OBM_CHECKBOXES 32759
OBM DNARROW 32752 OBM_BTNCORNERS 32758
OBM RGARROW 32751 OBM_ OLD REDUCE 32757
OBM_LFARROW 32750 OBM_OLD_ZOOM 32756
OBM_ REDUCE 32749 OBM_ OLD RESTORE 32755
OBM_ZOOM 32748 OCR NORMAL 32512
OBM_RESTORE 32747 OCR_IBEAM 32513
OBM_ REDUCED 32746 OCR_WAIT 32514
OBM_ZOOMD 32745 OCR_CROSS 32515
OBM_ RESTORED 32744 OCR_UP 32516
OBM UPARROWD 32743 OCR_SIZE 32640
OBM_ DNARROWD 32742 OCR_ICON 32641
OBM_RGARROWD 32741 OCR_SIZENWSE 32642
OBM LFARROWD 32740 OCR SIZENESW 32643
OBM MNARROW 32739 OCR SIZEWE 32644
OBM_ COMBO 32738 OCR_SIZENS 32645
OBM_ OLD CLOSE 32767 OCR_SIZEALL 32646
OBM_SIZE — 32766 OCR_ICOCUR 32647
OBM OLD UPARROW 32765 OIC SAMPLE 32512
OBM OLD DNARROW 32764 OIC HAND 32513
OBM OLD RGARROW 32763 OIC_QUES 32514
OBM OLD_LFARROW 32762 OIC BANG 32515
OBM_BTSIZE 32761 ~ OICINOTE 32516
546 BORLAND C++ 3.0 PROGRAMMING, Second Edition

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

Ternary Raster Operations

@ Note: values are DWORD (32-bit) hex.


SRGEORY 00CC 0020 MERGEPAINT OOBB 0226
SRCPAINT OOEE 0086 PATCOPY OOFO 0021
SRCAND 0088 00C6 PATPAINT OOFB 0A09
SRCIN VERT 0066 0046 PATINVERT 005A 0049
SRCERASE 0044 0328 DSTINVERT 0055 0009
NOTSRCCOPY 0033 0008 BLACKNESS 0000 0042
NOTSRCERASE 0011 00A6 WHITENESS OOFF 0062
MERGECOPY 00C0 00CA

StretchBIt() Modes
BLACKONWHITE il WHITEONBLACK 2 COLORONCOLOR 3
PolyFill() Modes
ALTERNATE 1 WINDING 2

Text Alignment Options


TA_NOUPDATECP 0 TA _UPDATECP 1
TA_LEFT 0 TA_RIGHT 2 TA_CENTER 6
TA_TOP 0 TA_BOTTOM 8 TA_BASELINE 24
ETO_GRAYED 1 ETO_OPAQUE 2 ETO_CLIPPED 4
ASPECT FILTERING 0x0001

Metafile Constants

a Note: all MetaFile constant names begin as META_xxxxxxxxxx


ANIMATEPALETTE 0x0436 POLYLINE 0x0325
ARC 0x0817 POLYPOLYGON 0x0538
BITBLT 0x0922 REALIZEPALETTE 0x0035
CHORD 0x0830 RECTANGLE 0x041B
CREATEBITMAP Ox06FE RESIZEPALETTE 0x0139
CREATEBITMAPINDIRECT 0x02FD RESTOREDC 0x0127
CREATEBRUSH Ox00F8 ROUNDRECT 0x061C
CREATEBRUSHINDIRECT 0x02FC SAVEDC 0x001E
CREATEFONTINDIRECT 0x02FB SCALEVIEWPORTEXT 0x0412
CREATEPALETTE 0x00F7 SCALEWINDOWEXT 0x0400
CREATEPATTERNBRUSH 0x01F9 SELECTCLIPREGION 0x012C
CREATEPENINDIRECT Ox02FA SELECTOBJECT 0x012D
CREATEREGION Ox06FF SELECTPALETTE 0x0234
DELETEOBJECT 0x01FO SETBKCOLOR 0x0201
DIBBITBLT 0x0940 SETBKMODE 0x0102
DIBCREATEPATTERNBRUSH 0x0142 SETDIBTODEV 0x0D33
Appendix A: Constants/Flags/Macros/Structures 547

Metafile Constants (cont.)


DIBSTRETCHBLT 0x0B41 SETMAPMODE 0x0103
DRAWTEXT 0x062F SETMAPPERFLAGS 0x0231
ELLIPSE 0x0418 SETPALENTRIES 0x0037
ESCAPE 0x0626 SETPIXEL 0x041F
EXCLUDECLIPRECT 0x0415 SETPOLYFILLMODE 0x0106
EXTTEXTOUT 0x0A32 SETRELABS 0x0105
FILLREGION 0x0228 SETROP2 0x0104
FLOODFILL 0x0419 SETSTRETCHBLTMODE 0x0107
FRAMEREGION 0x0429 SETTEXTALIGN 0x012E
INTERSECTCLIPRECT 0x0416 SETTEXTCHAREXTRA 0x0108
INVERTREGION 0Ox012A SETTEXTCOLOR 0x0209
LINETO 0x0213 SETTEXTJUSTIFICATION 0x020A
MOVETO 0x0214 SETVIEWPORTEXT 0x020E
OFFSETCLIPRGN 0x0220 SETVIEWPORTORG 0x020D
OFFSETVIEWPORTORG 0x0211 SETWINDOWEXT 0x020C
OFFSETWINDOWORG 0x020F SETWINDOWORG 0x020B
PAINTREGION 0x012B STRETCHBLT 0x0B23
PAN BEE 0x061D STRETCHDIB 0x0F43
PIE 0x081A TEXTOUT 0x0521
POLYGON 0x0324
GDI Escapes
NEWFRAME 1 SETDIBSCALING Sy
ABORTDOC 2 EPSPRINTING 33
NEXTBAND 3} ENUMPAPERMETRICS 34
SETCOLORTABLE 4 GETSETPAPERMETRICS 35
GETCOLORTABLE 3 POSTSCRIPT DATA 37
FLUSHOUTPUT 6 POSTSCRIPT_IGNORE 38
DRAFTMODE a GETEXTENDEDTEXTMETRICS 256
QUERYESCSUPPORT 8 GETEXTENTTABLE 257
SETABORTPROC 9 GETPAIRKERNTABLE 258
STARTDOC 10 GETTRACKKERNTABLE 259
ENDDOC 11 EXTTEXTOUT Sle
GETPHYSPAGESIZE 12 ENABLERELATIVEWIDTHS 768
GETPRINTINGOFFSET 13 ENABLEPAIRKERNING 769
GETSCALINGFACTOR 14 SETKERNTRACK 770
MFCOMMENT 15 SETALLJUSTVALUES 771
GETPENWIDTH 16 SETCHARSET 772
SETCOPYCOUNT 17 STRETCHBLT 2048
SELECTPAPERSOURCE 18 BEGIN PATH 4096
DEVICEDATA 19 GHLERIOR ATE 4097
PASSTHROUGH 19 END_PATH 4098
GETTECHNOLOGY 20 EXT DEVICE_CAPS 4099
SETENDCAP 21 RESTORE _CTM 4100
SETLINEJOIN 22 SAVE _CTM 4101
SETMITERLIMIT 25 SET_ARC_DIRECTION 4102
BANDINFO 24 SET_BACKGROUND_COLOR — 4103
DRAWPATTERNRECT 25) SET_POLY_MODE 4104
GETVECTORPENSIZE 26 SET_SCREEN_ ANGLE 4105
GETVECTORBRUSHSIZE 27 SET_SPREAD 4106
ENABLEDUPLEX 28 TRANSFORM _CTM 4107
GETSETPAPERBINS 29 SET_CLIP_BOX 4108
GETSETPRINTORIENT 30 SET_BOUNDS 4109
ENUMPAPERBINS 31 SET_MIRROR_MODE 4110
Spooler Error Codes
SP_ NOTREPORTED 0x4000 PR_JOBSTATUS 0x0000
SP_ERROR SP_OUTOFDISK
SP_APPABORT SP_OUTOFMEMORY
SP_USERABORT
548 BORLAND C++ 3.0 PROGRAMMING, Second Edition

Object Definitions for EnumObjects()


OBJ_PEN 1 OBJ_BRUSH 2
Bitmap Header Definition
BITMAP (€ struct tagBITMAP ) . ;
a iniG bmType; int bmWidth; int bmHeight,
int bmWidthBytes; BYTE bmPlanes; BYTE bmBitsPixel;
LPSTR bmBits; } ;
PBITMAP (pointer) BITMAP; NPBITMAP (pointer) BITMAP NEAR
LPBITMAP Cpointer) BITMAP FAR

RGBTRIPLE ( struct tagRGBTRIPLE )


(i Baie aig tis Wules BYTE rgbtGreen; BYTE) nabtRed, }
RGBQUAD (¢ struct tagRGBQUAD )
{ BYTE rgbBlue; BYTE srgbGreen; BYTE rgbRed; BYTE rgbReserved;
}
Structures for Defining DIBs
BITMAPCOREHEADER ( struct tagBITMAPCOREHEADER )
{ DWORD De Saizey // used to get to color table
WORD bcWidth; WORD bcHeight;
WORD bcPlanes; WORD bcBitCount; }
PBITMAPCOREHEADER Cpointer) BITMAPCOREHEADER
LPBITMAPCOREHEADER (pointer) BITMAPCOREHEADER FAR

BITMAPINFOHEADER (¢ struct tagBITMAPINFOHEADER )


{ DWORD biSize; DWORD biWidth; DWORD biHeight;
WORD biPlanes; WORD biBitCount; DWORD biCompression;
DWORD biSizelImage; DWORD biXPelsPerMeter;
DWORD biYPelsPerMeter; DWORD' biClrUsed; DWORD biClrimportant;
}
LPBITMAPINFOHEADER’ (Cpointer) FAR BITMAPINFOHEADER
PBITMAPINFOHEADER Cpointer) BITMAPINFOHEADER

Constants for the biCompression Field


BI_RGB OL BI_RLE8 iG BI_RLE4 Or
BITMAPINFO (€ struct tagBITMAPINFO )
{ BITMAPINFOHEADER bmiHeader; RGBQUAD bmi
ol ons Eley ae:
LPBITMAPINFO Cpointer) BITMAPINFO FAR
PBITMAPINFO Cpointer) BITMAPINFO
BITMAPCOREINFO ( struct tagBITMAPCOREINFO )
{ BITMAPCOREHEADER bmciHeader; RGBRRE PLES bimicn Color sted)
LPBITMAPCOREINFO Cpointer) BITMAPCOREINFO FAR
PBITMAPCOREINFO Cpointer) BITMAPCOREINFO
BITMAPFILEHEADER ( struct tagBITMAPFILEHEADER )
{ WORD bfType; DWORD bfSize;
WORD bfReservedi; WORD bfReserved2; DWORD bfOffBits; }
LPBITMAPFILEHEADER (pointer) BITMAPFILEHEADER FAR
PBITMAPFILEHEADER Cpointer) BITMAPFILEHEADER
MAKEPROUNT
GUD) 5 Ct GOPOUNT SEART * )ieiGl)
Dp
Clipboard Metafile Picture Structure
HANDLETABLE (¢ struct tagHANDLETABLE )
{ HANDLE objectHandleLli1]; hy
PHANDLETABLE (pointer) HANDLETABLE
Appendix A: Constants/Flags/Macros/Structures 549

Clipboard Metafile Picture Structure (cont.)


LPHANDLETABLE Cpointer) HANDLETABLE FAR
METARECORD (€ struct tagMETARECORD )
{ DWORD' rdSize; WORD rdFunction; WORD rdParm[1J]; }
PMETARECORD Cpointer) METARECORD
LPMETARECORD Cpointer) METARECORD FAR
METARDEEPLCT © struct tagMETAFILEPICT »
{ int mm; int CE AXatee dime se scie HANDLE hMF;
LPMETAFILEPICT (pointer) METAFILEPICT
METAHEADER (€ struct tagMETAHEADER )
{ WORD mtType; WORD mtHeaderSize; WORD mtVersion;
DWORD mtSize; WORD mtNoObjects; DWORD mtMaxRecord;
WORD mtNoParameters; }
ne
: XMM EMT ReliCame oneSitimul|Crtum talgiii
Exel MiEvtiRe
: lCrm)
int tmHeight; : Hine tmAscent; int tmDescent;
int tmInternalLeading; int tmExternalLeading;
int tmAveCharWidth; int tmMaxCharWidth;
int tmWeight; BiyaleE@etimpltral liics- BYTE tmUnderLlined;
Byaeeetiiic ner UiCiKOUiG.: Eins wHrirsweniawry
BYsPeS timlas than BYTE tmDefaultChar;
BYTE tmBreakChar; BYTE tmPitchAndFamily;
BYTE tmCharSet; int tmOverhang;
int tmDigitizedAspectX; int tmDigitizedAspectY; }
PTEXTMETRIC Cpointer) TEXTMETRIC
NPTEXTMETRIC (Cpointer) TEXTMETRIC NEAR
RAVE DGIMETR Can poniniven os HE Xen MEmRa Coe AR

GDI Logical Objects


Pel Array
PELARRAY (€ struct tagPELARRAY )
a int paXCount; int paYCount;
Hint —faaplescres Hin ta Draly.
Exe ty= BYTE paRGBs; 2
PPELARRAY Cpointer) PELARRAY; NPPELARRAY Cpointer) PELARRAY
NEAR
ERPPEEARRAY C€poanter) PELARRAY FAR
Logical Brush (or Pattern)
LOGBRUSH ( struct tagLOGBRUSH )
{ WORD LbStyle; DWORD lbColor; mone LbHatch; yy
PLOGBRUSH Cpointer) LOGBRUSH NPLOGBRUSH Cpointer) LOGBRUSH
NEAR
LPLOGBRUSH (pointer) LOGBRUSH FAR PATTERN Cpointer) LOGBRUSH
PPATTERN (pointer) PATTERN NPPATTERN Cpointer) PATTERN
NEAR
LPPATTERN Cpointer) PATTERN FAR
Logical Pen
LOGPEN (€ struct tagLOGPEN ) :
{ WORD LopnStyle; POINT LopnWidth; DWORD lopnColor,;
PLOGPEN (pointer) LOGPEN NPLOGPEN (Cpointer) LOGPEN NEAR
LPLOGPEN (pointer) LOGPEN FAR
550 BORLAND C++ 3.0 PROGRAMMING, Second Edition

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

HS_ HORIZONTAL 0 HS_FDIAGONAL 2) HS_CROSS 4


HS_VERTICAL 1 HS_BDIAGONAL 8 HS DIAGCROSS 5
Pen Styles
RomoO LID a0 PS_DASHDOT 3 PS_INSIDEFRAME 6
POEDASH *1 PS_DASHDOTDOT 4
ESEDOT a PSeNWEE 5)
Device Parameters for Get DeviceCaps()
DRIVERVERSION 0 NUMMARKERS 20 ASPECTX 40
TECHNOLOGY 2 NUMFONTS 22 ASPECTY 42
HORZSIZE 4 NUMCOLORS 24 ASPECTXY 44
VERTSIZE 6 PDEVICESIZE 26 LOGPIXELSX 88
HORZRES 8 CURVECAPS 28 LOGPIXELSY 90
VERTRES 10 LINECAPS 30 SIZEPALETTE 104
BITSPIXEL 12 POLYGONALCAPS 32 NUMRESERVED 106
PLANES 14 . THER ONCAN ES) 34 COLORRES 108
NUMBRUSHES 16 GHECARS 36
NUMPENS 18 RASTERCAPS 38

Device Capability Masks:


Device Technologies
DT_PLOTTER 0 DT_RASCAMERA 3 DT_DISPFILE 6 // Nector plotter
DT_RASDISPLAY 1 DT_CHARSTREAM 4
DT_RASPRINTER 2 DT_METAFILE S
552 BORLAND C++ 3.0 PROGRAMMING, Second Edition

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

DrawText() Format Flags (cont.)


DT_RIGHT 0x0002 DT_EXTERNALLEADING 0x0200
DT_VCENTER 0x0004 DT_CALCRECT 0x0400
DT_BOTTOM 0x0008 DT_NOPREFIX 0x0800
DT_WORDBREAK 0x0010 DT_INTERNAL 0x1000
DT_SINGLELINE 0x0020
ExtFloodFill Style Flags
FLOODFILLSURFACE if FLOODFILLBORDER 0

USER Section
Scroll Bar Constants

SB_HORZ 0 SbaGiile WD
SB_VERT Sail SB BOTH 3

Scroll Bar Commands

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

Show Window() Commands

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

Old Show Window() Commands

HIDE_WINDOW 0 SHOW_FULLSCREEN 3
SHOW_OPENWINDOW 1 SHOW_OPENNOACTIVATE 4
SHOW_ICONWINDOW 2

Identifiers for WM_SHOWWINDOW Message


SW_PARENTCLOSING 1 SW_PARENTOPENING 3
SW_OTHERZOOM 7) SW_OTHERUNZOOM 4

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

Virtual Keys, Standard Set


VK_LBUTTON 0x01 VK_MENU . OxAZ VK _UP 0x26
VK_RBUTTON 0x02 VK_PAUSE 0x13 VK_RIGHT 0x27
VK_CANCEL 0x03 VK_CAPITAL 0x14 VK_DOWN 0x28
554 BORLAND C++ 3.0 PROGRAMMING, Second Edition

Virtual Keys, Standard Set (cont.)


VK_MBUTTON 0x04 VK_ESCAPE 0x1B VK=SELECT 0x29
VK_BACK 0x08 VK_SPACE 0x20 VK_PRINT Ox2A
VK_TAB 0x09 VK_PRIOR 0x21 VK_EXECUTE 0x2B
VK_CLEAR 0x0C VK_NEXT 0x22 VK_SNAPSHOT 0x2C
VK_RETURN 0x0D VK_END 0x23 VK_INSERT 0x2D
VK_SHIFT 0x10 VK_HOME 0x24 VK_DELETE Ox2E
VK_CONTROL _ Oxil VK_LEFT 0x25 VK_HELP Ox2F
VK_A thru VK_Z are the same as their ASCII equivalents: ’A’ thru ‘Z’
VK_0 thru VK_9 are the same as their ASCII equivalents: ’0’ thru ‘9’
VK_NUMPADO 0x60 VK_ADD 0x6B VK_F7 0x76
VK_NUMPADI1 0x61 VK_SEPARATOR 0x6C VK_F8 0x77
VK NUMPAD2 0x62 VK_ SUBTRACT 0x6D VK_F9 0x78
VK_NUMPAD3 0x63 VK_DECIMAL Ox6E VK_F10 0x79
VK _NUMPAD4 0x64 VK_DIVIDE Ox6F VK_F11 Ox7A
VK_NUMPAD5 0x65 VK_F1 0x70 VK_F12 0x7B
VK NUMPAD6_— 0x66 VK_F2 0x71 VK_F13 Ox7C
VK _NUMPAD7 — 0x67 VK_F3 0x72 VK_F14 0x7D
VK _NUMPAD8 0x68 VK _F4 0x73 VK_F15 Ox7E
VK NUMPAD9 0x69 VK_F5 0x74 VK_F16 Ox7F
VK_MULTIPLY Ox6A VK_F6 0x75 VK _NUMLOCK 0x90
Set WindowsHook() Codes
WH_MSGFILTER -1 WH_CALLWNDPROC 4
WH_JOURNALRECORD 0 WH_CBT 5
WH_JOURNALPLAYBACK 1 WH_SYSMSGFILTER 6
WH_KEYBOARD D WH_WINDOWMGR 7
WH_GETMESSAGE 3
Hook Codes
HC_LPLPFNNEXT —2 HC_GETNEXT 1 HC_NOREMOVE 3
HC_LPFNNEXT -1 HC_SKIP 2 HC_SYSMODALON 4
HC_ACTION 0 HC_NOREM 3 HC_SYSMODALOFF Ss
CBT Hook Codes
HCBT_MOVESIZE 0 HCBT_MINMAX 1 ReBiIm@S2
WH_MSGFILTER Filter Proc Codes
MSGF_DIALOGBOX 0 MSGF_ MENU 2 MSGF SIZE 4
MSGF_MESSAGEBOX 1 MSGF_ MOVE 3 MSGF_ SCROLLBAR 5
MSGF NEXTWINDOW 6
Window Manager Hook Codes
WC _INIT 1 WC_MOVE 5
WC_SWP 2 WC SIZE 6
WC_DEFWINDOWPROC 3 WC_DRAWCAPTION a
WC_MINMAX 4
Message Structure Used In Journaling
EVENTMSG (€ struct tagEVENTMSG )
iC WORD message; WORD paramL; WORD paramH; DWORD time;
RE G (pointer) EVENTMSGMSG
NPEVENTMSG Cpointer) EVENTMSGMSG NEAR LPEVENTMSG (¢ i
EVE MSG FAR : ag

WNDCLASS (€ struct tagWNDCLASS )


{ WORD style;
LONG CFAR PASCAL *lpfnWndProc) ¢ HWND, WORD, WORD, LONG );
int CbiGilsixtrar. int cbWndExtra; -
HANDLE hInstance; HICON' hlIcon;
Appendix A: Constants/Flags/Macros/Structures 555

Message Structure Used In Journaling (cont.)


HCURSOR hCursor; HBRUSH hbrBackground;
ERSiiER lpszMenuName; EPSiiR lpszClassName; }
WNDCLASS (Cpointer) WNDCLASS
WNDCLASS (pointer) WNDCLASS NEAR WNDCLASS (pointer) WNDCLASS FAR
Message Structure
NMSG]G struct tagMSG )
{ HWND hwnd; WORD message; WORD wParam;
LONG lParam; DWORD time; RODIN pastes
PMSG Cpointer)
NPMSG (pointer) MSG NEAR LPMSG (pointer) MSG FAR
Window Field Offsets for Get WindowLong() and Get Window Word()
GWL_WNDPROC 4 GWW_ID =|
GWW_HINSTANCE —-6 GWL_STYLE -16
GWW_HWNDPARENT = GWL_EXSTYLE —20)
Class Field Offsets for GetClassLong() and GetClass Word()
GCL_MENUNAME -8 GCW_CBWNDEXTRA -18
GCW_HBRBACKGROUND -10 GCW_CBCLSEXTRA —20
GCW_HCURSOR S12 GCL_WNDPROC —24
GCW_HICON -14 GCW_STYLE —26
GCW_HMODULE =16
Window Messages
WM_NULL 0x0000
WM_CREATE 0x0001 WM_KEYFIRST 0x0100
WM_DESTROY 0x0002 WM_KEYDOWN 0x0100
WM_MOVE 0x0003 WM_KEYUP 0x0101
WM. SIZE 0x0005 WM_CHAR 0x0102
WM_ACTIVATE 0x0006 WM_DEADCHAR 0x0103
WM_SETFOCUS 0x0007 WM_SYSKEYDOWN 0x0104
WM_KILLFOCUS 0x0008 WM_SYSKEYUP 0x0105
WM_ENABLE 0x000A WM_SYSCHAR 0x0106
WM_SETREDRAW 0x000B WM_SYSDEADCHAR 0x0107
WM_SETTEXT 0x000C WM_KEYLAST 0x0108
WM_GETTEXT 0x000D WM_INITDIALOG 0x0110
WM_GETTEXTLENGTH 0x000E WM_COMMAND 0x0111
WM_PAINT 0x000F WM_SYSCOMMAND 0x0112
WM_CLOSE 0x0010 WM_TIMER 0x0113
WM_QUERYENDSESSION 0x0011 WM_HSCROLL 0x0114
WM_QUIT 0x0012 WM_VSCROLL 0x0115
WM_QUERYOPEN 0x0013 WM_INITMENU 0x0116
WM_ERASEBKGND 0x0014 WM_INITMENUPOPUP 0x0117
WM_SYSCOLORCHANGE 0x0015 WM_MENUSELECT Ox011F
WM_ENDSESSION 0x0016 WM_MENUCHAR 0x0120
WM_SHOWWINDOW 0x0018 WM_ENTERIDLE 0x0121
WM_CTLCOLOR 0x0019 WM_MOUSEFIRST 0x0200
WM_WININICHANGE 0x001A WM_MOUSEMOVE 0x0200
WM_DEVMODECHANGE 0x001B WM_LBUTTONDOWN 0x0201
WM_ACTIVATEAPP 0x001C WM_LBUTTONUP 0x0202
WM_FONTCHANGE 0x001D WM_LBUTTONDBLCLK 0x0203
WM_TIMECHANGE Ox001E WM_RBUTTONDOWN 0x0204
WM_CANCELMODE 0x001F WM_RBUTTONUP 0x0205
WM_SETCURSOR 0x0020 WM_RBUTTONDBLCLK 0x0206
WM_MOUSEACTIVATE 0x0021 WM_MBUTTONDOWN 0x0207
WM_CHILDACTIVATE 0x0022 WM_MBUTTONUP 0x0208
WM_QUEUESYNC 0x0023 WM_MBUTTONDBLCLK 0x0209
WM_GETMINMAXINFO 0x0024 WM_MOUSELAST 0x0209
WM_PAINTICON 0x0026 WM_PARENTNOTIFY 0x0210
556 BORLAND C++ 3.0 PROGRAMMING, Second Edition

Window Messages (cont.)


WM_ICONERASEBKGND 0x0027 WM_MDICREATE 0x0220
WM_NEXTDLGCTL 0x0028 WM_MDIDESTROY 0x0221
WM_SPOOLERSTATUS 0x002A WM_MDIACTIVATE 0x0222
WM_DRAWITEM 0x002B WM_MDIRESTORE 0x0223
WM_MEASUREITEM 0x002C WM_MDINEXT 0x0224
WM_DELETEITEM 0x002D WM_MDIMAXIMIZE 0x0225
WM_VKEYTOITEM 0x002E WM_MDITILE 0x0226
WM_CHARTOITEM 0x002F WM_MDICASCADE 0x0227
WM_SETFONT 0x0030 WM_MDIICONARRANGE 0x0228
WM_GETFONT 0x0031 WM_MDIGETACTIVE 0x0229
WM_QUERYDRAGICON 0x0037 WM_MDISETMENU. 0x0230
WM_COMPAREITEM 0x0039 WM_CUT 0x0300
WM_COMPACTING 0x0041 WM_COPY 0x0301
WM_NCCREATE 0x0081 WM_PASTE 0x0302
WM_NCDESTROY 0x0082 WM_CLEAR 0x0303
WM_NCCALCSIZE 0x0083 WM_UNDO 0x0304
WM_NCHITTEST 0x0084 WM_RENDERFORMAT 0x0305
WM_NCPAINT 0x0085 WM_RENDERALLFORMATS — 0x0306
WM_NCACTIVATE 0x0086 WM_DESTROYCLIPBOARD 0x0307
WM_GETDLGCODE 0x0087 WM_DRAWCLIPBOARD 0x0308
WM_NCMOUSEMOVE 0x00A0 WM_PAINTCLIPBOARD 0x0309
WM_NCLBUTTONDOWN Ox00A1 WM_VSCROLLCLIPBOARD 0x030A
WM_NCLBUTTONUP 0x00A2 WM_SIZECLIPBOARD 0x030B
WM_NCLBUTTONDBLCLK 0x00A3 WM_ASKCBFORMATNAME 0x030C
WM_NCRBUTTONDOWN 0x00A4 WM_CHANGECBCHAIN 0x030D
WM_NCRBUTTONUP 0x00A5 WM_HSCROLLCLIPBOARD 0x030E
WM_NCRBUTTONDBLCLK 0x00A6 WM_QUERYNEWPALETTE 0x030F
WM_NCMBUTTONDOWN 0x00A7 WM_PALETTEISCHANGING — 0x0310
WM_NCMBUTTONUP 0x00A8 WM_PALETTECHANGED 0x0311
WM_NCMBUTTONDBLCLK Ox00A9 WM_USER 0x0400
NOTE: All Message Numbers below 0x0400 are RESERVED.
WM_SYNCTASK Commands
ST_BEGINSWP 0 ST_ENDSWP 1
WinWhere() Area Codes
HTERROR —2 HTMENU is) Vel hI KO) P 12
HTTRANSPARENT -1 HTHSCROLL 6 HTTOPLEFT 13
HTNOWHERE 0 FIVSCROEE I HTTOPRIGHT 14
HTCLIENT 1 HTREDUCE 8 HTBOTTOM US
HTCAPTION a HTZOOM 9 HTBOTTOMLEFT 16
HTSYSMENU 3 HTLEFT 10 HTBOTTOMRIGHT 17
HTGROWBOX 4 HTRIGHT 1A
HTSIZE HTGROWBOX
HTSIZEFIRST HTLEFT HTSIZELAST HTBOTTOMRIGHT
WM_MOUSEACTIVATE Return Codes
MA_ ACTIVATE it MA NOACTIVATE 3
MA ACTIVATEANDEAT y,
Size Message Commands

SIZENORMAL 0 SIZEFULLSCREEN P2 SIZEZOOMHIDE 4


SIZEICONIC 1 SIZEZOOMSHOW 3
Key State Masks for Mouse Messages
MK _LBUTTON 0x0001 MK_ CONTROL 0x0008
MK _RBUTTON 0x0002 MK MBUTTON 0x0010
MK_SHIFT 0x0004
Appendix A: Constants/Flags/Macros/Structures 557

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

Common Window Styles


WS_OVERLAPPEDWINDOW = ==> ( WS_OVERLAPPED | WS_CAPTION |
WS_SYSMENU | WS_THICKFRAME |
WS_MINIMIZEBOX | WS_MAXIMIZEBOxX)
WS_POPUPWINDOW ==> ( WS_POPUP | WS_BORDER |
WS_SYSMENU )
WS_CHILDWINDOW SES ( WS_CHILD )
WS_TILEDWINDOW ==> ( WS_OVERLAPPEDWINDOW )
Extended Window Styles
WS_EX_DLGMODALFRAME — 0x00000001L WS_EX_NOPARENTNOTIFY — 0x00000004L
Class Styles
CS_VREDRAW 0x0001 CS_PARENTDC 0x0080
CS_HREDRAW 0x0002 CS_NOKEYCVT 0x0100
CS_KEYCVTWINDOW 0x0004 CS_NOCLOSE 0x0200
CS_DBLCLKS 0x0008 CS_SAVEBITS 0x0800
//no longer used // 0x0010 CS BYTEALIGNCLIENT 0x1000
CS_OWNDC 0x0020 CS_BYTEALIGNWINDOW 0x2000
GCSECLASSDG 0x0040 CS_GLOBALCLASS 0x4000
Predefined Clipboard Formats
CERTEXT 1 CF_SYLK 4 CF_OEMTEXT 7
CF_ BITMAP 2 CF DIF ) CF_DIB 8
CF_METAFILEPICT 3 CEaWEE 6 CEIPALE IDE 9
CF _OWNERDISPLAY —0x0080 CF_DSPBITMAP 0x0082
CF_DSPTEXT . 0x0081 CF_DSPMETAFILEPICT 0x0083
= “Private” formats don’t get GlobalFree()’d
CF_PRIVATEFIRST 0x0200 CF_PRIVATELAST Ox02FF
= “GDIOBJ” formats do get DeleteObject()’d
CF_GDIOBJFIRST 0x0300 CF GDIOBJLAST 0x03FF
PAINTSTRUCT ( struct tagPAINTSTRUCT ) ;
HDC hdc; BOOL fErase; RE Cie Gracinice
BOOL fRestore; BOOL fIncUpdate; BYTE srgbReserved[16]; }
PPAINTSTRUCT (pointer) PAINTSTRUCT
NPPAINTSTRUCT (pointer) PAINTSTRUCT NEAR
LPPAINTSTRUCT (pointer) PAINTSTRUCT FAR
CREATESTRUCT ( struct tagCREATESTRUCT )
{ EP Sak LpCreateParams; HANDLE hInstance;
HANDLE hMenu; HWND hwndParent;
558 BORLAND C++ 3.0 PROGRAMMING, Second Edition

Predefined Clipboard Formats (cont.)


int Cy int Coxer int ys int xes
LONG style; EPSaR LpszName;
LPSTR Upis:z Gilfaisis ; DWORD dwExStyle; }
EPGCREAT ESimRUIGT Cpointer) CREATESTRUCT FAR
Owner Draw Control Types / Actions / State Flags
ODT_MENU 1 ODT_COMBOBOX 3
ODT_LISTBOX 2 ODT_BUTTON 4
ODA_DRAWENTIRE 0x0001 ODA_SELECT 0x0002 ODA_FOCUS 0x0004
ODS_SELECTED 0x0001 ODS_DISABLED 0x0004 ODS_FOCUS 0x0010
ODS_GRAYED 0x0002 ODS_CHECKED 0x0008
MEASUREITEMSTRUCT ( struct tagMEASUREITEMSTRUCT )
{ WORD CtlType; WORD CtlID; WORD itemID;
WORD itemWidth; WORD itemHeight; DWORD itemData; }
PMEASUREITEMSTRUCT Cpointer) MEASUREITEMSTRUCT NEAR
LPMEASUREITEMSTRUCT (pointer) MEASUREITEMSTRUCT FAR
DRAWITEMSTRUCT ( struct tagDRAWITEMSTRUCT )
{ WORD CtlType; WIRD SeGrt.
lel Dis WORD itemID;
WORD itemAction; WORD itemState; HWND hwndIitem;
HDC hDC; REG | hel tem: DWORD itemData; }
PDRAWITEMSTRUCT Cpointer) DRAWITEMSTRUCT NEAR
LPDRAWITEMSTRUCT Cpointer) DRAWITEMSTRUCT FAR
DETERLELIEMSURUGT. © Vs:tiRucte tag DEE EGE MSR UG Te
{ WORD CtlType; WORD CtlLlID; WORD itemID;
HWND hwndItem; DWORD itemData }
RDB EET EdeneMomenUCm Cpointer) DELETEITEMSTRUCT NEAR
ERP DBE EEL eM SaRvien Cpointer) DEPERELREMS ERUCT RAR

COMPAREITEMSTRUCT ( struct tagCOMPAREITEMSTRUCT )


{ WORD CtlType; WORD Gt eDy HWND hwnditem;
_ WORD itemID1; DWORD itemDatai1;
WORD itemID2; DWORD itemData2; }
PCOMPAREITEMSTRUCT Cpointer) COMPAREITEMSTRUCT NEAR
LPCOMPAREITEMSTRUCT Cpointer) COMPAREITEMSTRUCT FAR
PeekMessage() Options
PM_NOREMOVE 0x0000 M_REMOVE 0x0001 PM_NOYIELD 0x0002
READ 0 WRITE 1 READ_WRITE 2
CW_USEDEFAULT (Cint)0x8000)
Set WindowPos Flags
SWP_NOSIZE 0x0001 SWP_DRAWFRAME 0x0020
SWP_NOMOVE 0x0002 SWP_SHOWWINDOW 0x0040
SWP_NOZORDER 0x0004 SWP_HIDEWINDOW 0x0080
SWP_NOREDRAW 0x0008 SWP_NOCOPYBITS 0x0100
SWP_NOACTIVATE 0x0010 SWP_NOREPOSITION 0x0200
DLGWINDOWEXTRA 30 // needed for private dialog classes
GetSystemMetrics() Codes
SM_CXSCREEN 0 SM_CXCURSOR 13 SM_RESERVED3 26
SM_CYSCREEN 1 SM_CYCURSOR 14 SM_RESERVED4 27
SM_CXVSCROLL 2 SM_CYMENU 15 SM_CXMIN 28
SM_CYHSCROLL 3 SM_CXFULLSCREEN 16 SM_CYMIN 20)
SM_CYCAPTION 4 SM_CYFULLSCREEN 17 SM_CXSIZE 30
SM_CXBORDER 5 SM_CYKANJIWINDOW 18
SM_CYBORDER 6 SM_MOUSEPRESENT 19 SM_CYSIZE 31
SM_CXDLGFRAME iv SM_CYVSCROLL 20 SM_CXFRAME 32
SM_CYDLGFRAME 8 SM_CXHSCROLL ZA SM_CYFRAME 33
Appendix A: Constants/Flags/Macros/Structures 559

GetSystemMetrics() Codes (cont.)


SM_CYVTHUMB 9 SM_DEBUG 7p SM_CXMINTRACK 34
SM_CXHTHUMB 10 SM_SWAPBUTTON 23 SM_CYMINTRACK 39
SM_CXICON IM SM_RESERVED1 24 SM_CMETRICS 36
SM_CYICON 12 SM_RESERVED2 25
MessageBox() Flags
MB_OK 0x0000 MB_DEFBUTTON1 0x0000
MB_OKCANCEL 0x0001 MB_DEFBUTTON2 0x0100
MB_ABORTRETRYIGNORE 0x0002 MB_DEFBUTTON3 0x0200
MB_YESNOCANCEL 0x0003 MB_APPLMODAL 0x0000
MB_YESNO 0x0004 MB_SYSTEMMODAL 0x1000
MB_RETRYCANCEL 0x0005 MB_TASKMODAL 0x2000
MB_ICONHAND 0x0010 MB_NOFOCUS 0x8000
MB_ICONQUESTION 0x0020 MB_TYPEMASK 0x000F
MB _ICONEXCLAMATION 0x0030 MB_ICONMASK 0x00FO
MB_ICONASTERISK 0x0040 MB_DEFMASK 0x0F00
MB_MODEMASK 0x3000
MB_MISCMASK 0xC000
MB_ICONINFORMATION ==> MB_ICONASTERISK
MB_ICONSTOP ==> MB_ICONHAND
Color Types
CTLCOLOR_MSGBOX 0 CTLCOLOR_DLG 4
CTLCOLOR_EDIT 1 CTLCOLOR_SCROLLBAR »
CTLCOLOR_LISTBOX 2 CTLCOLOR_ STATIC 6
CTLCOLOR_BTN 3 CTLCOLOR_MAX 8
COLOR_SCROLLBAR 0 COLOR_ACTIVEBORDER 10
COLOR_BACKGROUND 1 COLOR_INACTIVEBORDER 11
COLOR_ACTIVECAPTION 2 COLOR_APPWORKSPACE 12
COLOR_INACTIVECAPTION — 3 COLOR_HIGHLIGHT 1S
COLOR_MENU 4 COLOR_HIGHLIGHTTEXT 14
COLOR_WINDOW 5 COLOR_BTNFACE 15
COLOR_WINDOWFRAME 6 COLOR_BTNSHADOW 16
COLOR_MENUTEXT Zi COLOR_GRAYTEXT 17
COLOR_WINDOWTEXT 8 COLOR_BTNTEXT 18
COLOR_CAPTIONTEXT 2)
COLOR_ENDCOLORS ==> COLOR_BTNTEXT

Get Window() Constants


GW_HWNDEFIRST 0 GW_HWNDNEXT 2 GW_OWNER 4
GW_HWNDLAST 1 GW_HWNDPREV 3 GW_CHILD 5
Menu flags for Add/Check/EnableMenultem()
MF_INSERT 0x0000 MF_USECHECKBITMAPS 0x0200
MF_CHANGE 0x0080 MF_STRING 0x0000
MF_APPEND 0x0100 MF_BITMAP 0x0004
MF_DELETE 0x0200 MF_OWNERDRAW 0x0100
MF_REMOVE 0x1000 MF_POPUP 0x0010
MF_BYCOMMAND 0x0000 MF_MENUBARBREAK 0x0020
MF_BYPOSITION 0x0400 MF_MENUBREAK 0x0040
MF_SEPARATOR 0x0800 MF_UNHILITE 0x0000
MF_ENABLED 0x0000 MF_HILITE 0x0080
MF_GRAYED 0x0001 MF_SYSMENU 0x2000
MF_DISABLED 0x0002 MF_HELP 0x4000
MF_UNCHECKED 0x0000 MF_MOUSESELECT 0x8000
MF_CHECKED 0x0008
560 BORLAND C++ 3.0 PROGRAMMING, Second Edition

Menu Item Resource Format

MENUITEMTEMPLATEHEADER (¢ struct )
{ WORD versionNumber; WORD offset; }
MENUITEMTEMPLATE ( struct ) :
{ WORD mtOption; WORD mtID; char tMeEeSterime@e4
dip 2
MF_END 0x0080

System Menu Command Values


SEGISIZE O0xF000 SC_VSCROLL 0xF070
SC_MOVE 0xF010 S@UHSCROLE O0xF080
SC_MINIMIZE OxF020 SC_MOUSEMENU OxF090
SC_MAXIMIZE 0xF030 SC_KEYMENU OxF100
SC_NEXTWINDOW 0xF040 SC_ARRANGE OxF110
SC_PREVWINDOW OxF050 SC_RESTORE OxF120
SORGLOSE OxF060 SC_TASKLIST OxF130
SC_ICON SC_MINIMIZE
SC_ZOOM SC_MAXIMIZE
Standard Cursor IDs

IDC_ARROW MAKEINTRESOURCE(32512) IDC_IBEAM


MAKEINTRESOURCE(32513)
IDC_WAIT MAKEINTRESOURCE(32514) IDC_CROSS
MAKEINTRESOURCE(32515)
IDC_UPARROW MAKEINTRESOURCE(32516) TDiGmsS Ze
MAKEINTRESOURCE(32640)
IDC_ICON MAKEINTRESOURCE(32641) IDC_SIZENWSE
MAKEINTRESOURCE( 32642)
IDC_SIZENESW MAKEINTRESOURCE( 32643) IDC_SIZEWE
MAKEINTRESOURCE( 32644)
TDi Cae Stee NS MAKEINTRESOURCE( 32645)
ORD_LANGDRIVER 1
Standard Icon IDs

IDI_APPLICATION MAKEINTRESOURCE(32512) IDI_HAND


MAKEINTRESOURCE(32513)
IDI_QUESTION MAKEINTRESOURCE(32514) IDI_EXCLAMATION
MAKEINTRESOURCE(32515)
IDI_ASTERISK MAKEINTRESOURCE(32516)
CP_HWND 0 CP_OPEN 1 CP_DIRECT2
VK From The Keyboard Driver
VK _KANA 0x15 VK_HIRAGANA 0x18
VK_ROMAJI 0x16 VK_KANJI 0x19
VK_ZENKAKU 0x17
VK To Send To Applications

VK_CONVERT 0x1C VEKSACCERT Ox1E


VK_ NONCONVERT 0x1D VK_MODECHANGE Ox1F
Conversion Function Numbers

KNJ_START 0x01 KNJ_NEXT 0x22


KNJ_END 0x02 KNJ_PREVIOUS 0x23
KNJ_QUERY 0x03 KNJ_ACCEPT 0x24
KNJ_LEARN_MODE 0x10 KNJ_LEARN 0x30
Appendix A: Constants/Flags/Macros/Structures 561

Conversion Function Numbers (cont.)

KNJ_GETMODE 0x11 KNJ_REGISTER 0x31


KNJ_SETMODE 0x12 KNJ_REMOVE 0x32
KNJ_CODECONVERT 0x20 KNJ_CHANGE_UDIC 0x33
KNJ_CONVERT 0x21
NOTE: DEFAULT =0 JISIKATAKANA =4
JIS1 =1 SJISZHIRAGANA =5
JIS2 =2 SJISZKATAKANA =6
SjIs2=3 OEM = F
KNJ_JISItoJISIKATAKANA 0x14 KNJ_JIS1toDEFAULT 0x10
KNJ_JIS1toSJIS2 0x13 KNJ_JIS1toSJIS2ZOEM Ox1F
KNJ_JIS1toSJISZHIRAGANA 0x15 KNJ_JIS2toSJIS2 0x23
KNJ_JIS1toSJIS2KATAKANA 0x16 KNJ_SJIS2toJIS2 0x32
KNJ_MD_ALPHA 0x01 KNJ_MD _JIS 0x08
KNJ_MD_HIRAGANA 0x02 KNJ_MD_SPECIAL 0x10
KNJ_MD_HALF 0x04
KNJ_CVT_NEXT 0x01 KNJ_CVT_JIS1 0x05
KNJ_CVT_PREV 0x02 KNJ_CVT_SJIS2 0x06
KNJ_CVT_KATAKANA 0x03 KNJ_CVT_DEFAULT 0x07
KNJ_CVT_HIRAGANA 0x04 KNJ_€VT_TYPED 0x08
KANJISTRUCT, FAR *LPKANJISTRUCT ( struct )
{ int fice, int wParam;
PSalaks LpSource; LPSTR LpDest; int wCount;
EPRISoER lLpReservedi; Siar LpReserved2;

Dialog Box Command IDs


IDOK 1 IDRETRY | IDNO7
IDCANCEL 2 IDIGNORE 5
IDABORT 3 IDYES 6

Control Manager Structures and Definitions


Edit Control Styles
Jae) JUBA 0x0000 ES PASSWORD 0x0020
ES_CENTER 0x0001 ES _AUTOVSCROLL 0x0040
ES_RIGHT 0x0002 ES_AUTOHSCROLL 0x0080
ES_MULTILINE 0x0004 ES_NOHIDESEL 0x0100
ES_UPPERCASE 0x0008 ES_OEMCONVERT 0x0400
ES_LOWERCASE 0x0010
Edit Control Notification Codes
EN_SETFOCUS 0x0100 EN_ERRSPACE 0x0500
EN_KILLFOCUS 0x0200 EN_MAXTEXT 0x0501
EN_CHANGE 0x0300 EN_HSCROLL 0x0601
EN_UPDATE 0x0400 EN_VSCROLL 0x0602
Edit Control Messages
EM_GETSEL (WM_USER) EM_LINELENGTH (..+17)
EM_SETSEL (...+1) EM_REPLACESEL (...+18)
EM_GETRECT (oR) EM_SETFONT C219)
EM_SETRECT (ato) EM_GETLINE (...+20)
EM_SETRECTNP (...44) EM_LIMITTEXT Crt)
EM_SCROLL (to) EM_CANUNDO (...422)
EM_LINESCROLL (...+6) EM_UNDO (...+23)
EM_GETMODIFY (...+8) EM_FMTLINES (...+24)
562 BORLAND C++ 3.0 PROGRAMMING, Second Edition

Edit Control Messages (cont.)


EM_SETMODIFY (...4+9) EM_LINEFROMCHAR (...+25)
EM_GETLINECOUNT (...+10) EM_SETWORDBREAK (...+26)
EM_LINEINDEX (...+11) EM_SETTABSTOPS (...+27)
EM_SETHANDLE (...+12) EM _SETPASSWORDCHAR (...+28)
EM_GETHANDLE (...+13) EM_EMPTYUNDOBUFFER (...+29)
EM GETTHUMB (...4+14) EM MSGMAX (...+30)

Button Control Styles


BS_PUSHBUTTON Ox00L BS_GROUPBOX 0x07L
BS_DEFPUSHBUTTON 0x01L BS_USERBUTTON 0x08L
BS_ CHECKBOX 0x02L BS_-AUTORADIOBUTTON 0x09L
BS_AUTOCHECKBOX 0x03L BS_PUSHBOX Ox0AL
BS_RADIOBUTTON 0x04L BS_OWNERDRAW Ox0BL
BS_3STATE Ox05L BS_LEFTTEXT 0x20L
BS_AUTO3STATE Ox06L
User Button Notification Codes
BN_CLICKED 0 BN_HILITE D BN_DISABLE 4
BN_PAINT 1 BN_UNHILITE 3 BN_DOUBLECLICKED 5
Button Control Messages
BM_GETCHECK (WM_USER+0)
BM_SETCHECK (...+1) BM_SETSTATE (...43)
BM_GETSTATE (...+2) BM_SETSTYLE (...+4)
Static Control Constants
SOLLEPT 0x00L SS_BLACKFRAME 0x07L
SS_CENTER 0x01L SS_GRAYFRAME 0x08L
SS_RIGHT 0x02L SS_WHITEFRAME 0x09L
SS_ICON 0x03L SS_USERITEM Ox0AL
SS_BLACKRECT 0x04L SS_SIMPLE Ox0BL
SS_GRAYRECT 0x05L SS_LEFTNOWORDWRAP 0x0CL
SS_WHITERECT Ox06L
SS_NOPREFIX 0x80L // Don't do & character translation
Dialog Styles
DS_ABSALIGN 0x01L DS_SETFONT 0x40L
DS_SYSMODAL 0x02L DS_MODALFRAME 0x80L
DS_LOCALEDIT 0x20L DS_NOIDLEMSG Ox100L
DM_GETDEFID (WM_USER) DM_SETDEFID (...41)
DC_HASDEFID 0x534B
Dialog Codes
DLGC_WANTARROWS 0x0001 DLGC_UNDEFPUSHBUTTON 0x0020
DLGC_WANTTAB 0x0002 DLGC_RADIOBUTTON 0x0040
DLGC_WANTALLKEYS 0x0004 DLGC_WANTCHARS 0x0080
DLGC_WANTMESSAGE 0x0004 DLGC_STATIC 0x0100
DLGC_HASSETSEL 0x0008 DLGC_BUTTON 0x2000
DLGC_DEFPUSHBUTTON 0x0010 LB_CTLCODE OL
Listbox Return Values / Notification Codes / Messages
LBN_ERRSPACE =z LBN_SELCHANGE i LBN_SETFOCUS 4
LB_ERR =i! LBN_DBLCLK 2 LBN_KILLFOCUS 5
LB_OKAY 0 LBN_SELCANCEL 3
LB_ADDSTRING (WM_USER+1)
LB_INSERTSTRING (42) LB_FINDSTRING (...+16)
LB_DELETESTRING (...+3) LB_GETSELCOUNT (...+17)
LB_RESETCONTENT (...+5) LB_GETSELITEMS (...+18)
Appendix A: Constants/Flags/Macros/Structures 563

Listbox Return Values / Notification Codes / Messages (cont.)


LB_SETSEL (...+6) LB_SETTABSTOPS (ret)
LB_SETCURSEL (...+7) LB_GETHORIZONTALEXTENT (...+20)
LB_GETSEL (...+8) LB_SETHORIZONTALEXTENT (...+21)
LB_GETCURSEL (...+9) LB_SETCOLUMNWIDTH (6.422)
LB_GETTEXT (...+10) LB_SETTOPINDEX (...+24)
LB_GETTEXTLEN (...411) LB_GETITEMRECT ee)
LB_GETCOUNT (...+12) LB_GETITEMDATA (...+26)
LB_SELECTSTRING (...+13) LB_SETITEMDATA (...+27)
LB_DIR (...4+14) LB_SELITEMRANGE (...+28)
LB_GETTOPINDEX (...+15) LB_MSGMAX (2333)
Listbox Styles
LBS_NOTIFY 0x0001L LBS_HASSTRINGS 0x0040L
LBS_SORT 0x0002L LBS_USETABSTOPS 0x0080L
LBS_NOREDRAW 0x0004L LBS_NOINTEGRALHEIGHT 0x0100L
LBS_MULTIPLESEL 0x0008L LBS_MULTICOLUMN 0x0200L
LBS_OWNERDRAWFIXED —_—Ox0010L LBS_WANTKEYBOARDINPUT 0x0400L
LBS_OWNERDRAWVARIABLE 0x0020L LBS_EXTENDEDSEL 0x0800L
LBS_STANDARD ==> (LBS_NOTIFY | LBS_SORT |
WS_VSCROLL | WS_BORDER)
Combo Box Return Values
CB_OKAY 0 CB_ERR =ll CB_ERKSPACE —-2
Combo Box Notification Codes
CBN_ERRSPACE - 1 CBN _SETFOCUS 3 CBN_EDITUPDATE 6
CBN_SELCHANGE 1 CBN_KILLFOCUS 4 CBN_DROPDOWN Hi
CBN_DBLCLK 2. CBN_EDITCHANGE 5
Combo Box Styles
CBS_SIMPLE 0x0001L CBS_AUTOHSCROLL 0x0040L
CBS_DROPDOWN 0x0002L CBS_OEMCONVERT 0x0080L
CBS_DROPDOWNLIST 0x0003L CBS_SORT 0x0100L
CBS_OWNERDRAWFIXED 0x0010L CBS_HASSTRINGS 0x0200L
CBS_OWNERDRAWVARIABLE 0x0020L CBS_NOINTEGRALHEIGHT 0x0400L
Combo Box Messages
CB_GETEDITSEL (WM_USER+0)
CB_INSERTSTRING (Gall)
CB_LIMITTEXT C2) CB_RESETCONTENT (...+11)
GCBISETEDIMSEL (2) CB_FINDSTRING Ce 1D)
CB_ADDSTRING (Ceo) CB_SELECTSTRING (...+13)
CB_DELETESTRING (...+4) CB_SETCURSEL (...4+14)
CB_DIR (...+5) CB_SHOWDROPDOWN (...415)
CB_GETCOUNT (...+6) CB_GETITEMDATA (aL)
CB_GETCURSEL (...+7) CB_SETITEMDATA (...+17)
CB_GETLBTEXT (...+8)
CB_GETDROPPEDCONTROLRECT (...+18)
CB_GETLBTEXTLEN G9) CB_MSGMAX (S19)
Scroll Bar Styles
SBS_HORZ 0x0000L SBS_RIGHTALIGN 0x0004L
SBS_VERT 0x0001L SBS_SIZEBOXTOPLEFTALIGN 0x0002L
SBS_TOPALIGN 0x0002L
SBS_SIZEBOXBOTTOMRIGHTALIGN 0x0004L
SBS_LEFTALIGN 0x0002L SBS_SIZEBOX 0x0008L
SBS_BOTTOMALIGN Ox0004L
564 BORLAND C++ 3.0 PROGRAMMING, Second Edition

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; }

Help Engine Section


Commands to Pass WinHelp()
HELP_CONTEXT 0x0001 HELP_HELPONHELP 0x0004
HELP_QUIT 0x0002 HELP_SETINDEX 0x0005
HELP INDEX 0x0003 HELP_KEY 0x0101 HELP_MULTIKEY 0x0201
MULTIKEYHELP ( struct I See ant )
{ WORD mkSize; BYTE mkKeylist; Bayer szKeyphrasel1]; }
Index

386M AX utility, 9 ALTERNATE fill mode, 346


ANSI character set, 97-100
A generating characters, 99-100
AbortDlg dialog box, 462 supporting, 98-99
ABORTDOC subfunction, 463 AnsiLower function, 98
AbortProc function, 459-462 AnsiLowerBuff function, 99
About dialog box, 208-209 AnsiNext function, 99
Accelerator Editor, 152, 221-224 AnsiPrev function, 99
Accelerator keys AnsiToOem function, 99
ASCII vs. virtual keys, 222 AnsiToOemBuff function, 99
defining, 221-223 AnsiUpper function, 98
modifiers and conditions, 223-224 AnsiUpperBuff function, 99
Accelerator menu, 224 ANSI FIXED_FONT character set, 97
Accelerators, 154 ANSI VAR_ FONT character set, 98
resources, 221-224 AppendMenu function, 467
Accent marks, 47 Application windows, 16-18, 23, 49

AddAtom function, 513 child window, 17


Airbrush tool, 176-177 parent window, 17
Align Controls dialog box, 205 retrieving messages, 18-19
Align/Size dialog box, 204 Applications. See also programs
Alignment compiling, 28-35
dialog box, 204 designing for Windows, 39-40
identifier of most recent, 12
palette, 188
measurement units, 287
toolbar, 204
resources, 153-168

565
566 BORLAND C++ 3.0 PROGRAMMING, Second Edition

transferring memory metafiles defining color palette, 481


between, 480 device-independent (DIB), 375-385
working in device pixels, 287 larger, 370
Arc function, 338, 340 monochrome, 371-374
Arrow tool, 174-175 old-style, 371-372
ASCII keys vs. virtual keys, 222 OS/2, 380
ASM source file, 10 predefined, 368-369
Atoms, 512-514 retrieving image from, 373
stretching, 385-387
B text, 177
Banding, 457-459 transferring Windows 2.0, 480
BarGraph program, 341-344 WHITEONBLACK mode, 387
BarGraph.C listing, 354-357 BLACKONWHITE mode, 387
BarGraph.DEF listing, 357 BM.SETCHECK message, 146
BC directory, 3 BMP files, 156
BeginMetaFile function, 403 BM_CLICKED message, 140
BeginPaint function, 25, 41, 43-44, 50, BM_DOUBLECLICKED submessage,
278, 404 145
Bit block transfers and bitmaps, BM_GETCHECK message, 146, 148

383-385 BM_SETCHECK message, 146


BitBlt function, 383-384, 387, 489 BM_SETSTATE message, 146, 148
Bitmap (.BMP) resource image, 154 BM_SETSTYLE message, 147
Bitmap Editor, 152, 154, 367, 375 Borland C/C++ 2.0, compilers, 5
Bitmap files, 370-371 Borland C/C++ 3.0
BITMAP structure, 372 (BC) compiler vs. Turbo C++
BITMAPCOREHEADER structure, 380 (TCW) compiler, 5-6
BITMAPINFO structure, 481 command-line compiler, 28-29
BITMAPINFOHEADER structure, compiler group, 3
376-377 IDE integrated compiler, 32-35
Bitmapped brushes, 365-375 installing for Windows 3.0, 3-11
Bitmapped fonts, 46, 424-425, 428 task group, 4
Bitmaps, 169 Borland Graphic Interface (BGD),
BLACKONWHITE mode, 387 425
clipboard transfers, 488-490 Borland Resource Workshop, 5
color, 374-375 Borland Window Custom Control
COLORONCOLOR mode, 387 message box dialog box, 263-266
copying character array into, 372 BRICKS bitmap image, 367
Index 567

BS_3STATE check box, 140 moving, 88


BS_AUTOS3STATE check box, 140-141 positioning, 88-93
BS_-AUTOCHECKBOxX check box, revealing /hiding, 88
140-141 vs. cursor, 86
BS_AUTORADIOBUTTON radio Central Control Program, 152
button, 140-141, 145 CF-DSPMETAFILEPICT format, 494
BS_CHECKBOxX check box, 140 CF BITMAP data format, 480
BS_DEFPUSHBUTTON pushbutton, CF_DIB data format, 480-481
139, 147 CF_DIF format, 481
BS_GROUPBOX style, 140 CF DSPBITMAP format, 494
BS_LEFTTEXT style, 140 CF DSPTEXT format, 494
BS_OWNERDRAW< style, 141 CF METAFILEPICT data format, 480
BS_PUSHBOxX pushbutton, 139 CF OEMTEXT data format, 480
BS_PUSHBUTTON pushbutton, CF PALETTE data format, 481
138-140, 147 CF _SYLK format, 481
BS_RADIOBUTTON radio button, CF TEXT data format, 479-480
140, 145 CF TIFF format; 482
BS_USERBUTTON style, 145 CGA fonts, 428
Bucket tool, 177 CGA video card, 171
Business graphs, 341-344 CHAINS bitmap image, 367
Button1 program, 141, 143-145 Character
Button2 program, 143-146 descenders, 47
Buttons, 137 event messages, 81-83
BWCC.LIB file, 266 messages converting keystroke
BWCCSTYLE.RW file, 266 messages to, 82
strings, 512-514
CG width, 48

C files, 32 Character sets, 95-100


C source file, 10
ANSI, 97-100
Callback functions, 465-466
ANSI FIXED_FONT, 97
CallFileOpenDlg procedure, 239 ANSI VAR FONT, 98
DEVICE DEFAULT FONT, 98
CallFileTypeDlg function, 238
international, 95, 97
CareateCompatibleBitmap function,
logical fonts, 433
411
OEM_FIXED_ FONT, 95-97, 100
Caret
SYSTEM_FIXED_ FONT, 95-97, 100
blink rate, 89
SYSTEM FONT, 97
functions, 86-89
568 BORLAND C++ 3.0 PROGRAMMING, Second Edition

Check boxes, 138, 140 CF_ DIF format, 481


BS_3STATE, 140 CF DSPBITMAP format, 494
BS_AUTO3STATE, 140-141 CF DSPMETAFILEPICT format,
BS_AUTOCHECKBOxX, 140-141 494
BS_CHECKBOxX, 140 CF _DSPTEXT format, 494
dialog boxes, 196 CF EOMTEXT format, 480
setting or receiving state, 146 CF _METAFILEPICT format, 480
CheckRadioButton function, 239 CF PALETTE format, 481
Child dialog boxes, 191 CF _SYLK format, 481
Child windows, 120 CF TEXT format, 479-480
See also control buttons CF_TIFF format, 482
application windows, 17 copying to, 482-483
control buttons, 137-148 data formats, 479-481
creating and sizing, 118-119 data types, 484-485
ChildWndProc procedure, 119 delayed rendering, 495-496
cursor operations, 119-121 drawbacks, 478
Chord function, 338, 340 functions of, 478-479
Circles, 178, 337 item availability, 484-485
Circles tools (Hollow or Filled), 178 metafile transfers, 490-493
CLASS statement, 194 owner displayed data, 496
Classes custom dialog box, 194 private formats, 496-498
Clicking, 113 retrieving data, 485
Client window special formats, 481
converting to screen coordinates, text operations, 486
124-125 viewer, 477-478
operations, 278 ClipBrd.EXE program, 477-478
ClientTcScreen function, 124 CloseClipboard function, 482, 488
ClipBd program, 482, 485-487, 490 CloseMetaFile function, 403
ClipBd.C listing, 498-505 Code generation and C/C++ IDE
ClipBd.DEF listing, 505 integrated compiler, 35
ClipBd.H listing, 506 Color, 313-317
ClipBd.RC Menu File listing, 506 bitmaps, 374-375
Clipboard data transfers, 477-498 brushes, 320
accessing, 482-484 creation, 315-316
bitmap transfers, 488-490 custom brushes, 318-320
CF_ BITMAP format, 480 devices, 314-315
CF_DIB format, 480-481 dithered, 314, 316-317
Index 569

drawing modes, 321-322 creating, 141-144


graphics images custom, 171-172 grouping, 140, 143-144
information handling, 313-314 information requests, 148
remapping palettes, 320 messages, 145-147
table for device-independent notification codes, 145
bitmaps (DIB), 377-378 operating, 141
testing VGA and SVGA, 172-173 parameters, 142
Windows 3.0, 314-315 pushbuttons, 138-139
Color palettes radio buttons, 138-140
custom colors, 171-172 selecting, 138
graphics images, 170-171 submessages, 144-145
Color planes, querying, 313-314 Control elements
COLORONCOLOR mode, 387 aligning, 204-205
Colors program, 314, 317-322 duplicating, 201
colors and drawing modes, grouping, 202-203
o21-322 ID constants, 209
brushes, 318-320 ordering, 203-204
dithered color, 321 positioning, 204
Colors.C listing, 323-328 sizing, 204, 206-207
Colors.DEF listing, 328 spacing, 205-206
Combo boxes, 198-199 tabstops, 201-202
Command-line compiler, 28-29 Control function, 453
Compilers, preventing warning Control Panel/ Mouse utility, 174
messages, 235-236 Control types, 189
CONFIG.SYS file, QEMM-386 version Controls menu, 201-204
6.0, 7-8 Duplicate tool, 201
Constants, predefined and prefixes, 26 Order tool, 203-204
Control buttons, 137-148 Pick tool, 201, 203-204
BS_DEFPUSHBUTTON style, 147 Set Groups tool, 203
BS_GROUPBOxX style, 140 Tab Stop tool, 201-202
BS_LEFTTEXT style, 140 CPP source file, 10
BS_OWNERDRAW style, 141 CreateBitmap function, 366, 371-372
BS_PUSHBUTTON style, 147 CreateBitmapIndirect function, 372,
BS_USERBUTTON style, 145 oye!
button labels, 148 CreateCaret function, 87-88
check boxes, 138, 140 parameters, 88
communications, 144-145
570 BORLAND C++ 3.0 PROGRAMMING, Second Edition

CreateCompatibleBitmap function, CWINS.LIB filer29


372, 488 CW_USEDEFAULT message, 16
CreateCompatibleDC function, 382, 411
CreateDC function, 279, 453 D
CreateDiscardableBitmap function, DAT files, 156
372, 411 Data
CreateFont function, 435-436 Data Interchange Format (DIF),
CreateFontIndirect function, 435-436 481
CreateHatchBrush function, 335 structures, 27
CreateIC function, 279 Symbol Link format, 481
CreateMapMode subroutine, 491-492 Tag Image File Format (TIFF), 482
CreateMetaFile function, 402-403 transfers from clipboard, 477-498
CreatePatternBrush function, 335-366 fypés, 20727
CreatePen function, 334 Data Interchange Format (DIF), trans-
CreateSolidBrush function, 335 ferring data, 481
CREATESTRUCT structure, 142 DDE.H header file, 514-515
CreateWindow function, 16-17, 118, DDEACK structure, 514-515
138, 141-144, 520 DDEADVISE message record, 516
CS.LIB file, 29 DDEADVISE structure, 514, 516
CS_HREDRAW message, 14 DDEDATA structure, 514, 516
CS_VREDRAW message, 14 DDEPOKE structure, 514, 517
CUR files, 156 DDE_DATA program, 508, 517-521
Cursor, 112-113 DDE_Data.C listing, 525-534
default, 121 DDE_Data.DEF listing, 534
hiding, 121-122 DDE_Main program, 508, 517-518,
hotspot, 181 521-525
operations in ChildWndProc pro- DDE_Main.C listing, 534-539
cedure, 119-121 DDE_Main.DEF listing, 539
positioning, 89-93 DDL files, 28-29, 156
resources, 179-180 DEF files, 10, 29-32
vs. caret, 86
DefWindowProc function, 25, 77,
Cursor (.CUR) resource image, 154 125-126
Cursor Editor, 152
DeleteAtom function, 513
Cursor images, 169, 179
DeleteDC function, 411
text, 177
DeleteMetaFile function, 406, 408
Custom fonts, 430-439
DeleteObject function, 335, 369,
Custom message box dialog boxes, 259
373, 436
Index 571

DestroyCaret function, 87 RGBQUAD structure, 377-379


Device context (DC), 277-285 transferring to clipboard, 480-481
accessing, 278-279 Devices
bitmaps, 382-383 colors vs. Windows 3.0, 314-315
handle (hDC), 277 font identifiers, 428
HP LaserJet information, 284 information context, 279
indexes, 280-283 selecting, 279-280
information, 280-285 Devices program, 280, 283
modifying, 422-423 Devices.C listing, 294-303
printers, 451, 453-456 Devices.DEF listing, 303
retrieving handle, 278-279 Devices.H listing, 303
saving and restoring metafile, 412 DEVICE DEFAULT _FONT character
VGA information, 284 set, 98
video device resolutions, 285 DEVICE DEFAULT_FONT font, 423
Device Context Handles, 43-44 Dialog Box editor, 187-207
Device drivers (DRV), 156 Alignment palette, 188
KEYBOARD.DRYV, 71 control types, 189
printers, 451-452 Controls menu, 201-204
Device-independent bitmaps (DIB), custom classes, 194
367, 375-385 dialog window types, 191-192
bit block transfers, 383-385 fonts, 193
bitmap image, 378-380 including menus, 193
BITMAPINFOHEADER structure, styles, 190, 192-193
376-377 Tools menu, 188-189
cleaning up, 385 Tools palette, 188, 194-196
color table, 377-378 Dialog boxes, 187
creation and usage, 380-382 abort, 461-462
device context (DC), 382-383 as resources, 155
dimension functions, 380 auto-radio buttons, 195-196
file header, 376 Bitmap controls, 200
global instance handle, 381 check boxes, 196
information and mapping child, 191
coordinates, 383 control elements, 194-207
loading, 381-382 custom classes, 194
OS/2 bitmaps, 380 custom dialog control, 201
raster operations codes, 384-385 displaying, 189
edit fields, 196
572 BORLAND C++ 3.0 PROGRAMMING, Second Edition

enhancing, 200 Double-clicking, 113-114


FileView program, 238-241 DPtoLP function, 411
fonts, 193 Dragging, 113
frames, 192, 200 DrawBitmap function, 381-382, 385, 387
grouping controls, 200 DrawCenBitmap function, 387
Icon controls, 200 Drawicon function, 411
iconic popup, 192 DrawMetafile subroutine, 490
initiating, 239-241 DrawText function, 24, 46, 50, 420-421
menus, 193, 207 DRDOS 6.0 vs. MSDOS 5.0, 8-9
message boxes, 259-262 Drop-down combo list boxes, 199
overlapped, 192 Duplicate Control dialog box, 201
popup, 191 Duplicate tool, 201
pushbuttons, 195 DWORD data type, 27
radio buttons, 195-196 Dynamic Data Exchange (DDE),
resources, 187 507-525
scrollbars, 199-200 application name, 508
shade controls, 200-201 atoms, 512-514
static text fields, 197 character strings, 512-514
styles, 190, 192-193 client, 508
text fields, 196-199 cold link conversions, 509-510
white frame static control, 200 DDEACK message record, 515
window types, 191-192 DDEADVISE message record, 516
Dialog Editor, 152 DDEDATA structure, 516
DialogBox function, 238 DDEPOKE structure, 517
DIAMOND bitmap image, 367 hot link communications, 510-511
DispatchMessage function, 18-20 item name, 508
Display server, 508
overwritten areas, 40 topic name, 508
updating, 40-43 tracking message traffic, 521-525
Dithered color, 314, 316-317 warm link communications,
DLG files, 156, 163, 187 511-512
DlgDirList function, 240-241 window messages, 515
DUE niles, 1572157213 Dynamic Link Libraries (DLL), 29, 157
DOS
applications retrieving keyboard E
events, 72
Edit menu, 174
file attributes, 240
Editable resources, 155-156
Index 573

Editor program, 89-92 EXE files, 10-11, 28-29, 156-157, 187,


Editor toolbar, 169 PAAIS,
Editor.C listing, 105-110 EXETYPE statement, 30
Editor.DEF listing, 110 Expanded memory manager, 6-8
EGA EXPORTS statement, 31
fonts, 428 Extended-ASCII characters, generat-
system color creation, 315 ing, 99-100
video card, 170-171 ExtTextOut function, 418-419
Ellipse function, 337, 340, 401, 408
Ellipses, 337 F
EmptyClipboard function, 479, 483, FAR data type, 27
494-495 | Figures, 333, 336-340
EM_GETHANDLE message, 193 circles, 337
EM_SETHANDLE message, 193 ellipses, 337
ENDDOC subfunction, 456, 459, 463 rectangles, 336-338
EndPaint function, 24-25, 43-44, 278, File Open dialog box, 239
403, 405 File Selection dialog box, 210
EndWindow function, 462 File Type dialog box, 209-210
Entry /exit code for C/C++ IDE File View program, 183
integrated compiler, 35 FileOpenDlgProc function, 239, 241
EnumClipboardFormats function, Files
484-485, 498 bitmap (BMP), 156, 370-371
EnumFonts function, 465-466 BWCC.LIB, 266
EnumFontTypes function, 466 BWCCSTYL.RW, 266
EnumTypeFaces function, 466 Coe
Erase tool, 176 CSsLIB729
Escape function, 453 CUR, 156
ABORTDOC subfunction, 463 CWINS' LIB 29
ENDDOC subfunction, 456, 459, DDE 156
463 DEF, 32
NEWFRAME subfunction, 456, displaying name of printing, 462
459, 464 DEG r1b6 3165, 137
NEXTBAND subfunction, 457-458 DEL O77 1675213
parameters, 455 DRV, 156
SETABORTPROC subfunction, 460 EXE (executable), 29, 156-157,
STARTDOC subfunction, 456 1577219
subfunctions, 454-456 FileView.RES, 183, 220, 225, 236
574. BORLAND C++ 3.0 PROGRAMMING, Second Edition

FNT, 156 FillRect function, 411


FON, 156, 427-428 FindAtom function, 513
Hy 156 FNT files, 156
IC@OP 156 FON files, 156, 427-428 .
IMPORT.LIB, 29 Font header information dialog box, 185
library, 29 Font Size information dialog box, 184
module definition, 29-31 Fonts, 95-100, 426-429
OB] 7157 aspect ratios, 437
PenDraw4.RES, 367 available for printer, 464-466
PRINTMAN.EXE, 452 bitmapped, 46, 424-425, 428
PRJ, 32-33 Borland Graphic Interface
RE 1567163, 208,215 (BGI), 425
RES, 297515671577. 16371877203; 213 CGA, 428
rwcdemo.res, 229 custom, 183-186, 430-439
WIN.INI, 172, 279-280 device identifiers, 428
Windows.INC, 280 dialog boxes, 193
WinHello, 28, 33 EGA, 428
WinHello.DEF, 28 families in Windows.H, 426-427
WinHello.MAK, 31-32 fixed-width, 83
WinHello.RC, 33 height, 47
WIN_INI.16, 173 history, 424-426
WIN_INI.256, 173 images, 169
FileTypeDlgProc function, 239 logical, 430-439
FileView program, 208-212, 235-241 Logical TWIPS mode, 437-439
About dialog box, 208-209 mapping modes, 437-438
accelerator keys, 224-225 matching, 436-437
dialog boxes, 211-212, 238-241 metrics, 46-48
File Selection dialog box, 210 OEM_FIXED_FONT, 83
File Type dialog box, 209-210 parameter settings, 437-439
header file, 232-233 raster, 46, 424
menu creation, 219-220 resources, 183-186, 427-429
string table, 230 stock, 423-424
FileView.C listing, 242-257 stroked Graphics Device Interface
FileView.DEF listing, 257-258 (GDD, 428
FileView.H header file, 209-212, 219 Symbol, 430
FileView.H listing, 258 system, 46
FileView.RES file, 183, 220, 225, 236 Triplex, 425
Index 575

typefaces and styles, 417 GetTextMetrics function, 46, 411, 435


VGA, 428 GetViewportOrg function, 289
Windows 3.0, 97-98, 426 GetWindowDC function, 278
Fonts.H listing, 445-446 GetWindowOrg function, 289
Fonts1 program, 437-439 GetWindowText function, 148
Fonts1.C listing, 440-445 GetWindowTextLength function, 148
Fonts1.DEF listing, 445 GetWindowWord function, 119-120, 142
Fonts1.DLG listing, 446-449 GlobalAddAtom function, 514
FrameRect function, 411 GlobalAlloc function, 479, 483
GlobalDeleteAtom function, 514, 522
G GlobalFindAtom function, 514
GetAtomName function, 513-514 GlobalGetAtomName function, 514
GetBitmapBits function, 373 GlobalLock function, 487-488
GetBitmapDimensions function, 380 GlobalReAlloc function, 479
GetCapture function, 125 GlobalUnlock function, 487-488
GetCaretBlinkTime function, 89 gotoxy function, 39
GetCaretPos function, 88 Graphics
GetClassWord function, 318 banding output, 457-459
GetClientRect procedure, 24-25 bitmapped brushes, 365-370,
GetClipboard Data function, 479, 372-375
487, 498 circles, 178
GetClipboardFormatName function, 498 colors, 170-172
GetDC function, 43-44, 47, 278 copying, 174
GetDeviceCaps function, 283, 313, 411, cutting, 174
432-433, 492 deleting, 174
GetKeyState function, 79-80 drawing within, 176
GetMapMode function, 288 editing, 169-173
GetMessage function, 18-19, 72 enlarging, 175
GetMetaFile function, 408 erasing, 176
GetNearestColor function, 321 figures, 333, 336-340
GetObject function, 373, 383, 489
filling contiguous areas, 177
freeform areas, 176-177
GetStockObject function, 83, 424
GetSysColor function, 423
irregular area within, 175
GetSystemMetrics function, 112 logical brushes, 335-336
GetTempFileName function, 406 logical pens, 334-335
GetTextColor function, 422
operations, 49
GetTextFace function, 435
polygons, 344-346
576 BORLAND C++ 3.0 PROGRAMMING, Second Edition

rectangles, 178 Paintbrush tool, 176


rectangular area within, 174-175 Pencil tool, 176
replicating, 174-175 Rectangles tools (Hollow or
resources, 154-155 Filled), 178
shapes, 333 Rounded Rectangles tools
storing images, 370-371 (Hollow or Filled), 178
straight lines, 176 Scissors tool, 175
text display elements, 44-46 Text tool, 177
tools, 333-336 toolbar, 174-179
typefaces and styles, 417 Zoom tool, 175
Graphics Device Interface (GDI), 275-294 Graphics windows and text, 45
bitmapped brushes, 365-370, Graphs, business, 341-344
372-375 GrayString function, 411
business graphs, 341-344 Group control tool, 196
color, 313-317 GROUPS icon, 4
device context (DC), 277-285 GROUPS utility, 3-4
figures, 333, 336-340
font information, 466 H
PUNO NSIS HANDLE handle identifier, 27
information indexes, 280-283 Handle identifiers, 27
library module, 452 HBITMAP array, 367
mapping modes, 286-292 HBRUSH handle identifier, 27
metafiles, 401-412 HDC handle identifier, 27
output devices, 277 Header files (.H), 10, 156, 208
polygons, 344-346 Resource Workshop, 231-233
printers, 453 resources, 157-158
remapping palettes, 320 Whitewater Resource Toolkit, 231
selecting devices, 279-280 Headroom utility, 9
shapes, 333 HEAPSIZE instruction, 31
stroked fonts, 428 HELLO program, 11-12
Graphics editor HideCaret function, 87-88, 91
Airbrush tool, 176-177 HIMEMSSYS file, 7
Arrow tool, 174-175 Hit testing, mouse, 122-123
Bucket tool, 177 HIWORD macro, 115
Circles tools (Hollow or Filled), 178 Horizontal line length, 46
Erase tool, 176 Hotkeys, 221-224
Line tool, 176 Hotspots, cursor, 181
Index 577

HP LaserJet device context (DC) Keyboards


information, 284 accelerators, 237-238
Hungarian notation, 25-26 context code, 74-75
HWND handle identifier, 27 current key state, 75
hWnd window handle, 13 drivers, 71-72
event messages, 72, 81, 83-85
I events, 20
Icon Editor, 152 extended keys, 74
Icon images, 169 international support, 71
text; 177 resources, 154
Icon resources (.ICO), 154, 156, 181-183 scan code, 74
Iconic popup dialog boxes, 192 shift-state information, 73
IDC_ARROW cursor image, 113 virtual key code, 73
IDM_ABOUT message, 238 KeyCodes program, 74-77, 81, 83-85
IDM_OPEN message, 239 KeyCodes.C listing, 101-105
IDM_TYPE message, 211 KeyCodes.DEF listing, 105
IDOK message, 209, 238 Keys
IDS_NAME constant, 236 identifying pressed, 74
Import Library utility, 5 virtual codes, 76
IMPORT.LIB file, 29 Keystrokes
IMPORTS statement, 31 converting message to character
INC source file, 10 messages, 82

InfoProc procedure, 265 number of, 74


Information context, 279 typematic rate, 74
Install program, 3
Instance handle, 12 L
Integrated compiler, 32-35 Library files (LIB), 29
International character sets, 95, 97 LineGraph function, 387
InvalidateRect function, 41-44, 61, 497 Linker, C/C++ IDE integrated com-
InvalidateRgn function, 41, 61 pilerncs
IsClipboardFormatAvailable function, Linker dialog box, 35
484 List boxes
IsRectEmpty function, 458 combo boxes, 198-199
custom, 198-199
K drop-down combo boxes, 199

Key event messages, 74-76


simple combo boxes, 199
text lists, 198-199
KEYBOARD.DRV driver, 71
578 BORLAND C++ 3.0 PROGRAMMING, Second Edition

Listings. See program listings MakeProclInstance function, 460, 465


LoadAccelerators function, 237 MANIFEST utility, 8
LoadBitmap function, 368, 381 MAP file extension, 29
LoadCursor function, 118, 120 Mapping modes, 286-292
LOGFONT structure, 430 default, 288
Logical brushes, 335-336 fonts, 437-438
Logical fonts, 430-439 getting, 288
boldface, 432 MM_ANISOTROPIC, 287-288,
characters, 431-433 291, 341, 405, 490
creation, 435-437 MM_HIENGLISH, 287, 290-291
font clipping, 434 MM_HIMETRIC, 287, 290-291
font matching, 433-434 MM_ISOTROPIC, 287-288, 291,
handle to, 436 342, 405, 490
italics, 432 MM_LOENGLISH, 287
pitch and family, 434-435 MM _LOMETRIC, 287, 380
strikeouts, 433 MM_TEXT, 286-287, 291
typeface names, 435 MM_TWIPS, 287, 290-291
underlines, 433 physical units of measurement,
Logical pens, 334-335 290-292
Logical TWIPS mode, 437-439 screen origin, 289
calculating point sizes, 438-439 setting, 288
LONG data type, 27 variable, 291
LOWORD macro, 57, 115 max macro, 51
lParam variable, 73-77, 81-82, 115, 124, MB_ICONSTOPFP flag, 261
144 MB_SYSTEMMODEL flag, 261
parameters, 74-75 Memory options for resources, 164
LPCREATESTRUCT macro, 142 Menu editor, 207, 213-219
LPSTR data type, 27, 85 Menus
LPtoDP function, 411 as resources, 155
dialog boxes, 207
M editing, 216
Makefiles (MAK), 11, 31-32 identifying options, 218
OBJ files from C source files, 32 including in dialog boxes, 193
RES files from RC files, 32 menu items, 217-219
MAKEINTRESOURCE macro, 369 new elements, 215
MAKEINTRESOURCKE(...) macro, 120 popup, 467
MAKEPOINT macro, 125 resources, 213-219
Index 579

scripts, 214 structures, 408-410


separator lines, 215 temporary files, 406
setting level of element, 215 transferring memory between
size limitations, 216-217 applications, 480
standards, 215 writing to disk, 405-407
Message box dialog boxes, 259-262 MFxxxx. TMP, 453
Borland Windows Custom Con- METAHEADER structure, 408
trol, 263-266 METARECORD structure, 408
command IDs, 261-262 METFILEPICT structure, 408
custom, 259 MF_POPUP, 467
types, 260-261 MFxxxx.TMP file, 453
Message handlers, 22-25 Mickey, 114-115
default, 21 Microsoft Resource Compiler, 156
MessageBox function, 259, 261 Microsoft Software Development Kit
Messages (SDK), 166
handling loop for WinMain pro- Microsoft Windows 3.0. See
cedure, 18-19 Windows 3.0
mouse, 113-115 min macro, 51
non-client window, 123-125 MM_ANISOTROPIC mapping mode,
organization, 21 287-288, 291, 341, 405, 490
programming, 20 MM_HIENGLISH mapping mode, 287,
record structure, 21 290-291
requesting action, 93-95 MM_HIMETRIC mapping mode, 287,
scrollbar, 55-59 290-291
sending to control buttons, MM_ISOTROPIC mapping mode,
145-147 287-288, 291, 342, 405, 490
to user, 259-266 MM_LOENGLISH mapping mode, 287
writing to screen, 11-25 MM_LOMETRIC mapping mode,
METAFILE structure, 480 287, 380
METAFILEPICT structure, 480 MM_TEXT mapping mode, 286-287,
Metafiles, 370-371, 401-412 291
accessing from disk, 407-408 MM_TWIPS mapping mode, 287,
cautions, 411-412 290-291
clipboard transfers, 490-493 Modes Dialog Script listing, 310-311
recording, 401-405 Modes program, 286, 292-293
saving and restoring device con- Modes Script Menu listing, 311
text (DC), 412 Modes.C listing, 303-309
580 BORLAND C++ 3.0 PROGRAMMING, Second Edition

Modes.DEF listing, 309-310 MsgBox_1.H listing, 268


Modes.H listing, 310 MsgBox_2 file, 263, 266
Module definition files, 29-31 TWC and, 265
Mouse, 111-126 MsgBox_2.C listing, 269-272
buttons, 85, 111-113, 115 MsgBox_2.H listing, 273
clicking, 113
cursor, 112-113, 117-122 N
double-clicking, 113-114 New Resource dialog box, 162-163
dragging, 113 NEWFRAME subfunction, 456, 459, 464
duplicated messages, 126 NEXTBAND subfunction, 457-458
events in Windows 3.0, 20, 116 Non-client window messages, 123-125
history, 111-112 Notation, 25-26
hit testing, 122-123 NOTEPAD.EXE text editor, 160
left vs. right buttons, 174 Null-terminated string (ASCIIZ)
messages, 113-115, 125-126 pointer, 13
movement, 125
non-client, 123-125
O
pointers, 179-180
OBJ files, 10, 28, 157
testing for, 112
OemToAnsi function, 99-100
tracking, 116-117
OemToAnsiBuff function, 99
types; Eile112
OEM_FIXED_ FONT character set, 83,
Mousel program, 115-117
95-97, 100
Mousel.C listing, 126-128
OpenClipboard function, 482, 495
Mousel.DEF listing, 128
OPTIMIZE utility, 6
Mouse2 program, 117-122, 138
Order tool, 203-204
Mouse2.C listing, 128-132
OS/2 bitmaps, 380
Mouse2.DEF listing, 132
Output devices, 277
Mouse3 program, 112, 122-123
Overlapped dialog boxes, 192
Mouse3.C listing, 132-135
Mouse3.DEF listing, 135
MoveBitmap function, 387 BP
MoveWindow function, 119, 138 Paintbrush tool, 176
MSDOS 5.0 vs. DRDOS 6.0, 8-9 PainText program, 50-51, 55-56, 61, 94
MSG data structure, 21, 27 PainText.C listing, 63-67
MsgBox_1 file, 262 PainText.DEF listing, 67
MsgBox_1.C listing, 266-268 PAINTSTRUCT data structure, 27, 41,
MsgBox_1.DEF listing, 268 43, 278, 497
Index 581

PainTxt2.C listing, 67-68 PenDrawé.C Alternate Version listing,


Parent window and application 416
windows, 17 PenDrawé.C listing, 413-415
PASCAL data type, 27 PenDrawé6.DEF listing, 415
PatBlt function, 383 PenDrawé6.H listing, 415
PeekMessage function, 19 PenDraw6.RC listing, 415
Pencil tool, 176 Pick tool, 201, 203-204
PenDraw Menu Resource listing, Pie function, 338, 340-341
331-332 PieGraph program, 340-344
PenDraw program, 322, 336 PieGraph.C listing, 357-360
PenDraw.C listing, 328-331 PieGraph.DEF listing, 360-361
PenDraw.DEFEF listing, 331 PieGraph.H listing, 361
PenDraw.H listing, 331 PlayMetaFile function, 411-412, 492
PenDraw2 program, 334, 340 POINT structure, 21, 125
PenDraw2.C listing, 348-352 Pointers, null-terminated string
PenDraw2.DEF listing, 352 (ASCIIZ) 7A3
PenDraw2.H listing, 352-353 Polygon function, 334, 344-345, 347
PenDraw2.RC Menu Script listing, Polygons, 344-346
353-354 fill modes, 345-346
PenDraw3 program, 345-348 PolyPolygon function, 334, 344-345, 347
PenDraw3.C listing, 361-363 Popup, 467
PenDraw3.DEF listing, 364 Popup dialog boxes, 191
PenDraw3.H listing, 364 PostDataMsg subroutine, 524
PenDraw4 program, 369 PostMessage function, 523-524
PenDraw4.C listing, 389-391 Predefined constants, 26
PenDraw4.DEF listing, 391 Print Manager and PRINTMAN.EXE
PenDraw4.H listing, 392 file, 452
PenDraw4.RC Application Menu list- Print program, 463-464
Ing, 392 Print.C listing, 467-472
PenDraw4.RES file, 367 Print.DEF listing, 472
PenDraw5 program, 385, 387-388 Print.DLG listing, 472
PenDraw5.C listing, 392-399 Print.H listing, 473
PenDraw5.DEF listing, 399 Print2.C listing, 473-475
PenDraw5.H listing, 400 Printers, 451-467
PenDraw5.RC Menu Structure listing, aborting, 459-463
400 advancing to new page, 456
PenDraw6 program, 405 available fonts, 464-466
582 BORLAND C++ 3.0 PROGRAMMING, Second Edition

banding graphics output, 457-459 Editor.C, 105-110


callback functions, 465-466 Editor.DEF, 110
device capabilities, 464-466 FileView.C, 242-257
device context (DC), 451, 453-456 FileView.DEF, 257-258
device driver library (.DRV), 452 FileView.H, 258
device drivers, 451-452 Fonts.H, 445-446
directly accessing, 456-457 Fonts1.C, 440-445
explicit banding, 457 Fonts1.DEF, 445
Graphics Device Interface (GDI), Fonts1.DLG, 446-449
453 KeyCodes.C, 101-105
initiating new document, 456 KeyCodes.DEF, 105
operations, 452-453, 467 Modes Dialog Script, 310-311
specific document abortion, 463 Modes Script Menu, 311
text output, 463-464 Modes.C, 303-309
TEXTMETRIC information, 464 Modes.DEF, 309-310
vs. video, 452 Modes.H, 310
PrinText program, 48 Mousel.C, 126-128
printf function, 39, 52 Mousel.DEF, 128
PRINTMAN.EXE file, 452 Mouse2.C, 128-132
PRJ file, 32-33 Mouse2.DEF, 132
Process ID, 12 Mouse3.C, 132-135
Program listings Mouse3.DEF, 135
BarGraph.C, 354-357 MsgBox_1.C, 266-268
BarGraph.DEF, 357 MsgBox_1.DEF, 268
ClipBd.C, 498-505 MsgBox_1.H, 268
ClipBd.DEF, 505 MsgBox_2.C, 269-272
ClipBd.H, 506 MsgBox_2.H, 273
ClipBd.RC Menu File, 506 PainText.C, 63-67
Colors.C, 323-328 PainText.DEF, 67
Colors.DEF, 328 PainTxt2.C, 67-68
DDE_Data.C, 525-534 PenDraw Menu Resource, 331-332
DDE_Data.DEF, 534 PenDraw.C, 328-331
DDE_Main.C, 534-539 PenDraw.DEF, 331
DDE_Main.DEF, 539 PenDraw.H, 331
Devices.C, 294-303 PenDraw2.C, 348-352
Devices.DEF, 303 PenDraw2.DEF, 352
Devices.H, 303 PenDraw2.H, 352-353
Index 583

PenDraw2.RC Menu Script, Programs. See also applications


353-354 BarGraph, 341-342, 344
PenDraw3.C, 361-363 Buttonl, 141, 143-145
PenDraw3.DEF, 364 Button2, 143-146
PenDraw3.H, 364 ClipBd, 482, 485-487, 490
PenDraw4.C, 389-391 ClipBrd.EXE, 477-478
PenDraw4.DEF, 391 Colors, 314, 317-322
PenDraw4.H, 392 compiling for Windows 3.0, 9-11
PenDraw4.RC Application DDE_DATA, 508, 517-521
Menu, 392 DDE_MAIN, 508, 517-518, 521-525
PenDraw6s.C, 392-399 defining messages for control
PenDraw5.DEF, 399 buttons, 145
PenDraw5.H, 400 Devices, 280, 283
PenDraw5.RC Menu Structure, Editor, 89-92
400 FileView, 183, 208-212, 219-220,
PenDraw6.C, 413-415 224-225, 230, 232-233, 235-241
PenDrawé6.C Alternate Version, 416 Fonts1, 437-439
PenDraw6.DEF, 415 HELE@ aid-12
PenDrawé6.H, 415 KeyCodes, 74-77, 81, 83-85
PenDraw6.RC, 415 Modes, 286, 292-293
PieGraph.C, 357-360 Mousel, 115-117
PieGraph.DEF, 360-361 Mouse2, 117-122, 138
PieGraph.H, 361 Mouse3, 112, 122-123
Print.C, 467-472 normal or minimized windows, 13
Print.DEF, 472 PainText, 50-51, 55-56, 61, 94
Print.DLG, 472 PenDraw, 322, 336
Print.H, 473 PenDraw?2, 334, 340
Print2.C, 473-475 PenDraw3, 345-348
SysMets.H, 68-69 PenDraw4, 369
WinHello.C, 36-37 PenDraw5, 385, 387-388
WinHello.DEF, 272 PenDraw6, 405
WinHello.DEF module definition PieChart, 340
file, 37-38 PieGraph, 341-344
Program Manager and C/C++ com- Print, 463-464
piler group, 3 PrinText, 48
Program resources, 149 Solitaire, 153
Programming, 20
584 BORLAND C++ 3.0 PROGRAMMING, Second Edition

WINHELLO, 11-19, 22-25, 28-30, Resource compiler, 149-150


43, 50, 59-60 resource files (RC), 11, 153, 156, 163,
WinMain, 81-82 208 92.13
WINSTUB.EXE, 30 fonts, 427-429
Project dialog box, 33 MsgBox_1, 262
PT data structure, 27 MsgBox_2, 263, 265-266
Pushbuttons, 138-139 types, 156
BS_DEFPUSHBUTTON, 139 Resource images, 154-155
BS: PUSHBOX,; 139 Resource menu, 163
BS_PUSHBUTTON, 138-140 Resource scripts, 150
dialog boxes, 195 Resource Workshop, 10-11, 149-153
simulating button flash, 146 Accelerator Editor, 152
Bitmap Editor, 152
bitmap images, 169
OQEMM.-386 version 6.0, 6-9 Central Control Program, 152
CONFIG.SYS file, 7-8 Cursor Editor, 152
video conflict, 6-8 cursor hotspot, 181
cursor images, 169
Dialog Editor, 152
R
editing, 169-173
Radio buttons, 138-140
editing /creating resource files,
BS_AUTORADIOBUTTON, 140-141
156
BS_RADIOBUTTON, 140
editor toolbar, 169
dialog boxes, 195-196
file types, 156
menu breaks, 218-219
font images, 169
setting or receiving state, 146
header files, 158, 208, 231-233
Raster fonts, 46, 424
Icon Editor, 152
Raster operations codes, 384-385
icon images, 169
RC resource compiler, 29
NOTEPAD.EXE text editor, 160
Real clipboard, 477, 478
preferences, 160-162
RECT data structure, 27, 42,59, 458
resources, 157-160, 163-166
Rectangle function, 337, 340-341
String Editor, 152
Rectangles, 178, 336-338
Undo levels, 160
Rectangles tools (Hollow or Filled), 178
viewing options, 167
RegisterClass function, 15-16
vs. Whitewater Resource Toolkit,
ReleaseDC function, 44, 278, 411
15i
RES Tiles; 11) 297.150-157, 163, 187 208,
window options, 168
215
Index 585

Resources SB_HORIZ scrollbar identifier, 61


accelerator, 221-224 SB_LINEDOWN message, 54, 56
applications, 153-168 SB_LINEUP message, 54, 56
compiling, 157 SB_PAGEDOWN message, 56, 94
components, 152 SB_PAGEUP message, 56, 94
cursor, 179-180 SB_THUMBPOSITION message, 55, 57
developing, 150-151 SB_THUMBTRACK message, 54-55, 57
dialog boxes, 155, 187 SB_TOP message, 55
editable vs. untouchable, 155-156 SB_VERT scrollbar identifier, 61
editing, 158-161, 163, 166-167 Scissors tool, 175
fonts, 183-186 Screen
header files, 157-158 coordinates converting to client-
icons, 181-183 window coordinates, 124-125
identifiers, 165-166 origins, 289
keyboard, 154 updating, 278
linking, 157 ScreenToClient function, 124
loading in WinMain, 236-238 Scrollbars, 54-59
managing, 158-160 dialog boxes, 199-200
memory options, 164 errors, 61-62
menus, 155, 213-219 generating messages, 94
new, 158, 162-163 handling messages, 55-59
saving and renaming, 163-164 minimum or maximum ranges, 60
string, 1007 227-230 ScrollWindow function, 40, 58-59
viewing options, 167 SDKPAINT program, 375
RestoreDC function, 412 SelectObject function, 334-335, 342,
RGB macro, 318 367, 383, 424, 435, 488-489
RGBQUAD structure, 377-379 SendDlgItemMessage function, 240
Ribbons.BMP, 169 SendMessage function, 91-95, 146, 240-
Right-justify character (\a), 217 241, 523-524
Rounded Rectangles tools (Hollow or ServerProc subprocedure, 519, 521
Filled), 178 Set Application Options dialog box,
RoundRect function, 337 33-34
Run dialog box, 13 Set Groups tool, 203
rwcdemo.res file, 229 Set hotspot dialog box, 181
SETABORTPROC subfunction, 460
S SetBitmapBits function, 372
SB_BOTTOM message, 55 SetBitmapDimensions function, 380
SB_ENDSCROLL message, 54
586 BORLAND C++ 3.0 PROGRAMMING, Second Edition

SetBkColor function, 422 STACKSIZE instruction, 31


SetBkMode function, 83-84, 422 STARTDOC subfunction, 453, 456
SetBrushOrg function, 320, 411 StretchBitmap function, 387
SetCaretBlinkTime function, 89 StretchBitmap2Client function, 387
SetCaretPos function, 87-88, 91, 93 StretchBlt function, 383, 385-387, 488
SetClassWord function, 318 String Editor, 152, 227-230
SetClipboardData function, 479, 495, 498 string table, 228-230
SetCursor function, 120-121 String resources, 227-230
SetDlgItemText function, 241 attributes, 230
SetMapMode function, 288, 383 defining, 227
SetMapperFlags function, 437 names, 229-230
SetPixel function, 116 restrictions and limitations, 229
SetScrollPos function, 60 string groupings, 229-230
SetScrollRange function, 60 Strings
SetStretchBltMode function, 386 as resources, 155
SetTextAlign function, 51, 417-419 null-terminated ANSI character,
SetTextColor function, 422 479-480
SETUP program, 71 OEM character set, 480
SetUp.INF instructions, 13 STRIPES bitmap image, 367
SetViewportExt function, 291-292 strtok function, 280
SetViewportOrg function, 289 STUB statement, 30
SetWindowExt function, 291-292 SVGA video card, 171-172
SetWindowOrg function, 289 testing colors, 172-173
SetWindowText function, 148 SW_SHOWMINNOACTIVE message,
Shapes, 333 Loar
Shift keys, querying, 79-80 SW_SHOWNORMAL message, 13
ShowCaret function, 87-88, 91 Symbol font, 430
ShowCursor function, 121-122 Symbol Link format, transferring
ShowKey subprocedure, 83-85 data, 481
ShowWindow function, 17-18, 462, 520 SysMets.H listing, 68-69
Size controls dialog box, 206 System
size dialog box, 204 caret, 87
SMARTDRV.SYS, 7 font, 46
Solitaire program, 153 keystroke messages, 72-73
sprintf function, 53 System menu handle, 467
SP_OUTOFDISK error code, 459 SYSTEM_FIXED_FONT character set,
SPLxxxx.
TMP file, 453
89, 95-97, 100
SYSTEM_FONT character set, 97
Index 587

T TextOut function, 52-53, 84, 277, 417-419


Tab character (\t), 217 TextToClipboard subroutine, 486

Tab Stop tool, 201-202 TLINK command-line, 28-29


parameters, 29
TabbedTextOut function, 419-420
Tabstops, 201-202 Toggle keys, querying, 79-80
tolower function, 98
Tag Image File Format (TIFF), trans-
Tool palette, 194-196
ferring data, 482
Task Ds? auto-radio buttons, 195-196

Text check boxes, 196

bitmap, icon, or cursor images,


Group control tool, 196
IE
pushbuttons, 195
radio buttons, 195-196
caret vs. cursor, 86
Tools menu, 188-189
color, 422
Tools palette, 188
device context (DC) mod-
ifications, 422-423
toupper function, 98
Track-ball pointers, 112
drawing, 422
TransferToClipBD function, 482-483,
formatted, 52, 420-421
489, 491
horizontal line length, 46
TranslateAccelerator function, 238
input handling, 85-89
TranslateMessage procedure, 18-20, 82
line numbers for displaying, 51
TRANSPARENT instruction, 84
operations on clipboard, 486-488
Trident graphics processor SVGA
output, 417-423
Cancels
positioning, 44-45
Triplex font, 425
printer output, 463-464
Turbo C++ (TCW) compiler, vs. Bor-
scrolling, 45
land C++ (BC) compiler, 5-6
size, 45
Turbo Debugger for Windows, 5
tabbed output string, 419-420
Turbo Profiler for Windows, 5
vertical line spacing, 46
TWC and MsgBox_2.RES, 265
window limits, 45
Typefaces, 426-429
windowing output, 50-52
history, 424-426
writing character string, 419
styles, 417
Text applications transporting to
Windows, 39-62
Text tool, 177
U
TEXTMETRIC data structure, 47-48, Units of measure
50, 464 drawing, 287
parameters, 47-48 mapping modes, 290-292
588 BORLAND C++ 3.0 PROGRAMMING, Second Edition

unlink function, 407 header files, 231


UnrealizeObject function, 319-320 vs. Resource Workshop, 151
Untouchable resources, 155-156 WH_MBUTTONxxxx message, 114
UpdateWindow procedure, 17-18, 40, 59 WH_RBUTTONxxxx message, 114
Uppercase characters, 47 WIN.INI file, 172, 279-280
WINDING fill mode, 346
V Windows
Vertical line spacing, 46 application, 16-18
VGA, 170-171 classes, 13-16

color creation, 315


invalidated areas, 41-43

dithered color, 316 labels, 148

fonts, 428 message, 72-73


graphics card device context (DC) normal or minimized for pro-
information, 284 gram, 13
testing colors, 172-173 origins, 289
Video redrawing errors, 61-62

device resolutions, 285 Resource Workshop options, 168


vs. printers, 452 scrollbars, 54-59
View menu, 167, 175 sizing/resizing, 59-61, 291-292

Virtual key codes, 76-79 text output, 50-52


Windows.H header file, 77-79 various mapping modes, 292-293
Virtual keys Windows 2.0, transferring bitmaps, 480
current state, 79 Windows 3.0
vs. ASCII keys, 222 bitwise boolean operation, 321
VK_DELETE message, 90-91 clipboard data transfers, 477-498
VK_LBUTTON virtual key code, 80 colors vs. device colors, 314-315
VK_MBUTTON virtual key code, 80 compiling programs for, 9-11
VK_RBUTTON virtual key code, 80 default system font, 46
Vk_xxxx definitions, 77 designing applications, 39-40
dithered color, 316-317
WwW figures, 339
fonts, 46-48, 97-98, 426
WaitMessage function, 19
GDI library module, 452
wc window class, 13
installing Borland C/C++ 3.0, 3-11
WHITEONBLACK mode, 387
keyboard events, 72
Whitewater Resource Toolkit (WRT),
mouse events, 116
10-11, 166
predefined bitmaps, 368-369
cursor hotspot, 181
Index 589

Print Manager (PRINT- WinHello.DEF file, 28


MAN.EXE), 452 WinHello.DEF listing, 272
printer, 451-452 WinHello.DEF module definition file
QEMM.-386 version 6.0, 6-8 listing, 37-38
raster operation, 321 WinHello.MAK file, 31-32
stock fonts, 423-424 WinHello.MAK make file listing, 38
system keystroke messages, 72-73 WinHello.RC file, 33
transferring device-independent WinMain procedure, 12-16, 20, 22, 81-
bitmaps to clipboard, 480-481 82, 117-118, 142, 367, 381, 403, 518, 520
transporting text applications to, application windows, 16-18
39-62 initializing settings, 237
writing messages to screen, 11-25 loading keyboard accelerator, 237
Windows.H header file, 13, 21, 27, 41- loading resources, 236-238
APRS, OT ZO 2 097 S217335;1376 message handling loop, 18-19
boldface fonts, 432 parameters, 12-13
character sets, 433 registering window classes, 14-16
clipboard data formats, 479 WinProc procedure, 41, 48, 50, 55
control button messages, 145 Winsight, 5
Escape function subfunctions, 455 WINSTUB.EXE program, 30
fonts, 426-427, 434-435 WIN_INI.16 file, 173
identifying resources, 157-158 WINGINT256 tile, 175
LOGFONT structure, 430 WM...DBLCLK message, 114
logical font precision, 433 WM...DOWN message, 114
LOWARD and HIWORD macros, WM...UP message, 114
115 WMF file extension, 405
mouse messages, 113-114 WM_...CHAR message, 85
stock logical fonts, 423-424 WM._...KEYDOWN message, 85
virtual key codes, 77-79 WM_...KEYUP message, 85
virtual key definitions, 222 WM_ASKCBFORMATNAME mess-
wc window class structure, 13 age, 496
Windows.INC file, 280 WM_CHAR message, 72-75, 77, 81-83,
WindowsMAKER, 166 91-93, 98-100
WinHello file, 28, 33 WM_CHARDOWN message, 75
WINHELLO program, 11-30, 43, 50, WM_COMMAND message, 138, 141,
59-60 238-239
WndProc procedure, 22-25 WM_CREATE message, 48, 60, 83, 87,
WinHello.C listing, 36-37 118, 141, 402, 406, 467
590 BORLAND C++ 3.0 PROGRAMMING, Second Edition

WM_CTrLCOLOR message, 319-320 WM_LBUTTONxxxx message, 114


WM_DDE_ACK message, 509-512, 515- WM_MBUTTONDBLCLK message, 126
516, 522-524 WM_MOUSEACTIVATE message, 125
WM_DDE_ADVISE message, 510-511, WM_MOUSEFIRST message, 126
515, 522-524 WwW M_MOUSELAST message, 126
WM_DDE_DATA message, 510-512, WwW M_MOUSEMOVE message, 114-117,
515-516, 524 119-120, 124, 126
WM_DDE_EXECUTE message, 514 W M_NCHITTEST message, 125
WM_DDE_INITIATE message, 509- WwW M_NCMOUSEMOVE message, 124
511 524 WwW M_PAINT message, 19, 22-25, 40-44,
WM_DDE_POKE message, 514-515, 50, 59, 61, 83, 122, 278, 402-403, 487
517 WwW M_PAINTCLIPBOARD message,
WM_DDE_REQUEST message, 509- 496-497
5104592 5157522 WM_QUIT message, 18, 19, 112
WM_DDE_TERMINATE message, 509- WM_RENDERALLFORMATS mess-
STO) Se age, 495-496
WM_DDE_UNADVISE message, 511- WM_RENDERFORMAT message, 495-
SUA oa lias 496
WM_DEADCHAR message, 72, 82 WM_SETFOCUS message, 87, 119
WM_DESTROY message, 22-23, 25, 87, WM_SIZE message, 59-60, 119
320, 406 WM_SIZECLIPBOARD message, 496-
WM_DESTROYCLIPBOARD message, 497
495, 498 WM_SYS message, 81
W M_ENTERIDLE message, 193 WM_SYS-DEADCHAR message, 72
W M_HSCROLL message, 55 WM_SYSCHAR message, 72, 74-75, 82
WwW M_HSCROLLBAR message, 59 WM_SYSCHARDOWN message, 75
WM_HSCROLLCLIPBOARD message, WM_SYSDEADCHAR message, 82
496-497 WM_SYSKEYDOWN message, 72, 74-
WM_INITDIALOG message, 239 Thay ae:
WM_INITDIALOG message, 239 WM_SYSKEYUP message, 72, 74-75, 82
WM_KEYDOWN message, 72, 74-76, WM_SYSxxxx event messages, 85
81-82, 85, 89, 91, 93-94 WM_TIMER message, 19
WM_KEYUP message, 72, 74-77, 81-82, WM_USER identifier, 145
85 WM_VSCROLL message, 55-56
WM_KILLFOCUS message, 87, 119 WM_VSCROLLBAR message, 58
WM_LBUTTONDOWN message, 116, WM_VSCROLLCLIPBOARD message,
1212122 496-497
Index og

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:

©) C++ programming for Windows, Tat}


€=\i[ale =e)
r=laleM On crarGm OMe-laem (iaa(--lale
_ Clock operations

e The Resource Workshop, an interactive tool that allows users to write


oe Yo]0] cex~) code by Msuaty folie lallate) the application

e Graphics programming for Windows, areal te tate a guide to colors in


; oun: combiaing text with graphics, and printer input and output

ee cee oo Tools i sharing _—. including the Windows clipboard, Dynamic


Poe was Data Exctigiay and© Turbo Debugger

econd Edition offers everything Welemal-\10| teknow

Addison-Wesley P

You might also like