





         WW     WW     WW      PPPPPPPP              JJ
         WW     WW     WW      PP    PP              JJ
          WW   WWWW   WW       PP    PP              JJ
          WW  WW  WW  WW       PPPPPPPP              JJ
          WW  WW  WW  WW       PP             JJ     JJ
           WWWW    WWWW        PP              JJ   JJ
            WW      WW         PP               JJJJJ

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

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

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

     Delphi: PeteDavis

     GEnie: P.DAVIS5

     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.













                                     LEGAL STUFF


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

     -  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









                                       WPJ.INI
                                    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
     stuff.

          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 -






                                       Letters

     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{
     protected:
      int lgn,col;
      double* coeff;
      void init(int l,int c,double* cf=0);

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

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

                                     Jim

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

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

          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
     STYLE WS_POPUP | WS_DLGFRAME
     FONT 8, "Helv"
     BEGIN
          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

          CONTROL "", ID_VERSION, "STATIC", WS_CHILD  | WS_VISIBLE, 22, 45, 100,
     9

          CONTROL "OK", IDOK, "BUTTON", BS_DEFPUSHBUTTON | WS_CHILD | 
                                        WS_VISIBLE | WS_TABSTOP, 54, 63, 36, 24
     END

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

     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:

        button
        static
        edit
        scrollbar
        listbox
        combobox

          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 | WS_CHILD | WS_VISIBLE | WS_GROUP

     where

     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 | WS_CHILD | WS_VISIBLE | WS_TABSTOP

     where

     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.

          HELLO.DEF  has a  slight change:  DON'T FORGET TO  PUT THE  DIALOG BOX
     PROCEDURE IN THE  DEF FILE!! This means  the exports list  of the def  file
     becomes:

     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 

     BOOL FAR PASCAL AboutDlgProc(HWND, WORD, WORD, LONG);

     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:

     case IDM_HELPABOUT :
        lpfnAboutDlgProc = MakeProcInstance(AboutDlgProc, hInst);
        DialogBox(hInst, "About", hWnd, lpfnAboutDlgProc);
        FreeProcInstance(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.......
     .....now 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)
        {
        case WM_INITDIALOG :
           wsprintf(szBuffer, "%s at %s", (LPSTR) __DATE__, (LPSTR) __TIME__);
           SetWindowText(GetDlgItem(hDlg, ID_VERSION), szBuffer);
           return TRUE;

        case WM_COMMAND :
           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. 


     Menus
          

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

     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 -








     CUA
        

          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
     begin
          popup "&File"
          begin
               menuitem "&New",              IDM_FILENEW
               menuitem "&Open...",          IDM_FILEOPEN
           .
          end

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

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

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

     The menu has a basic structure:


                                        - 19 -







     "Name" menu
     begin
     end

     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"
          begin
               menuitem "Sub1",          IDM_SUB1NAME
               menuitem "Sub2",          IDM_SUB2NAME
           .
          end

     Popups can have popups can have...you 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
                       application
     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 -







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

           break;
         .
         }
     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:

     Restore      SC_RESTORE
     Move         SC_MOVE
     Size         SC_SIZE
     Minimize     SC_MINIMUM
     Maximize     SC_MAXIMUM
     Close        SC_CLOSE
     Switch To    SC_TASKLIST

     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 -







     Hello.C
            

          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.


     End.
         

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

          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  CtlID;
               UINT  itemID;
               UINT  itemAction;
               UINT  itemState;
               HWND  hwndItem;
               HDC   hDC;
               RECT  rcItem;
               DWORD itemData;
          } DRAWITEMSTRUCT;

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


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

          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:


     ODLISTBOX DIALOG LOADONCALL MOVEABLE DISCARDABLE 60, 25, 120, 120
     STYLE WS_CAPTION | WS_DLGFRAME | WS_POPUP
     CAPTION "Owner-Drawn List Box"
     BEGIN
          CONTROL "", IDB_LISTBOX, "listbox", LBS_OWNERDRAWFIXED |

                                        - 26 -







               LBS_HASSTRINGS | LBS_STANDARD | WS_VSCROLL | WS_GROUP |
               WS_TABSTOP | WS_CHILD, 25, 10, 75, 85
          CONTROL "OK", IDB_OK, "button", BS_DEFPUSHBUTTON | 
               WS_TABSTOP | WS_CHILD, 40, 100, 40, 14
     END

          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 <windows.h>
     #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
     LPDRAWITEMSTRUCT lpDIS;       // long pointer to DRAWITEMSTRUCT
     LPMEASUREITEMSTRUCT lpMIS;    // long pointer to MEASUREITEMSTRUCT
     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
       "One",
       "Two",
       "Three",
       "Four",
       "Five",
       "Six",
       "Seven",
       "Eight",
       "Nine",
       "Ten"
     };

     NumItems= 10;

     switch (message) {

                                        - 27 -







          case WM_INITDIALOG:
               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));
               break;

          case WM_COMMAND:

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

          case WM_DRAWITEM:

               // copy the DRAWITEMSTRUCT pointer from lParam
               lpDIS= (LPDRAWITEMSTRUCT)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,
                              hBrush);
                         DeleteObject(hBrush);

                         // 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->rcItem.top), (LPSTR)textStr,
                              lstrlen(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,
                              hBrush);

                         // next, draw the text
                         TextOut(lpDIS->hDC, (int)(lpDIS->rcItem.left),
                              (int)(lpDIS->rcItem.top), (LPSTR)textStr,
                              lstrlen(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;

               }
               break;

          case WM_MEASUREITEM:

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

     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 );
     BEGIN
        IF ( lpInfo^.CtlType <> ODT_BUTTON ) THEN
           Exit;
        ResID := ( lpInfo^.CtlID MOD 100 );
        Inc( ResID, 100 );
     { }
        IF ( lpInfo^.itemState AND ODS_DISABLED ) <> 0 THEN
             Inc( ResID, 300 )
        ELSE
           IF ( lpInfo^.itemState AND ODS_SELECTED ) <> 0 THEN
                Inc( ResID, 200 )
           ELSE
              IF ( lpInfo^.itemState AND ODS_FOCUS ) <> 0 THEN
                 Inc( ResID, 100 );

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

                                        - 32 -







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

     END;

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

     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,
     right?)

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

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

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

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

     GLOBAL HEADER FILE  INPUT FILE FORMAT   OUTPUT FILE FORMAT
     ------------------  -----------------   -------------------
     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 <mapfile name> <input  file>'. 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
          CONFIG.NT      NT's version of CONFIG.SYS
          AUTOEXEC.NT    NT's version of AUTOEXEC.BAT
          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
     passwords.

          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.

                   WynApse
                   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
           U.S.A.

          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:MikeS@asymetrix.com
     Subj:  Humor:  Health Tips for the 90s

     BURNOUT PREVENTION AND RECOVERY

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

     MICROSOFT   VIEW:  Work   until   the  physical   pain   forces  you   into
     unconsciousness.

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

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

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

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

     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 -




