[ ERCB Home |
New |
Feature |
Brief |
DDJ |
Letters |
Links
]
Ten Pounds of Windows Books
Review by Andrew Schulman
Copyright (C) Dr. Dobb's Journal, July, 1991
This month we will look at four new books on Microsoft Windows 3.0. When
I stepped on the bathroom scale holding these books and subtracted my own
weight, they turned out to weigh ten pounds. I guess I'm not the only one
who needs to go on a diet.
For the vast majority of programmers, graphics programming is, for better
or worse, going to increasing entail mastering -- not VGA palette registers
or Bresenham's line-drawing algorithm -- but the ins-and-outs of Application
Program Interfaces (APIs) such as the Macintosh Toolbox, Xt Intrinsics,
or the Microsoft Windows API. What I'll emphasize here is what these books
can teach us about the possibility of simplified programming for the notoriously
complex Windows API.
Programming Windows
The definitive book on Windows programming is, of course, Charles Petzold's
Programming Windows. Microsoft labels this "the authorized edition."
I've also heard Charles's book called "thunk," not because of
the essential discussion of "reload thunks" that appears on pp.
282-4, but because thunk is the sound this massive book makes if you drop
it on your foot!
In addition to Petzold's wonderfully smooth writing style (of which I am
completely jealous) and his ability to take even some of the more twisted
aspects of the Windows API and make them sound almost "programmer-friendly,"
I like this book for the large number of buried treasures it contains. While
Petzold lets you know that Windows programming is hard to do -- and that
this difficulty is good for you -- the book also contains many ingenious
ideas that irresistibly appeal to the lazy, good-for-nothing bum in all
of us. For example:
"Perhaps the epitome of lazy programming is the HEXCALC
program .... This program doesn't call CreateWindow at all, never processes
WM_PAINT messages, never obtains a device context, and never processes mouse
messages. Yet it manages to incorporate a ten-function hexadecimal calculator
with a full keyboard and mouse interface in fewer than 150 lines of source
code" (p. 479).
What a great idea!
Likewise, after a 40-page examination of Windows memory management, Charles
presents a two-paragraph note titled "If You Know You're Running in
Protected Mode" (p. 302). This is an amazing note because it says,
in essence, that you can ignore most of the preceeding 40 pages.
It turns out that, by punting Windows Real mode, you can vastly simplify
your memory management: "When allocating moveable global memory, you
can lock the memory block immediately to obtain the pointer. Even though
the block is locked, Windows can still move it in memory -- and the pointer
will remain valid. You don't even have to save the global memory handle."
Because Windows 3 Real mode is a complete joke which hardly anyone uses,
and which probably ought to have been left out of the product in the first
place, you lose nothing by developing only for the Windows protected (Standard
and Enhanced) modes. What you gain is some sanity. You can use the RCT switch
to mark a Windows application as "protected mode only."
Another gem in Petzold's book is the RANDRECT program on pp. 598-604, which
shows how to use the PeekMessage() function instead of the more typical
GetMessage(). PeekMessage() is the key, not only to using Windows "dead
time," but also to simulating a typical DOS application-driven approach
to programming on top of Windows's event-driven architecture.
I am particularly fond of two small sections of this great book. Like many
programmers, I was completely hopeless with Windows programming, and despaired
of ever getting the hang of it, until I read "The Message Box"
(pp. 439-441) and the MFCREATE.C program (p. 640). These two small sections
are the key to truly simple Windows programming in which, for example, "hello
world" takes five lines of code, not the 80 lines you're hit with in
Chapter 1 of this book.
Basically, MFCREATE.C shows that Windows programs don't require a window,
a window class, a window procedure, a message loop, or any of the other
Windows paraphernalia that makes the API so daunting to a newcomer who just
wants to display three lines of output on the screen. In fact, Petzold notes
that MFCREATE.C, buried on p. 640, is the shortest program in the book.
It happens to create a disk-based metafile, which may not be what you're
interested in, but the key point is that this little program may open your
eyes to a new way of writing simple Windows utilities, or of just plain
getting started with Windows programming.
That brings us to discussion of the Windows MessageBox() function on pp.
439-441. If, as MFCREATE.C shows, there's no rule stating that WinMain()
must register a window class, create a window, install a window procedure,
and start a message loop, then all we need to write "hello world!",
for example, is a call to the handy Windows MessageBox() function.
Microsoft's documentation states that the first parameter to MessageBox(
) must be a window handle (an HWND) that identifies the window that owns
the message box, so you might think that you still have to go through the
Windows rigamarole just to create a window that can "own" the
message box. However, Petzold shows that "if you don't have a window
handle available," you can use NULL for the handle. When you're new
to Window programming, or when you just want to write a simple utility for
use by your coworkers, the basic problem is precisely that you don't have
a window handle available, and Charles's NULL trick lets you avoid writing
the standard 80 lines of Windows boilerplate code in order to get one.
Example 1 shows the simple code you can write by applying some of the ideas
buried in Petzold's book. This program displays information on what the
Windows Program Manager's About box likes to call "System Resources."
No one knows what these system resources are, but at least we know what
percentage of them is free! I had read on BIX that "System Resources"
are in fact nothing more than the local heaps of the USER and GDI modules,
and that Program Manager gets this information with an undocumented Windows
call, GetHeapSpaces(). I wanted to write a simple test program to see if
this was true.
The resulting program, SYSTRES.C, is built around the OkMsgBox() function
from p. 441 of Petzold's book. I made one change to this function: Rather
than call vsprintf(), it calls wvsprintf(), which is declared in WINDOWS.H
and built into Windows. This partially explains why the resulting executable,
SYSTRES.EXE, is less than 3 Kbytes, much smaller than the typical character
mode DOS utility, in a program which is a lot nicer to look at.
To see if the GetHeapSpaces() information really is equivalent to the numbers
reported by Program Manager, we of course need to pull down the Program
Manager Help menu and look at the About box. But there's really no reason
why the program can't do this for us, so SYSTRES.C also contains a function,
progmgr_aboutbox(), which will do just that. It first sees if Program Manager
is already running; if it's not, it uses WinExec() to launch it. It then
sends Program Manager a message -- the same message that gets sent if you
manually click on the About ... menu item. It's amazing that we can perform
this type of interprocess communication with so few lines of code; the ability
to post messages from one program to another shows, by the way, that the
message-based architecture of Windows that Petzold describes is not just
arbitrary object-oriented terminology, but a true description of how Windows
works.
GetHeapSpaces() does turn out to provide the same numbers for System Resources
as Program Manager. The discrepancy of a percentage point or two seems to
be due to differences in how the two programs do integer-based percentages.
Incidentally, Windows programmers will soon not have to rely on this undocumented
function. A forthcoming Microsoft library called Toolhelp, to be provided
as part of its "Open Tools" strategy, will provide the GDI-HeapInfo()
and UserHeapInfo() functions, along with many other much-needed additions
to the Windows API. These functions will work in Windows 3.0, and will also
be part of the retail release of Windows 3.1. It may seem as if we've strayed
far afield from Petzold's Programming Windows. The point is simply
that this book has so much useful information and so many good ideas, that
you can even use the book to adapt a style of Windows programming with which
Petzold would probably disagree. This is some book. Thunk! Ouch!
Windows 3.0 for BASIC Programmers
Our next book has the unlikely title Windows 3.0 for BASIC Programmers.
If you are not a Basic programmer, and even if you are one of the many programmers
who despise this language, please keep reading anyway. The title of this
book is quite misleading. Let me quote the book's introduction:
This book will teach even the most inexperienced programmer
how to write Windows applications. It is unlike any other Windows book available
today. You don't need to learn hundreds of complex function calls, new styles
of programming, or the C language. Rather, through the use of a powerful
development package called Realizer Limited, included with this book, you
will learn how to create sophisticated Windows programs, quickly and painlessly.
If you can write a Basic program, you will be able to write a Windows program.
It's that simple.
This sounds like a piece of marketing literature but, oddly enough, it's
all true. For the price of a book, you get a "limited" version
of Within Technologies' Realizer Basic development environment for Windows.
This is an incredible bargain! You really will be able to write simple Windows
applications, and learn a lot about Windows, with this book/disk package.
Hyman had the excellent idea of trying to teach, not only how to use Realizer,
but also how Windows works. Several chapters contain "Behind the Scenes"
sections. In a few places these are somewhat confused (for example, the
explanation of Windows memory management), but the basic idea works well.
The key point is the presentation of a "higher level" on top of
the Windows API. For example, where Petzold's book has the reader sweat
out 40 pages on Dynamic Data Exchange (DDE), which end in the now somewhat
famous statement "The only advice I can offer here is simply to do
the best you can," Hyman's book and the Realizer software, like several
other Basics for Windows, simply provides a higher-level DDE interface.
Likewise, where most Windows programming books have to devote pages and
pages to reinventing the File Open ... dialog box found in every sizeable
Windows application (see Petzold, pp. 447-457, for example), the Realizer
software simply provides all the standard dialog boxes, built right into
the language. Interestingly, Microsoft is finally coming around to the novel
idea of building high-level interfaces. By the time Windows 3.1 is released,
developers will have two new dynamic link libraries (DLLs): one called COMMDLG,
which provides all the standard dialog boxes, and another called DDEML,
which provides a high-level DDE interface which looks surprisingly like
that found in WordBasic, the Basic development environment embedded in Microsoft
Word for Windows.
The demo programs that come with Hyman's book are impressive: two different
charting programs (Realizer specializes in forms, charts, and data analysis),
Blackjack and Poker simulations, a DDE "stock watcher" that communicates
with Excel, and so on.
Example 2 shows what Basic for Windows looks like. This is the SYSTRES utility
again, this time written with Realizer. Note that the Microsoft Software
Development Kit (SDK) is not needed for this program -- just Windows itself
and a $29.95 book/disk package. This is an interpreted, not a compiled program,
so we need some way to link to the Windows API routines. There's no WINDOWS.H
here; instead, Realizer lets you dynamic link at runtime to anything in
a DLL. That is the purpose of the EXTERNAL FUNC and EXTERNAL PROC statements
at the top of Example 2.
This brings out an important point about Windows: If we are linking to these
functions from a Basic interpreter, with the Windows SDK nowhere in sight,
then all the Windows API routines must be part of Windows itself, and not
something that comes with the SDK. All the functionality is built into every
copy of Window that someone buys off the shelf at Egghead; the SDK brings
remarkably little to the party.
Actually, I did have to refer to WINDOWS.H once in coding up this example,
because I needed to know the "magic number" for the WM_COMMAND
message. I got the HELP_ABOUT magic number by watching the behavior of Program
Manager with Microsoft's SPY utility, and by examining PROGMAN.EXE with
the excruciatingly slow but nonetheless useful Whitewater Resource Toolkit
(WRT) included with Borland C++ (WRT was apparently built with the Actor
language, and its poor performance is about the worst possible form of advertising
for Actor one could imagine; this is a shame, because WRT would otherwise
be addictive). In both cases, I had to convert hexadecimal numbers to base
ten because, incredibly, Realizer does not accept hex numbers.
One final point about the Realizer program in Example 2: Note the SHELL
statement which is used to launch Program Manager (if it isn't already running).
In Basic for Windows (and remember, there are several other such systems
besides Realizer), SHELL is built on top of the Windows WinExec( ) function
and is, therefore, fully asynchronous. The program that issues the SHELL
continues to run, even while the program it launched runs. This even happens
for DOS (non-Windows) applications in Enhanced mode, if the Background bit
is set in the DOS program's Settings. This is one of several nice multitasking
changes that Basic (and other languages) undergo under Windows. I generally
prefer Microsoft's WordBasic to Realizer; WordBasic has a more intuitive
syntax. However, Realizer and Hyman's book try to do several things that
WordBasic avoids. For one thing, dynamic linking works better with the Realizer
EXTERNAL statement than with the WordBasic DECLARE statement, because Realizer
distinguishes between the WORD/DWORD types and the INTEGER/LONG types: WordBasic
insists on using signed arithmetic for everything (this makes it difficult
to extract the low word and high word from the GetHeapSpaces( ) return value,
for example).
Realizer also allows you to write event-driven programs and DDE servers
(not just DDE clients). The designers of Realizer thought through the implications
of this for the built-in Basic debugger: A menu selection "Full Stop"
is sometimes necessary when your program is continuing to be bombarded with
DDE messages, even though you think you've halted it in the debugger. This
is one of several interesting "Concurrent Basic" issues.
Hyman's chapter on event-driven programming is quite interesting: "Realizer
hides most events from the programmer. Realizer programs appear to be state-based,
or linear .... Behind the scenes, as each Realizer line is run, Realizer
handles a variety of messages and events .... There are some situations
in which the event-driven nature shows through. For example, Realizer programs
can have their own menus. The user can select a menu item from these menus
at any time. This causes an event that the Realizer program needs to handle.
Likewise for modeless forms" (p. 188).
Even if you haven't the slightest interest in Basic, definitely check out
Hyman's book if you are at all interested in Windows programming. At the
very least, you will get a taste of the sort of development environments
and languages that can be built on top of Windows, and of the great flexibility
which Windows is capable of in the hands of thoughtful third-party developers.
Windows 3: A Developer's Guide
Jeffrey Richter's Windows 3: A Developer's Guide is the first book
to appear on advanced Windows programming. That is, it assumes you've read
Petzold's book, and makes no attempt to teach introductory Windows programming.
The book has eight lengthy chapters on various aspects of Windows that haven't
been properly covered elsewhere. For example, there is an entire 70-page
chapter titled "Installing Commercial Applications," an 80-page
chapter on "Setting Up Printers," and a 70-page chapter on "Subclassing
and Superclassing Windows."
Two chapters present excellent indepth examinations of Windows internals:
the opening chapter "Anatomy of a Window," and the chapter on
"Tasks, Queues, and Hooks." Together, these provide a coherent
explanation of the interrelationships of a program's Instance handle, Task
handle, Module handle, PSP, and so on.
I found many useful tips and suggestions here. For example, even the tiny
SYSTRES program shown in Examples 1 and 2 try to find Program Manager's
window handle by passing a class name, not a window title, to FindWindow(
). This comes directly from p. 80 of Richter's book. I've also been using
the VOYEUR utility that comes with the book.
Speaking of utilities, one of the most striking things about Richter's book
is the install program for the accompanying disks. It's simply a standard
Windows install program (which he describes in chapter 8); nothing very
exciting. But if you think about it, here is a $39.95 book, and the accompanying
disks have a better, more professional, install program than most commercial
character-mode DOS applications. This is typical of the Windows marketplace:
Windows raises the level of quality for software, even for the sample programs
that accompany computer books.
Windows 3 Power Tools
Okay, so Windows 3 Power Tools isn't a book for programmers, and
we all know how bad "user" books can be. (You should see the junk
stacked up in my basement: Using Fastback, Learning Fastback,
and An Advanced User's Guide to Installing Fastback are a few of
the titles that come to mind.)
But this really is a pretty good book on the intricacies of using and setting
up Windows. Setting up Windows so that it coexists properly with, say, a
network and an expanded memory manager, makes programming the Windows API
look like child's play. This book has good advice for such seemingly intractable
problems as getting QEMM 5.11 and Windows 3.0 Enhanced mode to coexist on
a Compaq computer. On the other hand, I saw nothing about the "out
of environment space" problem for the DOS box, nothing on the "instancing"
problems you can get when you run TSRs (such as the CED command-line editor)
before running multiple virtual machines in Windows Enhanced mode, and no
mention of the various bugs that plague the Windows 3.0 DOS box. This is
simply a decent user's book, by no means a great one.
The disks are the real reason to get this book/disk package. The centerpiece
is a graphical scripting language for Windows called "Oriel;"
ORIEL.EXE itself is only 18K, but it provides access to most of the Windows
graphical device interface (GDI) via an extremely reasonable-looking graphical
batch language. The output goes to a resizable, "persistent" window
(that is, Oriel takes care of repairing the window whenever it receives
a WM_PAINT message). A number of demo scripts show off the language's capabilities;
as an example of what the script language looks like, see the "hello
world!" program shown in Example 3 (page 144). Oriel stands as a further
example of how Windows really can be made easier for programmers as well
as for users.
Another programmable utility that comes on the disks is Morrie Wilson's
CommandPost. This is a replacement for Program Manager. Visually, it resembles
the old MS-DOS Executive program for Windows; the difference is the comprehensive
scripting language, CommandPost Menu Language (CPML), that lets you customize
menus, manipulate windows, create dialogs, run programs, manage files and
directories, and so on. CommandPost could use a spiffier interface, now
that Program Manager is available, but it's hard to complain since so much
of CommandPost is changeable via the script language anyhow. Just like Richter's
book, Windows 3 Power Tools comes with an install program that would
put most commercial character-based DOS software to shame. Little details
like these convince me that Windows is going to succeed.
Example 1: A simple Windows program, SYSTRES.C, consists of little more
than the OkMsgBox( ) function from Petzold's Programming Windows.
/* SYSTRES.C -- System Resources
Borland C++:
bcc -W systres.c
rc systres.exe */
#include
/* undocumented Windows call: KERNEL.138 */
extern DWORD FAR PASCAL GetHeapSpaces (WORD hModule);
void heap_info (char *module, WORD *pfree, WORD *ptotal, WORD *ppercent)
{
DWORD info = GetHeapSpaces (GetModuleHandle(module));
*pfree = LOWORD (info);
*ptotal = HIWORD (info);
*ppercent = (WORD) ((((DWORD) *pfree) * 100L) / ((DWORD) *ptotal));
}
#define PROGMAN_EXE "progman.exe"
#define PROGMAN_CLASS "Progman"
#define HELP_ABOUT 0x387 //subject to change!!!
//run the Program Manager Help menu About... box
void progmgr_aboutbox(void)
{
WORD progmgr;
// use class name ("Progman"), not window title ("Program Manager"):
// see Richter, Windows 3: A Developer's Guide, p. 80
if (! (progmgr = FindWindow(PROGMAN_CLASS, 0)))
{
// WinExec () is async: equivalent of spawn () P_NOWAIT
WinExec (PROGMAN_EXE, SW_SHOWMINIMIZED);
if (! (progmgr = FindWindow (PROGMAN_CLASS, 0)))
return; // couldn't find or load Program Manager
}
// use PostMessage instead of SendMessage so we run async
PostMessage (progmgr, WM_COMMAND, HELP_ABOUT, 0L);
}
// from Petzold, Programming Windows, p. 441
void OkMsgBox (char *szCaption, char *szFormat, ...)
{
char szBuffer [256] ;
char *pArguments ;
pArguments = (char *) &szFormat + sizeof szFormat ;
wvsprintf (szBuffer, szFormat, pArguments) ; // changed from
vsprintf
MessageBox (NULL, szBuffer, szCaption, MB_OK) ;
}
int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance, LPSTR
lpszCmdLine, int nCmdShow)
{
WORD user free, user_total, user_percent;
WORD gdi_free, gdi_total, gdi_percent;
heap_info ("USER", &user_free, &user_total, &user_percent);
heap_info ("GDI", &gdi_free, &gdi_total, &gdi_percent);
progmgr_aboutbox ();
OkMsgBox ("System Resources",
"USER heap: %u bytes free out of %u (%u%% free)\n"
"GDI heap: %u bytes free out of %u (%u%% free)\n"
"Free system resources: %u%%\n",
user_free, user_total, user_percent,
gdi_free, gdi_total, gdi_percent,
min(user_percent, gdi_percent));
}
Example 2: The same utility as in Example 1, but this time written with
the Realizer Basic development environment for Windows.
' SYSTRES.RLZ -- System Resources
' run-time dynamic linking to Windows API
external "kernel" func GetHeapSpaces (word) as dword
external "kernel" func GetModuleHandle (pointer) as word
external "user" func FindWindow (pointer, pointer) as word
external "user" proc PostMessage (word, word, word, dword)
func get_free (modname)
heap_space = GetHeapSpaces (GetModuleHandle (modname))
free = heap_space mod 65536 ' LOWORD
total = Floor (heap_space / 65536) ' HIWORD
percent = 100 * free / total
Print #1; modname, "heap: ", free, " bytes free out of ", total;
Print #1; " (", percent, "%)"
return percent
end func
WM_COMMAND = 273 ' 111h (yuk! no hex!)
HELP_ABOUT = 903 ' 387h
' pull down Program Manager Help About... box
progmgr = FindWindow ("Progman", Pointer (0))
if progmgr = 0 then
Shell "progman", _Minimize
progmgr = FindWindow ("Program", Pointer (0))
end if
PostMessage (progmgr, WM_COMMAND, HELP_ABOUT, 0)
LogNew (1; "System Resources")
user_percent = get_free ("USER")
gdi_percent = get_free ("GDI")
Print #1; "System Resources: ", min (user_percent, gdi_percent), "% free"
LogControl (_Show)
Example 3: "hello world!" using the Oriel graphical batch
language.
{ HELLO.ORL -- "hello world" for Oriel }
UseCaption("Hello")
SetMenu("&File", IGNORE, "E&xit", Do_Exit, ENDPOPUP)
UseFont("Roman", 0, 24, BOLD, NOITALIC, NOUNDERLINE, 64, 0, 0)
DrawText(10, 10, "Hello world!")
WaitInput()
Do_Exit:
End
Programming Windows, Second Edition
Charles Petzold
Redmond, Washington: Microsoft Press, 1990
944 pages, $29.95
ISBN 1-55615-244-7
Windows 3: A Developer's Guide
Jeffrey M. Richter
Redwood City, California: M&T Books, 1991
671 pages, $39.95 (with disks)
ISBN 1-55851-164-4
Windows 3.0 for BASIC Programmers
Michael Hyman
Reading, Mass.: Addison-Wesley, 1991
316 pages, $29.95
ISBN 0-201-57031-9
Windows 3 Power Tools
Geoffrey T. LeBlond, William B. LeBlond, and Jennifer L. Palonus
New York, NY: Bantam Books, 1991
664 pages, $49.95 (with disks)
ISBN 0-553-35298-9
Electronic Review of Computer Books
Created 5/1/96 / Last modified 6/11/96 / webmaster@ercb.com