Category : Windows 3.X Files
Archive   : WPJV1N4.ZIP
Filename : WPJV1N4.TXT

Output of file : WPJV1N4.TXT contained in archive : WPJV1N4.ZIP


The Windows Programmer's Journal Volume 01
Copyright 1993 by Peter J. Davis Number 04
and Mike Wallace Apr 93
A monthly forum for novice-advanced programmers to share ideas and concepts
about programming in the Windows (tm) environment. Each issue is uploaded
to the info systems listed below on the first of the month, but made
available at the convenience of the sysops, so allow for a couple of days.

You can get in touch with the editors via Internet or Bitnet at:


CompuServe: 71141,2071 (Mike) 71644,3570 (Pete)

Delphi: PeteDavis


or you can send paper mail to:

Windows Programmer's Journal
9436 Mirror Pond Dr.
Fairfax, Va. 22032

We can also be reached by phone at: (703) 503-3165.

The WPJ BBS can be reached at: (703) 503-3021.

The WPJ BBS is currently 2400 Baud (8N1). We'll be going to 14,400 in the
near future, we hope.


- Microsoft, MS-DOS, Microsoft Windows, Windows NT, Windows for Workgroups,
Windows for Pen Computing, Win32, and Win32S are registered trademarks of
Microsoft Corporation.

- Turbo Pascal for Windows, Turbo C++ for Windows, and Borland C++ for
Windows are registered trademarks of Borland International.

- WordPerfect is a registered trademark of WordPerfect Corporation.

- Other trademarks mentioned herein are the property of their respective

- WPJ is available from the WINSDK, WINADV and MSWIN32 forums on
CompuServe, and the IBMPC, WINDOWS and BORLAND forums on GEnie. It is also
available on America Online in the Programming library. On Internet, it's
available on WSMR-SIMTEL20.ARMY.MIL and FTP.CICA.INDIANA.EDU. We upload it
by the 1st of each month and it is usually available by the 3rd or 4th,
depending on when the sysops receive it.

- The Windows Programmer's Journal takes no responsibility for the content
of the text within this document. All text is the property and
responsibility of the individual authors. The Windows Programmer's Journal
is solely a vehicle for allowing articles to be collected and distributed
in a common and easy to share form.

- No part of the Windows Programmer's Journal may be re-published or
duplicated in part or whole, except in the complete and unmodified form of
the Windows Programmer's Journal, without the express written permission of
each individual author. The Windows Programmer's Journal may not be sold
for profit without the express written permission of the Publishers, Peter
Davis and Michael Wallace, and only then after they have obtained
permission from the individual authors.

Table of Contents

Subject Page Author(s)
WPJ.INI ....................................... 4 Pete Davis

Letters ....................................... 6 Readers

Midlife Crisis: Windows at 32 ................. 9 Pete Davis

Beginner's Column ............................. 14 Dave Campbell

Owner-Drawn List Boxes ........................ 25 Mike Wallace

Beginner's Column for Turbo Pascal for Windows 30 Bill Lenson

Hacker's Gash ................................. 31 Readers

Microsoft's Windows Strategy .................. 34 Pete Davis

Accessing Global Variables Across DLL ......... 38 Rod Haxton

Getting A Piece Of The Future ................. 41 Peter Kropf

The "ClickBar" Application .................... 44 WynApse

Getting in Touch with Us ..................... 45 Pete & Mike

Last Page .................................... 46 Mike Wallace

(If you're wondering where the C++ Beginner's column is, it's because we
haven't heard from the column's author. We hope to have it next month. See
WPJ.INI for further explanation.)

Windows Programmer's Journal Staff:

Publishers ......................... Pete Davis and Mike Wallace
Editor-in-Chief .................... Pete Davis
Managing Editor .................... Mike Wallace
Contributing Editor ................ Dave Campbell
Contributing Editor ................ Bill Lenson

Contributing Writer ................ Rod Haxton
Contributing Writer ................ Peter Kropf

by Pete Davis

Crazy, crazy, crazy. That's the only way to describe how things have
been lately. Got some good news, some bad news and a bit in-between. First
of all, I'll pour out some of the bad news.

We've been unable to get in touch with the author of the C++ beginners
column. This happens sometimes with a magazine like this and it could be
for any number of reasons. We will continue to try to get in touch with him
and hopefully have an article for next months issue.

The second piece of not-so-good news is about me and you may think of
it as good news, depending on your opinion of me. I probably won't be able
to do quite as much writing for the magazine for the next 7-10 months. I
will get in at least one article (for the Win 32 column) each month, but
other than that, I won't be able to do much else. For one thing, I'm going
to have to cancel the Install program. I'll discuss some points for the
Install program in a sec. I can't say yet why I won't be able to write as
much but hopefully, by the next issue, I will be able to tell you what it
is. Of course, things might not work out, and I'll be writing as much as
ever for the next 7-10 months. Well, I guess that was cryptic enough. I'll
discuss it next month.

As for the install program, it looks like I won't be able to continue
it. I've looked into the question of installing DLLs while the something is
using them. It seems that Windows'install program puts the files in a
temporary directory and copies them in before a restart or shutdown of
Windows. This is what I have been told by several people, including someone
from Microsoft. I have been through Microsoft's Install code in the past
and I don't remember seeing anything about that, but I wasn't really
looking in the right places, in retrospect. I will post this code on the
WPJ BBS if you're interested.

I would love for someone to pick up the install program and finish it
off for me. If anyone's interested, please, get in touch with us.

Let's try to get into some good news now. The good news is that we're
going to be covering Win32 A LOT! This doesn't just mean Windows NT, but
also Win32s and, eventually, Win32c (won't be released for another year or
so). This month I'm starting off with some NT and Win32c specific stuff.
Later I'll going into using the Win32s API to write 32-bit apps for Windows
3.1. There's really a lot of promise in the 32-bit application arena and we
intend on showing you all about it.

We've also got Peter Kropf who will also be doing a Win32 column. Most
of his is going to be discussing Windows NT so after this month, I'll try
to leave that to him and I'll move on to Win32s and some less NT-specific

We've got Bill Lenson who will be doing a new column on Turbo Pascal
for Windows for beginners. We've all been waiting for this, so we're glad

- 4 -

to have him aboard.

Rod Haxton is back with a really interesting way to handle Global
variables in Windows DLLs. As with all these sort of under-the-covers weird
things, it's not a recommended, it's just a method. DLLs shouldn't use
Global variables from the main program, but sometimes you don't have much
of a choice, as Rod will explain.

Well, that's about it for me, just to say that next month I hope to
have some really good news for all of you. Peace.

Pete Davis

- 5 -


Date: 21-Mar-93 22:07 EST
From: Eric Fildebrandt [76326,1470]
Subj: WPJ Format

I have read your past three issues and can't thank you enough. I am
venturing into the Windows programming environment and need all the
assistance I can get. As to the format, I really like the WinHelp format.
It is easily navigated and can be placed in a Program Group in Windows.
This way it can be used as a reference when programming.

I wonder if you could discuss the best way to change the background color
of a window since everyone else simply used the default white background
using GetStockObject. I have tried using CreateSolidBrush and it works
basically, however, after I close the window, the chosen background color
remains as the background color for the icons in the Program Manager window
until that window is somehow refreshed.

I am also interested in knowing how to drag and place bitmaps in a window.
I am also really confused on how to use the WM_PAINT message to send output
to the window, especially when that output changes during the run-time of
the program. Let's take the windows solitaire game as an example. How
would the WM_PAINT message be used to change the display as each card is
played, or is it?

Thank you for providing a forum for those of us trying to get acquainted
with Windows.

Eric Fildebrandt
Electrical Engineer/Computer Programmer

Editor [P.D.]: Eric, let me see what I can do for you here. I messed around
with some code that was supposed to be the basis for a solitaire game that
I was going to write. The world conspired against me and limited a day to
24 hours, so I didn't have time to finish it, but here's what I learned. My
first attempt was to just draw the bitmaps in their locations. When a mouse
button was pressed, I would check the mouse position. If it was over a
card, I would pick it up and the move it by BitBlt-ing it with the XOR
operator so that you could draw it over itself to erase it. This is used in
moving. You have two positions, old and new. You draw over the old (with
XOR) and draw in the new position (also with XOR) and your card just moved.
THIS IS NOT THE RECOMMENDED WAY. This was a disaster and it was a lot
slower than it had to be.

If I were to go back and do this, my first move would be to place the
card bitmaps inside of static objects. You could keep track of what the
mouse is currently above by checking the WM_SETCURSOR message and storing
the result in a static variable. Then when a WM_LBUTTONDOWN message comes,
check to see what the cursor was above. For a sample of using the
WM_SETCURSOR, see WPJV1N3 Hackers Gash, topic #2. I hope this helps. If
anyone else has some ideas, please send them in.

- 6 -

Date: 24-Mar-93 16:15 EST
From: Francois Bruneau [100113,1156]
Subj: Adapt the old Memory functions of Turbo C++

I have some code that works with Turbo C++. I don't know how adapt the
memory functions "memmove","memset","realloc" to the Windows 3.1 Borland
C++ .

For example I have code about matrix class:

class matrix{
int lgn,col;
double* coeff;
void init(int l,int c,double* cf=0);

matrix(int l,int c,double* cf) { init(l,c,cf); }

void matrix::init(int l,int c,double* cf)
{ lgn=col=0; coeff=0; coeff=new double[l*c]; lgn=l; col=c;
if (cf) memmove(coeff,cf,l*c*sizeof(double));
else memset(coeff,0,l*c*sizeof(double)); }

Elsewhere: coeff=(double*) realloc(coeff,lgn*col*sizeof(double));

I have no idea for the translation to GlobalLock,GlobalFree...

Can you help me?
Francois Bruneau.

Editor [P.D.]: Well, Francois, I'm not sure if I understand your question
exactly, but I'll we what I can do. First of all, for allocating the
memory, you're better of using Local memory if you can, but if you're data
is large, use Global. You'll need to lock the data before you can
manipulate it. That means before doing a memmove or memset, use the
Local/GlobalLock function (This assumes you've done your Local/Global Alloc
with LMEM/GMEM_MOVEABLE!) Next, if you're using LocalAlloc, then you can
use memmove and memset or their equivalent functions which are available in
all compilers. If you are using GlobalAlloc, you need to use functions that
will work on FAR pointers. If these functions aren't available in your
compiler, you'll have to write your own. Also, be aware the
Local/GlobalAlloc will return a handle, not an address. To get the address,
you need to do a Local/GlobalLock.

- 7 -

Date: 31-Mar-93 00:52 EST
From: James F. Stoves [72561,746]
Subj: Replacing DLL's

I was just reading your letter in WPJ regarding the replacement of
DLLs. I just installed EXCEL and WORD on a PC at work, and in thinking
about it, the Install Program took over the machine.

What I mean is that "MICROSOFT" logo appeared on the top,
miscellaneous propoganda in the middle, and a progress bar on the bottom.

The window had no "system menu" or minimize/maximize block. There was
a cancel pushbutton, but ostensibly that would have shut down the entire
install process. My clock (which is set to "always on top") did not
appear. I did try "Ctrl+ESCape" once during the install process and it did
not work. Yet the "setup" program is launched from within Windows, so it
must be there somewhere...

I was wondering if the way Microsoft gets around the "used DLL"
problem is by making sure that all DLL's are unused by booting them out. In
other words, causing everything to somehow be magically unloaded while the
install is running, replacing the DLLs, then a reload when the install
process ends. Just a thought, probably of minimal value, but presented for
your amusement.....


Editor [P.D.]: Jim, I mentioned in WPJ.INI that I've seen the code for the
install program. I don't remember this part either, but I can think of a
way it could be done. First of all, to get rid of everything else on the
screen and get a bitmap on the screen, all you need is a dialog box with no
SYSMENU, no CAPTION bar and no border to be sized for the entire screen.
Next, draw your bitmap on top of it. Now, why isn't your clock showing up?
Well, for your clock to be on top, it has to get a message letting it know
it was covered up so it can redraw itself on the top. Since Windows is a
non-preemptive multi-tasking system, all the Install program has to do is
not release control until it's done. You are taught from day one to keep
your programs nice and have them yield time on a regular basis. If you
don't yield the queue, nothing else will run (except the kernel and any
calls you make). So, your clock would stay hidden. That's how I would do
it, and I wouldn't be suprised if that's how the install program handles
it. If anyone else has comments on this, I'd love to hear them.

- 8 -

Midlife Crisis: Windows at 32
By Pete Davis

So, what's this new column all about? Well, Win32 obviously, and yes,
this column will get into the nitty gritty of Win32 programming. I'm not
sure how much appeal this will have because I don't know how many people
out there actually have it. Mike and I have had it for a couple months now
and I just recently got the Win32 API Programmer's Reference (Pre-Release).
This wallet-buster collection of a whole two books (at a mere $90) has
almost everything you need, NOT. Ok, well, as you saw in the last issue,
Microsoft's not known for their amazing book deals, but hey, when the
choices are Microsoft's API reference or Microsoft's API reference, I tend
to take the former, or was that the latter? Oh well, I think you get the

Second question: A whole column for Win32? Well, yes. Even Microsoft
thinks Windows NT will only take a small percentage (10-20 percent) of the
Windows 3.1 market. But, as the title implies, this isn't just a Windows NT
column, this is a Win32 column. Win32 includes the Win32s API which allows
Windows 3.1 to run 32-bit applications. This has a lot of promise, and
we'll be talking in detail about all of this.

Actually, there are going to be two Win32 columns. The second one is
going to be more NT specific.

Ok, let's get a little terminology covered first. You've got your
Windows 3.1, Windows NT, Win32 and Win32s. Windows 3.1, well, if you don't
know what it is, maybe you're reading this magazine by accident. Windows
NT, of course, is Microsoft's Windows New Technology (NoT, whatever).
Windows NT is a 32 bit operating system. It's more than just a faster
Windows, though. It's a huge departure from Windows 3.x. Windows NT is its
own operating system and runs without MS-DOS underneath (although an MS-DOS
clone runs underneath Windows NT). Windows NT, unlike Windows 3.x, is a
pre-emptive multi-tasking operating system. This means that while in
Windows 3.x, if a program locks up, so does Windows, while, under Windows
NT, if an application locks up, it doesn't affect the rest of the system.
The reason is that in Windows 3.x, the multi-tasking is based on the
message queue and when the message queue stops, so does Windows. (There is
one exception: MS-DOS sessions under Windows 3.x are pre-emptively multi-
tasked). Windows NT handles everything based on time-slicing instead of the
message queue. This means that each application gets a short period of time
to run, then the next application gets a bit of time, and then the next,
and so on, until the first one gets to go again. This is what pre-emptive
multi-tasking is all about.

Oops, where were we, oh yeah, the last two: Win32 and Win32s. These
are both about the same thing. Both are the 32-bit APIs for programming
Windows. Win32 is the 32-bit API that you use to program under Windows NT.
The Win32s is the same thing, except that it allows your 32-bit
applications to run under Windows 3.x through the use of some trickery of
VxDs (32 bit device drivers) and some DLLs. The Win32s API is actually a
subset of the Win32 API. It doesn't have threading, security, and some

- 9 -

other things. Actually, the functions are in the API, but since they can't
be supported under Windows 3.1, they simply return a failure. This means
that you can program in the features for NT, but if it's being run under
Windows 3.1, certain features just won't work.

So, Windows 3.x is covered with the rest of the magazine. The other
three, Windows NT, Win32 and Win32s are covered by this column and the NT
column. So, that's it for the little intro on terminology.

Win32 is big, and when I say big, I mean REAL BIG. It's about twice as
complex, programming-wise, as Windows 3.x. Now, that might not be exactly
right, there are some things that are made easier, but there are so many
new features, that it can be very overwhelming. I'm not going to write a
general overview of Win32 programming because I don't have 6 months to do
that. The best way to handle this whole thing, I guess, is to take it a bit
at a time, so each month I'll pick a topic to cover and after about 600
issues, we'll start to get comfortable with the Win32 API.

Before we get too deep into this (and believe me, we're going to get
in over my head, a bit), I want to make one thing very clear. I am not a
Win32 expert. I've been going by what is quite possibly out-of-date
documentation. I don't have the $360 for Windows NT Manuals. Microsoft says
to use the online documentation, to which I say, 'Yeah, you try going
through a 3 meg help file on a CD-ROM drive with 800ms access time!'. I am
going to try to get the rest of the documentation as soon as I can. I also
have Helen Custer's 'Inside Windows NT' which is a great book, but it isn't
exactly specific on the programming aspects. It does, however, provide an
excellent discussion on how NT works in a general sense.

Today we're going to talk about Processes and Threads and
Synchronization, and all that fun stuff. Damn, that means more terminology,
doesn't it? Well, this will be short. Processes, in terms of Windows NT,
are programs. Each program is a process. Within a single process you can
have multiple threads. Threads are basically just procedures. The only
difference is that you can have multiple threads execute simultaneously.
This is a really nice feature if you have multiple processors in your
machine, because with Windows NT, you can have different threads running on
different processors, which makes for some really nice multi-tasking.
Synchronization is something that comes up when you have multiple threads
running at once. Synchronization keeps things from getting out of control.
Since synchronization is such an integral part of concurrent thread
programming, we'll be talking about it quite a bit here.

I'm picking threads and syncronization as a starting point for several
reasons. It's one of the most important, yet difficult, obstacles to
overcome in programming for the higher end Win32 systems (Windows NT and
the yet-to-come 'Chicago'). Understanding how threads and syncronization
work will give you a good understanding of how many parts of NT work and
interact which is essential to programming for it. In later articles I will
move to less NT-specific subjects, however.

Let's look at an example:

- 10 -

Let's say you have a database program. As a feature of your database,
a user can go print a report, and even if it's not done spooling your
report, it lets you go on to work with the database. So, you go select your
print report option and then decide to make some changes to some of the
records in the file. Well, what happens if you update a record in the file
at the same time that the reporting thread is reading that record? Well,
that's a problem (and admittedly, not the most realistic, but bear with
me). Synchronization allows your update thread to keep all other processes
from touching that record until the record is updated. It's kind of like a
database record lock, which is usually handled by the operating system
(hence the lack of realism), but it's the idea that I'm trying to get

Here's another example, you have a a program that does a bunch of
repetetive calculations. To take advantage of your muliple CPUs (getting
realistic now, aren't I.) you decide to break it up into threads that run
concurrently to speed things up. Well, you could send off a thousand
threads at once, but sooner or later you're going to run out of memory,
performance will go through the floor, etc. So, you need to keep the number
of threads limited.

First, going to our last example of the calculations, that kind of
synchronization requires a Semaphore. A Semaphore keeps a count of how
many threads are currently using a resource (in this case, the CPU). When a
certain number of threads, as determined by the programmer, are using this
reousource, no other threads can get to it until one of the ones currently
using it ends, or gives up their lock.

Theres also the event object which is set when a particular event
takes place. This will release all threads waiting for the event when it's
set to the signalled state.

When you've got a few applications that want to use a single resource,
say a printer, you need to use a different type of synchronization called a
mutex. A mutex is a kind of synchronization, like Semaphores in a way,
except only one thing can use the resource at a time. Since we've just
discussed Mutex, let's go on to Critical Sections.

Critical Sections are a lot like Mutex objects. Critical Sections work
with blocking a single resource at a time. The Critical Section, however,
is different from the other three objects we've talked about in a few very
important ways. First of all, Critical Sections only work within a single
process (also known as a program, damn I love these fancy words). The other
synchronization objects will work within a single process, but they'll also
work across all processes. If you want to use mutual exclusion in a single
process and not globally, go with the Critical Section. If for no other
reason, it's faster, and we're all speed mongers, aren't we?

I'd like to back up just a bit, for a second. I've been saying a lot
of stuff like, 'multiple threads is great if you have multiple processors',
and you're probably thinking, well, I don't have multiple processors, so
why go through all the trouble of threading if it's not going to speed

- 11 -

things up? Well, there are other reasons for threading and that's what I
want to discuss for a second. In a multiple processor enironment, the
better the threading, the more the operating system can utilize all the
processors, so it's obviously a good idea there, I think we've established
that. In a single processor environment, however, situations can arise
where threading can still speed up applications. Let's say you have a
program that has to read in some data from a file and then create a report
from that data. Let's also say that before the report can be generated,
some initialization steps need to occur. Well, disk usage can really slow
down a program. Why not send off a thread to read in the file, and then go
on to do the initialization while it's reading the file and then both of
you meet back and do the report? Kind of a 'you take the high road and I'll
take the low road' idea. This way, the main program's not bogged down by
the speed of the hard drive quite as much. Threads are also excellent
candidates for server software in a client-server system. Each thread
could, say, handle a client.

I joked earlier about how we all have multi-processor machines, right?
Well, actually, don't be too surprised if they start showing up at
reasonable prices over the next few years. Don't laugh, look how much a 386
runs these days. You'd probably have laughed 2 years ago if I told you the
prices would get that low. Don't be to surprised if you see add-on cards,
at reasonable prices, that carry, say, 3 additional 486s as co-processors
for your 486, or 5 486s as co-processors, or whatever. The point being, it
could happen, and I'm betting that it will, especially now that there's an
operating system that can handle it.

So, on to the next thing, thread priorities. There are five levels of
thread priority that you can assign. Now, in Unix, the way that this works,
if I recall correctly (if you ask anyone, they'll tell you my recollection
is as reliable as bathroom graffiti) Unix will accept a thread priority,
but modifies it based on the kind of things the thread does, like disk I/O
lowers a thread priority, and the longer a thread runs, the lower it's
priority gets. Something like that. Anyway, NT lets you set your thread
priority at one of the 5 levels. The highest priority threads go first. As
each thread in the higher levels completes, threads in the lower levels are
run at each level one at a time, like the first. All of this is calculated
against the process' (not the thread, but process) base priority, which is
assigned by the operating system. This is modified by various factors, such
as a process running in the foreground has it's base priority raised,
whereas a process in the background has it's priority lowered. Also, to
make sure all threads get to run, NT will temporarily raise the priorities
of threads that have been waiting for a long time. It's all pretty complex,
but the idea of assigning priorities is just to give the operating system
an idea of how important you think a particular thread is. The best way to
think of it is this. Take the processes' (not the thread, again) base
priority. Your thread can be assigned a priority of -2 to +2 against the
processes' base priority. i.e. the lowest thread priority is the base
thread priority-2. This is the lowest priority your thread will ever see.
If, however, your thread's been sitting around for a while waiting to run,
NT will give it a little boost to make sure it runs.

- 12 -

I keep looking over this and I see a million ways to improve this
article, but given the time I can't do it this month. I know it's a lot to
absorb and it's really just meant to give you a taste. Next month I'll have
some code that actually uses threads and I'll explain how it works. I'm
also going to start talking about Win32s and give you some code that you
can run on your Windows 3.1 system with the Win32s DLLs. To avoid adding
the space of the API to the magazine, for those that aren't interested,
I'll have the code available on the BBS along with the necessary Win32s
DLLs and VxDs.

Don't let this months column scare you off. I probably could have
explained things a little better, with a little more time. Things should
become more clear when you see some code.

I suggest that if you have the means you do get a copy of the Win32s
API and DLLs. The future of Windows is going to be 32-bits. I hate to sound
like a Microsoft Rep, but they're right. 32-bits is the next logical step
and everyone's going to have to make it eventually. Better to hop on now
and get a jump on the competition.

I'm sure I'll get lots of questions on this article to clarify just
about everything I've written so sloppily here. That's fine, send me the
mail and I'll be sure to respond, if not personally, as part of the
magazine. The real problem was I bit off a bit more than I can chew this
month and I'll try not to do that again. See ya and don't forget to write.

- 13 -

Beginner's Column
By Dave Campbell

Remember last month I made some silly comment about getting buried in
work? I must be a prophet!! Anyway, here we are again, and with any kind of
luck next month you'll also find a review of Microsoft Visual C++ by me. I
have been using it since January, and am convinced it is a great way to go.

This month we are going to add an "ego" box with a live compile date
in it to the hello application, and talk some about menus.

Ego Box

I like to call the "About" box an Ego box, because when was the last
time you saw someone (me included) that put something other than 'Look who
wrote this' into an 'About' box? For that reason, we HAVE to do an Ego
box, or what would people think? Thanks to Jeffrey M. Richter in his great
book "Windows 3: A Developer's Guide", we can add a little bit of
usefulness to the Ego box by showing a live compile date. I find this very
useful for configuration management, when I have two (or more) copies of
the same thing floating around for testing.

The dialog box declaration looks very much like the setup dialog box
from last month:

ABOUT DIALOG 37, 10, 144, 92
FONT 8, "Helv"
CONTROL "Copyright (C) 1993 WynApse", -1, "STATIC", SS_CENTER |
WS_CHILD | WS_VISIBLE | WS_GROUP, 7, 8, 130, 11

CONTROL "CompuServe: 72251,445", -1, "STATIC", SS_CENTER | WS_CHILD |
WS_VISIBLE | WS_GROUP, 17, 17, 109, 8

CONTROL "Version Timestamp:", -1, "STATIC", WS_CHILD | WS_VISIBLE,
36, 35, 71, 12


WS_VISIBLE | WS_TABSTOP, 54, 63, 36, 24

Starting at the top, the STYLE line is different from the setup box.
Instead of WS_CAPTION, we have WS_DLGFRAME. For Ego boxes, I don't
generally put a caption. It doesn't serve a lot of purpose, other than
being able to move the box around, and how often do you do more than click
OK on an About box? This is personal preference, you understand. The

- 14 -

WS_DLGFRAME puts a nice frame around the box, and serves no purpose other
than aesthetics also.

The top three lines of the dialog are essentially identical in their

CONTROL "text", ID, "class", style, x-position, y-position,
x-width, y-width

This is the form of the generalized control statement. The first three
lines have an ID of -1. This is because they are static controls, and do
not send messages back to the parent window.

There are six control classes:


At this time, we are only dealing with two of them: button and static.
The version number will be written to the dialog box, but that is done by
the WinApp, not by the user, so it isn't an edit box.

The style parameter can consist of logical OR chains of individual
style declarations, of which there are a multitude. I am going to just talk
about them as we get to them.

The first control line contains:



SS_CENTER : Center the text in the box
WS_CHILD : child window
WS_VISIBLE : control is initially visible
WS_GROUP : first item in a group [we'll deal with this later]

The fourth control contains:



BS_DEFPUSHBUTTON : Default pushbutton, the one selected upon startup
WS_TABSTOP : goes along with the group declaration

The fourth control is different in that it has "" for the displayed
text, and has a defined control ID, ID_VERSION. ID_VERSION is defined in

- 15 -

hello.h, and is used in hello.c to place the text properly.

PROCEDURE IN THE DEF FILE!! This means the exports list of the def file

EXPORTS WndProc @1
HelloDlgProc @2
AboutDlgProc @3

Now we're ready to discuss displaying the box. This takes place
identically to the invocation of the setup box from last month. A menu
choice is made, and a message is sent to our main Windows Procedure,
telling us the user wants to see a dialog box. This time, we have added a
message IDM_HELPABOUT to hello.h to handle this. The function prototype


is listed with the other prototypes above the main body of code. "About" is
added to the system menu, which will be explained later in this article. A
static FARPROC for the pointer to the dialog procedure is declared early in
WndProc, inline with the one to the setup box. Then, just below the case
statement for IDM_SETUP, is one for IDM_HELPABOUT. The two case statements
match with the exception of the name of the dialog template to use from
hello.dlg. The About box code is:

lpfnAboutDlgProc = MakeProcInstance(AboutDlgProc, hInst);
DialogBox(hInst, "About", hWnd, lpfnAboutDlgProc);
return 0;

We must get a long pointer to a function, lpfnAboutDlgProc, to use in
the DialogBox function call. This is actually playing a game with memory
segments and far pointers, and segment reloading to enable Windows to find
our dialog box function in memory. All the segmentation manipulation that
Windows gives you for free is through small pieces of memory called
"thunks", or instance thunks, or reload thunks which binds a data segment
to the address of a function. MakeProcInstance takes the procedure in the
first parameter, and binds it to the data segment of the application whose
handle is in the second parameter. The return value from MakeProcInstance
is actually an address of a reload thunk.

Aren't you glad you know this? Now you do....... you've forgotten it, and can get on with programming Windows.

Since you know this, you'll realize how important it is to release
that "thunk" when you're finished with it by executing the FreeProcInstance
call. In DOS, we could be fairly certain that once our program exited,
things would get cleaned up. That wasn't good programming practice then
either, but it worked. If we make that kind of error in Windows, we leave
all manner of little potholes in memory, and very often run out.

- 16 -

The parameter "About" in the call is the title of the dialog box we
built in Hello.DLG. Because of the 'DialogBox' call, this dialog box will
be modal, and control will not return to the main window until OK is
pressed in the dialog box.

We do a return 0 here since we have handled the message, and have no
need to execute the default operation.

That gets us to the dialog box procedure itself, 'AboutDlgProc':

BOOL FAR PASCAL AboutDlgProc (HWND hDlg, WORD message, WORD wParam,
LONG lParam)
char szBuffer[100];

switch (message)
wsprintf(szBuffer, "%s at %s", (LPSTR) __DATE__, (LPSTR) __TIME__);
SetWindowText(GetDlgItem(hDlg, ID_VERSION), szBuffer);
return TRUE;

switch (wParam)
case IDOK :
EndDialog(hDlg, wParam);
return TRUE;

default :
return TRUE;

default :
return FALSE;
} /* AboutDlgProc */

The WM_COMMAND switch case is virtually identical to the setup box
from last time, the new stuff being the WM_INITDIALOG case. The
WM_INITDIALOG message is the first message a dialog box receives. At that
point in time, we fill in a message buffer with compile date and time in
this manner:

wsprintf(szBuffer, "%s at %s", (LPSTR) __DATE__, (LPSTR) __TIME__);

__DATE__ and __TIME__ are predefined global identifiers that contain the
date and time that processing began on the current file.

This line of text is inserted into the ID_VERSION control on the face
of the dialog by the following line:

- 17 -

SetWindowText(GetDlgItem(hDlg, ID_VERSION), szBuffer);

GetDlgItem returns the child window's handle for the control, and
allows us to insert our text into the dialog box.


Now let's discuss menus. Because everyone is familiar with them, we
should be able to beat them to death pretty soundly. How many menus are
there in Windows and how do we enable/disable them? Anybody? Ok, I'll
address it: There is a system menu which may be part of any window or
dialog box (which is just a child window). Then there is a menu that may be
declared in the window class declaration. There are popup menus that can be
used pretty much anywhere.

System Menu

The system menu is enabled/disabled with the properties of the window
declaration. This is invoked by clicking the dashed line boxed in on the
left of the caption bar (the System Menu button). This is also the menu you
get when you single click an icon.

The system menu is enabled by including the WS_SYSMENU style in the
window creation.

Application Menu

The Application menu is declared at class-registration time of the

wc.lpszMenuName = szAppName;

Normally, the name of the program is also the name of the menu, so
szAppName is useable here.

The ninth parameter in the CreateWindow call is of type HMENU, and
this overrides the menu in the menu class. This gives you the option of
having multiple windows use the same class, and have different menus. If
the one in the class is the one you want (as we are doing) leave the ninth
parameter as NULL.

Popup Menus

Popup menus are declared either as part of the main menu, or possibly
on the fly during run-time, and may displayed as an action from the main
menu, or possibly a run-time event.

- 18 -


As with any user interface, and more importantly, an interface that is
shared, you want to provide your users (it might be you) an easily
recognizeable way to use your product. It may be cute to put the help pull
down on the left side of the main menu, and it certainly would grab
people's attention, but not in the way most of us would like!

All of this melts down into me stating that in the case of menus, if
you want one, make it look like everyone elses. At least for the major
pieces. This is called CUA, which stands for Common User Access. The
orignial standard of CUA was developed by IBM. The current standard is
Microsoft's "Application Design Guide" (ISBN 1-55615-384-8). The most
important point being to be standard! The hello.rc code for the menu is
quite long, and only pieces of it will be show here:

Hello menu
popup "&File"
menuitem "&New", IDM_FILENEW
menuitem "&Open...", IDM_FILEOPEN

popup "&Edit"
menuitem "&Undo\tCtrl+Z", IDM_EDITUNDO
menuitem "&Repeat", IDM_EDITREPEAT

popup "&Sample"
menuitem "&Dialog", IDM_SAMPLEDLG
menuitem separator
menuitem "&Alternate..." IDM_ALTERNATE

popup "&Help"
menuitem "&Index", IDM_HELPINDX
menuitem "&Keyboard", IDM_HELPKBD
menuitem "&About Hello", IDM_HELPABOUT

The menu has a basic structure:

- 19 -

"Name" menu

inside the begin..end pair is the menu itself. Each entry in our menu is
listed as 'popup'. This means that each of the entries is a kick-off point
for a popup, or pull-down type of menu whose structure follows.

The structure for the popup menus is identical to that of the main
menu with the exception of the word 'popup':

popup "Name"
menuitem "Sub1", IDM_SUB1NAME
menuitem "Sub2", IDM_SUB2NAME

Popups can have popups can get the idea.

Each menu item has three parts:

1) The text that appears on the menu
2) The ID number of the menu item
3) An attribute for that item

Menu Text

The menu text is copied directly to the menu in the manner you type
it, with the addition that any character preceeded by an ampersand, "&", is
underlined in the menu. The underlined characters, when combined with the
Alt key, provide the user a 'hot-key' method to that item. Without the "&"
the user may still use the Alt key with the first letter of any menu item.

Two control characters '\t' and '\a' are optionally used inside the
text. The tab character, '\t' will space the menu item in a new column far
enough to the right to make room for the longest text line in an
accompanying popup menu. The '\a' is normally used with HELP, and will
right-justify the menu selection.

Menu ID

The menu IDs are defined as normal in the .H file, as we are doing
with Hello.H.

Menu Attributes

The attributes are a small enough number I will list them:

- 20 -

1) CHECKED - displays a check mark to the left of the menu text
2) GRAYED - grays out the text, and is also INACTIVE
3) INACTIVE - does not gray out the text, but sends no message to the
4) MENUBREAK - This item, and following appear in a new column
5) MENUBARBREAK - This item, and following appear in a new column,
separated by a vertical line

In order to keep the main menu standard, I didn't add any of this
stuff to it, however, there is a secondary menu, "Hello2", defined in
hello.rc that shows GRAYED and CHECKED items.

I took one slight excursion from the "standard" menuing, and to the
'Sample' popup, I added:

menuitem separator
menuitem "&Alternate..." IDM_ALTERNATE

Alternate is used in Hello.c to bring up the secondary menu. When the
second menu is being used, Grayed/Ungrayed, and One/Two show the use of the
attributes in your code.

System Menu

The system menu is not really there for us to add to, but many people
do, and it is definitly a quick way to get an about box up. Since the menu
is not defined in our code, we need to add to it at run time. This is done
by using the AppendMenu command. A menu pointer (of type HMENU) is
declared, and a pointer to the System Menu is captured:

hMenu = GetSystemMenu(hWndMain, FALSE);

Then the append is accomplished as follows:

AppendMenu(hMenu, MF_SEPARATOR, 0, NULL);
AppendMenu(hMenu, MF_STRING,IDM_SETUP, "Setup...");

I like to put a separator (a horizonal line) between the default setup
items and anything I add. There are a few things that need to be handled
when using the system menu:

1) the IDs used must be < F000H to avoid conflict with the normal system
menu commands

2) any WM_SYSCOMMAND messages not captured must be passed to the default
window processor, or else they won't get executed.

From hello.c, in WndProc, we find the following code:

switch (message)

- 21 -

switch (wParam)
case IDM_SETUP :
lpfnHelloDlgProc = MakeProcInstance(HelloDlgProc, hInst);
DialogBox(hInst, "Hello", hWnd, lpfnHelloDlgProc);
return 0;

return DefWindowProc(hWnd, message, wParam, lParam);

the message passed is WM_SYSCOMMAND if a menu item from the system menu is
selected. The item selected is passed in wParam. Therefore, a second switch
case is necessary to capture the system menu commands. In the above
example, IDM_SETUP is handled locally by displaying the setup dialog box,
but any other system commands are sent along to the default window
procedure DefWindowProc. This will ensure that nothing is lost.

You could 'trap' out certain system menu commands at this point by
executing the function yourself and returning 0. the normal system menu
options and their IDs are:


Menu Modification

Just as the system menu was appended above, there are other commands
that may be executed to modify menus:

1) AppendMenu
2) DeleteMenu - deletes a menu item, and destroys the item
3) InsertMenu - Inserts an item
4) ModifyMenu - changes an item
5) RemoveMenu - removes an item from a menu

Delete Menu would destroy a POPUP menu for example, and RemoveMenu
would only remove the ability to bring it up from the main.

- 22 -


Now let's look a little closer at the code from Hello.C this month
that handles menus:

As explained above, the main application menu is declared at
class-registration time of the window. That's all it takes to put the menu
up in your app. In the WM_CREATE case of WndProc, we added the line:

hMainMenu = GetMenu(hWnd);

This gives us a handle to restore back to the main menu "Hello", after
we have been using the alternate one. We normally wouldn't have had to
capture this handle. WM_COMMAND is where the action all takes place. wParam
is the menu entry selected by the user. Since we are going to be messing
around with the attributes in the sub-menu, we capture a menu handle inside
this case statement for that purpose.

For the menu items, all I did was to setup a print line for each menu
entry, and fall through to the MessageBox routine at the bottom.

IDM_ALTERNATE simply executes a SetMenu command which switches us to the
alternate menu defined in hello.rc.

ALTONE/ALTTWO and ALTGRAY/ALTUNGRAY use a brute-force method of toggling
each other on and off.

IDM_ALTRESTORE restores the main menu we started with.

Since we have only one Windows Application, you'll notice that
independent of which menu is alive at the time, we still have to handle all
the cases for both, all the time.


I think it probably takes two issues before the feedback comes in, so
this column is running slightly open-loop at this point. In other words, I
am still steering. I want some help steering, so get me some feedback. In
particular, I am looking for ideas on a useful WinApp we can build from
scratch explaining as we go, hopefully building off what we've already done

I have one correction, or addition to last month's article. I talked
about Borland programmers needing to add:

#define WINVER 0x0300

ahead of your include of windows.h in your hello.c file to compile under
the 3.1 compiler for Windows 3.0. This holds true for Microsoft programmers

- 23 -

also. Additionally, changing

wc.lpfnWndProc = WndProc; /* Name of proc to handle window */

to be:

wc.lpfnWndProc = (WNDPROC)WndProc; /* Name of proc to handle window */

holds true for Microsoft, too.

One other thing I noticed is that I held a pretty hard line on not
going beyond the page width for long lines. Since that was my first
article, I didn't realise that I had a line length of about 75, and while I
was complaining about long lines, I was displaying them, too...sorry!!
Please hang in there. If you are beyond the scope of this article, stick
with me - we are going to go places together. If you are way beyond this,
write us an article. If you are bogged down, just compile it, and stare at
the source. If you want help with something, send me a note.

That's it for this time. Next month we're going to add a file list box
the hard way, and discuss list boxes in general, and how to manipulate
text/filenames in them. Feel free to contact me in any of the ways below. I
want to rat out the things other people are having questions about, not
just what I think people want to hear.

Dave Campbell
WynApse PO Box 86247 Phoenix, AZ 85080-6247 (602)863-0411
CIS: 72251, 445
Phoenix ACM BBS (602) 970-0474 - WynApse SoftWare forum

- 24 -

Owner-Drawn List Boxes
By Mike Wallace

If you've ever wanted to liven up your list boxes with color, then you
need to know about owner-drawn list boxes. I recently wanted to create a
list box and color-code each item in the list based upon its value. The
only way to do this is with an owner-drawn list box, which is just what the
name implies - the owner (i.e., the programmer) has to draw the list box
himself. It's a little bit of work, but well worth the effort. The basis
for any owner-drawn control is the DRAWITEMSTRUCT structure, which is
declared as follows:

typedef struct tagDRAWITEMSTRUCT {
UINT CtlType;
UINT itemID;
UINT itemAction;
UINT itemState;
HWND hwndItem;
RECT rcItem;
DWORD itemData;

The owner of the owner-drawn control receive a pointer to this
structure as the lParam parameter of the WM_DRAWITEM message. This
structure is described completely in the Microsoft documentation, but I
want to describe a few (the important ones) so you won't have to get out
your manuals just to read this article:

hDC : the device context for the list box (very important)

itemID : the index of the itme in the list box

itemAction : describes the drawing action required by the
owner and will have one of the following values:

ODA_DRAWENTIRE : The entire list box needs to be drawn

ODA_FOCUS : The list box has gained or lost focus

ODA_SELECT : The selection status has changed

itemState : describes what the item will look like after it's
been drawn; it will have one of the following values:

ODS_CHECKED : The menu item is to be checked

ODS_DISABLED : The item is to be drawn as disabled

ODS_FOCUS : The item has input focus

- 25 -

ODS_GRAYED : The menu item is to be grayed

ODS_SELECTED : The item has been selected by the user

itemData : contains the value last assigned to the list box by
an LB_SETITEMDATA message. If the list box has the
LBS_HASSTRINGS style, this value is initially zero; else,
this value is initially the value passed to the list box
in the lParam parameter of either the LB_ADDSTRING or

Enough small talk. On to the program...

For this example, I'm using a dialog box containing only a list box
and an "OK" button. If the user selects an item in the list box, give the
item a different color. In the function handling this dialog box, we only
care about the following four messages: WM_INITDIALOG, WM_COMMAND,
WM_DRAWITEM and WM_MEASUREITEM. Here's a brief description of how the
program will handle each of these messages:

WM_INITDIALOG : The dialog box is getting created, so we have to do
two things: a) use the GetDlgItem() function on the list box control to get
a handle to the list box; and b) use that handle to fill the list box with
an array of strings using the SendMessage() function and the LB_ADDSTRING

WM_COMMAND : If the user presses the OK button, exit the function.

WM_DRAWITEM : If the status of any item in the list box gets changed
(including the initial filling of the list box), this message gets sent to
your function. Check the value of the itemAction field. For this example,
I'll handle the ODA_DRAWENTIRE and ODA_SELECT cases with the same code that
handles both the selected and unselected states. For the ODA_FOCUS case, I
will just call DrawFocusRect().

WM_MEASUREITEM : This message gets sent so you can set the height of
the items in the list box. Create a device context for the dialog box and
then use the GetTextMetrics() function to determine the font height.

The definition for the dialog box also needs to be discussed.
Primarily, the list box must have either the LBS_OWNERDRAWFIXED or the
LBS_OWNERDRAWVARIABLE styles. The latter allows each item to have a
different height. I'm using the former for this program. Here's the
"ODLBOX.DLG" file:

CAPTION "Owner-Drawn List Box"

- 26 -

WS_TABSTOP | WS_CHILD, 25, 10, 75, 85
WS_TABSTOP | WS_CHILD, 40, 100, 40, 14

I've defined ODLISTBOX, IDB_LISTBOX and IDB_OK in the "ODLBOX.H" file.
All that leaves is the code for the function itself. Here it is:

// Function to handle an owner-drawn list box
#include "odlbox.h"

BOOL FAR PASCAL ODListBox(HWND hDlg, unsigned message, WORD wParam,
LONG lParam) {

// define some colors for the selected text and background
#define GREEN RGB(0,255,0)
#define BLUE RGB(0,0,255)

HWND hLst; // handle to the list box
HDC hDC; // device context handle
TEXTMETRIC tm; // text info structure

HBRUSH hBrush; // draws text background
DWORD OldTextColor; // saves old text color
DWORD OldBkColor; // saves old background color

int NumItems; // number of items in list box
int i; // temporary variable

char textStr[80]; // list box item string buffer
char *textItems[] =
{ // strings going into the list box

NumItems= 10;

switch (message) {

- 27 -

hLst= GetDlgItem(hDlg, IDB_LISTBOX);
for(i = 0; i < NumItems; ++i)
SendMessage(hLst, LB_ADDSTRING, 0, (LONG)
(LPSTR) textItems[i]);
SetFocus(GetDlgItem(hDlg, IDB_OK));


if(wParam == IDB_OK) {
EndDialog(hDlg, NULL);
return TRUE;


// copy the DRAWITEMSTRUCT pointer from lParam

// if no items in the list box yet, set focus
if(lpDIS->itemID == -1) {
DrawFocusRect(lpDIS->hDC, (LPRECT)&lpDIS->rcItem);
return TRUE;

// draw items in list box and check for selection
if((lpDIS->itemAction & ODA_DRAWENTIRE) ||
(lpDIS->itemAction & ODA_SELECT)) {

// Get the text string and save in textStr
SendMessage(hLst, LB_GETTEXT, (WORD)lpDIS->itemID,
(LONG) (LPSTR) textStr);

// Handle the selection state
if (lpDIS->itemState & ODS_SELECTED) {

// text was selected, so make it green on blue
// first, draw and fill background
hBrush= CreateSolidBrush(BLUE);
FillRect(lpDIS->hDC, (LPRECT)&lpDIS->rcItem,

// set colors and output the text
OldTextColor= SetTextColor(lpDIS->hDC, GREEN);
OldBkColor= SetBkColor(lpDIS->hDC, BLUE);
TextOut(lpDIS->hDC, (int)(lpDIS->rcItem.left),
(int)(lpDIS->, (LPSTR)textStr,

- 28 -

// restore old colors
SetTextColor(lpDIS->hDC, OldTextColor);
SetBkColor(lpDIS->hDC, OldBkColor);

else {

// item not selected, so make it black on white
// first, draw and fill background
hBrush= GetStockObject(WHITE_BRUSH);
FillRect(lpDIS->hDC, (LPRECT)&lpDIS->rcItem,

// next, draw the text
TextOut(lpDIS->hDC, (int)(lpDIS->rcItem.left),
(int)(lpDIS->, (LPSTR)textStr,


// Check for focus state
if(lpDIS->itemState & ODS_FOCUS)
DrawFocusRect(lpDIS->hDC, (LPRECT)&lpDIS->rcItem);

return TRUE;


if(lpDIS->itemAction & ODA_FOCUS) {

DrawFocusRect(lpDIS->hDC, (LPRECT)&lpDIS->rcItem);
return TRUE;



/* get and use height of current font */
lpMIS= (LPMEASUREITEMSTRUCT)lParam; // copy info ptr
hDC= GetDC(hDlg); // create a DC
GetTextMetrics(hDC, &tm); // get text info
lpMIS->itemHeight= tm.tmHeight; // get text height
ReleaseDC(hDlg, hDC); // free the DC
return TRUE;

} /* switch(message) - end */
return FALSE;

} /* ODListBox() - end */

- 29 -

Beginner's Column for Turbo Pascal for Windows
By Bill Lenson

Welcome one, welcome all to the first of, hopefully, many beginner's Pascal
columns. If you've just purchased Turbo Pascal for Windows (TPW) or had it
for a while I think this column will be of interest to you. I'll try not
to go too fast so everyone gets lost but not too slow that nothing gets
said either. Hey, I'm a programmer but I haven't forgotten what it's like
learning a new language. Pascal is different from most languages and I
think it will be a lot of fun.

How much computer experience do you need to understand this stuff? Not
much. You should have a basic grasp of USING windows and know enough DOS
(or Windows File Manager if you prefer) to make directories and copy files.
That's it. Everything else will be covered in the articles.

All too often I've seen people try to learn a language like Pascal and
ultimately get frustrated after a month or two. Their problem is simple:
they may have picked up some really neat tricks to develop simple programs
but they lack the fundamentals needed to soundly write large programs. To
be a really good Pascal programmer you will need to learn lots of really
scary things. First you will need to learn the fundamentals of the
language. This isn't TOO bad because Pascal programs are fairly easy to
read. Next, you'll need to learn algorithms and data structures. These
are the building blocks of any program. Thirdly, given a problem, you'll
need to know how to analyze it and produce a program to turn concept into

Finally, you must learn all the extensions your particular environment
offers to Pascal. For this column we're using Windows so you'll need to
become aware of the thousand or so functions available to you in Windows.
To complicate matters, you can't effectively learn these things one at a
time. To be a successful learner, you need to be exposed to everything at
once but not so much of each that you'll run screaming into the night.

Have I scared anyone? I hope not. Pascal isn't a difficult language to
learn if you take it in small steps. I was fortunate enough to start
learning "3rd generation" languages out of a BASIC book in 1981 which
introduced the language in 7 steps. Two years later I started learning
Pascal in University. By applying small, logical steps (or at least I
should have - hindsight is 20-20) I could have dramatically reduced the
learning in my first four month course into a couple of weeks. Pascal is
a much more robust language than BASIC and will require probably three
times that many steps to learn. Hey, I was looking for a name for this
column. "Pascal in 21 Steps". I like it. Anyway, the emphasis in each
step (column) will be to study problem solving as you learn the language
and introduce Windows extensions as you go.

That's it for the first Turbo Pascal for Windows column. Next month we'll
start in on the writing your first Pascal program. Things will really
start to move then. See you next month.

- 30 -

Bill Lenson holds a Bachelor of Computer Science from the University of
Windsor. He is currently employed at Price Waterhouse Canada as a
consultant in their National Technology Services department. Bill is a
team leader in the development of a major Windows software package written
in C++ and Pascal.

- 31 -

Hacker's Gash

This month's column is by Wim Harrison (hope I have the last name
right; I forgot to write it down after downloading his note).

4 States of a button
I read with great interest, and mounting despair, the article about
bitmapped buttons in WPJ 1-3. Yet again we have someone (just the latest in
a long list) repeating the MS line that only 3 button states are needed.

NOT! A button has 4 states - Normal, Focussed-not pressed, Focussed-
pressed, and disabled (count 'em - 4). Everyone seems to omit the last
state - disabled.

What I do is to have a normal button (not focussed, not pressed) but
with the international 'no entry' red diagonal bar across it. Then if you
do a call to 'EnableWindow( hButton, FALSE)' it immediately acquires a red
bar to show it is not available. Even Borland don't catch this, and I think
it adds greatly to the user friendliness of the system.

I have included the source (the Pascal version, I have C code as well)
for the code I use. I use the following:

ID + 100 = Normal
ID + 200 = Focussed
ID + 300 = Pressed
ID + 400 = Disabled - the special one.

Note it takes only one extra line to handle this

------------------------ Cut here --------------------------

PROCEDURE DrawControl( hWind : HWND; lpInfo : PDRAWITEMSTRUCT );
IF ( lpInfo^.CtlType <> ODT_BUTTON ) THEN
ResID := ( lpInfo^.CtlID MOD 100 );
Inc( ResID, 100 );
{ }
IF ( lpInfo^.itemState AND ODS_DISABLED ) <> 0 THEN
Inc( ResID, 300 )
IF ( lpInfo^.itemState AND ODS_SELECTED ) <> 0 THEN
Inc( ResID, 200 )
IF ( lpInfo^.itemState AND ODS_FOCUS ) <> 0 THEN
Inc( ResID, 100 );

hBM := LoadBitmap( System.hInstance, MAKEINTRESOURCE( ResID ) );
IF ( hBM = 0 ) THEN

- 32 -

IF ( ( lpInfo^.itemAction AND ODA_DRAWENTIRE ) <> 0 ) OR
( ( lpInfo^.itemAction AND ODA_FOCUS ) <> 0 ) OR
( ( lpInfo^.itemAction AND ODA_SELECT ) <> 0 ) THEN
hMemDC := CreateCompatibleDC( lpInfo^.hDC );
hOldbm := SelectObject( hMemDC, hBM );
IF ( hOldbm <> 0 ) THEN
lpInfo^.rcItem.left, lpInfo^,
lpInfo^.rcItem.right - lpInfo^.rcItem.left,
lpInfo^.rcItem.bottom - lpInfo^,
hMemDC, 0, 0, 63, 39, SRCCOPY);
SelectObject( hMemDC, hOldbm );
DeleteDC( hMemDC );
DeleteObject( hbm );


------------------------------ CUT HERE -------------------------------
This is called from the main (dialog) proc to handle all custom button draw

BTW the 63, 39 values above are because I use 64x40 buttons.

- 33 -

Microsoft's Windows Strategy
By Pete Davis

Well, in March I got the honor of going to a Microsoft Windows
Strategy seminar put on by our buddies over at Microsoft. It was actually
pretty interesting (any seminar goer can tell you that most are real
sleepers.) I have to admit that at any seminar, the temptation to cop some
Zs is pretty high and this one was no different, but there was a lot of
interesting stuff for me to relay. (Otherwise I wouldn't be writing this,

What are the big things in Microsoft's future? Well, OLE 2.0 is
probably at the top of the list, according to Microsoft. This is both a
good thing and a bad thing. They showed a very impressive demo of OLE 2.0
in action and I think everyone there was ooing and ahing. But, as I said,
there is also a bad side, that being that OLE isn't the easiest thing in
the world to program. Seeing as that's the case, we're obviously going to
have to cover it here at some point and I'll start looking into that

My personal opinion, especially after talking to a few experts, is
that OLE 2.0 won't really pan out the way Microsoft wants. In all honesty,
it's just too much of a pain to program and it's very document oriented.
Microsoft might try to argue otherwise, but I don't think EVERY program can
be considered document-based. Microsoft, for some reason, thinks they are.
Maybe I'm wrong, but we'll see in the near future, won't we.

What can OLE 2.0 do for you? OLE 2.0 is VASTLY improved over 1.0. If
you're familiar with 1.0, picture this. Let's say you were in Microsoft
Word and you have an OLE link to an Excel spreadsheet. Let's say that link
shows maybe 5 columns and 3 rows. Well, in 1.0, when you double clicked on
the Word version, boom, Microsoft Excel pops up a big old window with your
spreadsheet. Not too bad, but try to picture the 2.0 version. You double
click on your link and boom, nothing happens. Well, not exactly nothing,
but it's real hard to notice. First of all, the title line that used to say
'Microsoft Word ....' whatever, has now changed to 'Microsoft Excel ....'.
The menu bar has changed to the Excel menu bar. Any tool bars are changed
over to Excel tool bars. The column headers A-F and row headers 1-3 show up
right around your existing copy of the spreadsheet. (Not the entire Excel
window.) The weird thing is, the rest of your document is right there,
unchanged. It looks like you're still in Word. The most noticable thing is
the column and row headers which suddenly surround your spreadsheet. If you
have bitmaps and graphs and stuff in your document, and they were on the
screen before you double-clicked, they're still there after you double
click. Pretty neat, eh?

On top of all that, let's say you have, not only a few rows and
columns, but also, say a link to a graph based on the numbers in your
spreadsheet, in the document. Well, again, you can double click on the
graph, and almost magically, you're in Excel and you hardly noticed the
change. Let's say you modify the values in the spreadsheet. Well, the graph
changes immediately. Let's say you move the bar graphs bars? The numbers

- 34 -

change to coincide with the graphs changes. It's really pretty cool.

Well, can't just rave about the great parts, writing code to do it
can't be easy. Haven't seen the API for OLE 2.0 yet, but I've seen it for
1.0 and it isn't exactly a piece of cake. You can be sure 2.0 isn't going
to be easier.

What were some other things.... 'Cairo', that's what. If you're around
Microsoft types, you've heard of Cairo for some time now. Cairo is the
secret (and I use that in terms of how secret anything in this country
usually gets) codename for Microsoft's 'Advanced' operating system. That
was about all I've known for a while. Got a little more news about it,
though. Cairo is going to be the next version of Windows NT, essentially.
It's going to be very object oriented, more so than the existing operating
system. What's more is Cairo is going to be based heavily on the use of OLE
2.0, so it's time to start brushing up on those OLE 2.0 skills.

Microsoft is going to be supporting 5 versions of Windows, over the
next few years. They are, from bottom to top, Modular Windows, Windows 3.1,
Windows 3.1 for Workgroups, Windows NT, and Windows NT Advanced Server.
Obviously, this list could be kinda grouped into a set of 3 operating
systems, but I'm giving it to you the way they fed it to me. Anyway, we all
know what Windows 3.1 is, I hope, and just think of 3.1 for Workgroups as
the network version with some nifty mail packages and stuff. There, two
down and three to go.

Modular Windows is going to be the kinda odd one. It's going to be for
several different types of hardware, including some sort of Nintendo-like
box, though it's purpose won't be to be a game machine, but in the sense
that it plugs into the TV and has a little controller and no keyboard. It's
also going into the Pen computers and such. They had a long list of things,
but no, I couldn't find Windows for Toasters in there, though they did make
a passing reference about Fax machines, and I don't know about you, but I
can dial a phone and press start without Windows, myself. Maybe it's for
the thinking impaired. Modular Windows is essentially a cut down version of
Windows 3.1. It has a subset of the API and a bunch of DLLs and unnecessary
stuff are removed. The idea is to get it down to 1 meg. They said they'd be
specializing it for each machine it was used on to keep the size at a
minimum. You could tell that they were really counting on getting computers
into every household, one way or another.

Actually, some of the ideas sounded pretty cool and they're actually
working with different vendors to try to make some of it a reality. For
example, one demo they showed was a program for working with Cable TV. You
could watch Cable, but let's say you were watching MTV, and a band you
liked was on. Well, with a little extra bandwidth that's not used by the
TV, but is used by your little Windows Cable Box, you have access to all
kinds of information, like what the name of the band is, a list of albums
and songs, when they'll be playing in your area, and if you want, you can
click on a button to order your tickets. Want the CD? Click on another
button and they'll send it to you and bill you through the cable company.
Now, I don't want to sound like one of those idiots from the 50s who

- 35 -

thought everyone in the 70s would have a helicopter in there back yard, but
I really think this kinda thing is going to happen. (By the way, later I'll
be coming back to that helicopter thing, 'cause I got a bit to say about
it.) They're actually working with cable box manufacturers and cable
companies to make this stuff a reality. It's some pretty neat stuff and
it's about time! They're saying they think this kind of stuff will be
available, large-scale, by '95. Well, I've never been too fond of
Microsoft's predictions, as far as timing, so I'm going to take a safe bet
with '97, but I definately think it'll be there.

Then there's NT. See my column on NT programming yet? Check it out.
I'm clueless when it comes to NT programming, therefore, over-qualified to
be teaching it. Anyway, their NT stuff was pretty impressive. Looks like
they're already heavy into building add-on stuff, like SNA servers (by the
way, that's the only thing I really thought OS/2 had on NT, not anymore, I
guess.) They're really pushing the idea of programming with the Win32 and
Win32s APIs and making everything portable between all the Windows systems.
Good luck, I say. I'd like to see it, but people are always going to try to
exploit the specific nooks and crannys that each different version has to
offer, and that's just life.

NT, staying or going? It's here to stay. It's not going to be on every
desktop and Microsoft knows it, hence the Win32s API, but it will be fairly
popular. It's going to be best for workstation stuff, software development,
servers, and the like. Personally, I love NT as a development environment.
Compile your 3.1 programs in the DOS box and test them under NT. If they
crash, who cares, you don't have to re-boot, ever. Great time saver.

Microsoft's going crazy. API-crazy, that is. They must have mentioned
6 or 7 of them while I was awake. They've got the Sockets API, Open
Database Connectivity (ODBC) API, SNA API, Win32 and Win32s API, Mail API
(MAPI), and, of course, PAPI, the Pizza API. Actually, the last was a joke
about an API to order Pizza for you, but I wouldn't be suprised to see it.
They've got APIs left and right and what I want to know is, who's going to
have time to learn half of them? When we start sending out resumes, we're
not going to be writing that we know how to program for Windows, we're
going to be listing the APIs we know how to program for Windows. The ones I
listed above are just the tip of the iceberg. There are a ton of them out
there. In an effort to help consolidate on some of this, Microsoft has come
up with the SPI, or Service Provider Interface. This is sort of an API for
the APIs. The ODBC API is a good example of how this works. What happens is
you program for the ODBC API. Then, the guys that write the database
software, like Oracle, Sybase, DBase, and all those guys, write the SPI.
That way, no matter which database program you're using the you just have
to write for the ODBC API. Sounds good, right? Well, yeah, that's fine, but
I've been programming for a long time, and I know programmer's. The first
thing they're going to do is get a hold of a bunch of SPIs, find neat
little tricks they can use that require them to bypass ODBC, because it
won't support them or something, and then have these 'hard-wired' tricks
for specific databases. Maybe I'm wrong but people have been talking
standards for years and what do we have? Undocumented DOS and Undocmented
Windows. People are always going to be bypassing these standard things so

- 36 -

they can make their programs jump through hoops.

The worst case of this was someone who was talking to me about writing
a standard set of APIs for programming in a GUI environment that would then
have the equivelent of SPIs (remember those) for each GUI, like Windows or
X-Windows. Then you'd just have to program for one API and it would re-
compile for each different GUI interface. Yeah, right. Sorry, it'll never
happen. What happens when you want to use NetBIOS? Sorry, X-Windows doesn't
have NetBIOS, so your universal API won't either, so you're S.O.L. Anyway,
just wanted to make that clear that that will not happen.

Ok, so back to Microsoft going API crazy. Well, I have to give them an
'A' for effort. They're really busting their collective hump to get tools
out there for us developpers. Over the next year or two, you can expect to
see a rich collection of APIs to do everything from getting data off of a
Nintendo machine via smoke signals to APIs for ordering Pizza. Microsoft's
really trying to standardize and I applaud their effort, but I've seen a
lot of people try to do that in this industry, so I remain skeptical.

Another new catch-phrase (or catch-acronym, as the case may be) is
WOSA which stands for Windows Open Services Architecture. This includes a
lot of the API and SPI stuff. WOSA is the collection of all these front-
ends (APIs) with changeable back-ends (SPIs) for different engines. I just
felt a need to throw the catch-acronym at you. I didn't want you to hear it
a few weeks later and then say, 'That's what Pete was writing about, but he
didn't call it that. What an idiot!' Well, I'm not an idiot, I'm just

Well, that about wraps it up. What do I think about all of this? Do
you care? Do I care? Cool. You Better. Of Course. In that order. I like the
idea of having a ton of APIs to play with. I've always been a bit of an API
nut, so I'll be messing with everything I can get my hands on. I think
Microsoft's going in the right direction and I think Bill's going to keep
raking in the bucks. I hope the cable-tv stuff happens and I hope I can run
Windows on my coffee maker in the near future. I can't make good coffee,
maybe Windows can. Hey, and maybe Windows to flush my toilet, wash my
clothes and mow my lawn. That's not too much to ask for, is it? Well, you
may be laughing but Bill's probably working these problems out in his head
as you read this. We'll see if he can do it.

P.S. Bet you thought I forgot about that helicopter nonsense. Well, I did,
and after re-reading, I'm adding this. I'm convinced Man has evolved
tremendously since the 50's. Let's face it, we all know, now, that everyone
having a helicopter in their driveway is a ridiculous idea. Most drivers
can't drive. Let's just say that if helicopters were that popular, I'd live
underground. 'nuff said.

- 37 -

Accessing Global Variables Across DLL
by Rod Haxton

In the February issue of WPJ I wrote an introductory DLL article. This
month I wish to share with the many readers of the WPJ of a way that I
discovered by necessity of handling global variables across DLLs. I
describe my approach as a 'kludge' but, to me it is a fairly good 'kludge'.
Like anything done against the norm there are some risks; like receiving
flammable E-mail about going against Windows standards, and possibilities
that Microsoft will change some things and therefore in later versions my
approach will not work. All of these are possibilities but, I can live with
those for now. Why? Because I think that it is a feasible kludge and what I
will show you is nothing that Windows has not been doing since version 2.0
and it has not changed yet. That is not to say that they will not change,
but that for now it seems safe to say that they will not.

Here's some background information on why I chose to use the method I
came up with. Windows already supplies ways of handling global variables
across DLLs, for instance, shared memory, passing far pointers and
redeclaring the variables in the DLL. These methods are workable and I
strongly suggest that they be used. Well, here's the background. I was
given the task of converting two DOS applications over to Windows and
making them into one package. My mission was to get it up and running as
quickly as possible. The original DOS application made use of numerous
global variables. So, I decided to do a straight port, and after demoing
the port and management making their decision, I would convert the App over
to DLLs. Well, the first two steps (ported and mgmt. decision) were
completed 7 months later. So, now the big job was to fix up everything over
to DLLs. This was no problem except for the numerous global variables. Hind
sight told me I was stupid for not dealing with them initially. Handling
the global variables the way Microsoft suggests would mean that a great
portion of the code would have to be rewritten, and you know that there
just is never any time to do that. So, I began to think to myself, and I
remembered reading about Micheal Geary's FIXDS. The same logic he applied
to not having to call 'MakeProcInstance' also applied to what I was

The stack segment (SS) for DLLs is that of the calling application.
And, the calling apps. DS == SS. Therefore, any global variables (CONST and
BSS) defined in the application is shared with the DLL via SS. The only
problem is that you have no clear way of accessing those variables. In
Windows global variables in segments do not change their offsets, only the
segments can be changed due to memory management. Thus, the offset values
of the global variables will always be at the same location inside the
segment. Knowing this, how do we determine those offsets? The answer is the
MAP file that the compiler will supply for you. The MAP file lists all
global symbols defined in the object file(s), sorted by name and a list
sorted by address. With this information in hand we can make a file of all
the global variables used in the program. The global list should use the
same case as the actual definitions. Also, since the MAP file sorts the
variables by name and thats what we are looking for, the matching variable
names and their offsets, our list file should be sorted, this will speed

- 38 -

the search. I wrote a utility program that I call "getoff.exe", that
searches the MAP file and creates an output constants file of the
variables, i.e.,

------------------ ----------------- -------------------
int variableA; variableA #define VARIABLEA 0x0001
int variableB; variableB #define VARIABLEB 0x0002
int variableC; variableC #define VARIABLEC 0x0003

The outputted constant file is to be included in the DLL modules. I forgot
to mention one thing. The global variables must be declared inside the DLLs
also. The cleanest way would be to include them in a global header file.
Now with the use of some in-line assembly code the global variables can be
accessed and updated inside the DLL.

#include "consts.h"
#include "global.h"
/* this code sets up the variables inside the DLL */
_asm mov ax, ss:[VARIABLEA]
_asm mov [variableA], ax

_asm mov ax, ss:[VARIABLEB]
_asm mov [variableB], ax

_asm mov ax, ss:[VARIABLEC]
_asm mov [variableC], ax

/* now use the variables like normal */
variableC= variableB;
variableB= variableA;
variableA= variableC;

/* when finish reasign the values to the global */
/* variables defined in the application */
_asm mov ax, [variableA]
_asm mov ss:[VARIABLEA], ax

_asm mov ax, [variableB]
_asm mov ss:[VARIABLEB], ax

_asm mov ax, [variableC]
_asm mov ss:[VARIABLEC], ax

This is all it takes to accomplish the task of using global variables
across DLLs. I created (for my company's purposes) entry/exit routines that
handle the assigning and re-assigning of the variables, only because our
application has over 100 global variables.

- 39 -

Included with this document is the "getoff.exe" utility and its
source, and source and executable example of a DLL accessing a global
variable. To run "getoff" type 'getoff '. As I
stated, this is only a kludge, and so this method should only be used if
you do not have time to rewrite code and you have too many global
variables. If you have the fortunate pleasure of creating your own
application try to stay away from global variables, but if you must, handle
them the way Microsoft suggests.

Rod Haxton can be reached at (202) 269-3780.

- 40 -

Getting A Piece Of The Future
By Peter Kropf

A few months ago, a computer trade publication ran an article in a
regular column stating that anyone can obtain a beta copy of Windows NT.
It would cost $69 without hard copy documentation, or $399 with a document
set. There have been other short blurbs that I had read which gave a very
brief view of Windows NT. Here was a chance to get the real thing.

While waiting for the package to arrive from Microsoft, a trip to the
local book store turned up a book called "Inside Windows NT" by Helen
Custer. The book was written during the development process. At times it
seems to read like a marketing brochure, but on the whole, I enjoyed
reading it. The first two chapters were outstanding. They gave a
wonderful overview to the various features and concepts of Windows NT. The
remaining chapters discuss the features and concepts in much more detail,
but the detail still remained at the surface level. Most of the features
described read like VMS, The Next Generation. For those of you that don't
know, Windows NT's chief driver is David Cutler, the same man who drove the
initial development of Digital's VMS operating system. After reading
through the book, I was very anxious to try Windows NT.

There were a few hurdles to overcome before getting the system up and
running. The distribution media is CD-ROM only. What that means is you
must actually have a CD-ROM drive. Some of us (namely me) are a little
slow. Until the media, computer and user were all physically in the same
place, it just didn't click.

The first drive purchased is a bundled package, one of those
multimedia upgrade kits. Sound card/SCSI port/CD-ROM all in one. Great.
Well, not so great. The SCSI port is not on the list of those currently
supported by Windows NT. That means Windows NT can not be installed
directly from the CD-ROM. Instead, all the required pieces are copied to a
temporary working directory and a bootable starter floppy is created while
running under MS-DOS. The system is then booted from the floppy and the
installation of Windows NT actually begins. Once the installation is
complete, Windows NT is booted directly from the hard disk. But, the SCSI
port on the sound card is not currently recognized by Windows NT, therefore
the CD-ROM drive cannot be accessed. That isn't really too bad until an
attempt is made to change the configuration. It tries to read from the
temporary load directory. But that directory was deleted after the
installation completed! (The solution here was to boot under MS-DOS and
copy the needed files into a directory that NT can then read.)

What's that you say? Most hardware vendors have a 30 day money back
guarantee/exchange policy? Gee you're right. Back the upgrade kit goes.
A faster drive which is accessible from NT is required. Why? Well I don't
know. Just because, that's why. An Adaptec 1542B with a Texel CD-ROM
drive does the trick. Now my multimedia windows applications can access
the CD-ROM directly while running under NT. Of course a sound card is now
needed to allow complete multimedia.

- 41 -

After the installation completed, poking around the directories
reveals some interesting things. There are a few new files in C:\ which
are required by NT.

WINNT Directory structure for NT.
NTDETECT.COM Machine boot process
NTLDR Machine boot process
USERS Directory structure for NT users.
BOOT.INI NT initialization information
PAGEFILE.SYS NT's primary page file.
BOOTSECT.DOS Purpose unknown. Hidden file.

When the machines boots, it goes through the standard hardware testing
and then loads NTDETECT/NTLDR/BOOTSECT.DOS. (At this time, I'm not certain
of the relevance of these three files, their order and use at boot time are
still unclear to me.) A menu is displayed with the entries in BOOT.INI:
Windows NT from C:\winnt and the previous operating system from C:\. This
allows either one to be easily booted. A timer is set for 30 seconds by
default to wait for a selection. After either the timer has reached zero
or the user has selected an OS, the highlighted entry is booted.

MS-DOS boots without any problems. It's nice to be able to easily
select which system to use.

Selecting NT to boot begins a much different process. First the
kernel and device drivers are loaded. Then all the known file systems are
checked. (NT supports FAT, HPFS and NTFS.) If the file systems need to be
rebuilt or fixed, this is where it would happen. All the partitions here
are setup as FAT. So far, the checking process has never complained and no
repair work has ever been done. After everything is checked, NT begins
working. A bitmap screen is displayed stating that this is Windows NT
BetaOctober '92 Release.

Once NT has started all the required processes, a message is displayed
indicating that CTRL-ALT-DEL are to be pressed to login. The standard
CTRL-ALT-DEL to reboot the system whenever a problem occurs is gone. NT is
a true pre-emptive multitasking operating system. One of the things this
being along with it is the fact that disk I/O is now buffered in memory.
Powering down the system without first requesting buffers to be flushed
will most certainly cause file corruption. Anyone whohas ever crashed a
Unix system will understand. The CTRL-ALT-DEL sequence will also ensure
that the real logon screen is being displayed, not an attempt at capturing

Once signed on, most things look the same. There are several new
features and programs available such as user maintenance, disk maintenance
and event log review, but their explanations are for a later time. Pressing
CTRL-ALT-DEL after signing on brings up a window which allows almost
complete control over the work currently being done. A task can be
stopped, the user can be logged off, the system can be shutdown, along with

- 42 -

a few others. Shutting down the system has two options, one for a complete
poweroff, and the second to shutdown NT and then reboot. The second option
behaves much like CTRL-ALT-DEL while running under MS- DOS, allowing the
system to be shutdown and rebooted in the event of a major problem.

This brings to a close a first look at getting NT running on a Intel
486-based machine. Since NT will run on many other hardware platforms
including the Mips R4000 and Digital's Alpha, getting a system up and
running on those machines might be a little different. The next article
will take a look at the additional features and programs available from the
Program Manager.

- 43 -

The "ClickBar" Application

ClickBar consists of a Dynamic Dialog pair that allows C developers for
Windows 3.0/3.1 to insert a "toolbar" into their application. Microsoft,
Borland, etc. developers may display a toolbar or tool palette inside their
application, according to a DLG script in their .RC or .DLG file.

Borland developers may install ClickBar as a custom control, providing
three custom widgets added to the Resource Workshop palette. These are
three different button profiles defining 69 custom button images total.
The buttons may be placed and assigned attributes identically to the
intrinsic Resource Workshop widgets.

Source is provided for a complete example of using the tools, including the
use of custom (owner-drawn) bitmaps for buttons.

Version 1.5 uses single-image bitmaps to reduce the DLL size, and includes
source for a subclass example for inserting a toolbar.

Registration is $35 and includes a registration number and printed manual.

PO Box 86247 (602) 863-0411
Phoenix, AZ 85050-6247 CIS: 72251,445

- 44 -

Getting in touch with us:

Internet and Bitnet:

HJ647C at GWUVM.GWU.EDU -or- HJ647C at GWUVM.BITNET (Pete)

GEnie: P.DAVIS5 (Pete)

CompuServe: 71141,2071 (Mike)

WPJ BBS (703) 503-3021 (Mike and Pete)

You can also send paper mail to:

Windows Programmer's Journal
9436 Mirror Pond Drive
Fairfax, VA 22032

In future issues we will be posting e-mail addresses of contributors
and columnists who don't mind you knowing their addresses. We will also
contact any writers from previous issues and see if they want their mail
addresses made available for you to respond to them. For now, send your
comments to us and we'll forward them.

- 45 -

The Last Page
by Mike Wallace

Got this in the mail today. WPJ has the coolest readers. Hope you
like it.

Date: 01-Apr-93 12:41 EST
From: Mike Strock >INTERNET:[email protected]
Subj: Humor: Health Tips for the 90s


1. STOP DENYING. Listen to the wisdom of your body. Begin to freely admit
the stresses and pressures which have manifested physically, mentally or

MICROSOFT VIEW: Work until the physical pain forces you into

2. AVOID ISOLATION. Don't do everything alone! Develop or renew
intimacies with friends and loved ones. Closeness not only brings new
insights, but also is anathema to agitation and depression.

MICROSOFT VIEW: Shut your office door and lock it from the inside if you
can so no one will distract you. They're just trying to hurt your

3. CHANGE YOUR CIRCUMSTANCES. If your job, your relationships, a situation
or a person is dragging you under, try to alter your circumstances, or if
necessary, leave.

MICROSOFT VIEW: If you feel something is dragging you down, suppress those
thoughts. This is a weakness. Drink more coffee (it's free).

4. DIMINISH INTENSITY IN YOUR LIFE. Pinpoint those areas or aspects which
summon up the most concentrated intensity and work toward alleviating that

MICROSOFT VIEW: Increase intensity. Maximum intensity = maximum
productivity. If you find yourself relaxed and with your mind wandering,
you are probably having a detrimental effect on the stock price.

5. STOP OVERNURTURING. If you routinely take on other people's problems
and responsibilities, learn to gracefully disengage. Try to get some
nurturing for yourself.

MICROSOFT VIEW: Always attempt to do everything. You ARE responsible for it
all. Perhaps you haven't thoroughly read your job description.

6. LEARN TO SAY "NO". You'll help diminish intensity by speaking up for
yourself. This means refusing additional requests or demands on your time
or emotions.

- 46 -

MICROSOFT VIEW: Never say no to anything. It shows weakness, and lowers
the stock price. Never put off until tomorrow what you can do at midnight.

7. BEGIN TO BACK OFF AND DETACH. Learn to delegate, not only at work, but
also at home and with friends. In this case, detachment means rescuing
yourself for yourself.

MICROSOFT VIEW: Delegating is a sign of weakness. Don't let someone else do
it (See # 5).

8. REASSESS YOUR VALUES. Try to sort out the meaningful values from the
temporary and fleeting, the essential from the nonessential. You'll
conserve energy and time, and begin to feel more centered.

MICROSOFT VIEW: Stop thinking about your own problems. This is selfish. If
your values change, we will make an announcement at the company meeting.
Until then, if someone calls you and questions your priorities, tell them
that you are unable to comment on this and give them the number for
Microsoft Marketing. It will be taken care of.

9. LEARN TO PACE YOURSELF. Try to take life in moderation. You only have
so much energy available. Ascertain what is wanted and needed in your
life, then begin to balance work with love, pleasure and relaxation.

MICROSOFT VIEW: A balanced life is a myth perpetuated by the Borland
Marketing Team. Don't be a fool: the only thing that matters is work and

10. TAKE CARE OF YOUR BODY. Don't skip meals, abuse yourself with rigid
diets, disregard your need for sleep or break the doctor appointments. Take
care of yourself nutritionally.

MICROSOFT VIEW: Your body serves your mind, your mind serves the company.
Push the mind and the body will follow. Drink Mountain Dew (it's free).

11. DIMINISH WORRY AND ANXIETY. Try to keep superstitious worrying to a
minimum - it changes nothing. You'll have a better grip on your situation
if you spend less time worrying and more time taking care of your real

MICROSOFT VIEW: If you're not worrying about work, you must not be very
committed to it. We'll find someone who is.

12. KEEP YOUR SENSE OF HUMOR. Begin to bring joy and happy moments into
your life. Very few people suffer burnout when they're having fun.

MICROSOFT VIEW: So, you think your work is funny? We'll discuss this with
your manager on Friday. At 7:00 pm.

- 47 -

  3 Responses to “Category : Windows 3.X Files
Archive   : WPJV1N4.ZIP
Filename : WPJV1N4.TXT

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: