


              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 08
          and Mike Wallace                                           Sep 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:

          Internet: 71644.3570@compuserve.com

          CompuServe: 71655,3570 (Pete) or 71141,2071 (Mike)

          GEnie: P.DAVIS5

          America Online: PeteDavis

          Delphi: PeteDavis

          or you can send paper mail to:

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

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

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

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








                                        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  

          Official Motto:  Served only in the finest restaurants.

          Bootup
           WPJ.INI  . . . . . . . . . . . . . . . . . .  4  Pete Davis 
           Letters  . . . . . . . . . . . . . . . . . .  6  Readers
           WPJ Survey . . . . . . . . . . . . . . . . .  11


          Programming
           Beginner's Column  . . . . . . . . . . . . .  14 Dave Campbell
           Hacker's Gash  . . . . . . . . . . . . . . .  25 Dennis Chuah
           GDI See GDI Do . . . . . . . . . . . . . . .  34 Bernard Andrys
           Windows Hooks  . . . . . . . . . . . . . . .  46 David S. Browne
           Shared Global Memory . . . . . . . . . . . .  51 Dennis Chuah
           Customizing File Dialogs in Visual C+  . . .  59 Tony Lee

          Software
           Installing Windows NT  . . . . . . . . . . .  63 Kurt Simmons

          Special Report
           Software Development '93 . . . . . . . . . .  65 Pete Davis

          The Leftovers
           Getting In Touch with Us . . . . . . . . . .  67 Pete & Mike
           Last Page  . . . . . . . . . . . . . . . . .  68 Mike Wallace 


          Windows Programmer's Journal Staff: 
          Publishers  . . . . . . . . . . . . . . . . .  Pete and Mike  
          Editor-in-Chief . . . . . . . . . . . . . . .  Pete Davis  
          Managing Editor . . . . . . . . . . . . . . .  Mike Wallace  
          Contributing Editor . . . . . . . . . . . . .  David Campbell  
          Contributing Editor . . . . . . . . . . . . .  Dennis Chuah

          Graphic Artist (Bad)  . . . . . . . . . . . .  Pete Davis
          Graphic Artist  . . . . . . . . . . . . . . .  Mark Coghlan
          Graphic Artist  . . . . . . . . . . . . . . .  Dennis Chuah
          Graphic Artist  . . . . . . . . . . . . . . .  Bernard Andrys

          Contributing Writer . . . . . . . . . . . . .  Dennis Chuah
          Contributing Writer . . . . . . . . . . . . .  David S. Browne
          Contributing Writer . . . . . . . . . . . . .  Bernard Andrys
          Contributing Writer . . . . . . . . . . . . .  Tony Lee
          Contributing Writer . . . . . . . . . . . . .  Kurt Simmons






                                          WPJ.INI
                                       by Pete Davis

               Well, the survey results  continue to pour in. Keep  them coming.
          If you haven't filled one out, there's another copy in this issue. The
          last day for the  prize is September 30th.  That means post-marked  or
          the e-mail is dated by September 30th. We will do  the drawing shortly
          after.

               Speaking of the  survey, I thought  I'd give you  all an idea  of
          what the results  have been like.  A lot of  you like the fact that we
          cover  a  wide  variety  of  levels.  We  are  commited  to  providing
          information  for   novice  programmers   as  well  as   more  advanced
          programmers. 

               We got  a lot of good and bad feedback.  In a sense, all feedback
          is good.  When I say bad,  I really mean  critical. And that's  a good
          thing.  One thing that  disturbed me is  this. We got  a few responses
          that mentioned errors, and one saying "Gross Errors", in the magazine.
          YES, there  are "Gross Errors". To  date, Mike and I  roughly skim the
          articles. We check spelling and grammer. We don't do a whole lot more.
          Why? We don't have time. It takes at least 25 hours a month to put the
          magazine together as  it is. I went through this  last month, and some
          people don't seem to  get it. That's not the  only problem. As I  said
          last  month, we  don't pay  the writers  and have  a hard  enough time
          getting  them. If  we made  it difficult  for them  by making  them do
          re-writes, we'd be  putting out the Windows Programmer's  Nothing. I'm
          serious about that. There are a couple people who might put up with it
          for a  while, but  if I wasn't  getting paid,  I sure wouldn't  make a
          habit of it, and I don't blame our writers for having the same feeling
          about that. If you're going to put up with serious editing, you may as
          well get paid for it.

               As far as "Gross Errors", Mike and I have always  been willing to
          publish  corrections,  which we  hoped we'd  get  more of,  but people
          haven't sent them in.  I got one in  the reader's survey, and  here it
          is:

               In Bernard Andrys'  article "GDI  See GDI Do",  he said that  the
          PASCAL  keyword in  a function declaration  meant that  the parameters
          were passed by reference  and not by value. (That was  the gist that I
          got, anyway.)  This is incorrect. What  it actually has to  do with is
          the way parameters are  placed on the stack. The  C calling convention
          places the arguments on the stack from right to left. For example, the
          call,  myFunction(a, b, c) pushes the contents onto the stack starting
          with 'c' and ending with 'a'. The Pascal calling convention places the
          arguments  on the  stack  from left  to  right, so  with the  previous
          example, 'a'  is pushed onto the stack first, followed by 'b' and then
          'c'.  This gives  C the  ability to  handle  an unspecified  number of
          parameters  on  the  stack.  It  makes  library  calls  like  printf()
          possible. Although  Pascal allows an unspecified  number of parameters
          to  be used  for the Read,  Write and  some other  procedures, this is
          handled  by the  compiler at compile  time. In  C, the  parameters are
          handled at run-time by the function. 

               OK, that was  easy and  relatively painless. If  anyone else  has
          corrections, send them  in. We'd be more  than happy to tell  everyone
          else. Our  original intent was  for this magazine  to be  a collective

                                           - 4 -






          effort. With many of our  readers and writers, it has been able  to be
          that. It bothers me, though, when people say, "you should fix this, do
          that, fix that,  and stop doing this..." and  go on through a  list 20
          things and then say that they still want it for free. Mike and  I have
          done the  best job we could  given the restrictions on  our time. This
          goes for  the writers we've  had and  still have. They  should all  be
          applauded  for their efforts in  getting articles done  and putting in
          the time. 

               I'm probably making a big deal out of  a little thing, in fact, I
          am. Most of you had few or no complaints. I have complaints about  the
          magazine, it's not perfect by  any stretch, but most of you  were very
          complimentary. We thank  you all. Mike  and I  have enjoyed doing  the
          magazine and we'll hopefully enjoy continuing it for years to come. 

               The  survey, all-in-all  was a great  success. It has  given us a
          better idea of the kinds of things we should focus on and the kinds of
          things we  might want to consider  leaving behind. Does that  mean you
          should  stop sending your comments,  suggestions and critiques. No sir
          (ma'am)! Send them  in. For those of you who  have complaints, why did
          you wait until the survey to send them in? Send them in as soon as you
          have them.  If there are problems  with the magazine, we  want to know
          about it. We want to do our best to please as many of you as possible.

               Ok, so  where does everything  stand? At  some point  Mike and  I
          would like to  actually start printing the magazine.  Many of you said
          you'd like us  to keep the WinHelp  format even if we go  to print. We
          will do that. We don't know when  we'll take the magazine to print and
          it may be  some time. Mike and  I are going  to start trying to  spend
          more time on the magazine  if possible. We're going to try  to improve
          the editing  of the articles also.  Again, all of this  takes time. If
          the magazine were to go to print, it would become a full time  job for
          Mike and I. The kind of "gross" mistakes seen in past issues would end
          (hopefully!).

               We will  keep  everyone informed  of  any developments  with  the
          magazine.  At  this   point  we've  talked  with  people  involved  in
          publishing about  the possibility of  printing the magazine.  How soon
          that happens remains to be seen.

               Peace.

               Pete
















                                           - 5 -





                                          Letters

          From: Pat White <P.WHITE@fs2.mbs.ac.uk>
          Date: 12 Aug 93 10:20:18 BST
          Subject: WPJ Mag

          Pete and Mike,
               Many  thanks for  your efforts  in producing  the magazine  - I'm
          brand new  to Windows programming and am finding a lot of useful info.
          Just  one very minor criticism  - when you  review shareware could you
          always include  where it can be  found, especially if it  is in either
          the Simtel or CICA archive as both are widely mirrored here in Europe.

               I am including  in this note two lists you  might want to include
          in a  future issue. The  file index  is a combination  of the  READ.ME
          files,    The  article  index  is  built  from  the  contents  of  the
          WPJVxNx.TXT files. They cover the first  6 issues - I hope to retrieve
          number 7 today.


          [Editor's Note: We felt this information would be useful for everyone,
          so it's  included here  and also seperately  as an ASCII  file. Thanks
          Pat.]

          File index
          Windows Programmer's Journal

          Volume 1 Number 1
          January 1993

          The following files are contained in this archive:

          TRASH.PAS      Pascal Source for trash can
          TRASH.RC       Resource code for trash can
          SUBMIT.TXT     How to submit an article to WPJ
          LINKLIST.C     C source code for linked list
          LINKLIST.MAK   Microsoft nMake compat make file
          LINKLIST.DEF   Module Definition file for linked list
          LINKLIST.RC    Resource file for Linked List
          LINKLIST.H     Header file for Linked List
          LINKLIST.ICO   Icon for Linked List
          MAKELIST.BAT   MSC 6.00 with 3.0 SDK batchfile to make
          PMDDE.C        Program Manager DDE C source code
          WPJV1N1.TXT    Windows Programmer's Journal Volume 1 Number 1
          README.TXT     Take a guess.


          Volume 1 Number 2
          February 1993

          The following files are contained in this archive:

          HELLO.ZIP      Source Code to the Hello World Program
          LINKLIST.ZIP   Mike's revised Linked List program
          HAXTON.ZIP     Rod's DLL examples
          D&DCLIE.PAS    Andreas Furrer's Drag and Drop Client Program
          D&DSERV.PAS    Andreas Furrer's Drag and Drop Server Program
          WPJV1N2.TXT    Windows Programmer's Journal Volume 1 Number 2

                                           - 6 -





          READ.ME        Well, I screwed it up in the last issue (pete).
          SUBMIT.TXT     How to submit articles to us.


          Volume 1 Number 3
          March 1993

          The following files are contained in this archive:

          WPJV1N3.TXT   The magazine in plain text format
          WPJV1N3.HLP   The magazine in Windows Help format
          WPJ.BAT       Quick way to view the WPJV1N3.HLP file in Windows
          BEGINNER.ZIP  Beginner's Column source code
          OWNERBTN.ZIP  Owner Draw Buttons source code.
          INSTALL3.ZIP  Source code for the Install Program article
          PAINT.ZIP     Advanced C++ source code
          READ.ME       Guess!
          SUBMIT.TXT    How to submit articles to us.


          Volume 1 Number 4
          April 1993

          The following files are contained in this archive:

          WPJV1N3.TXT   The magazine in plain text format
          HELPREAD.ME   Explanation of why there's no help file this month.
          ROD.ZIP       Rod Haxton's DLL code.
          HELLO.ZIP     Beginner's Column source code (Dave Campbell)
          ODLBOX.ZIP    Owner-Draw list boxes
          READ.ME       Guess!
          SUBMIT.TXT    How to submit articles to us.


          Volume 1 Number 5
          May 1993

          The following files are contained in this archive:

          WPJV1N5.TXT   The magazine in plain text format
          WPJV1N5.HLP   The magazine in WinHelp format
          HELLO.ZIP     Beginner's Column source code (Dave Campbell)
          WINSTUB.ZIP   Windows STUB source code
          READ.ME       Guess!
          SUBMIT.TXT    How to submit articles to us.


          Volume 1 Number 6
          June 1993

          The following files are contained in this archive:

          WPJV1N6.TXT   The magazine in plain text format
          WPJV1N6.HLP   The magazine in Winhelp format
          HELLO.ZIP     Beginner's Column source code (Dave Campbell)
          EDITPRO.ZIP   Goes with Eric Glass' article in Vol. 1, No. 5.
          READ.ME       Guess!
          SUBMIT.TXT    How to submit articles to us.

                                           - 7 -






          Article index

          Volume 1        Number 1
          -----------------------------------------------------------------
          WPJ.INI ......................................   3  Pete Davis
          Off Topic ....................................   6  Pete & Mike
          Beginner's  Corner   (C)  ....................   8  Pete Davis
                                                            & Mike Wallace
          A Drag and Drop Trashcan (TPW) ...............  16  Andreas Furrer
          Using DDE to Communicate With Program Manager.  18  Pete Davis
          Implementing a Linked List in the Global Heap.  22  Mike Wallace
          Book Review ..................................  26  Pete Davis
          Last Page ....................................  28  Mike Wallace
          Getting in Touch with Us .....................  29  Pete & Mike


          Volume 1        Number 2
          -----------------------------------------------------------------
          WPJ.INI .......................................  3  Pete Davis
          Letters .......................................  5  Readers
          Install Program Part II .......................  7  Pete Davis
          Programming a Drag&Drop Server ................  9  Andreas Furrer
          C++ Beginner's Column ......................... 12  Mike Wallace
          Beginner's   Corner   (C)   ................... 14  Pete Davis
          Using LZExpand Library ........................ 18  Alex Fedorov
          Implementing a Linked List - Revisited ........ 21  Mike Wallace
          An Introductory Look at DLLs and Make Files ... 22  Rod Haxton
          The Windows Help Magician ..................... 29  Jim Youngman
          Last Page ....................................  30  Mike Wallace
          Getting in Touch with Us .....................  31  Pete & Mike


          Volume 1        Number 3
          -----------------------------------------------------------------
          WPJ.INI .......................................  4  Pete Davis
          Letters .......................................  7  Readers
          Beginner's Column .............................  10  Dave Campbell
          Install Program Part III ......................  20  Pete Davis
          Home Cooking - C++ From Scratch ...............  22  Andrew Bradnan
          Creating and Using Owner Draw Buttons .........  26  Todd Snoddy
          Hacker's Gash .................................  30  Mike and Pete
          Special News ..................................  32  Mike Wallace
          Windows 3.1: Using Version Stamping Library ...  33  Alex Fedorov
          Book Review ...................................  36  Pete Davis
          Book Review ...................................  38  Mike Wallace
          Printing in Windows ...........................  40  Pete Davis
          Advanced C++ and Windows ......................  45  Andrew Bradnan
          Trials and Tribulations Part 1 ................  54  Jim Youngman
          Getting in Touch with Us .....................   57  Pete & Mike
          Last Page ....................................   58  Mike Wallace


          Volume 1        Number 4
          -----------------------------------------------------------------
          WPJ.INI .......................................  4   Pete Davis
          Letters .......................................  6   Readers
          Midlife Crisis: Windows at 32 .................  9   Pete Davis

                                           - 8 -





          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


          Volume 1        Number 5
          -----------------------------------------------------------------
          WPJ.INI .......................................  4   Pete Davis
          Beginner's Column  ............................  6   Dave Campbell
          Creating Windows Text File Editors  ........... 15   Eric Grass
          Midlife Crisis: Windows at 32  ................ 20   Pete Davis
          WDASM - Review of a Windows Disassembler  ..... 23   Pete Davis
          Hypertext, Sex and Winhelp  ................... 25   Loewy Ron
          Enhancing the WINSTUB Module  ................. 30   Rodney Brown
          Microsoft Developers Network .................. 33   Dave Campbell
          Getting in Touch with Us .....................  35   Pete & Mike
          Last Page ....................................  36   Mike Wallace


          Volume 1        Number 6
          --------------------------------------------------- ------
          WPJ.INI .....................................     4 Pete Davis
          Letters .....................................     8 Readers
          Beginner's Column ...........................    12 Dave Campbell
          Internally Yours ............................    15 Pete Davis
          Pascal in 21 Steps ..........................    18 Bill Lenson
          Moving Away from ...  .......................    29 Pete Davis
          C++ Beginner's Column .......................    30 Rodney Brown
          WPJ BBS Update ..............................    31 Pete Davis
          Windows Messaging ...........................    32 Mike Wallace
          WinEdit Review ..............................    35 Jeff Perkell
          White House Letter ..........................    38 Mike Strock
          Text Manager ................................    40 Mike Wallace
          Getting in Touch with Us ....................    42 Pete & Mike
          Last Page ...................................    43 Mike Wallace

          Keep up the good work.

          Pat White


          Pat White, Manchester Business School, Manchester, UK

          JANET address - P.WHITE@UK.AC.MBS.FS2
          EARN/BITNET   - P.WHITE@FS2.MBS.AC.UK
          INTERNET             - P.WHITE@FS2.MBS.AC.UK
                           or  - P.WHITE%FS2.MBS.AC.UK@NSFNET-RELAY
          UUCP/USENET          - P.WHITE%FS2.MBS.AC.UK@NSFNET-RELAY

          [Thanks  for the note  and the index,  Pat.  I think  people will find
          this useful.  We'll  have to start doing this every six  issues or so.

                                           - 9 -





          - mfw]


























































                                          - 10 -





                                        WPJ Survey

              Down  the page is  a survey. Please  fill it out  as completely as
          possible.  Don't forget to put your address  so we can mail your prize
          if you win.

              The prize options are:

          1) Win32 Reference Manuals   (5-book set)

          2) "Windows API Bible", "Windows Internals" and "Undocumented Windows"

          3) Windows NT Resource Kit


              Either print  it out, fill it in, and send  it via regular mail or
          edit  it with a  text editor  and fill  in the blanks  and send  it by
          E-Mail.


          On Compuserve    : 71644,3570
          On Internet      : 71644.3570 at compuserve.com
          On GEnie         : P.DAVIS5
          On America Online: PeteDavis
          On Delphi        : PeteDavis

          or regular mail to:

          WPJ Reader Survey
          9436 Mirror Pond Dr.
          Fairfax, Va   22032   U.S.A.


              Thanks for taking the time to fill out the survey.


              Pete & Mike






















                                          - 11 -





          Windows Programmer's Journal
          Reader Survey:  August, 1993


          Name:_______________________________   E-Mail Addresses
                                                 
          Profession:_________________________   Service           Address

          Title:______________________________   ___________  _______________

          Company:____________________________   ___________  _______________

          Address:____________________________   ___________  _______________

                  ____________________________

                  ____________________________

          Phone #:_________________           Fax #:________________



          Primary       operating       system      on       your      computer:
          ________________________________

          S  e  c  o  n  d  a  r  yo  p  e  r  a  t  i  n  g s  y  s  t  e  m  :
          _______________________________________________

          Primary     programming     language     used     (include     brand):
          ________________________

          O t h e r     P r o g r a m m i n g    l a n g u a g e s     u s e d :
          _________________________________________

          L i b r a r i e s    o r    C l a s s   L i b r a r i e s    u s e d :
          ________________________________________

          Development tools used (debuggers, editors, etc. Include brand.): 
          _________________________________________________________________
          _________________________________________________________________

          What     I      Like      about     the      Windows      Programmer's
          Journal:______________________________________________________________
          ___________________________________

          What     I     don't      like     about     Windows      Programmer's
          Journal:______________________________________________________________
          _________________________________

          Things    I'd   like    to   see    in   the    Windows   Programmer's
          Journal:______________________________________________________________
          ___________________________________

          O t h e r       p r o g r a m m i n g        m a g a z i n e s       I
          read:_______________________________________

          Would you pay for printed copies of WPJ? (Circle one)  YES   NO


                                          - 12 -





          Prize I want:_____________________________________


























































                                          - 13 -





                                     Beginner's Column
                                     By Dave Campbell

               Remember when you were in school, and the teachers would show you
          the REAL hard way to do something, and just about the time you thought
          you understood  it enough, they would show you the easy way? Well, I'm
          going  to do that to you right now.  The File Open box we've worked on
          the last two issues is  going to go away, and be replaced  by a single
          call to the Common Dialogs DLL, COMMDLG.DLL.

               Common Dialogs allow the developer to call up a functional dialog
          box with a single call,  rather than build the dialog box,  compile in
          the  dialog resource,  write  the code  for  handling the  dialog  box
          messages,  etc. At the same time,  the developer gets dialog boxes for
          quite a few of the standard configuration items that look identical to
          the ones  everyone else is using,  thereby making life  easier for the
          user.


          COMMDLG.DLL

               COMMDLG.DLL contains dialogs for the following:

                    ChooseColor
                    ChooseFont
                    FindText
                    GetFileTitle
                    GetOpenFileName
                    GetSaveFileName
                    PrintDlg
                    ReplaceText

          These may be used in Windows 3.0 or 3.1, but only in enhanced mode.

               My intention was to show the use of just the GetOpenFileName box,
          but it seemed so easy, I went ahead and added an implementation of the
          ChooseColor  box and ChooseFont box because they always seems to dress
          up an application.   Once you see  how easy this  is, filling out  the
          remainder of  the  menu selections  with  standard dialog  boxes  will
          become quite a bit less of a "chore".


          Removed Code

               First I'll quickly  run through what we can throw  away from last
          issue:

               1) All the Fileo.*  files, including the  DLL that we built  last
          time

               2) The DoFileOpenDlg procedure in Hello.c


          Hello.C

               The  #include of "fileo.h" is no longer needed, because we aren't
          using that DLL. However, we do need to add 


                                          - 14 -





             #include <commdlg.h> 

          to pick up the items defined there.

               Since we aren't going to use Fileo.DLL, we do not need 

             HANDLE hFileo

          but we will need 

             HANDLE hCommDlg

          so we just change that line, and  the lines where the DLL is loaded to
          load COMMDLG.DLL and get its handle into hCommDlg.

               'pof' and 'of' are removed, as they were used with the old code. 

               szFileSpec and szDefExt are moved to WndProc, and we add:

            COLORREF ColorRef;
            HANDLE   MyFont = NULL;


          COLORREF

               COLORREF  is  nothing more  than a  DWORD.  Defining it  as such,
          however, gives us a way  to remember we are using an RGB  color value.
          The RGB  method of  defining colors  is a  standardized way that  will
          carry  us into  the 24-bit  arena of  colors, but  is still  useful on
          systems  with 16  colors. Programming  in this  manner means  a little
          overhead up-front, but ease of programming, and portability later  on.
          The ColorRef  variable defined here will  be used to set  the color of
          our Hello World text via a common dialog call.


          MyFont

               MyFont is  a handle to  the font we  are going  to select in  the
          ChooseFont dialog box. Initially it is set to NULL, and this fact will
          be used in the code.


          WinMain

               The only changes to WinMain are the loading and freeing of  
          COMMDLG.DLL instead of Fileo.DLL, and the freeing of the handle, 


            if (MyFont != NULL)
              DeleteObject(MyFont);


          WndProc

               WndProc is the place where all the action happens, so most of the
          changes are here.



                                          - 15 -





          Variables

               The following are defined for our new version:

                    OPENFILENAME   openfile;
                    char           szFilter[25];
                    char           szExt[4 + 1];
                    char           szDirName[128];
                    char           szFileSpec[16];
                    char           szDefExt[5];
                    char           szFileTitle[128];
                    int                 i;

                    COLORREF       LocColorRef[16];
                    CHOOSECOLOR    ChooseMyColor;

                    DWORD               dwFlags=CF_SCREENFONTS;
                    CHOOSEFONT          ChooseMyFont;
                    LOGFONT             LogMyFont;

                    HANDLE              TempFont;


               OPENFILENAME  is  a structure  used by  the  file dialogs  in the
          common dialog DLL. It is defined as:

             typedef struct tagOPENFILENAME 
                {
                DWORD     lStructSize;
                HWND      hwndOwner;
                HINSTANCE hInstance;
                LPCSTR    lpstrFilter;
                LPSTR     lpstrCustomFilter;
                DWORD     nMaxCustFilter;
                DWORD     nFilterIndex;
                LPSTR     lpstrFile;
                DWORD     nMaxFile;
                LPSTR     lpstrFileTitle;
                DWORD     nMaxFileTitle;
                LPCSTR    lpstrInitialDir;
                LPCSTR    lpstrTitle;
                DWORD     Flags;
                UINT      nFileOffset;
                UINT      nFileExtension;
                LPCSTR    lpstrDefExt;
                LPARAM    lCustData;
                UINT      (CALLBACK *lpfnHook) (HWND, UINT, WPARAM, LPARAM);
                LPCSTR    lpTemplateName;
                } OPENFILENAME;

          We  are only  going to  use the  minimum in  our example,  but  I will
          mention the unused ones, to pique your interest to experiment.


          CHOOSECOLOR

               CHOOSECOLOR is a structure used by the color dialogs in 
          commdlg.DLL:

                                          - 16 -





             typedef struct tagCHOOSECOLOR 
                {
                DWORD   lStructSize;
                HWND    hwndOwner;
                HWND    hInstance;
                COLORREF rgbResult;
                COLORREF FAR* lpCustColors;
                DWORD   Flags;
                LPARAM  lCustData;
                UINT    (CALLBACK* lpfnHook)(HWND, UINT, WPARAM, LPARAM);
                LPCSTR  lpTemplateName;
                } CHOOSECOLOR;

          Again, before we are finished, all the entries will be explained.


          CHOOSEFONT

               CHOOSEFONT is a structure used by the dialogs in commdlg.DLL:

             typedef struct tagCHOOSEFONT
                 DWORD           lStructSize;
                 HWND            hwndOwner;
                 HDC             hDC;
                 LOGFONT FAR*    lpLogFont;
                 int             iPointSize;
                 DWORD           Flags;
                 COLORREF        rgbColors;
                 LPARAM          lCustData;
                 UINT (CALLBACK* lpfnHook)(HWND, UINT, WPARAM, LPARAM);
                 LPCSTR          lpTemplateName;
                 HINSTANCE       hInstance;
                 LPSTR           lpszStyle;
                 UINT            nFontType;
                 int             nSizeMin;
                 int             nSizeMax;
             } CHOOSEFONT;


          LOGFONT

               LOGFONT  is  a  structure  used by  the  dialogs  in  commdlg.DLL
          defining the characteristics of a font for drawing text on a window:

             typedef struct tagLOGFONT
                 int   lfHeight;
                 int   lfWidth;
                 int   lfEscapement;
                 int   lfOrientation;
                 int   lfWeight;
                 BYTE  lfItalic;
                 BYTE  lfUnderline;
                 BYTE  lfStrikeOut;
                 BYTE  lfCharSet;
                 BYTE  lfOutPrecision;
                 BYTE  lfClipPrecision;
                 BYTE  lfQuality;
                 BYTE  lfPitchAndFamily;

                                          - 17 -





                 BYTE  lfFaceName[LF_FACESIZE];
             } LOGFONT;

               The  first change to WndProc is in the WM_CREATE message handler.
          Since we  are gong  to be  changing colors, we  assign ColorRef  to be
          black:

               ColorRef = RGB(256, 256, 256); 

          and continue on. Then in the WM_PAINT handler, we use  ColorRef in the
          SetTextColor call:

               SetTextColor(hdc, ColorRef);

               Now  when we change ColorRef,  and force a  WM_PAINT message, the
          text  will be  painted in a  different color  of our  choice.  Another
          change  made to  the WM_PAINT message handler  is to check for a  font
          change:

             if (MyFont != NULL)
                TempFont = SelectObject(hdc, MyFont);

             DrawText(...);

             if (MyFont != NULL)
                SelectObject(hdc, TempFont);

               If MyFont  is set to a  handle value, that font  is selected into
          use by  doing a SelectObject  call. The  return value is  the previous
          handle, which we save in 'TempFont'. After displaying the text, we put
          the old font  back into use  by doing another SelectObject  call. This
          will  keep other display areas in our hdc from using the same font. In
          this  particular application, it may not be necessary, but good habits
          are hard to break.


          Files

               The IDM_FILEOPEN message handler has  changed quite a bit.  We've
          removed the  call to our DLL, and added the minimum that it would take
          to talk to the common dialog box GetOpenFileName procedure.

               We first use the  'memset' command to set the  structure openfile
          to  all nulls,  and insert  the structure  size into the  structure at
          lStructSize:

             case IDM_FILEOPEN :
                memset(&openfile, 0, sizeof(OPENFILENAME));
                openfile.lStructSize = sizeof(OPENFILENAME);

               We  then declare the  handle of the  owner of this  call as hWnd.
          This handle is used in the HELP call from the dialog box, to route the
          help message back to us:

               openfile.hwndOwner = hWnd;

               In the standard  file dialog boxes, there  is always a combo  box
          labeled  "List Files of Type", and  the list box contains entries such

                                          - 18 -





          as  "Text files (*.txt)" or  "All files (*.*)".  The developer defines
          those entries via the szFilter and szCustomFilter strings.

               Each of the  filter strings  is a concatenation  of string  pairs
          each null-terminated, and the  two filters are terminated by  a double
          null, one for  the final  filter declaration, and  one for the  filter
          itself. This is  reasonably confusing,  but maybe  a declaration  will
          clear it up. If  we want to have a  filter of "Text files" be  tied to
          "*.txt", we could fill our filter string as follows:

               sprintf(filter, "%s%c%s%c%c", "Text files", '\0', "*.txt",
                          '\0', '\0');

               An  alternate method  is useable  at run-time,  or if  you define
          strings in a  string table.  Substitute an unused  character for  your
          string separator, and then at run-time, change all those to '\0':

               strcpy(szFilter, "Text Files|*.txt|");

               for (i = 0; szFilter[i] != '\0'; i++)
                    {
                    if (szFilter[i] == '|')
                         szFilter[i] = '\0';
               }
               openfile.lpstrFilter = (LPSTR)szFilter;
               openfile.nFilterIndex = 1;

               Note  the  FilterIndex  begins with  "1",  not  0. A  value  of 0
          instructs Windows to use the CustomFilter string instead.

               lpstrFile  is a  pointer  to our  returned  file name,  which  we
          initialize to null:

               szFileName[0] = '\0';
               openfile.lpstrFile= (LPSTR)szFileName;
               openfile.nMaxFile = sizeof(szFileName);

               If we insert a file spec  in here, that is the name to  which the
          box is initialized.  This string  must be at  least 256 characters  to
          avoid problems with Windows.

               The FLAGS  entry  declares  various  parameters  controlling  the
          operation of the dialog box. The parameters we are using are:  

             OFN_SHOWHELP | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST

               OFN_SHOWHELP instructs the commdlg code  to display a help button
          in the dialog. As explained  above, this will assert the help  for our
          window, therefore the hwndOwner handle must not be NULL.

               OFN_PATHMUSTEXIST instructs  the commdlg  code to only  allow the
          user to type a path specification that exists currently on the system.
          An error is displayed if the path is non-existent.

               OFN_FILEMUSTEXIST is similar to OFN_PATHMUSTEXIST in that only 
          files that currently exist  on the currently enabled path  are allowed
          to   be  typed.  Selecting   OFN_FILEMUSTEXIST  automatically  selects
          OFN_PATHMUSTEXIST, but it is a helpful reminder to list both.

                                          - 19 -





               There  are other  parameters that  may be  defined  for selecting
          multiple  files, kicking  off  a new  file  message, enabling  a  hook
          function (explained  below)  or using  a custom  dialog box  template,
          among others.

               Next comes the call that does all the work:

               GetOpenFileName(&openfile);

               Normally,  the result of this would be checked for errors, but in
          this application, we aren't searching for a file, or intending to open
          one. We are simply demonstrating the use of the dialog.

               Staying  standard with  previous articles,  we copy  the returned
          file name to OutMsg, and finish this message handler:

               lstrcpy(OutMsg, openfile.lpstrFile);
               break;


          Other parameters

               The unused parameters of the OPENFILENAME structure are:

          lpTemplateName declares a custom  dialog template to be used  in place
          of the common  one. To do this, hInstance must be set to  the instance
          value  of the window  owning the template,  and  the  FLAGS value must
          enable templates.

          nMaxCustFilter contains the size of the custom filter, if one is used.

          lpstrFileTitle  is  a  string that  will  receive  the  file name  and
          extension only, that is the path specification will be stripped.

          nMaxFileTitle is the length of lpstrFileTitle.

          lpstrInitialDir is the starting  directory of the dialog box.  If this
          is  NULL,  the  current  directory is  used.  If,  however,  lpstrFile
          contains the  full  path specification  and file  specification for  a
          file, the path portion of lpstrFile is used as the initial directory.

          lpstrTitle is  a user-definable title for the  dialog box. A NULL will
          cause a standard default value to be used.

          nFileOffset is a returned value denoting where in the lpstrFile string
          the filename begins.

          nFileExtension  is a returned  value denoting  where in  the lpstrFile
          string the file extension begins.

          lpstrDefExt  is a user-definable default  extension to be  used in the
          search if the user does not type an extension on a filename.

          lpfnHook is  a pointer to  a function that  snags dialog box  messages
          prior  to  the  common  dialog  box  handler,  allowing  the  user  to
          intercept, or pre-process calls.

          lCustData defines the data that is passed to the hook function.

                                          - 20 -




               That  should  be enough  variation  to make  anyone  pleased with
          "common" dialogs. And that is only one of the set.


          Color

               To enable a  developer to allow an end-user to  define colors for
          text or dialogs,  the ChooseColor  common dialog was  built. An  extra
          entry in  the menu structure of our  code, under "SAMPLE" was declared
          as IDM_COLOR, and handled as follows:

               case IDM_COLOR :
                    memset(&ChooseMyColor, 0, sizeof(CHOOSECOLOR));
                    ChooseMyColor.lStructSize=sizeof(CHOOSECOLOR);

               As  with  the  File dialog,  the  CHOOSECOLOR  structure must  be
          declared, and its size passed to the dialog.

               The  initial 16 custom colors are declared going into the dialog,
          and coming out of the dialog via lpCustColors:

               ChooseMyColor.lpCustColors=LocColorRef;

               The handle of  the window owning the  dialog is declared as  with
          the file dialog:

               ChooseMyColor.hwndOwner = hWnd;

               rgbResult  is the  value  that is  initially  displayed with  the
          dialog is opened, and the value that is returned by the user:

               ChooseMyColor.rgbResult = RGB(256, 256, 256);

               As with the file dialogs, flags may enable hooks, templates, help
          buttons, and control the full usage  of the dialog. In our example, we
          have  chosen  to use  the rbgResult  value  as the  initially selected
          value.

               ChooseMyColor.Flags = CC_RGBINIT;

               The  action  happens  with   "ChooseColor".  The  dialog  box  is
          displayed, and  the user is allowed to choose colors as in the control
          panel of Windows. The result may be checked for an  error value (other
          than 0), and the resulting color is returned via rgbResult.

               if (ChooseColor(&ChooseMyColor))
                    {
                    ColorRef = ChooseMyColor.rgbResult;
                    InvalidateRect(hWnd, NULL, FALSE);
                    UpdateWindow(hWnd);
                    }
               break;

               In  our case, rbgResult is  written to ColorRef,  and used when a
          repaint takes place,  allowing the user to  pick the color  with which
          Hello/Goodbye Windows is painted. A  repaint is forced by Invalidating
          the entire window client  area (the NULL parameter), and  not painting
          the background (the FALSE parameter).

                                          - 21 -





               UpdateWindow  sends a  WM_PAINT message  straight to  the window,
          causing  the InvalidateRect area to  be repainted. This  takes care of
          the  change we have made with  the ChooseColor call, without having to
          resize the window to see the change.

               The structure CHOOSECOLOR contains lpfnHook, lCustData and 
          lpTemplateName as does the FILEOPEN structure.


          Fonts

               I  don't really want  to get into  a full discussion  of fonts at
          this time, because it  is extensive, but I do want to  show how to use
          the  ChooseFont dialog  box. If  some of  the information  is sketchy,
          accept it for  now, and  we'll dig  into it later,  or look  it up  in
          Petzold.

               An extra entry in the menu  structure of our code, under "SAMPLE"
          was declared as IDM_FONT, and handled as follows:

               case IDM_COLOR :
                    memset(&ChooseMyFont, 0, sizeof(CHOOSEFONT));
                    ChooseMyFont.lStructSize = sizeof(CHOOSEFONT);
                    ChooseMyFont.hwndOwner = hWnd;

               As  with the  other  dialogs, the  CHOOSEFONT  structure must  be
          declared,  its  size  passed to  the  dialog,  and  our window  handle
          inserted into the structure.

               A  variable  of  LOGFONT type  is  pointed  to  by the  lpLogFont
          structure entry, and filled out as follows:

               ChooseMyFont.lpLogFont = &LogMyFont;
               lstrcpy(LogMyFont.lfFaceName, "Times New Roman");
               LogMyFont.lfHeight = 10;
               LogMyFont.lfItalic = FALSE;
               LogMyFont.lfWeight = FW_BOLD;
               LogMyFont.lfStrikeOut = FALSE;
               LogMyFont.lfUnderline = FALSE;

               This declares 'Times New Roman' in bold 10 point type.

               As  I said above, at this point  in time, I am choosing to ignore
          the  other  structure  variables in  LOGFONT,  and  at  least for  our
          application,  the  default  values  of  NULL  (as  inserted  with  the
          'memset') work fine.

               Our ColorRef  value is  inserted into the  ChooseMyFont structure
          because color may come into play in the ChooseFont dialog:

               ChooseMyFont.rgbColors = ColorRef;

               The flags selected in our example are

               ChooseMyFont.Flags = CF_SCREENFONTS | CF_EFFECTS 
                    | CF_INITTOLOGFONTSTRUCT;

          CF_SCREENFONTS  forces  the  dialog  box  to  display  only  the fonts

                                          - 22 -





          available currently for the screen.

          CF_EFFECTS enables the strikeout, underline, and color  effects of the
          dialog box.

          CF_INITTOLOGFONTSTRUCT inits  the dialog box to  the LOGFONT structure
          as declared above.

               Now the  call to ChooseFont is made,  and if successful, the font
          handle  'MyFont' is  set to  the new  font chosen  in the  dialog, and
          ColorRef is set likewise.

               if (ChooseFont(&ChooseMyFont))
                    {
                    if (MyFont != NULL)
                         DeleteObject(MyFont);

                    MyFont = CreateFontIndirect(&LogMyFont);
                    ColorRef = ChooseMyFont.rgbColors;

                    InvalidateRect(hWnd, NULL, TRUE);
                    UpdateWindow(hWnd);
                    }
               break;
           

          Include file

               IDM_COLOR and IDM_FONT must  be added to hello.h to  allow access
          to the new menu items:

               #define IDM_COLOR          326
               #define IDM_FONT           327


          Resource file

               The  addition of  the Color selection  to the menu  is defined in
          hello.rc:

               menuitem separator
               menuitem "&Color",  IDM_COLOR
               menuitem "&Font",        IDM_FONT


          EndDialog

               I have  included  the Microsoft  make file,  MHello.mak for  this
          issue. I received no help  so far on the request from  help wizards in
          the last issue, so the offer still stands.

               For the next issue, I had intentions of  starting to discuss Help
          files,  but WPJ  seems to  have  started on  that already,  so I  will
          continue  with my schedule, and  discuss displaying a  dialog box as a
          main window.

               Feel  free to contact  me in  any of the  ways below.   Thanks to
          those of you that have e-mailed me questions; keep them coming.

                                          - 23 -





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

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




























                                          - 24 -





                                       Hacker's Gash
                                      By Dennis Chuah

               In this instalment of Hacker s Gash, we will look at system level
          entities, the  ToolHelp library and  extend the discussion  to private
          class  dialogboxes.   Plus,  we will  continue  last month s  metafile
          discussion.  Here is a summary of the topics presented:

          1.   Instances, Modules and Tasks
          2.   When Should HINSTANCES, HMODULES and HTASKS be Used
          3.   List of Modules
          4.   Private Dialogbox Class
          5.   Metafiles revisited

               All  example programs  are  compiled with  Borland  C++ with  the
          following options:

          Memory model:  MEDIUM
          Optimization:  Fastest Code
          Entry/Exit Code:  Smart Callbacks
          Floating Point:  None
          Instruction Set:  80286
          Calling Convention:  C
          Instances, Modules and Tasks.
               Most of you should have come across instances.   Windows passes a
          handle to an application  (HINSTANCE) in the WinMain function.   Calls
          made to  RegisterWindow  and  CreateWindow  require  this  value.    A
          HINSTANCE is something you have used often but have you  stop to think
          of what  a  HINSTANCE is?    Then there  is  the handle  to  a  module
          (HMODULE) and the handle  to a task (HTASK).   You have heard of  them
          before, but have never needed to use them.  What are they anyway?

               I  wrote a  .DLL and  an application  to illustrate  these system
          entities.  The source code and executable are in the file GASH.ZIP, in
          the TEST  subdirectory.  This  .ZIP file  contains subdirectories,  so
          remember to use the  -d flag when unzipping  it.  Figure 1a  shows the
          output of the application.


          Figure 1a

          I then ran another instance of the application and the result is shown
          in figure 1b.


          Figure 1b

               With the aid  of figures 1a and 1b, we can see that the HINSTANCE
          is  unique  for  different instances  of  the  application.   This  is
          sensible  --  HINSTANCE uniquely  identifies an  application instance.
          The  HINSTANCE  for the  .DLL however,  is  the same  across different
          instances of the application that used it.  This is  also sensible, as
          there is only one  instance of a .DLL no  matter how many times  it is
          used.   HMODULE appears to  be the same  across different instances of
          the application and .DLL.  HTASK on the other hand, is the same within
          an instance, whether it was obtained from the .EXE module or the .DLL.

               A  little  explanation is  in  order.   HINSTANCE  identifies the

                                          - 25 -





          current instance.  This is why every instance of an  application has a
          unique  HINSTANCE.   For  every instance  of  an application,  Windows
          creates  a data  segment  (This is  usually  the  case as  the  module
          definition file in an .EXE usually specifies multiple data  segments).
          Although  undocumented, I have found  that HINSTANCE is  the handle to
          the application s data segment.

               Take figure 1a for example.   I ran the test application and used
          Turbo Debugger to debug it.   On examination, the DS register  has the
          value,  0x2E47.  I then  inspected hInstance with  the following Turbo
          Debugger typecast:

          (gh2fp) (unsigned) hInstance

               This is equivalent to doing a GlobalLock on hInstance.  The value
          was,  2E47:0000.  Presto!  The selector  value was exactly the same as
          the value in the DS register.

          Undocumented:  HINSTANCE is the handle to the data segment.

               Every .DLL has a unique  HINSTANCE, but as the .DLL has  only one
          data segment that is shared across every application that makes use of
          it, there is  only one HINSTANCE  value.  As a  rule, DLL modules  are
          only  instanced once  no matter  how many  times they  are used.   EXE
          modules on  the other hand are  instanced once for each  time they are
          used.

               Knowing this is  they key to understanding why exported functions
          in  an application  needs  to instanced  through MakeProcInstance  and
          those in a .DLL do not.  Window functions do not need to be instanced.
          A field in the  windows information structure is GWW_HINSTANCE.   When
          messages  are dispatched,  the correct  data segment  is bound  to the
          window procedure via  this HINSTANCE value.   Subclass procedures must
          be instanced before  use.   The HINSTANCE in  the GWW_HINSTANCE  field
          refers to  the  window  procedure s  data segment,  not  the  subclass
          procedure s.   Also, the subclass  procedure must not  call the window
          procedure  directly,  but  should   instead  use  the   CallWindowProc
          function.     CallWindowProc   binds  the   HINSTANCE  value   in  the
          GWW_HINSTANCE field to the window procedure.

               As explained in the  last instalment, exported functions compiled
          with  Borland s  smart  callbacks  entry/exit code  automatically bind
          their data segment on entry.

               HMODULE  refers to the file that Windows loads the code segments,
          resources,  etc.   It can  either be an  .EXE or  a .DLL  file.  Well,
          actually,  HMODULE is  used  to  keep  track  of  resources  that  are
          associated with the .EXE or .DLL  file.  Take a look at the  following
          declaration for CLASSENTRY.

          typedef struct tagCLASSENTRY
            {DWORD   dwSize;
             HMODULE hInst;
             char    szClassName[MAX_CLASSNAME + 1];
             WORD    wNext;
            } CLASSENTRY;

               This structure  is used  by the ToolHelper  function, ClassFirst.

                                          - 26 -





          We can  see that there is  a HMODULE  field  in it.  When  a module is
          freed,  all classes  associated with  the module  will also  be freed.
          When  a  class is  registered, Windows  obtains  the HMODULE  from the
          specified HINSTANCE.  The class is then bound to the module  specified
          by the HMODULE value.  This  is why in Windows 3.1, only  one instance
          of an application is  supposed to register classes.  Every instance of
          an application share the same HMODULE.

               Tasks  are like  logical  CPUs.   Each  has its  own  instruction
          pointer and set of registers.  Each task also  has its own stack.  The
          stack is created in  the application s data segment (the  data segment
          bound  to  the  .EXE  module).    Every  application  on  the  desktop
          (including  the shell application --  eg. Program Manager)  is a task.
          In 386 enhanced mode, all tasks  run in a virtual machine in protected
          mode.  Every DOS  window is a virtual machine that  runs in real mode.
          Windows performs  preemtive multitasking  across virtual machines  but
          only  performs cooperative  multitasking across  the tasks  within the
          protected mode virtual machine.


          Figure 2

               For each  task, Windows creates an  execution context (HINSTANCE)
          associated to  an .EXE module.   The task may call  functions in other
          modules.   The functions  entry code will switch the execution context
          to the module s.  Unless the  .DLL switches stacks, it executes on the
          task s stack.    Execution context  switching  is transparent  to  the
          programmer, but incurs a penalty in CPU clock cycles.

               Processors  > 80286 cache  their segment  registers.   Well, they
          actually  cache  the  descriptors  pointed to  by  selector  registers
          (segment registers  in real mode).   As a  result, when a  selector is
          loaded, the CPU reloads  the descriptor cache, taking up  valuable CPU
          clock cycles.   A call causing an execution context  switch causes two
          descriptors to be  reloaded (one each  for the  code segment and  data
          segment).

               When a task calls  GetMessage, PeekMessage, WaitMessage, Yield or
          any other API that  causes a dialog box to  pop up, a task  switch may
          occur.  If there  are other tasks  waiting to be  executed and any  of
          those functions were called, Windows will switch the  next task in the
          task  queue in.    Although  task  switches  are  transparent  to  the
          programmer, a  misbehaving application  can prevent Windows  from task
          switching.     As  I  have  mentioned   earlier,  Windows  operates  a
          cooperative  multitasking system,  meaning  it is  up to  well-behaved
          applications to give their time-slice back to Windows periodically.  A
          task switch  incurs  a  lot  more CPU  clock  cycle  penanty  than  an
          execution context switch.

          Trap:     Code that causes too many task switches can cause Windows to
          slow  down due to  CPU clock cycle  penalty.  This  penalty is usually
          refered to as the multitasking overhead.
          When Should HINSTANCE, HMODULE and HTASK be Used?

               A number of Windows  functions accept and return HINSTANCE.   The
          following functions accepts HINSTANCE as a parameter.

          FreeModule

                                          - 27 -





          GetModuleFileName
          GetModuleUsage

               It may seem  strange that these  functions accept HINSTANCE  when
          they actually  deal with  modules.   The fact is,  both HINSTANCE  and
          HMODULE can be used.  It is relatively easy to obtain the HMODULE from
          a  given  HINSTANCE.   The header  file,  WINDOWSX.H contains  a macro
          definition  called  GetInstanceModule  that  accepts a  HINSTANCE  and
          returns a HMODULE.  It passes  the HINSTANCE to the low order word  of
          the lpszModuleName  parameter in  the call  to GetModuleHandle.   This
          function returns  the HMODULE.   The  following functions  also accept
          HMODULE in addition to HINSTANCE.

          FreeLibrary
          GetProcAddress

          The following functions return HINSTANCE:

          LoadLibrary
          LoadModule
          WinExec

               As  explained  earlier,  the GetModuleHandle  function  returns a
          HMODULE value.

               The  class registration  functions  (RegisterClass, etc),  window
          functions  (CreateWindow, etc)  and resource  functions (LoadResource,
          etc) all  accept  HINSTANCE.    MakeProcInstance  and  GetInstanceData
          expect HINSTANCE.
          To  extract HINSTANCE from a given HMODULE is not straightforward, and
          in some cases, impossible.  This is because .EXE modules can have more
          than  one execution context (one  per instance).   Therefore, given an
          .EXE  HMODULE, it is  still ambigiuos as  to which HINSTANCE  is to be
          extracted.  If  there is only one instance of  an application running,
          the ToolHelper library can be used to extract the HINSTANCE.

               However, it is possible to extract the HINSTANCE of a .DLL  given
          the HMODULE.  The following  function obtains the HINSTANCE of  a .DLL
          from a  HMODULE.  It  retrieves the full  path name of the  module and
          loads  it.   This  increments  the usage  count  of the  module.   The
          LoadLibrary functions,  in addition to loading the module, returns the
          HINSTANCE.   The module is then freed to decrement the usage count and
          the HINSTANCE returned.

          HINSTANCE GetDllModuleInstance (HMODULE hModule)
            {static char path[256];
             HINSTANCE hInstance;
             
             GetModuleFileName (hModule, path, 256);
             hInstance = LoadLibrary (path);
             FreeLibrary (hInstance);
             return hInstance;
            }

               It assumes that  the HMODULE passed to it  is a handle to  a .DLL
          module.  GASH.ZIP contains the full source code to the above function,
          in the INSTANCE subdirectory.


                                          - 28 -





          Tip: As it is easier to obtain HINSTANCE, it is recommended that it be
          used in place  of HMODULE where possible.   It is the  value passed to
          the  WinMain and LibMain  functions.  The  GetWindowWord function will
          return the HINSTANCE of a HWND if GWW_HINSTANCE is specified.

               The  GetCurrentTask function  returns the  HTASK for  the current
          application.  EnumTaskWindows and  PostAppMessage both accept HTASK as
          a parameter.   The ToolHelp  library contains  several functions  that
          allow all tasks to be enumerated.  
          List of Modules

               In  the INSTANCE\TEST subdirectory  of GASH.ZIP, there  is a test
          application  named,  TESTINS.EXE.    The  full  source  code  is  also
          contained  in the same subdirectory.   TESTINS is  an application that
          displays  a list of  modules in the  system, their names,  their usage
          count  and their full path name.   The Update button updates the list.
          As the list is not updated automatically, any modules added or removed
          from  the system  will not  show in  the list.   The  hInstance button
          displays  the hInstance  of the module  (provided it  is a  .DLL) in a
          messagebox.

               The ToolHelp library is used to obtain the list of modules in the
          system.    The ModuleFirst  function was  called  to return  the first
          module  in Windows  module  list.   Information  is returned  via  the
          MODULEENTRY data structure.   The ModuleNext function was  then called
          repeatedly until every module was enumerated.

          Tip: Although  the ToolHelp library is  meant to be  used by debugging
          applications,  it  is  by  no means  limited  to  these  applications.
          Non-debugging applications can make use  of the functions exported  by
          the  ToolHelp  library  to  get  access  into  Windows  internal data.
          ToolHelp  provides a documented set of API that allows applications to
          access undocumented Windows API.  To use the ToolHelp library, include
          the TOOLHELP.H header file.  All the exported functions are already in
          the default import library (IMPORT.LIB).

               When  a  selection is  made  in  the  listbox,  another  ToolHelp
          function  is  called, the  ModuleFindHandle.    This function  returns
          information  regarding a  hModule in  the MODULEENTRY  data structure.
          Information in  the structure is then  used to update the  usage count
          and path information boxes.

               When the hInstance button  is pressed, ModuleFindHandle is called
          again.    A  rudimentary method  was  used  to  determine whether  the
          selected module is a .DLL.   If it was  determined that it is a  .DLL,
          the GetDLLModuleInstance function is called to return the hInstance of
          the .DLL.
          Private Dialogbox Class
               The TESTINS  application uses a private  class modeless dialogbox
          as its main  window.  The application  is a simple  one.  There is  no
          need  for  the  normal   sort of  window.   A  dialogbox significantly
          simplifies the coding.  Why did I  use a private class dialogbox?   In
          this example, the main reason was instructional.

          Tip: Private class  dialogboxes provide  an easy  way to associate  an
          icon to a dialogbox.  To  create a private class dialogbox, define the
          dialogbox  template  in  the  resource file  with  the  optional CLASS
          statement.  Specify the name of the class.

                                          - 29 -





          MyDialogBox DIALOG 20, 20, 183, 193
          STYLE ...
          CLASS "myclassname"
          BEGIN
            .
            .
            .
          END

          In  your application, register the  class with the  handle to the icon
          assigned  to the  hIcon  field of  the  WNDCLASS structure.    Specify
          DefDialogProc  in the lpfnWndProc  field.  Important:   The cbWndExtra
          field must be larger or equal to DLGWINDOWEXTRA.

          if (hPrev == NULL)
            {wc.style = CS_HREDRAW | CS_VREDRAW;
             wc.lpfnWndProc = DefDlgProc;
             wc.cbClsExtra = 0;
             wc.cbWndExtra = DLGWINDOWEXTRA;
             wc.hInstance = hInstance;
             wc.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE (MAIN));
             wc.hCursor = LoadCursor (NULL, IDC_ARROW);
             wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
             wc.lpszMenuName = NULL;
             wc.lpszClassName = MAINCLASS;
             RegisterClass (&wc);
            }

          If you wish  to use any of  the window extra  bytes, you must add  the
          number  of extra bytes  to this value.   To access  those bytes, start
          from offset DLGWINDOWEXTRA instead of 0.  Use the usual  way to create
          the dialogbox.

          hWnd = CreateDialog (hInstance, "MyDialogBox", NULL, DlgProc);

          CreateDialog create  a modeless  dialogbox, while DialogBox  creates a
          modal  one.  Specifying DefDialogProc  in the lpfnWndProc field allows
          the   normal  way  of  using  a  dialogbox  (ie.  using  a  DIALOGPROC
          callback)  to  be employed.   This  is  easier than  writing  a window
          procedure that has DefDialogProc characteristics.

          Trap:     Failing  to set  the cbWndExtra  field to  DLGWINDOWEXTRA or
          larger will cause the USER module to GP fault!

          Trap:     Failing  to call  IsDialogMessage in  the message  loop will
          result  in  your dialog  box  losing  the  normal  dialogbox  keyboard
          interface (ie. tab moves from control to control).
          Metafiles revisited:
               Last   month,  we   looked  at  using   metafiles  saved   in  an
          application s  resources.   GASH.ZIP  contains the  source  code of  a
          function that loads a metafile from an instance s resources.  It is in
          the MET1.C file in  the METAFILE subdirectory.  The  associated header
          file, METAFILE.H, contains a #define that defines the  symbol METAFILE
          to the value 2000.  Include this file in your resource (.RC) file.  To
          include a metafile, use the following line;

          MetafileName  METAFILE "filename.wmf"


                                          - 30 -





          where MetafileName is the name  to be given to your metafile.   It can
          either be  a #define integer symbol or a string  symbol.  If you use a
          #define integer symbol, be sure to use the MAKEINTRESOURCE macro.

               I  also mentioned that Windows API cannot handle metafiles with a
          placeable  header.  In this instalment I will show you the format of a
          placeable  metafile  header  and  write  a   few  functions  that  can
          manipulate metafiles with placeable headers.  MET2.C and MET3.C in the
          METAFILE subdirectory  of GASH.ZIP  contains two functions  that reads
          and  writes to  disk  based metafiles  with  placeable headers.    The
          functions are GetMetaFileBetter and CopyMetaFileBetter.

               Metafiles with  placeable headers are ordinary  metafiles with an
          additional 22-byte header.  The following show the structure 


          typedef struct
            {DWORD   key;
             HANDLE  hmf;
             RECT    bbox;
             WORD    inch;
             DWORD   reserved;
             WORD    checksum;
            } METAFILEHEADER;

               The  key field  contains  the magic  number,  0x9AC6CDD7L.   This
          identifies  the metafile  as one  with a  placeable header.   Ordinary
          metafiles do not have an identifying signature.

          Tip: The  first  four  bytes  of  a  placeable metafile  contains  the
          following signature:  0x9AC6CDD7L.  To identity  ordinary metafiles is
          not as easy.   You can use the signature 0x90001L  but this may not be
          unique.  Metafiles created by Windows version 3.0 and 3.1 contains the
          value  0x300  in the  following  2 bytes.    This value  is  0x100 for
          metafiles created by earlier versions of Windows.

               The hmf and reserved fields should be set to 0.

               The bbox field specifies the bounding rectangle  of the metafile.
          It is specified in metafile units.

               The inch field specifies the metafile units in units per inch.

               The check sum field is  an XOR value of the first 10 words in the
          header.  The following C statement calculates this value:

          checksum = LOWORD (key) ^ HIWORD (key) ^
                     LOWORD (*((DWORD *) &bbox)) ^ HIWORD (*((DWORD *) &bbox)) ^
                     inch

               The LOWORD/HIWORD key  values can be replaced  with the constants
          0xCDD7 and 0x9AC6.

          The GetMetaFileBetter function

               The  GetMetaFileBetter function  reads a  metafile just  like the
          GetMetaFile function in Windows API.  However, GetMetaFileBetter knows
          how to distinguish between an ordinary metafile and a  metafile with a

                                          - 31 -





          placeable  header.  It accepts  one additional parameter,  lpMh, a far
          pointer  to the  METAFILEHEADER  structure.   If  a valid  pointer  is
          specified  and  a  metafile with  a  placeable  header  was read,  the
          placeable header will be copied into this structure.

               GetMetaFileBetter  loads  the first  22  bytes  of the  specified
          metafile.  It checks it this is a placeable header.  If it is not, the
          GetMetaFile  function  is  called to  load  the  metafile.   The  lpMh
          parameter  is not filled in.   If GetMetaFileBetter  finds a placeable
          header,  memory is allocated to  load the rest  of the metafile, minus
          the  placeable  header.     The  metafile  is  then  loaded   and  the
          SetMetaFileBitsBetter function  is then  called to convert  the loaded
          file into a memory metafile.

          The CopyMetaFileBetter function

               The CopyMetaFileBetter function copies a metafile to disk with an
          optional  placeable  header, otherwise  it  functions  similar to  the
          CopyMetaFile   function  in   Windows   API.     Unlike  CopyMetaFile,
          CopyMetaFileBetter accepts one more parameter,  lpMh, a far pointer to
          the  METAFILEHEADER structure.    If this  pointer  is valid  and  the
          structure filled  in correctly, CopyMetaFileBetter will  create a disk
          metafile with  a placeable header.   The placeable header will  not be
          copied  if a  memory  metafile is  specified  as the  destination  (by
          specifying NULL for the lpszFile parameter).

               If the  lpMh parameter is not a valid pointer or if the structure
          wasn t filled in correctly,  or if a memory  metafile is specified  as
          the  destination,  CopyMetaFileBetter calls  CopyMetaFile to  copy the
          metafile.  Otherwise, CopyMetaFileBetter  opens the specified file and
          writes  the placeable header.  It then calls the CopyMetaFile function
          to  create a  duplicate of  the metafile and  passes the  duplicate to
          GetMetaFileBits function  to retrieve a  handle to the  metafile data.
          This data is then written to disk and the file closed.

          An example usage of GetMetaFileBetter and CopyMetaFileBetter

               The   METAFILE\TEST   subdirectory   of  GASH.ZIP   contains   an
          application   (TESTMET.EXE)  that  tests   the  GetMetaFileBetter  and
          CopyMetaFileBetter functions.  TESTMET  reads in GASH1.WMF, a metafile
          with a  placeable header, and  displays it in  the client area  of its
          window.   It then saves  two metafiles, GASH-YES.WMF  and GASH-NO.WMF.
          GASH-NO.WMF is saved from the memory metafile read from GASH1.WMF.  It
          is an  ordinary metafile.   GASH-YES.WMF is  also saved from  the same
          memory metafile but it contains a placeable header.  You can use DOS s
          fc  to compare  GASH1.WMF and  GASH-YES.WMF.   GASH2.WMF  is GASH1.WMF
          without the placeable  header.   Use fc again  to compare  GASH-NO.WMF
          with  it.  By  the way, GASH1.WMF  is this article s  logo in metafile
          format.

          Next month...

          Next month, I will write on these topics:

          More metafiles DIBs in metafiles
          Dealing with misbehaving metafiles
          Decoding the placeable header information
          Text placement in metafiles

                                          - 32 -





          Placing a metafile
          Moveable windows    Child windows that resize and  move every time the
          parent is resized
          Window panes - splitting a window into resizeable panes
          MDI windows    Accelerator for each type of MDI windows
          Dialogboxes    Integer vs string resource names
          Dialogbox font
          Dialogboxes as child windows
          Dialogboxes as MDI child windows
          Coloring dialogbox controls
          Keyboard interface and hotkeys

          The author:

               I am currently doing  a PhD. degree in Electrical  And Electronic
          Engineering  at   the  University  Of  Canterbury.     My  programming
          experience dates back to  the days where  real programmers   code with
          Z80 machine code.  For the past two years I have taken an  interest in
          Windows programming.

               Please send any comments, questions, criticisms to me via:

          email: chuah@elec.canterbury.ac.nz
          or post mail:  Dennis Chuah
          c/o The Electronic and Electrical Engineering Department
          University of Canterbury
          Private Bag
          Christchurch
          New Zealand.

               All mail (including  hate mail) is  appreciated.  I  will try  to
          answer  all questions personally, or if the answer has general appeal,
          in  the next  issue.   If  you will  like  to see  a particular  topic
          discussed in up and coming issues, drop me a line.   I also appreciate
          discussions on topics published.  If you  are sending me email, please
          make  sure  you provide  me with  an address  that  I can  reach (most
          internet and bitnet nodes are reachable, and so is compuserve).






















                                          - 33 -





                                      Gdi See Gdi Do
                               Graphics Animation in Windows
                                     By Bernard Andrys

               Well, it's next month already and  I bet all 3 of my  readers are
          wondering when I'll turn my "Do Nothing Program" into a way cool, time
          wasting, blow'em'all'up arcade game.

               But first, a VERY, VERY important correction to the last article:


               As was  pointed out by Michael  Birk (one of the  three readers),
          using the PASCAL  keyword for a function  declaration does not make  a
          function  pass  parameters by reference.  I read  about the purpose of
          the PASCAL  keyword  in an  article  but I  really  should have  tried
          passing parameters using  the PASCAL keyword  before claiming that  it
          works.  I apologize for any confusion that it may have caused.

               ...and now back to blowing things up.

               In  this article,  we'll at  least get  something moving.   We're
          going  to put the missile base on the screen and make it move when you
          press the cursor  keys.   Let's start with  what we'll need  to put  a
          missile base on the screen and make it move:

          1. A missile base
          2. A way to put the missile base in the window.
          3. A way to link the keyboard to the drawing of the missile base.

               The first  requirement is pretty easy.  We need to define all the
          properties that a missile base has:

          1. Its appearance
          2. Its location
          3. How many lives it has left.
          4.  Whether it can shoot or not. (We're only going to let it shoot one
          missile at a time.)
          5. Has it been hit by  an invader's missile? (so we know if  we should
          draw an explosion instead of a missile base)

               We'll work on the first two  properties now and save the rest for
          later.

               The first property,  position, is its x and y coordinates.  We'll
          use  the variables x  and y  to represent the  missile base's  x and y
          coordinates.   Before  we use  the variables  x and  y, we'll  need to
          declare them.  Declaring a variable  tells the C compiler what type of
          variable is  going to be  used.   We should pick  a data type  for our
          variables x and y that is big enough to handle the largest values that
          might be assigned to x and y.

               So how big are the data types that we have to work with?

          Data Type           Size
          char                -128 to 127
          int                 -32,768 to 32,767
          unsigned char       0 to 255
          unsigned int        0 to 65,535

                                          - 34 -





          unsigned int        0 to 4,294,967,295
          float                    3.4E38
          double              1.7xE308
          void                nothing

               There are a lot of other data types supported by C compilers such
          as long int and  short int but considering that Win32  and NT are just
          around  the corner you probably  don't need the  confusion of learning
          X86  segmentation now. (Especially since this IS a beginner's column.)
          Windows  has its  own data  types as  well.   For example  HBITMAP and
          HINSTANCE are two data types that  are specific to Windows.  You would
          declare a variable as HBITMAP  if the variable was going to  contain a
          handle to a bitmap.  A handle is a number that a program uses to refer
          to a file, bitmap, or  some other object.  You don't normally  have to
          care  about  the  size of  Windows'  own data  types  like  HBITMAP or
          HINSTANCE  because Windows' own data types are normally used as return
          values  or parameters for Windows' own functions.   For example, if we
          used a function called LoadBitmap() it would return a variable of type
          HBITMAP.   The HBITMAP variable would then  be used where ever we want
          to refer to the actual bitmap.

          EXAMPLE:

          MyFunction()
          { 
          /* declare a variable called mybitmap which is data type HBITMAP */
          HBITMAP mybitmap;
          /* Window's function LoadBitmap() returns an HBITMAP variable */
          mybitmap = LoadBitmap(hInstance, "bitmap");
          }

               Actually,  Windows doesn't have its  own data types.   Data types
          like HBITMAP are defined in the file windows.h as an unsigned integer.
          You  declare  the  variable as  HBITMAP  instead  of unsigned  integer
          because the size of HBITMAP might  (and will) change under NT or maybe
          even in  future versions of  Windows like  Windows 4.0.   The size  of
          HBITMAP could even change  for each CPU  that the program is  compiled
          for (this is an NT consideration again).

               Now that we  know what data  types are available,  we can pick  a
          data type for x  and y.  Since the  window is 400 pixels high  and 400
          pixels  wide, we should  define x and  y as type  int.  We'll probably
          have to use the position of the missile base in many places throughout
          our  code so it's best  that we define x and  y as global variables by
          defining them in the header file "invid.h".

               The  second property, its appearance, is defined by a bitmap that
          we'll have represent the missile  base.  You can create the  bitmap of
          the missile base using any program that can create .bmp  files.  We're
          going  to use 16  color bitmaps so  be sure to  save the file  in that
          format.    Take a  look at  the bitmap,  base.bmp,  that I  created to
          represent the missile base.  

               Notice  that the bitmap has  black pixels to  its right and left.
          Since the missile base only moves  right and left, and the  background
          is solid  black, we can  cheat in creating  the bitmap animation.   We
          don't have  to use a bitmap mask to draw the bitmap without disturbing
          the background.  We also don't have to bother redrawing the background

                                          - 35 -





          when the base is  moved.  As long as we move the  base to the right or
          left 5 pixels or  less at a  time (There's a 5  pixel black border  on
          each side  of the base.),  drawing the base  covers over  the previous
          base  bitmap that was on  the screen.   This way we can  just draw the
          bitmap to the screen directly.  

               To use a bitmap in a Windows  program you first need to define it
          in your  program's .rc  file.   Here is the  contents of  the invid.rc
          file:

          #include "windows.h"
          invid     ICON      invid.ico
          base BITMAP    base.bmp

               We've  added the missile base's  bitmap (base.bmp) to  the end of
          the .rc file after the icon file.  The first entry, base, will be what
          we call  the bitmap  file in  the program's source  code.   The second
          entry,  BITMAP,  tells the  resource compiler  that  this is  a bitmap
          resource.   The last entry, base.bmp,  is the name of  the bitmap file
          that we want  to load.  Now  our program will contain the  bitmap as a
          resource  that can  be loaded and  used by  our program.   To  use the
          resource, you call  the function LoadBitmap(Program_Instance,  "bitmap
          name").   The first  parameter to  LoadBitmap() is  the handle  of the
          program instance.  So  we'll need to save the hInstance  variable that
          is passed to  WinMain() by assigning  it to our  own variable hInst.  
          That way we can use hInst when we call the LoadBitmap() function.  The
          second parameter  is the name of the bitmap that we defined in the .rc
          file.  The LoadBitmap function returns a variable of type HBITMAP that
          we  use whenever we want to  refer to the bitmap.   I added a variable
          called hbBase of type HBITMAP in our header file invid.h.


          Fast and Friendly Animation

               Now that we've  got our missile base, how do we display it on the
          screen? There are two ways we can go about it:

          1)  Display the missile base at its position every time we get a Timer
          message. 

          2)  Display the missile base at its position whenever it moves.

               The first  method would  be ideal for  multitasking friendliness.
          However, this method  is too slow.  At best,  windows can only provide
          18 timer messages per second.  I originally thought that this would be
          fast enough.   After  all, TV is  at 30 frames/sec,  movies run  at 24
          frames/sec  and Microsoft  Video  files look  fine  at 15  frames/sec.
          Unfortunately it  didn't work out that  way.  18 frames  per second is
          just  too slow for arcade game speed.   We want the animation to be as
          smooth as  possible.   That means  that if the  user tapped  the right
          cursor key  quickly, the missile base would move 1 pixel to the right.
          If  the user  held  the  cursor  key  down, the  base  would  move  18
          pixels/second across the screen.  However, our window is currently 400
          pixels wide.  So it would take  over 22 seconds to move the base  from
          one end of the screen to the other.  Twenty-two seconds is a LONG time
          in  arcade style  action  games.   This method  just  won't work  with
          Windows only sending  18 timer messages  per second.   Well, we  could
          make it work if we added motion blur but I'd rather not try and tackle

                                          - 36 -





          motion blur  ...yet.  We could  make this method work if  we could get
          Windows to send us more than 18  timer messages per second.  There are
          ways  of doing this using the  multimedia timer services but they seem
          fairly complicated to me.  (The real reason is that I've been too lazy
          to upgrade from Quick C which only supports the Win 3.0 API.)

               The second method is much easier to deal with but creates its own
          problems.  Instead of having our program only do something in response
          to   a  message,  we  can  write  our  program  to  run  continuously.
          Unfortunately we need to  be careful with this method  because we want
          our  program to  be friendly  to other  Windows programs and  not stop
          every other  Windows program just because ours is running.  To do this
          we  can  use Windows'  PeekMessage() function  to  see if  Windows has
          anything  else to do.  If it does,  we'll let Windows take care of the
          other programs  before  coming back  to  our program.    This way  our
          program will run continuously as  long as no other program  has things
          to do.   An obstacle to  using PeekMessage is that  you shouldn't keep
          your  program  in a  PeekMessage loop  because  it keeps  Windows from
          posting idle messages.  Another problem with this method is that since
          our program runs as fast as the CPU will allow, we will have to do our
          own  timing to  keep the  animation rate constant  no matter  what CPU
          we're running on.

               I  created two  sample programs  that demonstrate  the difference
          between timer based animation  and peek message based animation.   The
          code in the two programs is essentially the same.  Tball.exe creates a
          timer that sends 18 timer  messages per second (the maximum  under Win
          3.1).  

          Condensed code:

          int  PASCAL  WinMain(HANDLE  hInstance,  HANDLE  hPrevInstance,  LPSTR
          lpCmdLine, int nCmdShow)
          {
               MSG msg;
               if (!hPrevInstance) InitApplication(hInstance)); 
          InitInstance(hInstance, nCmdShow)
               while (GetMessage(&msg, NULL, NULL,NULL))
               {
                    TranslateMessage(&msg);
                    DispatchMessage(&msg);
               }
               return (msg.wParam);    
          }

          LONG FAR PASCAL WndProc(HWND hWnd, unsigned message, 
          WORD wParam, LONG lParam) 
          {
               switch (message)
               {
                    case WM_CREATE:
                    // create a timer 
                         SetTimer (hWnd, 1, 1, NULL);
                         // get handle to bitmap
                         hbBall=LoadBitmap(hInst, "ball");
                         break;

                    case WM_TIMER:

                                          - 37 -





          /* if a timer message arrives, draw the ball */
                         DrawBall();
                         break;

                    case WM_DESTROY:
                         KillTimer(hWnd, 1);
                         DeleteObject(hbBall);
                         PostQuitMessage(0);
                         break;

                    default:
                         return (DefWindowProc(hWnd, message, wParam, lParam)); 
               }
               return (NULL);
          }

          void DrawBall(void)
          {
               MoveBall();
               DrawBitmap(hbBall, x, y); 
               EraseOldBall();
               return;
          }


               If  you run Tball.exe, you'll  notice that the  ball moves fairly
          quickly and smoothly across the screen.  Unfortunately this is as fast
          as you can get with timer based animation without making the animation
          jerky.   The  menu selection  Speed allows  you to  control change  in
          position  of the ball  per timer message.   AnimeStep=1 means that the
          ball moves 1  pixel on every  timer message.  AnimeStep=50  means that
          the ball moves 50 pixels on every timer message.  Changing how far the
          object moves per  timer message is the only way to effectively control
          the speed of an object in timer based animation.

               In contrast,  if  you  run  Pball.exe,  you'll  notice  that  the
          animation is  smoother and faster.  The code is different in one area,
          the message  loop.  Instead of the while(GetMessage()) loop, we have a
          loop  that  runs  as long  as  a  WM_QUIT  message  is  not  received.
          PeekMessage() checks  to see if there  are any messages waiting  to be
          dispatched  by Windows.   If  there aren't  any messages  waiting, the
          function  DrawBall() is run.   Notice that WndProc()  doesn't do much.
          The  Setup, Control, and Exiting of the  Window is in WndProc, but all
          the animation is performed whenever Windows isn't doing anything else.

          int PASCAL WinMain(HANDLE hInstance, HANDLE hPrevInstance, 
          LPSTR lpCmdLine, int nCmdShow)
          {
               MSG msg;        
               if (!hPrevInstance) InitApplication(hInstance)); 
          InitInstance(hInstance, nCmdShow))
               while (TRUE)
               {
                    if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
                    {
                         if (msg.message == WM_QUIT) break ;
                         TranslateMessage (&msg) ;
                         DispatchMessage (&msg) ;

                                          - 38 -





                    }
                    else
                    {
          /* if no messages waiting, run DrawBall() */
                         DrawBall() ;
                    }
               }
               return (msg.wParam);
          }

          LONG FAR PASCAL WndProc(HWND hWnd, unsigned message, WORD wParam, LONG
          lParam) 
          {
               switch (message)
               {
                    case WM_CREATE:
                         hbBall=LoadBitmap(hInst, "ball");
                         break;

                    case WM_DESTROY:
                         DeleteObject(hbBall);
                         PostQuitMessage(0);
                         break;

                    default:
                         return (DefWindowProc(hWnd, message, wParam, lParam)); 
               }
               return (NULL);
          }

          void DrawBall(void)
          {
               MoveBall();
               DrawBitmap(hbBall, x, y); 
               EraseOldBall();
               return;
          }


               The menu item Speed works  the same in Pball as it did  in Tball.
          AnimeStep  controls how  many  pixels  the  ball  is  moved  in  every
          animation step.   But instead of  18 animation steps  per second,  you
          have as many animation steps as your CPU can crank  out before Windows
          has a message to process.   If Windows has  a message to process,  the
          ball stops moving while the message is handled.  The ball will then go
          back to  moving as soon as  there are no more  messages.  Fortunately,
          messages  are handled  so quickly  that you  don't see  any noticeable
          pause in the animation of the bouncing ball.

               So let's apply this PeekMessage animation technique to The Game:

          /* invid.c */
          #include <windows.h>
          #include "invid.h"

          /*---------- Global Variables --------*/
          /* In the real source code I keep my global variables in "invid.h" but
          it's easier for you to read the source code if I put them here. */ 

                                          - 39 -





          /* Declare a structure called decodeWord  that will be used by WinProc
          to run a function in response to a message */
          struct decodeWord {
                    WORD Code;
                    LONG (*Fxn)(HWND, WORD, WORD, LONG); };
          /*  An array  called  messages made  up  of decodeWord  structures  is
          created. The array  lists the  Windows Message and  the function  that
          will  be  called  when  that message  is  received.    I've  added two
          messages, WM_KEYDOWN  and WM_KEYUP.   When WM_KEYDOWN  or WM_KEYUP  is
          received, the function DoKeydown or DoKeyUp is run. */
          struct decodeWord messages[] = {
               WM_KEYDOWN, DoKeydown,
               WM_KEYUP, DoKeyup,
               WM_CREATE, DoCreate,
               WM_DESTROY, DoDestroy,} ;

          /* Declare a character string that contains the name of the program.*/
          char szAppName [] = "Invid";

          /*  Declare  a variable  called  hInst that  contains  the data  for a
          particular instance of the program. */
          HANDLE hInst;

          /*  Declare  a variable  called  hWnd that  contains a  handle  to the
          programs window. */
          HWND hWnd;

          /* Declare an integer  called AnimeStep.  AnimeStep is how many pixels
          a bitmap should be moved during animation. */
          int AnimeStep = 2;

          /* Declare two variables of type  BOOL (boolean).  That means that the
          variables can  be  a 1  or  a 0.    The variables  fRightKeyState  and
          fLeftKeyState will be set by the functions DoKeyUp and DoKeyDown.  The
          variables will then  be used by other functions so  that they can know
          the state of the right  and left cursor keys.  (I could  have used the
          Windows function GetAsyncKeyState() instead  of waiting for a keypress
          message to be sent by Windows.  But I wanted the program to be written
          in the standard Windows  programming style of waiting for  a message.)
          */
          BOOL fRightKeyDown = FALSE, fLeftKeyDown = FALSE;

          /*  Decalaring  a  structure   called  BaseStruct  that  will  contain
          everything  about the  missile base.    I could  have  just as  easily
          declared  x, y, and  Bmp as separate  variables.  Putting  them into a
          structure groups related variables  under one title and makes  it easy
          to remember what variable does what. */
          struct BaseStruct {
          /*  Declare  an  integer  called x  that  will  contain  the base's  x
          coordinate */
               int x ;
          /*  Declare  an  integer  called y  that  will  contain  the  base's y
          coordinate */
               int y ;
          /* Declare a variable called Bmp that contains a handle  to the bitmap
          of the missile base. */
               HBITMAP Bmp ;
               };

                                          - 40 -





          /* Declare a Structure called Base of type BaseStruct (defined above).
          You can now access the missile base's  variables x, y and Bmp by using
          Base.x, Base.y, and Base.Bmp. */
          struct BaseStruct Base;


          /*------------ WinMain ------------*/
          int  PASCAL  WinMain (HANDLE  hInstance,  HANDLE  hPrevInstance, LPSTR
          lpszCmdParam, int nCmdShow)
          {
            MSG msg ;
            hInst = hInstance;
            if (!hPrevInstance) InitApplication(hInstance);
            InitInstance(hInstance, nCmdShow);

          /* This is the old message loop from the last program. 
               while (GetMessage (&msg, NULL, 0, 0))
               {
                    TranslateMessage (&msg) ;
                    DispatchMessage (&msg) ;
               }
          */

          /* This  is the new message  loop (from Petzold).   Instead of calling
          GetMessage() to retrieve  a message  from Windows,  this message  loop
          calls PeekMessage  to see if there  are any messages waiting.   If the
          waiting message  is a WM_QUIT message  then break out of  the loop and
          end our program.  If the waiting message is not a WM_QUIT message then
          translate and dispatch the message.   If there are no waiting messages
          then  run our function  DoInv().  This  message loop  will allow other
          Windows programs to run at  the same time but  it will also keep  idle
          messages  from being  generated.   We'll have  to fix  it in  a future
          article to make it more multitasking friendly.  */
          while (TRUE)
               {
               if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
               {
                    if (msg.message == WM_QUIT) break ;
                    TranslateMessage (&msg) ;
                    DispatchMessage (&msg) ; }
               }
               else
                    {
                    DoInv() ;
                    }
               return (msg.wParam) ;
          }

          /*---------- InitApplication ---------*/
          BOOL InitApplication(HANDLE hInstance)
          {
               WNDCLASS wndclass;
               wndclass.style = 0 ;
               wndclass.lpfnWndProc = WndProc ; 
               wndclass.cbClsExtra = 0 ;
               wndclass.cbWndExtra = 0 ;
               wndclass.hInstance = hInstance ; 
               wndclass.hIcon = LoadIcon (hInstance, szAppName) ;

                                          - 41 -





               wndclass.hCursor = LoadCursor (hInstance, IDC_ARROW);
               wndclass.hbrBackground = GetStockObject (BLACK_BRUSH);
               wndclass.lpszMenuName = NULL ;
               wndclass.lpszClassName = szAppName ;
               return(RegisterClass (&wndclass));
          }

          /*------------ InitInstance ----------*/
          BOOL InitInstance(HANDLE hInstance, WORD nCmdShow)
          {
               hWnd = CreateWindow (szAppName,
                    "Invid",
                    WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU,
                    CW_USEDEFAULT, CW_USEDEFAULT,
                    WINDOW_WIDTH, WINDOW_HEIGHT ,
                    NULL,
                    NULL,
                    hInstance,
                    NULL) ;
               ShowWindow (hWnd, nCmdShow) ;
               UpdateWindow (hWnd) ;
               return (TRUE);
          }

          /*------------- WndProc ------------*/
          long  FAR  PASCAL WndProc  (HWND hWnd,  WORD  wMsg, WORD  wParam, LONG
          lParam)
          {
               int i;
               for(i=0; i < dim(messages); i++)
               {
               if(wMsg == messages[i].Code)
                    return((*messages[i].Fxn)(hWnd, wMsg, wParam, lParam));
               }
               return(DefWindowProc(hWnd, wMsg, wParam, lParam));
          }

          /*------------- DoCreate -----------*/
          /* DoCreate() is called  when our window receives a  WM_CREATE message
          at startup. */
          LONG DoCreate(HWND hWnd, WORD wMsg, WORD wParam, LONG lParam)
          {
          /*  Call a function called BaseInit that will initialize the variables
          in the missile base structure Base. */
               BaseInit();
               return 0;
          }

          /*------------ BaseInit -----------*/
          /*  This function  initializes  the variables  and  loads the  missile
          base's bitmap. */
          void BaseInit(void)
          {
               Base.x = 40;
               Base.y = WINDOW_HEIGHT - 80;
               Base.Bmp = LoadBitmap(hInst, "base");
          }


                                          - 42 -





          /*------------ DoDestroy ----------*/
          /* DoDestroy is called when our program receives a WM_QUIT message */
          LONG DoDestroy(HWND hWnd, WORD wMsg, WORD wParam, LONG lParam)
          {
          // Delete the handle to the missile base's bitmap 
          // before exiting. (otherwise Windows system 
          // resource's won't be released. 
               DeleteObject(Base.Bmp);
               PostQuitMessage (0) ;
               return 0 ;
          }

          /*----------- DoKeyDown ---------*/
          /*  DoKeyDown is  run  whenever  our  program  receives  a  WM_KEYDOWN
          message. */
          LONG DoKeydown(HWND hWnd, WORD wMsg, WORD wParam, LONG lParam)
          {
               switch (wParam) {
                    case VK_RIGHT :
                         fRightKeyDown = TRUE ;
                         break  ;
                    case VK_LEFT :
                         fLeftKeyDown = TRUE ;
                         break  ;
               }
                    return 0 ;
          }

          /*---------- DoKeyUp ------------*/
          /* DoKeyUp is run whenever our program receives a WM_KEYUP message.*/
          LONG DoKeyUp(HWND hWnd, WORD wMsg, WORD wParam, LONG lParam)
          {
               switch (wParam) {
                    case VK_RIGHT :
                         fRightKeyDown = FALSE ;
                         break  ;
                    case VK_LEFT :
                         fLeftKeyDown = FALSE ;
                         break  ;
               }
               return 0 ;
          }

          /*----------- DoInv -------------*/
          /* This function runs whenever Windows isn't doing anything else */
          void DoInv(void)
          {
               BaseAnimation (hWnd);
               return ;
          }

          /*----------- BaseAnimation -------*/
          /* This  function draws the bitmap of the base at a new position based
          on whether the cursor keys are pressed */
          void BaseAnimation (void)
          {
          /* Move the x coordinate of the missile base based on which cursor key
          is  pressed.  Base.x +=  AnimStep is  shorthand  forBase.x =  Base.x +

                                          - 43 -





          AnimeStep */
               if (fRightKeyDown) Base.x += AnimeStep; 
               if (fLeftKeyDown) Base.x -= AnimeStep;
          /* Keep the missile base on the screen */
               if (Base.x < 5)  Base.x = 5;
               if (Base.x > (WINDOW_WIDTH-50)) Base.x = WINDOW_WIDTH-50;
          /*  Another Petzold function slightly  modified to make  it simpler to
          use. (If you havn't bought Programming Windows by Charles Petzold yet,
          stop reading this and go to your local bookstore and buy it now!)  The
          function takes three arguments.  The first argument is a handle to the
          bitmap that will be drawn.  The second and third parameters are the  x
          and y position of the bitmap to be drawn. */
               DrawBitmap (Base.Bmp, Base.x, Base.y);
          }

          /*----------- DrawBitmap ------------*/
          /* This is where all the action takes place.  It's great for getting a
          bitmap on  your window as  simply as  possible.  Unfortunately  it has
          performance  problems when you  try and use  it for  all your graphics
          work. (This  might be why Windows doesn't have this function built in)
          */
          void DrawBitmap (HBITMAP hBitmap, int xStart, int yStart)
          {
          /* Declare  a handle  to a  device  context.   A handle  for a  Device
          Context is needed to draw to a window. */
               HDC hdc;
          /*  Declare  a  variable bm  of  type BITMAP.    It is  used  to store
          information about a bitmap such as its size and colordepth. */
               BITMAP bm ;
          /*  Declare a handle to  a device context.  This  handle will be for a
          block of memory that will be used as storage  for the bitmap that will
          be drawn. */
               HDC hdcMem ;
          /* Get a handle for a device context for our window. */
               hdc = GetDC(hWnd);
          /* Get a handle for a device context to a block of memory.  The memory
          context will be based on the device context of our Window. */
               hdcMem = CreateCompatibleDC (hdc) ;
          /* Put the bitmap into the block of memory that was created above. */
               SelectObject (hdcMem, hBitmap) ;
          /* Get information about the bitmap and put it in the structure bm */
               GetObject (hBitmap, sizeof (BITMAP), (LPSTR) &bm) ;
          /* The  BitBlt function copies  the bitmap in  memory (hdcMem) to  the
          screen (hdc) */
               BitBlt (hdc, xStart, yStart, bm.bmWidth, bm.bmHeight, 
                    hdcMem, 0, 0, SRCCOPY) ;
          /* Free up the resources we used before leaving */
               DeleteDC (hdcMem) ;
               ReleaseDC (hWnd, hdc);
          }

               When  you run  the program,  it will  put a  small bitmap  on the
          screen that moves back and  forth when you press the cursor keys.   If
          you're running it on  a fast 486, you'll notice that  it's too fast to
          be  controllable in  a game.   Fortunately  slowing down a  program is
          always easier than speeding one up.  

               You now  have the basics of  an arcade game:   Fast graphics with

                                          - 44 -





          user interaction.  In the next issue, we'll add the invaders and cover
          BitBlt'ing in detail.  

               I hope you  enjoyed my second article.  I spent  a lot of time on
          formatting details for both the .hlp  file and the .txt file.   In the
          .hlp  file  I  added bitmaps  and  linked  bitmaps to  run  the sample
          programs while you're  reading the .hlp file.   I also spent  a lot of
          time on the format of the source code with details such  as using bold
          for the code and blue highlights  for the start of functions.  If  you
          like this  formatting, (or  don't like  it!)  send me  or the  editors
          feedback.    


          About the author:

          (short version)
          SWM 25 ISO SWF.  PC Compatible, no experience necessary.

          (long version)
          Bernard  Andrys is a engineer  at CSI, an  ISDN networking development
          company.   He holds a bachelor's degree in Mechanical Engineering from
          the University of Maryland at College Park.  He can be reached through
          the  Internet  at  andrys@csisdn.com  or on  the  Windows  Programming
          Journal BBS.

          (longer version)
          I was born in the house my  father built.   ...No wait, that's someone
          else.  umm... Ok...  I bet you're wondering what a Mechanical Engineer
          is  doing working for a networking company and writing Windows program
          in his spare time.   Well I'm  wondering too.  If you find out,  write
          me.   ...and another  question.  Is  it the  company I keep  or do all
          programmers  like  The  Hitchhiker's Guide  to  the  Galaxy  and Monty
          Python?


























                                          - 45 -





                                       Windows Hooks
                                    By David S. Browne

          What is a Windows Hook ? 

               Ever  want  to  get  control just  when  Windows  processes  that
          keydown,  or at that mouse move ? Well,  Windows Hooks are for you ! A
          hook is really  nothing more than  Windows making a  call to a DLL  at
          various points  in Kernel, User, GDI,  etc.   There  are some gotchas,
          though, in using a hook and keeping Windows alive to process work, and
          that's what I  hope to  show here.   There are  three types of  common
          hooks in Windows applications  these days; 3.0 style hooks,  3.1 style
          hooks and Dynamic Intercept of Windows API functions for your own evil
          doings.   The first two  of these are  somewhat documented  in various
          Microsoft and OA ('Other Authors') books.  The last is not  documented
          by Microsoft at all, but has been shown in various trade publications.
          We will cover all  three of these, in reverse order. We will present a
          real application usage of hooks when we look at the documented 3.0/3.1
          hook functions.  The application  we will develop is a windows  pop-up
          (I know, I'm still stuck in DOS !)  that will allow you to activate it
          with any user defined hotkey and it will allow you to  END or KILL any
          running window  in your system.   The problem with PROGMAN  is that in
          many cases its  END-TASK simply doesn't do the job.  The target window
          must  be reading its  message queue to  see the WM_QUIT  message.  The
          KILL  function will  call a  nice new  TOOLHELP debugging  function to
          force kill any window, even if its not got its ear to the queue, so to
          speak.


          Dynamic Intercept Of Windows API functions

               Dynamic  Intercept of Windows API functions is not the main basis
          of this  article but  I wanted to  touch on the  subject a  bit before
          getting  on  with the  real  meat.  When  you  issue a  GetMessage  or
          BeginPaint API call in your Windows program,  it's nothing more than a
          call to a Windows system DLL to process this function.  It is possible
          to  intercept this to do any front  end processing (or for that matter
          back  end after  the real  API has  finished  playing) that  you wish.
          Windows provides  a function  ("GetProcAddress") that will  return the
          memory address of any function call you  wish to look at.   With  this
          information you  can store  a jump  at the DLL  routine entry  to your
          routine.

               It's actually a bit more complicated  than this as Windows has  a
          habit  of discarding  code  segments when  it's  short of  memory  and
          reloading  fresh copies  from disk  as needed.   So  for this  kind of
          dynamic  intercept  it's also  necessary to  use  the TOOLHELP  DLL to
          install a  NotifyRegister function so that you see the segment reloads
          to  re-apply your hook.  If this is  not done, then all will work fine
          until Windows decides that a bit  more memory is in order, purges some
          code segments (that your hook  is in !) then reloads a fresh copy and,
          Presto!  Chango! NoHookO !  It's really not  as complicated  as it all
          sounds; a very good example  of this was used in the May  1993 edition
          of Windows/DOS Developer's Journal.  If you want to start intercepting
          Windows  API functions then I  recommend you read,  then re-read, then
          sleep read the article ! Now to the beef !



                                          - 46 -





          Windows 3.0 and 3.1 Hooks 

               There  are  two  API's  to  set  a  hook  in   Windows,  the  3.0
          SetWindowsHook  and  the 3.1  SetWindowsHookEx.  The  basic change  is
          that  the 3.0  SetWindowsHook is  system wide,  while the  3.1 version
          allows a hook  to be JUST for  a given instance or task.   This allows
          you to develop  that Keyboard Hook you always wanted,  but were afraid
          of because of the overhead of getting called for EVERY SYSTEM KEYBOARD
          EVENT.    The example code  we present here though  uses a system-wide
          keyboard hook (in for a penny, in for a pound)  so no matter where the
          current focus  is we see  the keydown and  keyup.  In  addition to the
          hook function  ID the main  param you  must provide on  both of  these
          calls is the address of the function that you are providing to process
          these hooks.   This function must  reside in a DLL.   As with  the 3.0
          hook  or a system wide 3.1  hook, it can be called  in the instance of
          any task in the  system.  A DLL is  required so as to be  available to
          not just the API calling task.

               There are twelve hooks you can set with these functions:

               WH_CALLWNDPROC  -  WNDPROC Filter  Hook  for  all SendMessage API
          Call's
               WH_CBT - Computer Based Training Filter        
               WH_DEBUG - Called before any other hook 
               WH_GETMESSAGE -  Called when  an application issues  a GetMessage
          API 
               WH_HARDWARE   Called for  any non-keyboard or  non-mouse hardware
          message
               WH_JOURNALPLAYBACK - Allows user to substitute mouse and keyboard
          input
               WH_JOURNALRECORD  -  Allows  user  to  save  mouse  and  keyboard
          messages
               WH_KEYBOARD - Called when any key is struck. 
               WH_MOUSE  - Called for EVERY  mouse movement in  the system. (Did
          someone say Pentium ?)
               WH_MSGFILTER  - Called for user  messages to any  Dialog, Menu or
          Scroll Bar Window
               WH_SHELL - Called whenever a  new top level window is  created or
          destroyed.
               WH_SYSMSGFILTER - Called for system messages to any Dialog,  Menu
          or Scroll Bar Window

               Some documentation I have seen does not list the WH_SHELL hook as
          valid for SetWindowsHookEx.  It  is, it works, I've used it.   Now for
          the actual API calls:

          3.0
               STATIC  HHOOK  hook1 = NULL;
                                  .
                                  .
                                  .
               hook1 =  SetWindowsHook(WH_KEYBOARD,  (HOOKPROC)KeyHook);
                                  .
                                  .
               DWORD  CALLBACK  KeyHook(int iCode, WPARAM wParam, LPARAM lParam)
               {
               WhoKnowsOrCares();
               ?????????

                                          - 47 -





               }


          3.1
               STATIC HHOOK hook1 =  NULL;
                                   .
                                   .
                                   .
               hook1    =     SetWindowsHookEx(WH_KEYBOARD,   (HOOKPROC)KeyHook,
          hInstance, NULL);
                                   .
                                   .
               DWORD  CALLBACK  KeyHook(int iCode, WPARAM wParam, LPARAM lParam)
               {
               WeNowKnowAndCare();
               CallNextHookEx(hook1, iCode, wParam, lParam);
               return(0);
               }

               Simple right ? Really it is ! Honest ! Have I ever lied to you ?

               Now to discuss an actual application usage for all this.

               As I said, when you pop up Windows TASKMAN with the ever faithful
          Ctrl-Esc we have an END-TASK  Button.  Ever had a case where it didn't
          end that misbehaving  task? Well that's because all it  does is send a
          WM_CLOSE message to the application.  If it is processing its messages
          normally it will see this message then terminate.  I  don't know about
          you  but  whenever  I use  that  button  the  application is  normally
          stalled!   If  I could  end the  application I  would have done  it by
          selecting  close from the Application  itself!  OK,  now we understand
          the problem.  Solution time.   We will implement a WH_KEYBOARD hook to
          scan for any ALT-S  key sequence and if we see it pop up a window that
          will list all active top level windows on it and allow the user to END
          the  task (same as the  dreaded TASKMAN does!)  or KILL the  task by a
          call to the new 3.1 TOOLHELP DLL TerminateApp() function.  The program
          is implemented  in  two  separate modules  WINSWAT,  the  main  window
          procedure and SWATDLL the DLL with the keyboard hook function.  If you
          look at WINSWAT it will look as any normal Windows  C program does, it
          has a WinMain function that sets up a main window  for the application
          and  it  has a  WndProc  for  all the  real  processing.   During  the
          WM_CREATE processing, we make  a call to InitialiseSwat to  SWATDLL to
          setup the keyboard hook.  The code in SWATDLL looks like this:

               void WINAPI _export InitialiseSwat(BOOL fAction, HWND hwnd)
               {
               if (fAction)
                    {
                    OutputDebugString("SWATDLL: Pre--SetHook\n");
                    hKhook     =    SetWindowsHookEx(WH_KEYBOARD,     (HOOKPROC)
          KeyboardHook,
                         hInstance, NULL);
                    OutputDebugString("SWATDLL: Post-SetHook\n");
                    hTASK = GetWindowTask(hwnd);
                    hHWND = hwnd;
                    }
               else
                    {

                                          - 48 -





                    UnhookWindowsHookEx(hKhook);
                    hKhook  = NULL;
                    hHWND = NULL;
                    hTASK = NULL;
                    }
               }

               This  sets up a hook callback to KeyboardHook for keyboard events
          from any task in the system  (The last NULL param says all tasks,  but
          you could  substitute a task  value here if  you ONLY wanted  keyboard
          events for that  task).  The HWND param we save  here is so that later
          in the  actual keyboard hook code  we can PostMessage to  that window.
          The  last part  of the  code is  for termination  to remove  our hook.
          After  this processing is complete, WINSWAT simply hides itself with a
          ShowWindow call and waits to be woken up with a message from SWATDLL.

               If you look at the KeyboardHook code in SWATDLL you will see that
          we check for our key, the S key, then we make sure the ALT key is  on.
          If all this  is true we  PostMessage a message  to WINSWAT so  that it
          will wake up.  Take a look:


          DWORD CALLBACK KeyboardHook(int nCode, WPARAM wParam, LPARAM lParam)
               {
               static BOOL fPost;
               BYTE iBit;
               WORD iWord;
               static UINT iQky = 83;

               if (nCode >= 0)
                    if (wParam == iQky)
                         {
                         OutputDebugString("SWATDLL: Key Hook Hit\n");
                         iWord = HIWORD(lParam);
                         iBit  = HIBYTE(iWord);
                         if (iBit & 0x20)
                              {
                              fPost  = PostMessage(hHWND,  WM_COMMAND, WAKESIGL,
          0);
                              return 1;
                              }
                         }

               CallNextHookEx(hKhook, nCode, wParam, lParam);
               return 0;
               }

               When the WAKESIGL message is  received in the WndProc  WM_COMMAND
          processing  section, it simply changes the Main Window to SHOW status,
          updates the listbox with all top level window names and then waits for
          a command.  If a  KILLBUTN is received, it is used  the TerminateApp()
          function to really SWAT the thing out of Windows!

               hTask = GetWindowTask(WorkHwnd);
               TerminateApp(hTask, NO_UAE_BOX);

               As  you can see it's really  not at all hard to  use hooks.  They
          can  add functionality  to any  application when  used in  the correct

                                          - 49 -





          manner.   Don't  substitute hooks,  though, for  things that  could be
          easily  done with normal Windows messages like WH_KEYDOWN, etc.  Hooks
          can add  overhead to your  system, and that's something  no one wants.
          Used correctly, however, they can add a multitude of features to  your
          application.

               All the source  code, H  files, Icons, Definition  file etc.  are
          included with this WPJ edition, take a look at it and get Hooking!



















































                                          - 50 -





                                   Shared Global Memory
                                      By Dennis Chuah

               In  this  article I  will present  a  way to  implement shareable
          global memory in Windows 3.1.  It  is also the aim of this article  to
          demonstrate two different methods  of detecting whether an application
          has been loaded or not.  Although the code is written in C, aiming for
          the Borland  C++ compiler, the  binaries produced  and the  associated
          header  file can  be  used  by  any other  C  compiler  that  supports
          programming in Windows.  It is also possible to use the libraries with
          Turbo Pascal or  Borland Pascal with the proper import definitions.  I
          am not an expert  in Pascal and I do not own a  Pascal compiler.  So I
          hope  someone who  is better versed  in Pascal  can write  a unit that
          allows the libraries to be used in Pascal programs.

               First,  let me introduce a  few concepts of  memory allocation in
          Windows 3.1.  Windows  3.1 only runs in  protected mode and  therefore
          most of  the memory allocation practices  that were once used  in real
          mode are now obsolete.  Prior to version 3.0[1], Windows  runs in real
          mode.  The  API inherits a number of real  mode features from previous
          version of  Windows.  In  real mode, Windows  needs to perform  memory
          moves  to accommodate  a multitasking environment.   This  is achieved
          through handles.   When a  block of memory  is allocated, a  handle is
          assigned to  it.  The  owner can request  that the block  of memory be
          locked  in memory  (meaning it  will not  be moved  by Windows).   The
          locking process also retrieves a pointer to the block of memory.  When
          the owner no longer needs  to access the block of memory, it  can tell
          Windows to  unlock it.   Windows  is then  free to  move the  block of
          memory around.   The next time the owner needs  to access the block of
          memory, it locks it and  retrieve another pointer.  The value  of this
          pointer is not necessary the same as the previous pointer it retrieved
          (Windows  may have  moved  the  block  of  memory  elsewhere).    This
          complicated process is necessary because  in real mode, pointers point
          to  physical  addresses.   The  only  way  for  Windows to  inform  an
          application  that a  block of  memory  it owns  has been  moved is  by
          passing a new pointer to it.  The locking process ensures that Windows
          does not move memory that is still being actively used.

               In  protected mode  however, pointers  point to  logical address.
          Each  pointer, instead  of  having  a  segment:offset address,  has  a
          selector value and an offset address.  The selector points to an entry
          in a  global table (The Global Descriptor  Table -- GDT) maintained by
          Windows.   When a memory access occurs, the CPU translates the logical
          address in a pointer to physical  address using the GDT.  This process
          is  transparent to software.   When Windows running  in protected mode
          needs  to move  blocks of  memory, it  only needs  to update  the GDT.
          Locked memory  can also be  moved as the CPU  translates memory access
          via  the GDT  transparently.   Therefore the  process of  allocating a
          handle, locking it and unlocking it becomes obsolete.  The  owner of a
          block of memory only needs to maintain a pointer to it.  The following
          table illustrates the differences in  the process of allocating memory
          under different modes.

          Real mode
          Protected mode

          Allocating Memory
          handle = GlobalAlloc...

                                          - 51 -





          handle = GlobalAlloc...
          ptr = GlobalLock (handle);

          Accessing memory
          ptr = GlobalLock (handle);
          *ptr = ...
          GlobaalUnlock (handle);
          *ptr = ...

          Freeing memory
          GlobalFree (handle);
          handle = LOWORD (GlobalHandle
             (SELECTOROF (ptr)));
          GlobalUnlock (handle);
          GlobalFree (handle);


               The  extra work  in maintaining  handles in  protected mode  is a
          legacy  inherited  from  real   mode.    It  is  there   for  backward
          compatibility.   For  a  more detailed  discussion  of Windows  memory
          allocation I  suggest  C. Petzold,  Programming  Windows --  2nd  Ed.,
          (1990)  Microsoft  Press.   For more  information  in the  Intel 80x86
          protected  mode  memory  management,  consult  Intel s,  80386  System
          Software  Writer's Guide , (1988)  Intel, Intel's,  80386 Programmer's
          Reference  Manual,  (1988)  Intel,  and  J Crawford  &  P.  Gelsinger,
          Programming the 80386, (1987) SYBEX Inc.


          Shared Memory

               Normally global memory allocated to an application belongs to the
          application  and no other application can access it.  Should any other
          application  happen  to  access  it,  a  GP  fault  will  occur.    To
          accommodate DDE, Windows 3.0 enables blocks of global memory allocated
          with  the  GMEM_DDESHARE  flag  to  be  shared  by  all  applications.
          According to the API documentation, memory allocated with this flag is
          automatically freed when the owner terminates.

               Now, suppose  we want to allocate  a block of memory  that can be
          shared between two or more applications.  One application can allocate
          it  with  the GMEM_DDESHARE  set and  pass  the handle/pointer  to the
          others.   This  is  fine, provided  the  owning application  does  not
          terminate  before any  of the  others that  make use  of the  block of
          global memory.   In the   user friendly  environment  of Windows,  the
          programmer cannot guarantee the life of  an application.  In fact, the
          programmer  should  not  write  code  that  exhibits  this  behaviour.
          Imagine the  frustration of  the user  of not being  able to  close an
          application if it was the first one in a group to be opened!

               To  get around  this problem,  we must allocate  shareable global
          memory  that can  live  longer than the application that allocated it.
          I  wrote a  .DLL and  a  background   application that  only allocates
          shareable  global memory.    It involves  writing an  application that
          always remain  active, but is hidden  from the user.   All requests to
          allocate shareable memory  is made through the .DLL and carried out by
          the hidden application.   This  way, every block  of shareable  memory
          that  is allocated remains  allocated until  explicitly freed  or when
          Windows is terminated.  This means that an application can request the

                                          - 52 -





          .DLL to  allocate a block of memory on its behalf, pass the pointer to
          other applications and  terminate.  When  the block  of memory is  not
          required anymore, it can be freed.

               More  specifically, a  .DLL when  called for  the first  time can
          allocate shareable  memory and  store the  pointer away.   When it  is
          called again (probably  from another  application), it  can reuse  the
          block of memory, without causing a GP fault.


          The hidden application

               Included is a  file named SHAREMEM.ZIP.   The source code of  the
          hidden  application  (_MALLOC.EXE) is  found  in  the root  directory.
          Unzip this file with the -d flag to preserve its directory  structure.
          The binaries, ie. the .DLL and the .EXE must be placed in  the Windows
          directory, the System directory or any other directories pointed to by
          the PATH environment variable.

               There are two sections in _MALLOC.EXE.  The first initialises and
          registers the hidden window class.  It also checks if there is another
          instance of itself in memory.  It will not  load if it found one.  The
          second  section  contains  a hidden  window  (a  message  target) that
          accepts requests to allocate shareable memory.

               _MALLOC.EXE check whether there is another instance of itself via
          the  call to  RegisterClass.   RegisterClass  fails if  an application
          registers more  than once across instances.   This is a  simple way to
          check if another instance exist.  The hidden window is registered as a
          global  class  with  the  CS_GLOBALCALSS style.    When  RegisterClass
          attempts to register  a second class  instance of a  global class,  it
          will  fail.  This prevents  the user from  accidently running a second
          instance of MALLOC.EXE.


          The .DLL

               The .DLL (MALLOC.DLL) interfaces MALLOC.EXE to  applications that
          make  use   of  it.    It  is  located  in  the  MALLOC  directory  of
          SHAREMEM.ZIP.   Like _MALLOC.EXE, _MALLOC.DLL  contains two  sections.
          The first  section performs initialisations and  checks if _MALLOC.EXE
          has been loaded.  If it not, it will be loaded.   If MALLOC.DLL cannot
          load MALLOC.EXE, the  initialisation will  fail.   The second  section
          contains the shareable memory allocation API.

               MALLOC.DLL  determines whether  _MALLOC.EXE  is  loaded by  first
          registering  a pivate Windows message.   It then creates a hidden test
          window  and broadcasts  the message  to  all top  level windows.   The
          wParam of the message contains the handle of the test window.

          HWND hWnd;
          HINSTANCE hInst;
          #define MAL_ACK 1
          #define TEST_ID 0x41A5
            .
            .
            .
          static void _gethwnd (void)

                                          - 53 -





            {HWND hwndTest;

             hwndTest = CreateWindow (TEST_CLASS, "", WS_OVERLAPPED,
                0, 0, 0, 0, NULL, NULL, hInst, NULL);
             if (hwndTest == NULL) return;
             ShowWindow (hwndTest, SW_HIDE);
             SendMessage (HWND_BROADCAST, uTestMsg, (WPARAM) hwndTest,
                MAKELPARAM (0, TEST_ID));
             DestroyWindow (hwndTest);

             if (hWnd == NULL)
               {WinExec ("_MALLOC.EXE", SW_HIDE);
                hwndTest = CreateWindow (TEST_CLASS, "", WS_OVERLAPPED,
                   0, 0, 0, 0, NULL, NULL, hInst, NULL);
                if (hwndTest == NULL) return;
                ShowWindow (hwndTest, SW_HIDE);
                SendMessage (HWND_BROADCAST, uTestMsg, (WPARAM) hwndTest,
                   MAKELPARAM (0, TEST_ID));
                DestroyWindow (hwndTest);
               } // endif
            } // end gethwnd

               The  _gethwnd  function  first tests  to  see  if  _MALLOC.EXE is
          loaded.  If not, it will attempt to  load it.  It then tests again  to
          see  if _MALLOC.EXE is loaded.  If  the second check fails, it assumes
          that it has failed  to load _MALLOC.EXE and the  initialisation fails.
          Every time the .DLL is called it checks whether the initialisation was
          successful.  If it was not, the .DLL will return with error.

               _MALLOC.EXE also registers the same Windows message.  Its message
          procedure contains the following code extract.

          UINT uRegMessage;
          #define MAL_ACK 1
            .
            .
            .
          if (msg == uRegMessage)
            {return SendMessage ((HWND) wParam, WM_COMMAND, HIWORD (lParam),
                MAKELPARAM (hWnd, MAL_ACK));
            } // endif

               Where    uRegMessage    is   the    value    returned    by   the
          RegisterWindowsMessage function.

               When it receives  the registered message,  it sends a  WM_COMMAND
          message  to the  sender with its  hWnd in  the loword of  lParam.  The
          following code extract is from the hidden test window of MALLOC.DLL:

          LRESULT CALLBACK TestProc (HWND hWndTest, UINT msg, WPARAM wParam, 
                                     LPARAM lParam)
            {if (msg == WM_COMMAND)
               {if (wParam == TEST_ID && HIWORD (lParam) == MAL_ACK)
                  {hWnd = (HWND) LOWORD (lParam);
                  } // endif
                return 0;
               } // endif
             return DefWindowProc (hWndTest, msg, wParam, lParam);

                                          - 54 -





            } // end TestProc

               The test  window  then saves  the  hWnd of  _MALLOC.EXE s  hidden
          window to the  variable hWnd.   As hWnd is  initialised to NULL  every
          time the .DLL  is loaded, a NULL value indicates  that the test window
          did not receive the WM_COMMAND message.  This implies that _MALLOC.EXE
          has not been loaded.

               The second section of MALLOC.DLL contains these four API function
          calls.

          API function name
          Description/function

          gmalloc
          Allocates a block of shareable global memory.

          grealloc
          Changes the size of an already allocated block of global memory.

          gfree
          Frees a block of shareable global memory.

          gisptr
          Checks  if a  given  pointer points  to  a block  of shareable  global
          memory.


               The gmalloc  function first  checks if  _MALLOC.EXE is  loaded by
          calling _gethwnd.   It fails if _MALLOC.EXE cannot be loaded.  It then
          sends the MAL_ALLOC message to the hidden window of _MALLOC.EXE.   The
          return  value of  the  message is  a  pointer to  the  newly allocated
          shareable global  memory.  If there  was an error in  the process, the
          return  value will  be  NULL.   The  following  code  extract is  from
          _MALLOC.EXE.

          void far *lpPtr;
          HGLOBAL handle;
            .
            .
            .
          case MAL_ALLOC:
             handle = GlobalAlloc (GMEM_DDESHARE | GHND, (DWORD) lParam);
             if (handle == NULL) return NULL;
             lpPtr = GlobalLock (handle);
             return (LRESULT) lpPtr;

               This code simply allocates shareable global memory,  locks it and
          returns a pointer to it.

               The  grealloc   function  checks   if  initialisation  has   been
          successful.    It then  sends the  MAL_REALLOC  message to  the hidden
          window  of _MALLOC.EXE.    It fills  the  REALLOCSTRUCT structure  and
          passes a pointer in the lParam of the message.  The process is similar
          to gmalloc s.   When _MALLOC.EXE receives the MAL_REALLOC message, the
          following code extract processes it:

          typedef struct tagREALLOCSTRUCT

                                          - 55 -





            {DWORD dwSize;
             void far *lpPtr;
            } REALLOCSTRUCT, far *LPREALLOCSTRUCT;

          LPREALLOCSTRUCT lpRa;
            .
            .
            .
          case MAL_REALLOC:
             lpRa = (LPREALLOCSTRUCT) lParam;
             handle = (HANDLE) LOWORD (GlobalHandle (SELECTOROF (lpRa->lpPtr)));
             if (handle == NULL) return NULL;
             if (GlobalUnlock (handle)) return NULL;
             handle = GlobalReAlloc (handle, lpRa->dwSize, GMEM_ZEROINIT);
             if (handle == NULL) return NULL;
             lpPtr = GlobalLock (handle);
             return (LRESULT) lpPtr;

               The handle to the allocated memory block is first retrieved via a
          call  to   GlobalHandle.     The  memory   block  is   then  unlocked.
          GlobalUnlock returns the number of times a handle has been locked.  If
          the return  value is  greater than zero  (meaning the memory  is still
          locked),   this  message   returns  NULL.     Otherwise,  a   call  to
          GlobalRealloc is then issued  to reallocate the memory.  The  block of
          memory is then locked and the pointer returned.

               The gfree function  sends the  MAL_FREE to the  hidden window  of
          _MALLOC.EXE.  The  process is  similar to grealloc s  except that  the
          block of memory is freed instead of reallocated.

               The following code extract shows how gisptr determines whether  a
          pointer points to shareable global memory:

          BOOL WINAPI gisptr (void far *lpPtr)
            {HGLOBAL handle;

             handle = (HGLOBAL) LOWORD (GlobalHandle ((UINT) FP_SEG (lpPtr)));
             if (handle == NULL) return FALSE;
             if (FP_OFF (lpPtr) != NULL) return FALSE;
             return TRUE;
            } // end gisptr

               First,  it tries to retrieve  the handle to  the block of memory.
          If that fails the pointer  is not valid.  It then checks if the offset
          of the  pointer is 0 (NULL).   Windows 3.x GlobalLock  function always
          return  pointers with  0 offset.   This  is not  documented so  it may
          change.   The aim  of this  function is to  provide an  alternative to
          Windows 3.x IsBadHugeReadPtr and IsBadHugeWritePtr API functions.


          My methods:

               You  may argue  that  you have  a  better method  of  determining
          whether  an   application  is   loaded  (i.e.,  Using   FindWindow  or
          GetModule).  Well, you may be right.  I justify my methods because;

          1.   they are different,
          2.   they  serve to illustrate the fact  that there are more ways than

                                          - 56 -





          one to achieve something in software, and
          3.   they  are  meant  to demonstrate  alternative  (and interesting?)
          ways.

               _MALLOC.EXE  creates a  hidden window,  which only purpose  is to
          receive  messages.   I call  this kind  of windows   Message Targets .
          They  serve only  as a  method of  communications.   Windows does  not
          provide an object  that an  application can use  to receive  messages.
          The only way  is to use a  window that is always hidden.   _MALLOC.EXE
          has no other  window.  It  is a hidden  application, meaning the  user
          will never see  it on the screen.   In a way,  it is like a  .DLL.  It
          provides services that  other application can use.  You  might ask why
          don t  I use  a .DLL  instead.   The problem  is any  shareable global
          memory allocated by a .DLL is owned by the  application that requested
          it.  Our aim  is to allocate global memory that  has a longer lifespan
          than the  application.   To achieve  that, the global  memory must  be
          allocated   by  an  application  that  is  always  active.    As  this
          contradicts with  the notion that the  user should have the  choice of
          terminating applications, _MALLOC.EXE is made to be hidden.


          Notes:

          [1]  -  Although prior  to  WIndows 3.0,  there were  Windows  286 and
          Windows 386, these were relatively unpopular and hence this discussion
          excludes them.


          Other compilers/languages:

               The source code  provided will compile  with Borland C++  version
          3.1.   To use the  binaries with other  C compilers, use  the included
          wmalloc.h (This is  located in the INCLUDE  directory of SHAREMEM.ZIP)
          header  file,  the  associated binaries  (_MALLOC.EXE  and  MALLOC.DLL
          --These can be  found in the BIN directory of  SHAREMEM.ZIP. ) and the
          import  library, MALLOC.LIB (This is  located in the  LIB directory of
          SHAREMEM.ZIP).  Windows.h  must be included  before wmalloc.h.   Turbo
          Pascal users may  want to create external  referenced import functions
          to link up with MALLOC.DLL.   Visual Basic and Word for  Windows users
          can  use   the  declare  function/sub  statements  to   link  up  with
          MALLOC.DLL.

               Next article  we will look  at ways to  use the shareable  global
          memory blocks.


          The author:

               I am currently doing  a PhD. degree in Electrical  And Electronic
          Engineering  at   the  University  Of  Canterbury.     My  programming
          experience dates back to  the days where  real programmers   code with
          Z80 machine code.  For the past two years  I have taken an interest in
          Windows programming.

               Please send any comments, questions, criticisms to me via
          email: chuah@elec.canterbury.ac.nz
          or post mail:  Dennis Chuah
          c/o The Electronic and Electrical Engineering Department

                                          - 57 -





          University of Canterbury
          Private Bag
          Christchurch
          New Zealand.

               All mail (including  hate mail)  is appreciated.   I will try  to
          answer  all questions personally, or if the answer has general appeal,
          in  the next issue.  If you are sending me email, please make sure you
          provide me  with an address that I can reach (most internet and bitnet
          nodes are reachable, and so is compuserve).

















































                                          - 58 -





                           Customizing FileDialog in Visual C++
                                        By Tony Lee

          Overview

               I  have developed two application with Visual  C++.  Both of them
          are fairly good size applications with > 300K lines of C++ source code
          each.  I use  common dialogs  extensively in  my projects.     In this
          article,  I  will  try  to  show you  how  to  use  and  customize the
          CFileDialog  class in the Visual C++ environment.    I would also like
          to  share with  you why  I think  C++ and object  oriented development
          environment is  such a great tool  for developing Windows programs.   
          Since I  have limited time  to write  this article, I  want to   refer
          directly to the WinSDK help for most of the  basic information related
          to  Common  Dialog.     My  background:   I  have  been doing  Windows
          programming for  4 months now.   One of  the applications I wrote is a
          diagnostic  utility   for  a   complicated  instrument.     The  other
          application I wrote  is a C/C++ cross reference utility  that lets you
          cross   reference  C/C++  source  code.    As  you  can  see,  Windows
          programming with C++ is not hard at all.  


          What is Common Dialog?

               Common dialog is a set of dialog box functions.   These functions
          simplify the programming for  the common dialogs such as  File, Color,
          Font  and Print  Dialogs for Windows.   You can  find more information
          about them in the  "Common Dialog Box OverView" section of the Windows
          SDK  Help.   [Editor's note:  The common  dialog routines  are in  the
          COMMDLG.DLL file - mfw]


          Why C++?

               One  of  the best  reasons for  using  C++ in  developing Windows
          programs  is encapsulation.  The  class structure of  the C++ langauge
          hides the most  implicit details from you.   For example, to implement
          the FileDialog using C and SDK, you have to  write about 40 lines of C
          code.    (You  can count them  yourself in the  "Filename Dialog Boxes
          (3.1)" part of the Windows 3.1 SDK help.)  You also need to understand
          the "OPENFILENAME" structure and the "GetOpenFileName" functions. 

               To  implement the same open  file dialog function  in C++, you do
          the following:

               Void OnOpenFile() {
                    CFileDialog  dlg(TRUE,  "txt",  NULL,   OFN_FILEMUSTEXIST  |
          OFN_HIDEREADONLY,  "Text Files (*.txt)  | *.txt  All Files  (*.*) |*.*
          ||");

                    if (dlg.DoModal() != IDOK) return ;
                    CString sFilename = dlg.GetPathName();
                    // You can use the filename now.
               }

               As you  can see, the  C++ implementation  of the same  feature is
          much  more elegant and simpler than the C implementation.  CFileDialog
          encapsulates all of the structures and function initializations.   

                                          - 59 -






          Drawback of C++

               What  is the drawback of  using C++ in  implementing your Windows
          project?  In my honest opinion, the best reason  for using C++ is also
          the  biggest drawback.     If your  project's features  fit  the class
          library  very well,  you can  use the  class directly  or with  little
          modifications.    However, if  you want to  differentiate your product
          from  what's  avaiable in  the  market, you  will  have to  modify the
          existing class library  by creating inheritance  from the base  class,
          understanding the class interface  and implementing the needed virtual
          functions of the  inherited class.   The benefit  of the encapsulation
          in a class library also hide a lot  of details from you.   You have to
          spend  time to peel the layers  off the class hierarchy and understand
          each  layer's functionalities so your  new class adds  features to the
          existing  class in an optimized  fashion. From my  experience of doing
          C++ projects, to do  a reasonable job in  deriving a new class from  a
          class framework you should do the following:

               1. You  need to understand the existing  class hiearchy.  In this
          example,  you should know that the "CFileDialog" class is derived from
          the "CDialog" class.  CDialog is derived from the "CWnd" class, and so
          on.   You can find out  the MFC class hierarchy  relationship from the
          MFC Help's "Hierarchy Button".

               2.  You need  to  understand what  are the  public/protected data
          members and methods for every classes in that particular hiearchy.

               3.  You need to understand  the virtual functions in the hiearchy
          and  how  these  virtual  functions  interact  between layers  of  the
          classes.     For  example,  you should  know  that  the Windows  SDK's
          subclass mechanism  is inherited  in the "virtual  LRESULT WindowProc(
          UINT message, WPARAM wParam, LPARAM lParam ); " function.   

               As  you can see, encapsulation really  complicates the process of
          understanding the  class library.   In my project, in  order to extend
          the existing class successfully, I have to spend about five times  the
          amount of time to learn  the existing C++ code  than C code.  After  I
          understand the existing  C++ code,  I spend about  the same amount  of
          time writing  the new code.    The biggest benefit  of object oriented
          design  is that the  interface to that particular  object is very well
          defined by the class methods.   Almost all of the modifications I made
          were internal to the class.   The interface between this object to the
          rest  of  the  system  changes  very little.    Thus,  after  I finish
          modifying the  class, with only minor  change to the interface,  I can
          easily  test the  new system.      Testing of  the new  system is much
          easier.  


          Why customize the Common Dialog?

               The simplest form Common dialog is great, if  it fits your needs.
          With just six line of  C++ code, your users can get the professionally
          designed dialog box for open  and save file.  Most   important benefit
          of all,  there are  fewer chance for  errors.    However, most  of the
          time,  getting a  single file  name  from the  dialog box  may not  be
          exactly what you'd like to do.   Also properly designed and customized
          dialog box  make your  program easier  to use.   It adds  value and  a

                                          - 60 -





          professional look to your final product.

                For  example,  in my  Interactive  Cross  Reference for  Windows
          (IXFW)  program,  the FileDialog has two additional controls: 

               1) Add a listbox to the dialog.

               2) Add an "Add File" button to the dialog box.  

               When an user pushs the "Add File" button, the dialog box will add
          the selected files in  the file listbox to my  custom listbox.    This
          way,  the user  can  select  all the  files  he  wants from  different
          directories  before  the file  dialog box  is  closed and  the program
          continues.  The user can save a lot of  tedious mouse/keyboard actions
          when  he tries to  gather files from  different directories.    A very
          good value is added to the final product.


          How to customize the common dialog in C?

               The easiest way learn how to customize the file dialog box with C
          is  by studying  the excellent article  "Using and  Customizing Common
          Dialogs", written by Kraig Brockschmidt of Microsoft System Developers
          Relations.  You can download this file from CompuServe.  Basically, to
          customize the file dialog, you need to do the following:

               1)  You need  to  edit  the dialog  template  by adding  the  new
          controls to the default template.

               2)  You have to hook  the dialog message  processing procedure by
          subclassing the dialog control.

               3) You need to initalize the proper data structure.

               4) You have  to write  message handler  code to  support the  new
          controls in your dialog template.


          How to customize the Common Dialog in C++?

               You  customize  the  File  dialog  in  a  very  similar  fashion.
          However, it's much more desirable  to use all the Visual C++  's tools
          in the customization process.   Here is how to do that:

               1) Use the "App Studio" to customize the template.
                 I did this by  inserting the default file dialog  template into
          my .rc file.   You also include the "dlgs.h" in the include section of
          the .rc file,  because most  of the resource IDs defined for the  file
          dialog box are located in the "dlgs.h" file.    

               2)  Derive a  new class  from the  CFileDialog class  with "Class
          Wizard".  
               The best  way to do this  is to run the Class  Wizard from inside
          the "App Studio". Since I can't tell  the Class Wizard to derive a new
          class from the CFileDialog directly, I  have to tell the Class  Wizard
          to  derive the  new class  for the  dialog template  from the  CDialog
          class.    After the Class  Wizard created the necessary  files for the
          new class,   I edited the  parent class from CDialog  to CFileDialog. 

                                          - 61 -





          (You   need   to   change   the  constructor   for   this   particular
          implementation.)

               3) Subclass the new dialog box.
               I  didn't  do  it  here,  since  the  dialog  box  is  subclassed
          automatically  in the MFC class  hiearchy.   However,   if you want to
          use any of the fancy features  like customizing the Common Dialog, you
          really  should  understand how  MFC subclasses  a  window and  how the
          messages are  routed to your handlers.    Since the subclass mechanism
          is in the MFC's class hiearchy already, I  can use the Class Wizard to
          add handler functions to the dialog controls' messages.   The handlers
          I added  are: a) OnOK()  -  to handle  the OK button  from the default
          actions to the new action of adding selected files to my listbox.   b)
          OnClickButton1() - for adding files to the listbox.  c) OnClickButton2
          - for deleting files from the listbox.  

               I also added several  data members which map  to the new  control
          IDs with Class  Wizard.  This way, I can use  the class methods of the
          CButton or  CListBox  instead of  trying  to figure  out how  to  send
          control messages to those IDs.  Isn't C++ wonderful?

               One  last data  member I  added to  this particular class  is the
          "CStringArray" for  storing the collection  of filenames after  the OK
          button  is pushed.     You  see,  when you  push  the OK  button,  the
          filedialog will  destoy the dialog  box with all  the controls  in it.
          You  have to  copy the  strings  from the  listbox control  to a  safe
          location before the listbox control is destroyed.

               4) Properly initialize the new class in the constructor.
               You  have to  tell the  "OPENFILENAME" structure  to use  the new
          template.  This is done in the constructor of this particular class.

               5)  Add methods to retrieve the collections of filenames from the
          dialog class.

               Since the  CStringArray data member  is public,  I  choose to let
          the  outside world  have access to  the filelist  directly.    Not the
          purest form of C++, but.... 

               OK, all of the files needed to compile and run the new FileDialog
          Box are in the filedia.zip file.  Have fun!


          * If you  have any comments, questions, suggestions, feel free to send
          email to Tony Lee at CompuServe 72064,1235. 

          ** "Interactive  Cross Reference for  Windows" is a  shareware utility
          that lets you build a  database from existing source code so  that you
          can  quickly browse  megabytes of source  code.  You  can download the
          program from IBMPRO forum of the CIS.









                                          - 62 -





                                   Installing Windows NT
                              (As Done by a non-Windows Guru)
                                      By Kurt Simmons

               A couple of  months ago at a company meeting  it was decided that
          we  should  get in  on the  ground  floor of  software  developing for
          Windows NT,  and that we  would order  the preliminary release  of the
          SDK.  When I called Microsoft, I found that the CD was $69.00 and that
          the documentation  would be more than $300, if you wanted a hard copy.
          I figured that if it was  on the CD that would be sufficient,  even if
          it was in  PostScript format (though in fact some  is in Windows Write
          format).

               After breathlessly  waiting a week,  the box arrived;  finally we
          too  would  enter the  next step  in giving  a  PC a  "real" operating
          system.  I had moved many of our files around to make over 200Meg free
          on  a  drive  so that  even  Windows  NT  could  not  complain  during
          installation.  Then I found that we can't run Stacker with Windows NT,
          nor could we run  the current drivers for our LAN (LBL).   NT requires
          that you  use 32bit drivers.  While  this will improve performance, it
          made  life difficult at  the current time.   OK, back  to moving files
          around and freeing up a drive  that is unStacked.  Two hours later,  I
          am back on my way to installing NT.

               NTFS:  would I like  to convert my drive to that format?  Options
          are available to convert with or without destroying the existing files
          or to simply keep the  existing FAT format.  The first time  through I
          figured I would keep  the existing format (an option exists to convert
          later  if we  desire).   After  some  trial and  error  of getting  NT
          configured to work with the  Ethernet cards, I decided to give  up and
          completely re-install as I have changed so many of the settings (you'd
          think  after doing  this sort  of  thing for  this long  I would  have
          learned to  keep notes!)   So, here I go  again installing it  for the
          second time.   By now I  have read about  the advantages of using  the
          NTFS for reasons  of security and  data integrity;  I figured that  it
          would be a good idea  to convert the drive to the  format that Windows
          NT will be most comfortable with.

               Everything  went along fine  until I  found out  that "Converting
          while leaving your files intact" doesn't mean you can boot off of that
          drive anymore or even access it from  MS-DOS!  Well, since I needed to
          be able to run DOS as my primary  OS at this point, I realized I  must
          reformat the drive.  Except  I couldn't do that from within NT.   So I
          booted  from floppy and found   that my D: drive  was now listed as my
          C:, and  C: was nowhere  to be  found.  The  installation program  for
          Windows  NT was  kind enough to  reformat the  drive in  MS-DOS for me
          after my partner got done performing CPR.

               The moral of the above story is that taking a day or so to search
          the  on-disk  documentation might  have done  us  some good.    In the
          DOC\ENDUSER\WRITE  (this  is   the  first  place  I   would  look  for
          information  on  "How to  plan  your installation")  directory  were a
          series of files on installing Windows NT.  I found that I had become a
          little too used to the standard OS  of the PC's being more than a  bit
          lax in security, I should be able to destroy my system from the inside
          if I want to, right?

               The  first eight  hours of NT  seemed to  have drained  me to the

                                          - 63 -





          point  of  actually considering  giving up  caffeine  as a  major food
          group, but after that life  got easier.  I hope to be able to continue
          my adventures in NT next month.  The articles will be written from the
          point of a programmer but not a Windows NT guru, as I think many of us
          fit that description.

               Kurt A. Simmons
               Systems Administrator
               Millennium Technologies, Inc.
               Suite 100
               160 Edgehill Rd.
               Glenside, Pa 19038-3004
               Voice (215) 886-7366   Fax (215) 886-8602














































                                          - 64 -





                                 Software Development '93
                                       by Pete Davis

               Mike and I  went to  the Software Development  '93 conference  in
          Boston. The  conference ran the week  of the August 23-27.  Mike and I
          were  there  from  the  24th  to the  26th.  This  main  point  of the
          conference was to let  Software Developers strut their stuff.  All the
          big boys were there, Microsoft, Borland, Miller-Freeman and others. 

               So,  what were  people  showing off?  Well,  I think  the  single
          product that impressed Mike and I  the most, and this is strictly from
          a demo  point-of-view, but Symantec's C++  development environment won
          hands  down for best demo. They  also won in the  best "Star Trek: The
          Next Generation"  rip-off. Basically they  did a big  show as  part of
          their  demo. It  was cute,  but  the demo  was really  impressive. The
          development is a lot like Visual C++, but better. You  can create your
          dialog  boxes,  menus,  and  other resources  from  within  Symantec's
          Integrated Development  and Debugging Environment (IDDE).  You tie the
          buttons  to dialog boxes and dialog boxes to menu items, and so on and
          so on. All the code will be  generated by the IDDE. You can even throw
          in  things like tool-bars. Yep,  that's right, just  throw in the tool
          bar, make your  own icons for it  and bang, all  the code you need  is
          there.  It also allows you  to go in and modify  the code and have new
          generations  of the design take  on your existing  code changes. We're
          hoping to get a review copy of this soon. It's a great product  and it
          deserves some good press. (That's a hint  to any of you who happen  to
          work for Symantec!!!).

               Another product that really blew  peoples socks off was Nu-Mega's
          Bounds  Checker   2.0,  which  was  officially   released  during  the
          conference.  This   new  version   of  Bounds  Checker   has  terrific
          improvements  on version 1. More checking is done and the best feature
          is the new event tracing feature.  Essentially, it will trace all  API
          and  library  calls made  by  your  program  and  keep track  of  what
          parameters were passed  to each. It also  keeps track of messages  and
          other  things.  Really, it's  just  a  plain  fantastic product.  Look
          forward to a review in the next issue.

               Microsoft is working on  MFC 2.5. One of the main improvements is
          encapsulation of  OLE 2.0. A lot of people have agreed that OLE 2.0 is
          just too big and  bulky for most people. MFC 2.5 is  going to fix that
          problem. What about us C programmers? Well, the best thing to do might
          be to  do all the  OLE 2.0 work  in C++ and then  do the rest  of your
          coding in C. Really, doing OLE  2.0 in C is just about  impossible. In
          fact,  we went  to  a  class taught  by  a  Microsoft employee,  Nigel
          Thompson. Nigel  humorously described  his 4.5  months learning to  do
          simple OLE (as he says,  Objectionable Learning Experience) 2.0 things
          in C,  like  containers. We're  talking  about a  guy  who has,  as  a
          resource,  the people who wrote OLE 2.0.  Obviously if it took him 4.5
          months to do this,  most of us would probably  be better off doing  it
          with MFC 2.5, which is  supposed to cut that 4.5 month  learning curve
          down to 30 minutes or so. A slight improvement!

               Borland announced OWL 2.0 which is planned to be a cross-platform
          class  library. We'll see how far that goes. Microsoft released Visual
          C++ for Win32. That was all pretty public, though.

               We got  to meet some of  our readers who recognized  the names on

                                          - 65 -





          our  badges. I  have to say,  I was  awfully surprised  to have people
          recognize me at  the conference. Anyway, the  entire conference seemed
          to be  a big success and Mike and I certainly enjoyed meeting everyone
          there, especially the readers that made it.

               We were  able to get some review copies of software and books, so
          you'll be seeing  some reviews of some really good  products and books
          in the near future. Hopefully Mike  and I will have enough warning for
          the  next conference  that we  can let  you all  know we'll  be there.
          Hopefully  we'll  be able  to  run  into some  more  of  you at  other
          conferences.  It's nice to meet  the readers and  hear first-hand what
          they have to say about the magazine.

               I'd like to thank all the people who made SD '93  so much fun for
          me. Especially, Michael Liben,  Matt Pietrek and  all the guys at  Nu-
          Mega,  Dave Thielen,  Paul  Yao, Julianne  Sharer  and the  others  at
          WexTech,  all the  guys at  ProtoView Development  for being  so cool,
          Richard  Hale Shaw,  and Matt  Trask. Those  are the  only ones  I can
          remember off the top of my head.  








































                                          - 66 -





                                 Getting In Touch With Us

          Internet: 71644.3570@compuserve.com

          GEnie: P.DAVIS5 (Pete)

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

          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.




































                                          - 67 -





                                       The Last Page
                                      by Mike Wallace

               
          "The first thing we do, we kill all the OS/2 programmers."
                         -William Shakespeare

               No, this isn't an OS/2-bashing column.  Pete and I spent a couple
          of days last week at the Software Development '93 convention in Boston
          meeting  people  and  going   to  conferences  (see  Pete's  "Software
          Development  '93" article  elsewhere in  this issue).   Here  are some
          notes:

               One  of the speakers I saw  (from Borland) gave a presentation on
          an informal  survey he took of  programmers.  One of  the questions he
          asked was whether they thought programming was an art or a science.  A
          majority said  "an art",  and I  agreed with this  at first,  but then
          after  thinking about it for  a while I  had to change my  mind.  Take
          physics,  for  example.   Most people  would  consider that  a science
          (versus an art).  What's involved with a physicist's work?  Usually it
          starts off with an idea or an observation about the way  things are in
          nature.  He then takes that idea and applies the laws of physics to it
          in  an effort to understand  what's happening.   He's "doing Physics."
          For  instance, in 1960, Edward Lorenz created a machine that simulated
          weather (the  "Royal McBee") and after  staring at it for  a while, he
          thought he  noticed a  pattern to the  cloud formations.   He  started
          working on  the math, and  along the  way helped create  the field  of
          chaos, an unusual and fascinating branch of physics.  My point is that
          he  had an  initial  burst  of  creative  thought  that  preceded  the
          remainder of his  work in  the field, which  sounds like  programming.
          When I start on a program,  I usually first think about how  I'm going
          to approach the  problem and then I use what  I know about programming
          to implement my solution, so maybe programming is as much of a science
          as Physics, and not so much an art.  

               Another question  this  speaker asked  in  his survey  was  about
          favorite  foods for  programmers.   The winners  were pizza,  beer and
          hamburgers.  The loser, coming  in as the favorite food of 1% of those
          programmers surveyed, was fresh vegetables.  I'm glad I'm not the only
          one favoring beef over beets.

               The  speaker also  asked  about favorite  outside  hobbies.   The
          winner  was "Watching Star Trek reruns".   How did I know he was going
          to say that?  The  audience gave a very  loud cheer when he  announced
          that.    Am  I  the  only  programmer  who  doesn't  watch  "The  Next
          Generation"?  [Pete's Note: Don't buy that for a second. Mike reminded
          me it  was on the other night!  Obviously he keeps better  track of it
          than me!]  I went  to Symantec's party where they announced  their C++
          compiler,  and it  was a 30-minute  skit straight out  of "Star Trek".
          They  talked  about  getting  to  the  planet  C++  and  fighting  the
          "Borland-ians" and  "Microsoft-ians" from  their space  ship.   It was
          pretty corny.

               I spent  a good while  talking to the  president of a  well-known
          software company about what albums are good for prgramming to (you can
          tell  I did  a lot of  work at  this convention).   We agreed  on Pink
          Floyd's "The Wall".  I'd  also have to nominate Pearl Jam's  "Ten" and
          Joe Satriani's "Surfing with the Alien".  I want your responses.  What

                                          - 68 -





          music do  you listen to when you're pounding out code?  Van Halen with
          Visual Basic?  "Purple Haze" with  Pascal?  Send me your comments, and
          I'll reprint the best ones after I get enough  for a column (so it may
          take a while).  Hope you enjoy this issue.  Talk to you next month.























































                                          - 69 -


