home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS - Coast to Coast / simteldosarchivecoasttocoast2.iso / wpj_mag / wpjv1n8.zip / WPJV1N8.TXT < prev   
Text File  |  1993-09-04  |  183KB  |  4,301 lines

  1.  
  2.  
  3.  
  4.               WW     WW     WW      PPPPPPPP              JJ
  5.               WW     WW     WW      PP    PP              JJ
  6.                WW   WWWW   WW       PP    PP              JJ
  7.                WW  WW  WW  WW       PPPPPPPP              JJ
  8.                WW  WW  WW  WW       PP             JJ     JJ
  9.                 WWWW    WWWW        PP              JJ   JJ
  10.                  WW      WW         PP               JJJJJ
  11.  
  12.           ----------------------------------------------------------------
  13.           The Windows Programmer's Journal                        Volume 01
  14.           Copyright 1993 by Peter J. Davis                        Number 08
  15.           and Mike Wallace                                           Sep 93
  16.           ----------------------------------------------------------------
  17.           A  monthly forum  for novice-advanced  programmers to share  ideas and
  18.           concepts  about programming  in the  Windows (tm)  environment.   Each
  19.           issue is uploaded to the info systems listed below on the first of the
  20.           month,  but made available at the convenience  of the sysops, so allow
  21.           for a couple of days.
  22.  
  23.           You can get in touch with the editors via Internet or Bitnet at:
  24.  
  25.           Internet: 71644.3570@compuserve.com
  26.  
  27.           CompuServe: 71655,3570 (Pete) or 71141,2071 (Mike)
  28.  
  29.           GEnie: P.DAVIS5
  30.  
  31.           America Online: PeteDavis
  32.  
  33.           Delphi: PeteDavis
  34.  
  35.           or you can send paper mail to:
  36.  
  37.           Windows Programmer's Journal
  38.           9436 Mirror Pond Dr.
  39.           Fairfax, Va. 22032
  40.  
  41.           We can also be reached by phone at: (703) 503-3165.
  42.  
  43.           The WPJ BBS can be reached at: (703) 503-3021.
  44.  
  45.           The WPJ BBS is currently 2400 Baud (8N1). We'll be going to  14,400 in
  46.           the near future, we hope.
  47.  
  48.  
  49.  
  50.  
  51.  
  52.  
  53.                                         LEGAL STUFF
  54.  
  55.           -  Microsoft,  MS-DOS,  Microsoft  Windows, Windows  NT,  Windows  for
  56.           Workgroups,  Windows   for  Pen  Computing,  Win32,   and  Win32S  are
  57.           registered trademarks of Microsoft Corporation.
  58.  
  59.           - Turbo Pascal for Windows, Turbo C++ for Windows, and Borland C++ for
  60.           Windows are registered trademarks of Borland International.
  61.  
  62.           - WordPerfect is a registered trademark of WordPerfect Corporation.
  63.  
  64.           -  Other  trademarks  mentioned  herein  are  the  property  of  their
  65.           respective owners.
  66.  
  67.           -  WPJ is  available from  the  WINSDK, WINADV  and MSWIN32  forums on
  68.           CompuServe, and the IBMPC, WINDOWS and BORLAND forums on Genie.  It is
  69.           also  available  on America  Online in  the  Programming library.   On
  70.           Internet,    it's    available    on     WSMR-SIMTEL20.ARMY.MIL    and
  71.           FTP.CICA.INDIANA.EDU.  We upload it by the 1st of each month and it is
  72.           usually  available by  the 3rd or  4th, depending  on when  the sysops
  73.           receive it.
  74.  
  75.           -  The Windows  Programmer's Journal  takes no responsibility  for the
  76.           content of the text within this document. All text is the property and
  77.           responsibility  of the  individual authors.  The Windows  Programmer's
  78.           Journal is solely a vehicle for allowing articles to be collected  and
  79.           distributed in a common and easy to share form.
  80.  
  81.           - No part  of the Windows Programmer's Journal may  be re-published or
  82.           duplicated in part  or whole,  except in the  complete and  unmodified
  83.           form of the Windows Programmer's Journal, without the express  written
  84.           permission of each individual author. The Windows Programmer's Journal
  85.           may not be sold for profit  without the express written permission  of
  86.           the Publishers, Peter Davis  and Michael Wallace, and only  then after
  87.           they have obtained permission from the individual authors.
  88.  
  89.  
  90.  
  91.  
  92.  
  93.  
  94.                                     Table of Contents  
  95.  
  96.           Official Motto:  Served only in the finest restaurants.
  97.  
  98.           Bootup
  99.            WPJ.INI  . . . . . . . . . . . . . . . . . .  4  Pete Davis 
  100.            Letters  . . . . . . . . . . . . . . . . . .  6  Readers
  101.            WPJ Survey . . . . . . . . . . . . . . . . .  11
  102.  
  103.  
  104.           Programming
  105.            Beginner's Column  . . . . . . . . . . . . .  14 Dave Campbell
  106.            Hacker's Gash  . . . . . . . . . . . . . . .  25 Dennis Chuah
  107.            GDI See GDI Do . . . . . . . . . . . . . . .  34 Bernard Andrys
  108.            Windows Hooks  . . . . . . . . . . . . . . .  46 David S. Browne
  109.            Shared Global Memory . . . . . . . . . . . .  51 Dennis Chuah
  110.            Customizing File Dialogs in Visual C+  . . .  59 Tony Lee
  111.  
  112.           Software
  113.            Installing Windows NT  . . . . . . . . . . .  63 Kurt Simmons
  114.  
  115.           Special Report
  116.            Software Development '93 . . . . . . . . . .  65 Pete Davis
  117.  
  118.           The Leftovers
  119.            Getting In Touch with Us . . . . . . . . . .  67 Pete & Mike
  120.            Last Page  . . . . . . . . . . . . . . . . .  68 Mike Wallace 
  121.  
  122.  
  123.           Windows Programmer's Journal Staff: 
  124.           Publishers  . . . . . . . . . . . . . . . . .  Pete and Mike  
  125.           Editor-in-Chief . . . . . . . . . . . . . . .  Pete Davis  
  126.           Managing Editor . . . . . . . . . . . . . . .  Mike Wallace  
  127.           Contributing Editor . . . . . . . . . . . . .  David Campbell  
  128.           Contributing Editor . . . . . . . . . . . . .  Dennis Chuah
  129.  
  130.           Graphic Artist (Bad)  . . . . . . . . . . . .  Pete Davis
  131.           Graphic Artist  . . . . . . . . . . . . . . .  Mark Coghlan
  132.           Graphic Artist  . . . . . . . . . . . . . . .  Dennis Chuah
  133.           Graphic Artist  . . . . . . . . . . . . . . .  Bernard Andrys
  134.  
  135.           Contributing Writer . . . . . . . . . . . . .  Dennis Chuah
  136.           Contributing Writer . . . . . . . . . . . . .  David S. Browne
  137.           Contributing Writer . . . . . . . . . . . . .  Bernard Andrys
  138.           Contributing Writer . . . . . . . . . . . . .  Tony Lee
  139.           Contributing Writer . . . . . . . . . . . . .  Kurt Simmons
  140.  
  141.  
  142.  
  143.  
  144.                                           WPJ.INI
  145.                                        by Pete Davis
  146.  
  147.                Well, the survey results  continue to pour in. Keep  them coming.
  148.           If you haven't filled one out, there's another copy in this issue. The
  149.           last day for the  prize is September 30th.  That means post-marked  or
  150.           the e-mail is dated by September 30th. We will do  the drawing shortly
  151.           after.
  152.  
  153.                Speaking of the  survey, I thought  I'd give you  all an idea  of
  154.           what the results  have been like.  A lot of  you like the fact that we
  155.           cover  a  wide  variety  of  levels.  We  are  commited  to  providing
  156.           information  for   novice  programmers   as  well  as   more  advanced
  157.           programmers. 
  158.  
  159.                We got  a lot of good and bad feedback.  In a sense, all feedback
  160.           is good.  When I say bad,  I really mean  critical. And that's  a good
  161.           thing.  One thing that  disturbed me is  this. We got  a few responses
  162.           that mentioned errors, and one saying "Gross Errors", in the magazine.
  163.           YES, there  are "Gross Errors". To  date, Mike and I  roughly skim the
  164.           articles. We check spelling and grammer. We don't do a whole lot more.
  165.           Why? We don't have time. It takes at least 25 hours a month to put the
  166.           magazine together as  it is. I went through this  last month, and some
  167.           people don't seem to  get it. That's not the  only problem. As I  said
  168.           last  month, we  don't pay  the writers  and have  a hard  enough time
  169.           getting  them. If  we made  it difficult  for them  by making  them do
  170.           re-writes, we'd be  putting out the Windows Programmer's  Nothing. I'm
  171.           serious about that. There are a couple people who might put up with it
  172.           for a  while, but  if I wasn't  getting paid,  I sure wouldn't  make a
  173.           habit of it, and I don't blame our writers for having the same feeling
  174.           about that. If you're going to put up with serious editing, you may as
  175.           well get paid for it.
  176.  
  177.                As far as "Gross Errors", Mike and I have always  been willing to
  178.           publish  corrections,  which we  hoped we'd  get  more of,  but people
  179.           haven't sent them in.  I got one in  the reader's survey, and  here it
  180.           is:
  181.  
  182.                In Bernard Andrys'  article "GDI  See GDI Do",  he said that  the
  183.           PASCAL  keyword in  a function declaration  meant that  the parameters
  184.           were passed by reference  and not by value. (That was  the gist that I
  185.           got, anyway.)  This is incorrect. What  it actually has to  do with is
  186.           the way parameters are  placed on the stack. The  C calling convention
  187.           places the arguments on the stack from right to left. For example, the
  188.           call,  myFunction(a, b, c) pushes the contents onto the stack starting
  189.           with 'c' and ending with 'a'. The Pascal calling convention places the
  190.           arguments  on the  stack  from left  to  right, so  with the  previous
  191.           example, 'a'  is pushed onto the stack first, followed by 'b' and then
  192.           'c'.  This gives  C the  ability to  handle  an unspecified  number of
  193.           parameters  on  the  stack.  It  makes  library  calls  like  printf()
  194.           possible. Although  Pascal allows an unspecified  number of parameters
  195.           to  be used  for the Read,  Write and  some other  procedures, this is
  196.           handled  by the  compiler at compile  time. In  C, the  parameters are
  197.           handled at run-time by the function. 
  198.  
  199.                OK, that was  easy and  relatively painless. If  anyone else  has
  200.           corrections, send them  in. We'd be more  than happy to tell  everyone
  201.           else. Our  original intent was  for this magazine  to be  a collective
  202.  
  203.                                            - 4 -
  204.  
  205.  
  206.  
  207.  
  208.           effort. With many of our  readers and writers, it has been able  to be
  209.           that. It bothers me, though, when people say, "you should fix this, do
  210.           that, fix that,  and stop doing this..." and  go on through a  list 20
  211.           things and then say that they still want it for free. Mike and  I have
  212.           done the  best job we could  given the restrictions on  our time. This
  213.           goes for  the writers we've  had and  still have. They  should all  be
  214.           applauded  for their efforts in  getting articles done  and putting in
  215.           the time. 
  216.  
  217.                I'm probably making a big deal out of  a little thing, in fact, I
  218.           am. Most of you had few or no complaints. I have complaints about  the
  219.           magazine, it's not perfect by  any stretch, but most of you  were very
  220.           complimentary. We thank  you all. Mike  and I  have enjoyed doing  the
  221.           magazine and we'll hopefully enjoy continuing it for years to come. 
  222.  
  223.                The  survey, all-in-all  was a great  success. It has  given us a
  224.           better idea of the kinds of things we should focus on and the kinds of
  225.           things we  might want to consider  leaving behind. Does that  mean you
  226.           should  stop sending your comments,  suggestions and critiques. No sir
  227.           (ma'am)! Send them  in. For those of you who  have complaints, why did
  228.           you wait until the survey to send them in? Send them in as soon as you
  229.           have them.  If there are problems  with the magazine, we  want to know
  230.           about it. We want to do our best to please as many of you as possible.
  231.  
  232.                Ok, so  where does everything  stand? At  some point  Mike and  I
  233.           would like to  actually start printing the magazine.  Many of you said
  234.           you'd like us  to keep the WinHelp  format even if we go  to print. We
  235.           will do that. We don't know when  we'll take the magazine to print and
  236.           it may be  some time. Mike and  I are going  to start trying to  spend
  237.           more time on the magazine  if possible. We're going to try  to improve
  238.           the editing  of the articles also.  Again, all of this  takes time. If
  239.           the magazine were to go to print, it would become a full time  job for
  240.           Mike and I. The kind of "gross" mistakes seen in past issues would end
  241.           (hopefully!).
  242.  
  243.                We will  keep  everyone informed  of  any developments  with  the
  244.           magazine.  At  this   point  we've  talked  with  people  involved  in
  245.           publishing about  the possibility of  printing the magazine.  How soon
  246.           that happens remains to be seen.
  247.  
  248.                Peace.
  249.  
  250.                Pete
  251.  
  252.  
  253.  
  254.  
  255.  
  256.  
  257.  
  258.  
  259.  
  260.  
  261.  
  262.  
  263.  
  264.  
  265.  
  266.  
  267.                                            - 5 -
  268.  
  269.  
  270.  
  271.                                           Letters
  272.  
  273.           From: Pat White <P.WHITE@fs2.mbs.ac.uk>
  274.           Date: 12 Aug 93 10:20:18 BST
  275.           Subject: WPJ Mag
  276.  
  277.           Pete and Mike,
  278.                Many  thanks for  your efforts  in producing  the magazine  - I'm
  279.           brand new  to Windows programming and am finding a lot of useful info.
  280.           Just  one very minor criticism  - when you  review shareware could you
  281.           always include  where it can be  found, especially if it  is in either
  282.           the Simtel or CICA archive as both are widely mirrored here in Europe.
  283.  
  284.                I am including  in this note two lists you  might want to include
  285.           in a  future issue. The  file index  is a combination  of the  READ.ME
  286.           files,    The  article  index  is  built  from  the  contents  of  the
  287.           WPJVxNx.TXT files. They cover the first  6 issues - I hope to retrieve
  288.           number 7 today.
  289.  
  290.  
  291.           [Editor's Note: We felt this information would be useful for everyone,
  292.           so it's  included here  and also seperately  as an ASCII  file. Thanks
  293.           Pat.]
  294.  
  295.           File index
  296.           Windows Programmer's Journal
  297.  
  298.           Volume 1 Number 1
  299.           January 1993
  300.  
  301.           The following files are contained in this archive:
  302.  
  303.           TRASH.PAS      Pascal Source for trash can
  304.           TRASH.RC       Resource code for trash can
  305.           SUBMIT.TXT     How to submit an article to WPJ
  306.           LINKLIST.C     C source code for linked list
  307.           LINKLIST.MAK   Microsoft nMake compat make file
  308.           LINKLIST.DEF   Module Definition file for linked list
  309.           LINKLIST.RC    Resource file for Linked List
  310.           LINKLIST.H     Header file for Linked List
  311.           LINKLIST.ICO   Icon for Linked List
  312.           MAKELIST.BAT   MSC 6.00 with 3.0 SDK batchfile to make
  313.           PMDDE.C        Program Manager DDE C source code
  314.           WPJV1N1.TXT    Windows Programmer's Journal Volume 1 Number 1
  315.           README.TXT     Take a guess.
  316.  
  317.  
  318.           Volume 1 Number 2
  319.           February 1993
  320.  
  321.           The following files are contained in this archive:
  322.  
  323.           HELLO.ZIP      Source Code to the Hello World Program
  324.           LINKLIST.ZIP   Mike's revised Linked List program
  325.           HAXTON.ZIP     Rod's DLL examples
  326.           D&DCLIE.PAS    Andreas Furrer's Drag and Drop Client Program
  327.           D&DSERV.PAS    Andreas Furrer's Drag and Drop Server Program
  328.           WPJV1N2.TXT    Windows Programmer's Journal Volume 1 Number 2
  329.  
  330.                                            - 6 -
  331.  
  332.  
  333.  
  334.           READ.ME        Well, I screwed it up in the last issue (pete).
  335.           SUBMIT.TXT     How to submit articles to us.
  336.  
  337.  
  338.           Volume 1 Number 3
  339.           March 1993
  340.  
  341.           The following files are contained in this archive:
  342.  
  343.           WPJV1N3.TXT   The magazine in plain text format
  344.           WPJV1N3.HLP   The magazine in Windows Help format
  345.           WPJ.BAT       Quick way to view the WPJV1N3.HLP file in Windows
  346.           BEGINNER.ZIP  Beginner's Column source code
  347.           OWNERBTN.ZIP  Owner Draw Buttons source code.
  348.           INSTALL3.ZIP  Source code for the Install Program article
  349.           PAINT.ZIP     Advanced C++ source code
  350.           READ.ME       Guess!
  351.           SUBMIT.TXT    How to submit articles to us.
  352.  
  353.  
  354.           Volume 1 Number 4
  355.           April 1993
  356.  
  357.           The following files are contained in this archive:
  358.  
  359.           WPJV1N3.TXT   The magazine in plain text format
  360.           HELPREAD.ME   Explanation of why there's no help file this month.
  361.           ROD.ZIP       Rod Haxton's DLL code.
  362.           HELLO.ZIP     Beginner's Column source code (Dave Campbell)
  363.           ODLBOX.ZIP    Owner-Draw list boxes
  364.           READ.ME       Guess!
  365.           SUBMIT.TXT    How to submit articles to us.
  366.  
  367.  
  368.           Volume 1 Number 5
  369.           May 1993
  370.  
  371.           The following files are contained in this archive:
  372.  
  373.           WPJV1N5.TXT   The magazine in plain text format
  374.           WPJV1N5.HLP   The magazine in WinHelp format
  375.           HELLO.ZIP     Beginner's Column source code (Dave Campbell)
  376.           WINSTUB.ZIP   Windows STUB source code
  377.           READ.ME       Guess!
  378.           SUBMIT.TXT    How to submit articles to us.
  379.  
  380.  
  381.           Volume 1 Number 6
  382.           June 1993
  383.  
  384.           The following files are contained in this archive:
  385.  
  386.           WPJV1N6.TXT   The magazine in plain text format
  387.           WPJV1N6.HLP   The magazine in Winhelp format
  388.           HELLO.ZIP     Beginner's Column source code (Dave Campbell)
  389.           EDITPRO.ZIP   Goes with Eric Glass' article in Vol. 1, No. 5.
  390.           READ.ME       Guess!
  391.           SUBMIT.TXT    How to submit articles to us.
  392.  
  393.                                            - 7 -
  394.  
  395.  
  396.  
  397.  
  398.           Article index
  399.  
  400.           Volume 1        Number 1
  401.           -----------------------------------------------------------------
  402.           WPJ.INI ......................................   3  Pete Davis
  403.           Off Topic ....................................   6  Pete & Mike
  404.           Beginner's  Corner   (C)  ....................   8  Pete Davis
  405.                                                             & Mike Wallace
  406.           A Drag and Drop Trashcan (TPW) ...............  16  Andreas Furrer
  407.           Using DDE to Communicate With Program Manager.  18  Pete Davis
  408.           Implementing a Linked List in the Global Heap.  22  Mike Wallace
  409.           Book Review ..................................  26  Pete Davis
  410.           Last Page ....................................  28  Mike Wallace
  411.           Getting in Touch with Us .....................  29  Pete & Mike
  412.  
  413.  
  414.           Volume 1        Number 2
  415.           -----------------------------------------------------------------
  416.           WPJ.INI .......................................  3  Pete Davis
  417.           Letters .......................................  5  Readers
  418.           Install Program Part II .......................  7  Pete Davis
  419.           Programming a Drag&Drop Server ................  9  Andreas Furrer
  420.           C++ Beginner's Column ......................... 12  Mike Wallace
  421.           Beginner's   Corner   (C)   ................... 14  Pete Davis
  422.           Using LZExpand Library ........................ 18  Alex Fedorov
  423.           Implementing a Linked List - Revisited ........ 21  Mike Wallace
  424.           An Introductory Look at DLLs and Make Files ... 22  Rod Haxton
  425.           The Windows Help Magician ..................... 29  Jim Youngman
  426.           Last Page ....................................  30  Mike Wallace
  427.           Getting in Touch with Us .....................  31  Pete & Mike
  428.  
  429.  
  430.           Volume 1        Number 3
  431.           -----------------------------------------------------------------
  432.           WPJ.INI .......................................  4  Pete Davis
  433.           Letters .......................................  7  Readers
  434.           Beginner's Column .............................  10  Dave Campbell
  435.           Install Program Part III ......................  20  Pete Davis
  436.           Home Cooking - C++ From Scratch ...............  22  Andrew Bradnan
  437.           Creating and Using Owner Draw Buttons .........  26  Todd Snoddy
  438.           Hacker's Gash .................................  30  Mike and Pete
  439.           Special News ..................................  32  Mike Wallace
  440.           Windows 3.1: Using Version Stamping Library ...  33  Alex Fedorov
  441.           Book Review ...................................  36  Pete Davis
  442.           Book Review ...................................  38  Mike Wallace
  443.           Printing in Windows ...........................  40  Pete Davis
  444.           Advanced C++ and Windows ......................  45  Andrew Bradnan
  445.           Trials and Tribulations Part 1 ................  54  Jim Youngman
  446.           Getting in Touch with Us .....................   57  Pete & Mike
  447.           Last Page ....................................   58  Mike Wallace
  448.  
  449.  
  450.           Volume 1        Number 4
  451.           -----------------------------------------------------------------
  452.           WPJ.INI .......................................  4   Pete Davis
  453.           Letters .......................................  6   Readers
  454.           Midlife Crisis: Windows at 32 .................  9   Pete Davis
  455.  
  456.                                            - 8 -
  457.  
  458.  
  459.  
  460.           Beginner's Column ............................. 14   Dave Campbell
  461.           Owner-Drawn List Boxes ........................ 25   Mike Wallace
  462.           Beginner's Column for Turbo Pascal for Windows  30   Bill Lenson
  463.           Hacker's Gash ................................. 31   Readers
  464.           Microsoft's Windows Strategy .................. 34   Pete Davis
  465.           Accessing Global Variables Across DLL ......... 38   Rod Haxton
  466.           Getting A Piece Of The Future ................. 41   Peter Kropf
  467.           The "ClickBar" Application .................... 44   WynApse
  468.           Getting in Touch with Us .....................  45   Pete & Mike
  469.           Last Page ....................................  46   Mike Wallace
  470.  
  471.  
  472.           Volume 1        Number 5
  473.           -----------------------------------------------------------------
  474.           WPJ.INI .......................................  4   Pete Davis
  475.           Beginner's Column  ............................  6   Dave Campbell
  476.           Creating Windows Text File Editors  ........... 15   Eric Grass
  477.           Midlife Crisis: Windows at 32  ................ 20   Pete Davis
  478.           WDASM - Review of a Windows Disassembler  ..... 23   Pete Davis
  479.           Hypertext, Sex and Winhelp  ................... 25   Loewy Ron
  480.           Enhancing the WINSTUB Module  ................. 30   Rodney Brown
  481.           Microsoft Developers Network .................. 33   Dave Campbell
  482.           Getting in Touch with Us .....................  35   Pete & Mike
  483.           Last Page ....................................  36   Mike Wallace
  484.  
  485.  
  486.           Volume 1        Number 6
  487.           --------------------------------------------------- ------
  488.           WPJ.INI .....................................     4 Pete Davis
  489.           Letters .....................................     8 Readers
  490.           Beginner's Column ...........................    12 Dave Campbell
  491.           Internally Yours ............................    15 Pete Davis
  492.           Pascal in 21 Steps ..........................    18 Bill Lenson
  493.           Moving Away from ...  .......................    29 Pete Davis
  494.           C++ Beginner's Column .......................    30 Rodney Brown
  495.           WPJ BBS Update ..............................    31 Pete Davis
  496.           Windows Messaging ...........................    32 Mike Wallace
  497.           WinEdit Review ..............................    35 Jeff Perkell
  498.           White House Letter ..........................    38 Mike Strock
  499.           Text Manager ................................    40 Mike Wallace
  500.           Getting in Touch with Us ....................    42 Pete & Mike
  501.           Last Page ...................................    43 Mike Wallace
  502.  
  503.           Keep up the good work.
  504.  
  505.           Pat White
  506.  
  507.  
  508.           Pat White, Manchester Business School, Manchester, UK
  509.  
  510.           JANET address - P.WHITE@UK.AC.MBS.FS2
  511.           EARN/BITNET   - P.WHITE@FS2.MBS.AC.UK
  512.           INTERNET             - P.WHITE@FS2.MBS.AC.UK
  513.                            or  - P.WHITE%FS2.MBS.AC.UK@NSFNET-RELAY
  514.           UUCP/USENET          - P.WHITE%FS2.MBS.AC.UK@NSFNET-RELAY
  515.  
  516.           [Thanks  for the note  and the index,  Pat.  I think  people will find
  517.           this useful.  We'll  have to start doing this every six  issues or so.
  518.  
  519.                                            - 9 -
  520.  
  521.  
  522.  
  523.           - mfw]
  524.  
  525.  
  526.  
  527.  
  528.  
  529.  
  530.  
  531.  
  532.  
  533.  
  534.  
  535.  
  536.  
  537.  
  538.  
  539.  
  540.  
  541.  
  542.  
  543.  
  544.  
  545.  
  546.  
  547.  
  548.  
  549.  
  550.  
  551.  
  552.  
  553.  
  554.  
  555.  
  556.  
  557.  
  558.  
  559.  
  560.  
  561.  
  562.  
  563.  
  564.  
  565.  
  566.  
  567.  
  568.  
  569.  
  570.  
  571.  
  572.  
  573.  
  574.  
  575.  
  576.  
  577.  
  578.  
  579.  
  580.  
  581.  
  582.                                           - 10 -
  583.  
  584.  
  585.  
  586.                                         WPJ Survey
  587.  
  588.               Down  the page is  a survey. Please  fill it out  as completely as
  589.           possible.  Don't forget to put your address  so we can mail your prize
  590.           if you win.
  591.  
  592.               The prize options are:
  593.  
  594.           1) Win32 Reference Manuals   (5-book set)
  595.  
  596.           2) "Windows API Bible", "Windows Internals" and "Undocumented Windows"
  597.  
  598.           3) Windows NT Resource Kit
  599.  
  600.  
  601.               Either print  it out, fill it in, and send  it via regular mail or
  602.           edit  it with a  text editor  and fill  in the blanks  and send  it by
  603.           E-Mail.
  604.  
  605.  
  606.           On Compuserve    : 71644,3570
  607.           On Internet      : 71644.3570 at compuserve.com
  608.           On GEnie         : P.DAVIS5
  609.           On America Online: PeteDavis
  610.           On Delphi        : PeteDavis
  611.  
  612.           or regular mail to:
  613.  
  614.           WPJ Reader Survey
  615.           9436 Mirror Pond Dr.
  616.           Fairfax, Va   22032   U.S.A.
  617.  
  618.  
  619.               Thanks for taking the time to fill out the survey.
  620.  
  621.  
  622.               Pete & Mike
  623.  
  624.  
  625.  
  626.  
  627.  
  628.  
  629.  
  630.  
  631.  
  632.  
  633.  
  634.  
  635.  
  636.  
  637.  
  638.  
  639.  
  640.  
  641.  
  642.  
  643.  
  644.  
  645.                                           - 11 -
  646.  
  647.  
  648.  
  649.           Windows Programmer's Journal
  650.           Reader Survey:  August, 1993
  651.  
  652.  
  653.           Name:_______________________________   E-Mail Addresses
  654.                                                  
  655.           Profession:_________________________   Service           Address
  656.  
  657.           Title:______________________________   ___________  _______________
  658.  
  659.           Company:____________________________   ___________  _______________
  660.  
  661.           Address:____________________________   ___________  _______________
  662.  
  663.                   ____________________________
  664.  
  665.                   ____________________________
  666.  
  667.           Phone #:_________________           Fax #:________________
  668.  
  669.  
  670.  
  671.           Primary       operating       system      on       your      computer:
  672.           ________________________________
  673.  
  674.           S  e  c  o  n  d  a  r  yo  p  e  r  a  t  i  n  g s  y  s  t  e  m  :
  675.           _______________________________________________
  676.  
  677.           Primary     programming     language     used     (include     brand):
  678.           ________________________
  679.  
  680.           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 :
  681.           _________________________________________
  682.  
  683.           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 :
  684.           ________________________________________
  685.  
  686.           Development tools used (debuggers, editors, etc. Include brand.): 
  687.           _________________________________________________________________
  688.           _________________________________________________________________
  689.  
  690.           What     I      Like      about     the      Windows      Programmer's
  691.           Journal:______________________________________________________________
  692.           ___________________________________
  693.  
  694.           What     I     don't      like     about     Windows      Programmer's
  695.           Journal:______________________________________________________________
  696.           _________________________________
  697.  
  698.           Things    I'd   like    to   see    in   the    Windows   Programmer's
  699.           Journal:______________________________________________________________
  700.           ___________________________________
  701.  
  702.           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
  703.           read:_______________________________________
  704.  
  705.           Would you pay for printed copies of WPJ? (Circle one)  YES   NO
  706.  
  707.  
  708.                                           - 12 -
  709.  
  710.  
  711.  
  712.           Prize I want:_____________________________________
  713.  
  714.  
  715.  
  716.  
  717.  
  718.  
  719.  
  720.  
  721.  
  722.  
  723.  
  724.  
  725.  
  726.  
  727.  
  728.  
  729.  
  730.  
  731.  
  732.  
  733.  
  734.  
  735.  
  736.  
  737.  
  738.  
  739.  
  740.  
  741.  
  742.  
  743.  
  744.  
  745.  
  746.  
  747.  
  748.  
  749.  
  750.  
  751.  
  752.  
  753.  
  754.  
  755.  
  756.  
  757.  
  758.  
  759.  
  760.  
  761.  
  762.  
  763.  
  764.  
  765.  
  766.  
  767.  
  768.  
  769.  
  770.  
  771.                                           - 13 -
  772.  
  773.  
  774.  
  775.                                      Beginner's Column
  776.                                      By Dave Campbell
  777.  
  778.                Remember when you were in school, and the teachers would show you
  779.           the REAL hard way to do something, and just about the time you thought
  780.           you understood  it enough, they would show you the easy way? Well, I'm
  781.           going  to do that to you right now.  The File Open box we've worked on
  782.           the last two issues is  going to go away, and be replaced  by a single
  783.           call to the Common Dialogs DLL, COMMDLG.DLL.
  784.  
  785.                Common Dialogs allow the developer to call up a functional dialog
  786.           box with a single call,  rather than build the dialog box,  compile in
  787.           the  dialog resource,  write  the code  for  handling the  dialog  box
  788.           messages,  etc. At the same time,  the developer gets dialog boxes for
  789.           quite a few of the standard configuration items that look identical to
  790.           the ones  everyone else is using,  thereby making life  easier for the
  791.           user.
  792.  
  793.  
  794.           COMMDLG.DLL
  795.  
  796.                COMMDLG.DLL contains dialogs for the following:
  797.  
  798.                     ChooseColor
  799.                     ChooseFont
  800.                     FindText
  801.                     GetFileTitle
  802.                     GetOpenFileName
  803.                     GetSaveFileName
  804.                     PrintDlg
  805.                     ReplaceText
  806.  
  807.           These may be used in Windows 3.0 or 3.1, but only in enhanced mode.
  808.  
  809.                My intention was to show the use of just the GetOpenFileName box,
  810.           but it seemed so easy, I went ahead and added an implementation of the
  811.           ChooseColor  box and ChooseFont box because they always seems to dress
  812.           up an application.   Once you see  how easy this  is, filling out  the
  813.           remainder of  the  menu selections  with  standard dialog  boxes  will
  814.           become quite a bit less of a "chore".
  815.  
  816.  
  817.           Removed Code
  818.  
  819.                First I'll quickly  run through what we can throw  away from last
  820.           issue:
  821.  
  822.                1) All the Fileo.*  files, including the  DLL that we built  last
  823.           time
  824.  
  825.                2) The DoFileOpenDlg procedure in Hello.c
  826.  
  827.  
  828.           Hello.C
  829.  
  830.                The  #include of "fileo.h" is no longer needed, because we aren't
  831.           using that DLL. However, we do need to add 
  832.  
  833.  
  834.                                           - 14 -
  835.  
  836.  
  837.  
  838.              #include <commdlg.h> 
  839.  
  840.           to pick up the items defined there.
  841.  
  842.                Since we aren't going to use Fileo.DLL, we do not need 
  843.  
  844.              HANDLE hFileo
  845.  
  846.           but we will need 
  847.  
  848.              HANDLE hCommDlg
  849.  
  850.           so we just change that line, and  the lines where the DLL is loaded to
  851.           load COMMDLG.DLL and get its handle into hCommDlg.
  852.  
  853.                'pof' and 'of' are removed, as they were used with the old code. 
  854.  
  855.                szFileSpec and szDefExt are moved to WndProc, and we add:
  856.  
  857.             COLORREF ColorRef;
  858.             HANDLE   MyFont = NULL;
  859.  
  860.  
  861.           COLORREF
  862.  
  863.                COLORREF  is  nothing more  than a  DWORD.  Defining it  as such,
  864.           however, gives us a way  to remember we are using an RGB  color value.
  865.           The RGB  method of  defining colors  is a  standardized way that  will
  866.           carry  us into  the 24-bit  arena of  colors, but  is still  useful on
  867.           systems  with 16  colors. Programming  in this  manner means  a little
  868.           overhead up-front, but ease of programming, and portability later  on.
  869.           The ColorRef  variable defined here will  be used to set  the color of
  870.           our Hello World text via a common dialog call.
  871.  
  872.  
  873.           MyFont
  874.  
  875.                MyFont is  a handle to  the font we  are going  to select in  the
  876.           ChooseFont dialog box. Initially it is set to NULL, and this fact will
  877.           be used in the code.
  878.  
  879.  
  880.           WinMain
  881.  
  882.                The only changes to WinMain are the loading and freeing of  
  883.           COMMDLG.DLL instead of Fileo.DLL, and the freeing of the handle, 
  884.  
  885.  
  886.             if (MyFont != NULL)
  887.               DeleteObject(MyFont);
  888.  
  889.  
  890.           WndProc
  891.  
  892.                WndProc is the place where all the action happens, so most of the
  893.           changes are here.
  894.  
  895.  
  896.  
  897.                                           - 15 -
  898.  
  899.  
  900.  
  901.           Variables
  902.  
  903.                The following are defined for our new version:
  904.  
  905.                     OPENFILENAME   openfile;
  906.                     char           szFilter[25];
  907.                     char           szExt[4 + 1];
  908.                     char           szDirName[128];
  909.                     char           szFileSpec[16];
  910.                     char           szDefExt[5];
  911.                     char           szFileTitle[128];
  912.                     int                 i;
  913.  
  914.                     COLORREF       LocColorRef[16];
  915.                     CHOOSECOLOR    ChooseMyColor;
  916.  
  917.                     DWORD               dwFlags=CF_SCREENFONTS;
  918.                     CHOOSEFONT          ChooseMyFont;
  919.                     LOGFONT             LogMyFont;
  920.  
  921.                     HANDLE              TempFont;
  922.  
  923.  
  924.                OPENFILENAME  is  a structure  used by  the  file dialogs  in the
  925.           common dialog DLL. It is defined as:
  926.  
  927.              typedef struct tagOPENFILENAME 
  928.                 {
  929.                 DWORD     lStructSize;
  930.                 HWND      hwndOwner;
  931.                 HINSTANCE hInstance;
  932.                 LPCSTR    lpstrFilter;
  933.                 LPSTR     lpstrCustomFilter;
  934.                 DWORD     nMaxCustFilter;
  935.                 DWORD     nFilterIndex;
  936.                 LPSTR     lpstrFile;
  937.                 DWORD     nMaxFile;
  938.                 LPSTR     lpstrFileTitle;
  939.                 DWORD     nMaxFileTitle;
  940.                 LPCSTR    lpstrInitialDir;
  941.                 LPCSTR    lpstrTitle;
  942.                 DWORD     Flags;
  943.                 UINT      nFileOffset;
  944.                 UINT      nFileExtension;
  945.                 LPCSTR    lpstrDefExt;
  946.                 LPARAM    lCustData;
  947.                 UINT      (CALLBACK *lpfnHook) (HWND, UINT, WPARAM, LPARAM);
  948.                 LPCSTR    lpTemplateName;
  949.                 } OPENFILENAME;
  950.  
  951.           We  are only  going to  use the  minimum in  our example,  but  I will
  952.           mention the unused ones, to pique your interest to experiment.
  953.  
  954.  
  955.           CHOOSECOLOR
  956.  
  957.                CHOOSECOLOR is a structure used by the color dialogs in 
  958.           commdlg.DLL:
  959.  
  960.                                           - 16 -
  961.  
  962.  
  963.  
  964.              typedef struct tagCHOOSECOLOR 
  965.                 {
  966.                 DWORD   lStructSize;
  967.                 HWND    hwndOwner;
  968.                 HWND    hInstance;
  969.                 COLORREF rgbResult;
  970.                 COLORREF FAR* lpCustColors;
  971.                 DWORD   Flags;
  972.                 LPARAM  lCustData;
  973.                 UINT    (CALLBACK* lpfnHook)(HWND, UINT, WPARAM, LPARAM);
  974.                 LPCSTR  lpTemplateName;
  975.                 } CHOOSECOLOR;
  976.  
  977.           Again, before we are finished, all the entries will be explained.
  978.  
  979.  
  980.           CHOOSEFONT
  981.  
  982.                CHOOSEFONT is a structure used by the dialogs in commdlg.DLL:
  983.  
  984.              typedef struct tagCHOOSEFONT
  985.                  DWORD           lStructSize;
  986.                  HWND            hwndOwner;
  987.                  HDC             hDC;
  988.                  LOGFONT FAR*    lpLogFont;
  989.                  int             iPointSize;
  990.                  DWORD           Flags;
  991.                  COLORREF        rgbColors;
  992.                  LPARAM          lCustData;
  993.                  UINT (CALLBACK* lpfnHook)(HWND, UINT, WPARAM, LPARAM);
  994.                  LPCSTR          lpTemplateName;
  995.                  HINSTANCE       hInstance;
  996.                  LPSTR           lpszStyle;
  997.                  UINT            nFontType;
  998.                  int             nSizeMin;
  999.                  int             nSizeMax;
  1000.              } CHOOSEFONT;
  1001.  
  1002.  
  1003.           LOGFONT
  1004.  
  1005.                LOGFONT  is  a  structure  used by  the  dialogs  in  commdlg.DLL
  1006.           defining the characteristics of a font for drawing text on a window:
  1007.  
  1008.              typedef struct tagLOGFONT
  1009.                  int   lfHeight;
  1010.                  int   lfWidth;
  1011.                  int   lfEscapement;
  1012.                  int   lfOrientation;
  1013.                  int   lfWeight;
  1014.                  BYTE  lfItalic;
  1015.                  BYTE  lfUnderline;
  1016.                  BYTE  lfStrikeOut;
  1017.                  BYTE  lfCharSet;
  1018.                  BYTE  lfOutPrecision;
  1019.                  BYTE  lfClipPrecision;
  1020.                  BYTE  lfQuality;
  1021.                  BYTE  lfPitchAndFamily;
  1022.  
  1023.                                           - 17 -
  1024.  
  1025.  
  1026.  
  1027.                  BYTE  lfFaceName[LF_FACESIZE];
  1028.              } LOGFONT;
  1029.  
  1030.                The  first change to WndProc is in the WM_CREATE message handler.
  1031.           Since we  are gong  to be  changing colors, we  assign ColorRef  to be
  1032.           black:
  1033.  
  1034.                ColorRef = RGB(256, 256, 256); 
  1035.  
  1036.           and continue on. Then in the WM_PAINT handler, we use  ColorRef in the
  1037.           SetTextColor call:
  1038.  
  1039.                SetTextColor(hdc, ColorRef);
  1040.  
  1041.                Now  when we change ColorRef,  and force a  WM_PAINT message, the
  1042.           text  will be  painted in a  different color  of our  choice.  Another
  1043.           change  made to  the WM_PAINT message handler  is to check for a  font
  1044.           change:
  1045.  
  1046.              if (MyFont != NULL)
  1047.                 TempFont = SelectObject(hdc, MyFont);
  1048.  
  1049.              DrawText(...);
  1050.  
  1051.              if (MyFont != NULL)
  1052.                 SelectObject(hdc, TempFont);
  1053.  
  1054.                If MyFont  is set to a  handle value, that font  is selected into
  1055.           use by  doing a SelectObject  call. The  return value is  the previous
  1056.           handle, which we save in 'TempFont'. After displaying the text, we put
  1057.           the old font  back into use  by doing another SelectObject  call. This
  1058.           will  keep other display areas in our hdc from using the same font. In
  1059.           this  particular application, it may not be necessary, but good habits
  1060.           are hard to break.
  1061.  
  1062.  
  1063.           Files
  1064.  
  1065.                The IDM_FILEOPEN message handler has  changed quite a bit.  We've
  1066.           removed the  call to our DLL, and added the minimum that it would take
  1067.           to talk to the common dialog box GetOpenFileName procedure.
  1068.  
  1069.                We first use the  'memset' command to set the  structure openfile
  1070.           to  all nulls,  and insert  the structure  size into the  structure at
  1071.           lStructSize:
  1072.  
  1073.              case IDM_FILEOPEN :
  1074.                 memset(&openfile, 0, sizeof(OPENFILENAME));
  1075.                 openfile.lStructSize = sizeof(OPENFILENAME);
  1076.  
  1077.                We  then declare the  handle of the  owner of this  call as hWnd.
  1078.           This handle is used in the HELP call from the dialog box, to route the
  1079.           help message back to us:
  1080.  
  1081.                openfile.hwndOwner = hWnd;
  1082.  
  1083.                In the standard  file dialog boxes, there  is always a combo  box
  1084.           labeled  "List Files of Type", and  the list box contains entries such
  1085.  
  1086.                                           - 18 -
  1087.  
  1088.  
  1089.  
  1090.           as  "Text files (*.txt)" or  "All files (*.*)".  The developer defines
  1091.           those entries via the szFilter and szCustomFilter strings.
  1092.  
  1093.                Each of the  filter strings  is a concatenation  of string  pairs
  1094.           each null-terminated, and the  two filters are terminated by  a double
  1095.           null, one for  the final  filter declaration, and  one for the  filter
  1096.           itself. This is  reasonably confusing,  but maybe  a declaration  will
  1097.           clear it up. If  we want to have a  filter of "Text files" be  tied to
  1098.           "*.txt", we could fill our filter string as follows:
  1099.  
  1100.                sprintf(filter, "%s%c%s%c%c", "Text files", '\0', "*.txt",
  1101.                           '\0', '\0');
  1102.  
  1103.                An  alternate method  is useable  at run-time,  or if  you define
  1104.           strings in a  string table.  Substitute an unused  character for  your
  1105.           string separator, and then at run-time, change all those to '\0':
  1106.  
  1107.                strcpy(szFilter, "Text Files|*.txt|");
  1108.  
  1109.                for (i = 0; szFilter[i] != '\0'; i++)
  1110.                     {
  1111.                     if (szFilter[i] == '|')
  1112.                          szFilter[i] = '\0';
  1113.                }
  1114.                openfile.lpstrFilter = (LPSTR)szFilter;
  1115.                openfile.nFilterIndex = 1;
  1116.  
  1117.                Note  the  FilterIndex  begins with  "1",  not  0. A  value  of 0
  1118.           instructs Windows to use the CustomFilter string instead.
  1119.  
  1120.                lpstrFile  is a  pointer  to our  returned  file name,  which  we
  1121.           initialize to null:
  1122.  
  1123.                szFileName[0] = '\0';
  1124.                openfile.lpstrFile= (LPSTR)szFileName;
  1125.                openfile.nMaxFile = sizeof(szFileName);
  1126.  
  1127.                If we insert a file spec  in here, that is the name to  which the
  1128.           box is initialized.  This string  must be at  least 256 characters  to
  1129.           avoid problems with Windows.
  1130.  
  1131.                The FLAGS  entry  declares  various  parameters  controlling  the
  1132.           operation of the dialog box. The parameters we are using are:  
  1133.  
  1134.              OFN_SHOWHELP | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST
  1135.  
  1136.                OFN_SHOWHELP instructs the commdlg code  to display a help button
  1137.           in the dialog. As explained  above, this will assert the help  for our
  1138.           window, therefore the hwndOwner handle must not be NULL.
  1139.  
  1140.                OFN_PATHMUSTEXIST instructs  the commdlg  code to only  allow the
  1141.           user to type a path specification that exists currently on the system.
  1142.           An error is displayed if the path is non-existent.
  1143.  
  1144.                OFN_FILEMUSTEXIST is similar to OFN_PATHMUSTEXIST in that only 
  1145.           files that currently exist  on the currently enabled path  are allowed
  1146.           to   be  typed.  Selecting   OFN_FILEMUSTEXIST  automatically  selects
  1147.           OFN_PATHMUSTEXIST, but it is a helpful reminder to list both.
  1148.  
  1149.                                           - 19 -
  1150.  
  1151.  
  1152.  
  1153.                There  are other  parameters that  may be  defined  for selecting
  1154.           multiple  files, kicking  off  a new  file  message, enabling  a  hook
  1155.           function (explained  below)  or using  a custom  dialog box  template,
  1156.           among others.
  1157.  
  1158.                Next comes the call that does all the work:
  1159.  
  1160.                GetOpenFileName(&openfile);
  1161.  
  1162.                Normally,  the result of this would be checked for errors, but in
  1163.           this application, we aren't searching for a file, or intending to open
  1164.           one. We are simply demonstrating the use of the dialog.
  1165.  
  1166.                Staying  standard with  previous articles,  we copy  the returned
  1167.           file name to OutMsg, and finish this message handler:
  1168.  
  1169.                lstrcpy(OutMsg, openfile.lpstrFile);
  1170.                break;
  1171.  
  1172.  
  1173.           Other parameters
  1174.  
  1175.                The unused parameters of the OPENFILENAME structure are:
  1176.  
  1177.           lpTemplateName declares a custom  dialog template to be used  in place
  1178.           of the common  one. To do this, hInstance must be set to  the instance
  1179.           value  of the window  owning the template,  and  the  FLAGS value must
  1180.           enable templates.
  1181.  
  1182.           nMaxCustFilter contains the size of the custom filter, if one is used.
  1183.  
  1184.           lpstrFileTitle  is  a  string that  will  receive  the  file name  and
  1185.           extension only, that is the path specification will be stripped.
  1186.  
  1187.           nMaxFileTitle is the length of lpstrFileTitle.
  1188.  
  1189.           lpstrInitialDir is the starting  directory of the dialog box.  If this
  1190.           is  NULL,  the  current  directory is  used.  If,  however,  lpstrFile
  1191.           contains the  full  path specification  and file  specification for  a
  1192.           file, the path portion of lpstrFile is used as the initial directory.
  1193.  
  1194.           lpstrTitle is  a user-definable title for the  dialog box. A NULL will
  1195.           cause a standard default value to be used.
  1196.  
  1197.           nFileOffset is a returned value denoting where in the lpstrFile string
  1198.           the filename begins.
  1199.  
  1200.           nFileExtension  is a returned  value denoting  where in  the lpstrFile
  1201.           string the file extension begins.
  1202.  
  1203.           lpstrDefExt  is a user-definable default  extension to be  used in the
  1204.           search if the user does not type an extension on a filename.
  1205.  
  1206.           lpfnHook is  a pointer to  a function that  snags dialog box  messages
  1207.           prior  to  the  common  dialog  box  handler,  allowing  the  user  to
  1208.           intercept, or pre-process calls.
  1209.  
  1210.           lCustData defines the data that is passed to the hook function.
  1211.  
  1212.                                           - 20 -
  1213.  
  1214.  
  1215.  
  1216.                That  should  be enough  variation  to make  anyone  pleased with
  1217.           "common" dialogs. And that is only one of the set.
  1218.  
  1219.  
  1220.           Color
  1221.  
  1222.                To enable a  developer to allow an end-user to  define colors for
  1223.           text or dialogs,  the ChooseColor  common dialog was  built. An  extra
  1224.           entry in  the menu structure of our  code, under "SAMPLE" was declared
  1225.           as IDM_COLOR, and handled as follows:
  1226.  
  1227.                case IDM_COLOR :
  1228.                     memset(&ChooseMyColor, 0, sizeof(CHOOSECOLOR));
  1229.                     ChooseMyColor.lStructSize=sizeof(CHOOSECOLOR);
  1230.  
  1231.                As  with  the  File dialog,  the  CHOOSECOLOR  structure must  be
  1232.           declared, and its size passed to the dialog.
  1233.  
  1234.                The  initial 16 custom colors are declared going into the dialog,
  1235.           and coming out of the dialog via lpCustColors:
  1236.  
  1237.                ChooseMyColor.lpCustColors=LocColorRef;
  1238.  
  1239.                The handle of  the window owning the  dialog is declared as  with
  1240.           the file dialog:
  1241.  
  1242.                ChooseMyColor.hwndOwner = hWnd;
  1243.  
  1244.                rgbResult  is the  value  that is  initially  displayed with  the
  1245.           dialog is opened, and the value that is returned by the user:
  1246.  
  1247.                ChooseMyColor.rgbResult = RGB(256, 256, 256);
  1248.  
  1249.                As with the file dialogs, flags may enable hooks, templates, help
  1250.           buttons, and control the full usage  of the dialog. In our example, we
  1251.           have  chosen  to use  the rbgResult  value  as the  initially selected
  1252.           value.
  1253.  
  1254.                ChooseMyColor.Flags = CC_RGBINIT;
  1255.  
  1256.                The  action  happens  with   "ChooseColor".  The  dialog  box  is
  1257.           displayed, and  the user is allowed to choose colors as in the control
  1258.           panel of Windows. The result may be checked for an  error value (other
  1259.           than 0), and the resulting color is returned via rgbResult.
  1260.  
  1261.                if (ChooseColor(&ChooseMyColor))
  1262.                     {
  1263.                     ColorRef = ChooseMyColor.rgbResult;
  1264.                     InvalidateRect(hWnd, NULL, FALSE);
  1265.                     UpdateWindow(hWnd);
  1266.                     }
  1267.                break;
  1268.  
  1269.                In  our case, rbgResult is  written to ColorRef,  and used when a
  1270.           repaint takes place,  allowing the user to  pick the color  with which
  1271.           Hello/Goodbye Windows is painted. A  repaint is forced by Invalidating
  1272.           the entire window client  area (the NULL parameter), and  not painting
  1273.           the background (the FALSE parameter).
  1274.  
  1275.                                           - 21 -
  1276.  
  1277.  
  1278.  
  1279.                UpdateWindow  sends a  WM_PAINT message  straight to  the window,
  1280.           causing  the InvalidateRect area to  be repainted. This  takes care of
  1281.           the  change we have made with  the ChooseColor call, without having to
  1282.           resize the window to see the change.
  1283.  
  1284.                The structure CHOOSECOLOR contains lpfnHook, lCustData and 
  1285.           lpTemplateName as does the FILEOPEN structure.
  1286.  
  1287.  
  1288.           Fonts
  1289.  
  1290.                I  don't really want  to get into  a full discussion  of fonts at
  1291.           this time, because it  is extensive, but I do want to  show how to use
  1292.           the  ChooseFont dialog  box. If  some of  the information  is sketchy,
  1293.           accept it for  now, and  we'll dig  into it later,  or look  it up  in
  1294.           Petzold.
  1295.  
  1296.                An extra entry in the menu  structure of our code, under "SAMPLE"
  1297.           was declared as IDM_FONT, and handled as follows:
  1298.  
  1299.                case IDM_COLOR :
  1300.                     memset(&ChooseMyFont, 0, sizeof(CHOOSEFONT));
  1301.                     ChooseMyFont.lStructSize = sizeof(CHOOSEFONT);
  1302.                     ChooseMyFont.hwndOwner = hWnd;
  1303.  
  1304.                As  with the  other  dialogs, the  CHOOSEFONT  structure must  be
  1305.           declared,  its  size  passed to  the  dialog,  and  our window  handle
  1306.           inserted into the structure.
  1307.  
  1308.                A  variable  of  LOGFONT type  is  pointed  to  by the  lpLogFont
  1309.           structure entry, and filled out as follows:
  1310.  
  1311.                ChooseMyFont.lpLogFont = &LogMyFont;
  1312.                lstrcpy(LogMyFont.lfFaceName, "Times New Roman");
  1313.                LogMyFont.lfHeight = 10;
  1314.                LogMyFont.lfItalic = FALSE;
  1315.                LogMyFont.lfWeight = FW_BOLD;
  1316.                LogMyFont.lfStrikeOut = FALSE;
  1317.                LogMyFont.lfUnderline = FALSE;
  1318.  
  1319.                This declares 'Times New Roman' in bold 10 point type.
  1320.  
  1321.                As  I said above, at this point  in time, I am choosing to ignore
  1322.           the  other  structure  variables in  LOGFONT,  and  at  least for  our
  1323.           application,  the  default  values  of  NULL  (as  inserted  with  the
  1324.           'memset') work fine.
  1325.  
  1326.                Our ColorRef  value is  inserted into the  ChooseMyFont structure
  1327.           because color may come into play in the ChooseFont dialog:
  1328.  
  1329.                ChooseMyFont.rgbColors = ColorRef;
  1330.  
  1331.                The flags selected in our example are
  1332.  
  1333.                ChooseMyFont.Flags = CF_SCREENFONTS | CF_EFFECTS 
  1334.                     | CF_INITTOLOGFONTSTRUCT;
  1335.  
  1336.           CF_SCREENFONTS  forces  the  dialog  box  to  display  only  the fonts
  1337.  
  1338.                                           - 22 -
  1339.  
  1340.  
  1341.  
  1342.           available currently for the screen.
  1343.  
  1344.           CF_EFFECTS enables the strikeout, underline, and color  effects of the
  1345.           dialog box.
  1346.  
  1347.           CF_INITTOLOGFONTSTRUCT inits  the dialog box to  the LOGFONT structure
  1348.           as declared above.
  1349.  
  1350.                Now the  call to ChooseFont is made,  and if successful, the font
  1351.           handle  'MyFont' is  set to  the new  font chosen  in the  dialog, and
  1352.           ColorRef is set likewise.
  1353.  
  1354.                if (ChooseFont(&ChooseMyFont))
  1355.                     {
  1356.                     if (MyFont != NULL)
  1357.                          DeleteObject(MyFont);
  1358.  
  1359.                     MyFont = CreateFontIndirect(&LogMyFont);
  1360.                     ColorRef = ChooseMyFont.rgbColors;
  1361.  
  1362.                     InvalidateRect(hWnd, NULL, TRUE);
  1363.                     UpdateWindow(hWnd);
  1364.                     }
  1365.                break;
  1366.            
  1367.  
  1368.           Include file
  1369.  
  1370.                IDM_COLOR and IDM_FONT must  be added to hello.h to  allow access
  1371.           to the new menu items:
  1372.  
  1373.                #define IDM_COLOR          326
  1374.                #define IDM_FONT           327
  1375.  
  1376.  
  1377.           Resource file
  1378.  
  1379.                The  addition of  the Color selection  to the menu  is defined in
  1380.           hello.rc:
  1381.  
  1382.                menuitem separator
  1383.                menuitem "&Color",  IDM_COLOR
  1384.                menuitem "&Font",        IDM_FONT
  1385.  
  1386.  
  1387.           EndDialog
  1388.  
  1389.                I have  included  the Microsoft  make file,  MHello.mak for  this
  1390.           issue. I received no help  so far on the request from  help wizards in
  1391.           the last issue, so the offer still stands.
  1392.  
  1393.                For the next issue, I had intentions of  starting to discuss Help
  1394.           files,  but WPJ  seems to  have  started on  that already,  so I  will
  1395.           continue  with my schedule, and  discuss displaying a  dialog box as a
  1396.           main window.
  1397.  
  1398.                Feel  free to contact  me in  any of the  ways below.   Thanks to
  1399.           those of you that have e-mailed me questions; keep them coming.
  1400.  
  1401.                                           - 23 -
  1402.  
  1403.  
  1404.  
  1405.           Dave Campbell 
  1406.              WynApse PO Box 86247 Phoenix, AZ 85080-6247 (602)863-0411    
  1407.              wynapse@indirect.com
  1408.              CIS: 72251,445
  1409.              Phoenix ACM BBS (602) 970-0474 - WynApse SoftWare forum
  1410.  
  1411.           ---------------------------------------------------------------
  1412.           ClickBar  consists of a Dynamic  Dialog pair that  allows C developers
  1413.           for  Windows 3.0/3.1  to insert  a  "toolbar" into  their application.
  1414.           Microsoft,  Borland, etc.  developers may  display a  toolbar  or tool
  1415.           palette inside their application,  according to a DLG script  in their
  1416.           .RC or .DLG file. 
  1417.  
  1418.           Borland developers may install ClickBar as a custom control, providing
  1419.           three   custom widgets added  to the Resource  Workshop palette. These
  1420.           are three different button  profiles defining 69 custom  button images
  1421.           total.  The buttons may be placed  and assigned attributes identically
  1422.           to the intrinsic Resource Workshop widgets.
  1423.  
  1424.           Source  is provided  for  a  complete  example  of  using  the  tools,
  1425.           including the the use of custom (owner-drawn) bitmaps for buttons.
  1426.  
  1427.           Version  1.5 uses  single-image bitmaps  to reduce  the DLL  size, and
  1428.           includes source for a subclass  example for inserting a toolbar.
  1429.  
  1430.           Registration is  $35 and includes  a registration  number and  printed
  1431.           manual.
  1432.  
  1433.                         WynApse
  1434.                         PO Box 86247               (602) 863-0411
  1435.                         Phoenix, AZ 85050-6247     CIS: 72251,445
  1436.  
  1437.  
  1438.  
  1439.  
  1440.  
  1441.  
  1442.  
  1443.  
  1444.  
  1445.  
  1446.  
  1447.  
  1448.  
  1449.  
  1450.  
  1451.  
  1452.  
  1453.  
  1454.  
  1455.  
  1456.  
  1457.  
  1458.  
  1459.  
  1460.  
  1461.  
  1462.  
  1463.  
  1464.                                           - 24 -
  1465.  
  1466.  
  1467.  
  1468.                                        Hacker's Gash
  1469.                                       By Dennis Chuah
  1470.  
  1471.                In this instalment of Hacker s Gash, we will look at system level
  1472.           entities, the  ToolHelp library and  extend the discussion  to private
  1473.           class  dialogboxes.   Plus,  we will  continue  last month s  metafile
  1474.           discussion.  Here is a summary of the topics presented:
  1475.  
  1476.           1.   Instances, Modules and Tasks
  1477.           2.   When Should HINSTANCES, HMODULES and HTASKS be Used
  1478.           3.   List of Modules
  1479.           4.   Private Dialogbox Class
  1480.           5.   Metafiles revisited
  1481.  
  1482.                All  example programs  are  compiled with  Borland  C++ with  the
  1483.           following options:
  1484.  
  1485.           Memory model:  MEDIUM
  1486.           Optimization:  Fastest Code
  1487.           Entry/Exit Code:  Smart Callbacks
  1488.           Floating Point:  None
  1489.           Instruction Set:  80286
  1490.           Calling Convention:  C
  1491.           Instances, Modules and Tasks.
  1492.                Most of you should have come across instances.   Windows passes a
  1493.           handle to an application  (HINSTANCE) in the WinMain function.   Calls
  1494.           made to  RegisterWindow  and  CreateWindow  require  this  value.    A
  1495.           HINSTANCE is something you have used often but have you  stop to think
  1496.           of what  a  HINSTANCE is?    Then there  is  the handle  to  a  module
  1497.           (HMODULE) and the handle  to a task (HTASK).   You have heard of  them
  1498.           before, but have never needed to use them.  What are they anyway?
  1499.  
  1500.                I  wrote a  .DLL and  an application  to illustrate  these system
  1501.           entities.  The source code and executable are in the file GASH.ZIP, in
  1502.           the TEST  subdirectory.  This  .ZIP file  contains subdirectories,  so
  1503.           remember to use the  -d flag when unzipping  it.  Figure 1a  shows the
  1504.           output of the application.
  1505.  
  1506.  
  1507.           Figure 1a
  1508.  
  1509.           I then ran another instance of the application and the result is shown
  1510.           in figure 1b.
  1511.  
  1512.  
  1513.           Figure 1b
  1514.  
  1515.                With the aid  of figures 1a and 1b, we can see that the HINSTANCE
  1516.           is  unique  for  different instances  of  the  application.   This  is
  1517.           sensible  --  HINSTANCE uniquely  identifies an  application instance.
  1518.           The  HINSTANCE  for the  .DLL however,  is  the same  across different
  1519.           instances of the application that used it.  This is  also sensible, as
  1520.           there is only one  instance of a .DLL no  matter how many times  it is
  1521.           used.   HMODULE appears to  be the same  across different instances of
  1522.           the application and .DLL.  HTASK on the other hand, is the same within
  1523.           an instance, whether it was obtained from the .EXE module or the .DLL.
  1524.  
  1525.                A  little  explanation is  in  order.   HINSTANCE  identifies the
  1526.  
  1527.                                           - 25 -
  1528.  
  1529.  
  1530.  
  1531.           current instance.  This is why every instance of an  application has a
  1532.           unique  HINSTANCE.   For  every instance  of  an application,  Windows
  1533.           creates  a data  segment  (This is  usually  the  case as  the  module
  1534.           definition file in an .EXE usually specifies multiple data  segments).
  1535.           Although  undocumented, I have found  that HINSTANCE is  the handle to
  1536.           the application s data segment.
  1537.  
  1538.                Take figure 1a for example.   I ran the test application and used
  1539.           Turbo Debugger to debug it.   On examination, the DS register  has the
  1540.           value,  0x2E47.  I then  inspected hInstance with  the following Turbo
  1541.           Debugger typecast:
  1542.  
  1543.           (gh2fp) (unsigned) hInstance
  1544.  
  1545.                This is equivalent to doing a GlobalLock on hInstance.  The value
  1546.           was,  2E47:0000.  Presto!  The selector  value was exactly the same as
  1547.           the value in the DS register.
  1548.  
  1549.           Undocumented:  HINSTANCE is the handle to the data segment.
  1550.  
  1551.                Every .DLL has a unique  HINSTANCE, but as the .DLL has  only one
  1552.           data segment that is shared across every application that makes use of
  1553.           it, there is  only one HINSTANCE  value.  As a  rule, DLL modules  are
  1554.           only  instanced once  no matter  how many  times they  are used.   EXE
  1555.           modules on  the other hand are  instanced once for each  time they are
  1556.           used.
  1557.  
  1558.                Knowing this is  they key to understanding why exported functions
  1559.           in  an application  needs  to instanced  through MakeProcInstance  and
  1560.           those in a .DLL do not.  Window functions do not need to be instanced.
  1561.           A field in the  windows information structure is GWW_HINSTANCE.   When
  1562.           messages  are dispatched,  the correct  data segment  is bound  to the
  1563.           window procedure via  this HINSTANCE value.   Subclass procedures must
  1564.           be instanced before  use.   The HINSTANCE in  the GWW_HINSTANCE  field
  1565.           refers to  the  window  procedure s  data segment,  not  the  subclass
  1566.           procedure s.   Also, the subclass  procedure must not  call the window
  1567.           procedure  directly,  but  should   instead  use  the   CallWindowProc
  1568.           function.     CallWindowProc   binds  the   HINSTANCE  value   in  the
  1569.           GWW_HINSTANCE field to the window procedure.
  1570.  
  1571.                As explained in the  last instalment, exported functions compiled
  1572.           with  Borland s  smart  callbacks  entry/exit code  automatically bind
  1573.           their data segment on entry.
  1574.  
  1575.                HMODULE  refers to the file that Windows loads the code segments,
  1576.           resources,  etc.   It can  either be an  .EXE or  a .DLL  file.  Well,
  1577.           actually,  HMODULE is  used  to  keep  track  of  resources  that  are
  1578.           associated with the .EXE or .DLL  file.  Take a look at the  following
  1579.           declaration for CLASSENTRY.
  1580.  
  1581.           typedef struct tagCLASSENTRY
  1582.             {DWORD   dwSize;
  1583.              HMODULE hInst;
  1584.              char    szClassName[MAX_CLASSNAME + 1];
  1585.              WORD    wNext;
  1586.             } CLASSENTRY;
  1587.  
  1588.                This structure  is used  by the ToolHelper  function, ClassFirst.
  1589.  
  1590.                                           - 26 -
  1591.  
  1592.  
  1593.  
  1594.           We can  see that there is  a HMODULE  field  in it.  When  a module is
  1595.           freed,  all classes  associated with  the module  will also  be freed.
  1596.           When  a  class is  registered, Windows  obtains  the HMODULE  from the
  1597.           specified HINSTANCE.  The class is then bound to the module  specified
  1598.           by the HMODULE value.  This  is why in Windows 3.1, only  one instance
  1599.           of an application is  supposed to register classes.  Every instance of
  1600.           an application share the same HMODULE.
  1601.  
  1602.                Tasks  are like  logical  CPUs.   Each  has its  own  instruction
  1603.           pointer and set of registers.  Each task also  has its own stack.  The
  1604.           stack is created in  the application s data segment (the  data segment
  1605.           bound  to  the  .EXE  module).    Every  application  on  the  desktop
  1606.           (including  the shell application --  eg. Program Manager)  is a task.
  1607.           In 386 enhanced mode, all tasks  run in a virtual machine in protected
  1608.           mode.  Every DOS  window is a virtual machine that  runs in real mode.
  1609.           Windows performs  preemtive multitasking  across virtual machines  but
  1610.           only  performs cooperative  multitasking across  the tasks  within the
  1611.           protected mode virtual machine.
  1612.  
  1613.  
  1614.           Figure 2
  1615.  
  1616.                For each  task, Windows creates an  execution context (HINSTANCE)
  1617.           associated to  an .EXE module.   The task may call  functions in other
  1618.           modules.   The functions  entry code will switch the execution context
  1619.           to the module s.  Unless the  .DLL switches stacks, it executes on the
  1620.           task s stack.    Execution context  switching  is transparent  to  the
  1621.           programmer, but incurs a penalty in CPU clock cycles.
  1622.  
  1623.                Processors  > 80286 cache  their segment  registers.   Well, they
  1624.           actually  cache  the  descriptors  pointed to  by  selector  registers
  1625.           (segment registers  in real mode).   As a  result, when a  selector is
  1626.           loaded, the CPU reloads  the descriptor cache, taking up  valuable CPU
  1627.           clock cycles.   A call causing an execution context  switch causes two
  1628.           descriptors to be  reloaded (one each  for the  code segment and  data
  1629.           segment).
  1630.  
  1631.                When a task calls  GetMessage, PeekMessage, WaitMessage, Yield or
  1632.           any other API that  causes a dialog box to  pop up, a task  switch may
  1633.           occur.  If there  are other tasks  waiting to be  executed and any  of
  1634.           those functions were called, Windows will switch the  next task in the
  1635.           task  queue in.    Although  task  switches  are  transparent  to  the
  1636.           programmer, a  misbehaving application  can prevent Windows  from task
  1637.           switching.     As  I  have  mentioned   earlier,  Windows  operates  a
  1638.           cooperative  multitasking system,  meaning  it is  up to  well-behaved
  1639.           applications to give their time-slice back to Windows periodically.  A
  1640.           task switch  incurs  a  lot  more CPU  clock  cycle  penanty  than  an
  1641.           execution context switch.
  1642.  
  1643.           Trap:     Code that causes too many task switches can cause Windows to
  1644.           slow  down due to  CPU clock cycle  penalty.  This  penalty is usually
  1645.           refered to as the multitasking overhead.
  1646.           When Should HINSTANCE, HMODULE and HTASK be Used?
  1647.  
  1648.                A number of Windows  functions accept and return HINSTANCE.   The
  1649.           following functions accepts HINSTANCE as a parameter.
  1650.  
  1651.           FreeModule
  1652.  
  1653.                                           - 27 -
  1654.  
  1655.  
  1656.  
  1657.           GetModuleFileName
  1658.           GetModuleUsage
  1659.  
  1660.                It may seem  strange that these  functions accept HINSTANCE  when
  1661.           they actually  deal with  modules.   The fact is,  both HINSTANCE  and
  1662.           HMODULE can be used.  It is relatively easy to obtain the HMODULE from
  1663.           a  given  HINSTANCE.   The header  file,  WINDOWSX.H contains  a macro
  1664.           definition  called  GetInstanceModule  that  accepts a  HINSTANCE  and
  1665.           returns a HMODULE.  It passes  the HINSTANCE to the low order word  of
  1666.           the lpszModuleName  parameter in  the call  to GetModuleHandle.   This
  1667.           function returns  the HMODULE.   The  following functions  also accept
  1668.           HMODULE in addition to HINSTANCE.
  1669.  
  1670.           FreeLibrary
  1671.           GetProcAddress
  1672.  
  1673.           The following functions return HINSTANCE:
  1674.  
  1675.           LoadLibrary
  1676.           LoadModule
  1677.           WinExec
  1678.  
  1679.                As  explained  earlier,  the GetModuleHandle  function  returns a
  1680.           HMODULE value.
  1681.  
  1682.                The  class registration  functions  (RegisterClass, etc),  window
  1683.           functions  (CreateWindow, etc)  and resource  functions (LoadResource,
  1684.           etc) all  accept  HINSTANCE.    MakeProcInstance  and  GetInstanceData
  1685.           expect HINSTANCE.
  1686.           To  extract HINSTANCE from a given HMODULE is not straightforward, and
  1687.           in some cases, impossible.  This is because .EXE modules can have more
  1688.           than  one execution context (one  per instance).   Therefore, given an
  1689.           .EXE  HMODULE, it is  still ambigiuos as  to which HINSTANCE  is to be
  1690.           extracted.  If  there is only one instance of  an application running,
  1691.           the ToolHelper library can be used to extract the HINSTANCE.
  1692.  
  1693.                However, it is possible to extract the HINSTANCE of a .DLL  given
  1694.           the HMODULE.  The following  function obtains the HINSTANCE of  a .DLL
  1695.           from a  HMODULE.  It  retrieves the full  path name of the  module and
  1696.           loads  it.   This  increments  the usage  count  of the  module.   The
  1697.           LoadLibrary functions,  in addition to loading the module, returns the
  1698.           HINSTANCE.   The module is then freed to decrement the usage count and
  1699.           the HINSTANCE returned.
  1700.  
  1701.           HINSTANCE GetDllModuleInstance (HMODULE hModule)
  1702.             {static char path[256];
  1703.              HINSTANCE hInstance;
  1704.              
  1705.              GetModuleFileName (hModule, path, 256);
  1706.              hInstance = LoadLibrary (path);
  1707.              FreeLibrary (hInstance);
  1708.              return hInstance;
  1709.             }
  1710.  
  1711.                It assumes that  the HMODULE passed to it  is a handle to  a .DLL
  1712.           module.  GASH.ZIP contains the full source code to the above function,
  1713.           in the INSTANCE subdirectory.
  1714.  
  1715.  
  1716.                                           - 28 -
  1717.  
  1718.  
  1719.  
  1720.           Tip: As it is easier to obtain HINSTANCE, it is recommended that it be
  1721.           used in place  of HMODULE where possible.   It is the  value passed to
  1722.           the  WinMain and LibMain  functions.  The  GetWindowWord function will
  1723.           return the HINSTANCE of a HWND if GWW_HINSTANCE is specified.
  1724.  
  1725.                The  GetCurrentTask function  returns the  HTASK for  the current
  1726.           application.  EnumTaskWindows and  PostAppMessage both accept HTASK as
  1727.           a parameter.   The ToolHelp  library contains  several functions  that
  1728.           allow all tasks to be enumerated.  
  1729.           List of Modules
  1730.  
  1731.                In  the INSTANCE\TEST subdirectory  of GASH.ZIP, there  is a test
  1732.           application  named,  TESTINS.EXE.    The  full  source  code  is  also
  1733.           contained  in the same subdirectory.   TESTINS is  an application that
  1734.           displays  a list of  modules in the  system, their names,  their usage
  1735.           count  and their full path name.   The Update button updates the list.
  1736.           As the list is not updated automatically, any modules added or removed
  1737.           from  the system  will not  show in  the list.   The  hInstance button
  1738.           displays  the hInstance  of the module  (provided it  is a  .DLL) in a
  1739.           messagebox.
  1740.  
  1741.                The ToolHelp library is used to obtain the list of modules in the
  1742.           system.    The ModuleFirst  function was  called  to return  the first
  1743.           module  in Windows  module  list.   Information  is returned  via  the
  1744.           MODULEENTRY data structure.   The ModuleNext function was  then called
  1745.           repeatedly until every module was enumerated.
  1746.  
  1747.           Tip: Although  the ToolHelp library is  meant to be  used by debugging
  1748.           applications,  it  is  by  no means  limited  to  these  applications.
  1749.           Non-debugging applications can make use  of the functions exported  by
  1750.           the  ToolHelp  library  to  get  access  into  Windows  internal data.
  1751.           ToolHelp  provides a documented set of API that allows applications to
  1752.           access undocumented Windows API.  To use the ToolHelp library, include
  1753.           the TOOLHELP.H header file.  All the exported functions are already in
  1754.           the default import library (IMPORT.LIB).
  1755.  
  1756.                When  a  selection is  made  in  the  listbox,  another  ToolHelp
  1757.           function  is  called, the  ModuleFindHandle.    This function  returns
  1758.           information  regarding a  hModule in  the MODULEENTRY  data structure.
  1759.           Information in  the structure is then  used to update the  usage count
  1760.           and path information boxes.
  1761.  
  1762.                When the hInstance button  is pressed, ModuleFindHandle is called
  1763.           again.    A  rudimentary method  was  used  to  determine whether  the
  1764.           selected module is a .DLL.   If it was  determined that it is a  .DLL,
  1765.           the GetDLLModuleInstance function is called to return the hInstance of
  1766.           the .DLL.
  1767.           Private Dialogbox Class
  1768.                The TESTINS  application uses a private  class modeless dialogbox
  1769.           as its main  window.  The application  is a simple  one.  There is  no
  1770.           need  for  the  normal   sort of  window.   A  dialogbox significantly
  1771.           simplifies the coding.  Why did I  use a private class dialogbox?   In
  1772.           this example, the main reason was instructional.
  1773.  
  1774.           Tip: Private class  dialogboxes provide  an easy  way to associate  an
  1775.           icon to a dialogbox.  To  create a private class dialogbox, define the
  1776.           dialogbox  template  in  the  resource file  with  the  optional CLASS
  1777.           statement.  Specify the name of the class.
  1778.  
  1779.                                           - 29 -
  1780.  
  1781.  
  1782.  
  1783.           MyDialogBox DIALOG 20, 20, 183, 193
  1784.           STYLE ...
  1785.           CLASS "myclassname"
  1786.           BEGIN
  1787.             .
  1788.             .
  1789.             .
  1790.           END
  1791.  
  1792.           In  your application, register the  class with the  handle to the icon
  1793.           assigned  to the  hIcon  field of  the  WNDCLASS structure.    Specify
  1794.           DefDialogProc  in the lpfnWndProc  field.  Important:   The cbWndExtra
  1795.           field must be larger or equal to DLGWINDOWEXTRA.
  1796.  
  1797.           if (hPrev == NULL)
  1798.             {wc.style = CS_HREDRAW | CS_VREDRAW;
  1799.              wc.lpfnWndProc = DefDlgProc;
  1800.              wc.cbClsExtra = 0;
  1801.              wc.cbWndExtra = DLGWINDOWEXTRA;
  1802.              wc.hInstance = hInstance;
  1803.              wc.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE (MAIN));
  1804.              wc.hCursor = LoadCursor (NULL, IDC_ARROW);
  1805.              wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
  1806.              wc.lpszMenuName = NULL;
  1807.              wc.lpszClassName = MAINCLASS;
  1808.              RegisterClass (&wc);
  1809.             }
  1810.  
  1811.           If you wish  to use any of  the window extra  bytes, you must add  the
  1812.           number  of extra bytes  to this value.   To access  those bytes, start
  1813.           from offset DLGWINDOWEXTRA instead of 0.  Use the usual  way to create
  1814.           the dialogbox.
  1815.  
  1816.           hWnd = CreateDialog (hInstance, "MyDialogBox", NULL, DlgProc);
  1817.  
  1818.           CreateDialog create  a modeless  dialogbox, while DialogBox  creates a
  1819.           modal  one.  Specifying DefDialogProc  in the lpfnWndProc field allows
  1820.           the   normal  way  of  using  a  dialogbox  (ie.  using  a  DIALOGPROC
  1821.           callback)  to  be employed.   This  is  easier than  writing  a window
  1822.           procedure that has DefDialogProc characteristics.
  1823.  
  1824.           Trap:     Failing  to set  the cbWndExtra  field to  DLGWINDOWEXTRA or
  1825.           larger will cause the USER module to GP fault!
  1826.  
  1827.           Trap:     Failing  to call  IsDialogMessage in  the message  loop will
  1828.           result  in  your dialog  box  losing  the  normal  dialogbox  keyboard
  1829.           interface (ie. tab moves from control to control).
  1830.           Metafiles revisited:
  1831.                Last   month,  we   looked  at  using   metafiles  saved   in  an
  1832.           application s  resources.   GASH.ZIP  contains the  source  code of  a
  1833.           function that loads a metafile from an instance s resources.  It is in
  1834.           the MET1.C file in  the METAFILE subdirectory.  The  associated header
  1835.           file, METAFILE.H, contains a #define that defines the  symbol METAFILE
  1836.           to the value 2000.  Include this file in your resource (.RC) file.  To
  1837.           include a metafile, use the following line;
  1838.  
  1839.           MetafileName  METAFILE "filename.wmf"
  1840.  
  1841.  
  1842.                                           - 30 -
  1843.  
  1844.  
  1845.  
  1846.           where MetafileName is the name  to be given to your metafile.   It can
  1847.           either be  a #define integer symbol or a string  symbol.  If you use a
  1848.           #define integer symbol, be sure to use the MAKEINTRESOURCE macro.
  1849.  
  1850.                I  also mentioned that Windows API cannot handle metafiles with a
  1851.           placeable  header.  In this instalment I will show you the format of a
  1852.           placeable  metafile  header  and  write  a   few  functions  that  can
  1853.           manipulate metafiles with placeable headers.  MET2.C and MET3.C in the
  1854.           METAFILE subdirectory  of GASH.ZIP  contains two functions  that reads
  1855.           and  writes to  disk  based metafiles  with  placeable headers.    The
  1856.           functions are GetMetaFileBetter and CopyMetaFileBetter.
  1857.  
  1858.                Metafiles with  placeable headers are ordinary  metafiles with an
  1859.           additional 22-byte header.  The following show the structure 
  1860.  
  1861.  
  1862.           typedef struct
  1863.             {DWORD   key;
  1864.              HANDLE  hmf;
  1865.              RECT    bbox;
  1866.              WORD    inch;
  1867.              DWORD   reserved;
  1868.              WORD    checksum;
  1869.             } METAFILEHEADER;
  1870.  
  1871.                The  key field  contains  the magic  number,  0x9AC6CDD7L.   This
  1872.           identifies  the metafile  as one  with a  placeable header.   Ordinary
  1873.           metafiles do not have an identifying signature.
  1874.  
  1875.           Tip: The  first  four  bytes  of  a  placeable metafile  contains  the
  1876.           following signature:  0x9AC6CDD7L.  To identity  ordinary metafiles is
  1877.           not as easy.   You can use the signature 0x90001L  but this may not be
  1878.           unique.  Metafiles created by Windows version 3.0 and 3.1 contains the
  1879.           value  0x300  in the  following  2 bytes.    This value  is  0x100 for
  1880.           metafiles created by earlier versions of Windows.
  1881.  
  1882.                The hmf and reserved fields should be set to 0.
  1883.  
  1884.                The bbox field specifies the bounding rectangle  of the metafile.
  1885.           It is specified in metafile units.
  1886.  
  1887.                The inch field specifies the metafile units in units per inch.
  1888.  
  1889.                The check sum field is  an XOR value of the first 10 words in the
  1890.           header.  The following C statement calculates this value:
  1891.  
  1892.           checksum = LOWORD (key) ^ HIWORD (key) ^
  1893.                      LOWORD (*((DWORD *) &bbox)) ^ HIWORD (*((DWORD *) &bbox)) ^
  1894.                      inch
  1895.  
  1896.                The LOWORD/HIWORD key  values can be replaced  with the constants
  1897.           0xCDD7 and 0x9AC6.
  1898.  
  1899.           The GetMetaFileBetter function
  1900.  
  1901.                The  GetMetaFileBetter function  reads a  metafile just  like the
  1902.           GetMetaFile function in Windows API.  However, GetMetaFileBetter knows
  1903.           how to distinguish between an ordinary metafile and a  metafile with a
  1904.  
  1905.                                           - 31 -
  1906.  
  1907.  
  1908.  
  1909.           placeable  header.  It accepts  one additional parameter,  lpMh, a far
  1910.           pointer  to the  METAFILEHEADER  structure.   If  a valid  pointer  is
  1911.           specified  and  a  metafile with  a  placeable  header  was read,  the
  1912.           placeable header will be copied into this structure.
  1913.  
  1914.                GetMetaFileBetter  loads  the first  22  bytes  of the  specified
  1915.           metafile.  It checks it this is a placeable header.  If it is not, the
  1916.           GetMetaFile  function  is  called to  load  the  metafile.   The  lpMh
  1917.           parameter  is not filled in.   If GetMetaFileBetter  finds a placeable
  1918.           header,  memory is allocated to  load the rest  of the metafile, minus
  1919.           the  placeable  header.     The  metafile  is  then  loaded   and  the
  1920.           SetMetaFileBitsBetter function  is then  called to convert  the loaded
  1921.           file into a memory metafile.
  1922.  
  1923.           The CopyMetaFileBetter function
  1924.  
  1925.                The CopyMetaFileBetter function copies a metafile to disk with an
  1926.           optional  placeable  header, otherwise  it  functions  similar to  the
  1927.           CopyMetaFile   function  in   Windows   API.     Unlike  CopyMetaFile,
  1928.           CopyMetaFileBetter accepts one more parameter,  lpMh, a far pointer to
  1929.           the  METAFILEHEADER structure.    If this  pointer  is valid  and  the
  1930.           structure filled  in correctly, CopyMetaFileBetter will  create a disk
  1931.           metafile with  a placeable header.   The placeable header will  not be
  1932.           copied  if a  memory  metafile is  specified  as the  destination  (by
  1933.           specifying NULL for the lpszFile parameter).
  1934.  
  1935.                If the  lpMh parameter is not a valid pointer or if the structure
  1936.           wasn t filled in correctly,  or if a memory  metafile is specified  as
  1937.           the  destination,  CopyMetaFileBetter calls  CopyMetaFile to  copy the
  1938.           metafile.  Otherwise, CopyMetaFileBetter  opens the specified file and
  1939.           writes  the placeable header.  It then calls the CopyMetaFile function
  1940.           to  create a  duplicate of  the metafile and  passes the  duplicate to
  1941.           GetMetaFileBits function  to retrieve a  handle to the  metafile data.
  1942.           This data is then written to disk and the file closed.
  1943.  
  1944.           An example usage of GetMetaFileBetter and CopyMetaFileBetter
  1945.  
  1946.                The   METAFILE\TEST   subdirectory   of  GASH.ZIP   contains   an
  1947.           application   (TESTMET.EXE)  that  tests   the  GetMetaFileBetter  and
  1948.           CopyMetaFileBetter functions.  TESTMET  reads in GASH1.WMF, a metafile
  1949.           with a  placeable header, and  displays it in  the client area  of its
  1950.           window.   It then saves  two metafiles, GASH-YES.WMF  and GASH-NO.WMF.
  1951.           GASH-NO.WMF is saved from the memory metafile read from GASH1.WMF.  It
  1952.           is an  ordinary metafile.   GASH-YES.WMF is  also saved from  the same
  1953.           memory metafile but it contains a placeable header.  You can use DOS s
  1954.           fc  to compare  GASH1.WMF and  GASH-YES.WMF.   GASH2.WMF  is GASH1.WMF
  1955.           without the placeable  header.   Use fc again  to compare  GASH-NO.WMF
  1956.           with  it.  By  the way, GASH1.WMF  is this article s  logo in metafile
  1957.           format.
  1958.  
  1959.           Next month...
  1960.  
  1961.           Next month, I will write on these topics:
  1962.  
  1963.           More metafiles DIBs in metafiles
  1964.           Dealing with misbehaving metafiles
  1965.           Decoding the placeable header information
  1966.           Text placement in metafiles
  1967.  
  1968.                                           - 32 -
  1969.  
  1970.  
  1971.  
  1972.           Placing a metafile
  1973.           Moveable windows    Child windows that resize and  move every time the
  1974.           parent is resized
  1975.           Window panes - splitting a window into resizeable panes
  1976.           MDI windows    Accelerator for each type of MDI windows
  1977.           Dialogboxes    Integer vs string resource names
  1978.           Dialogbox font
  1979.           Dialogboxes as child windows
  1980.           Dialogboxes as MDI child windows
  1981.           Coloring dialogbox controls
  1982.           Keyboard interface and hotkeys
  1983.  
  1984.           The author:
  1985.  
  1986.                I am currently doing  a PhD. degree in Electrical  And Electronic
  1987.           Engineering  at   the  University  Of  Canterbury.     My  programming
  1988.           experience dates back to  the days where  real programmers   code with
  1989.           Z80 machine code.  For the past two years I have taken an  interest in
  1990.           Windows programming.
  1991.  
  1992.                Please send any comments, questions, criticisms to me via:
  1993.  
  1994.           email: chuah@elec.canterbury.ac.nz
  1995.           or post mail:  Dennis Chuah
  1996.           c/o The Electronic and Electrical Engineering Department
  1997.           University of Canterbury
  1998.           Private Bag
  1999.           Christchurch
  2000.           New Zealand.
  2001.  
  2002.                All mail (including  hate mail) is  appreciated.  I  will try  to
  2003.           answer  all questions personally, or if the answer has general appeal,
  2004.           in  the next  issue.   If  you will  like  to see  a particular  topic
  2005.           discussed in up and coming issues, drop me a line.   I also appreciate
  2006.           discussions on topics published.  If you  are sending me email, please
  2007.           make  sure  you provide  me with  an address  that  I can  reach (most
  2008.           internet and bitnet nodes are reachable, and so is compuserve).
  2009.  
  2010.  
  2011.  
  2012.  
  2013.  
  2014.  
  2015.  
  2016.  
  2017.  
  2018.  
  2019.  
  2020.  
  2021.  
  2022.  
  2023.  
  2024.  
  2025.  
  2026.  
  2027.  
  2028.  
  2029.  
  2030.  
  2031.                                           - 33 -
  2032.  
  2033.  
  2034.  
  2035.                                       Gdi See Gdi Do
  2036.                                Graphics Animation in Windows
  2037.                                      By Bernard Andrys
  2038.  
  2039.                Well, it's next month already and  I bet all 3 of my  readers are
  2040.           wondering when I'll turn my "Do Nothing Program" into a way cool, time
  2041.           wasting, blow'em'all'up arcade game.
  2042.  
  2043.                But first, a VERY, VERY important correction to the last article:
  2044.  
  2045.  
  2046.                As was  pointed out by Michael  Birk (one of the  three readers),
  2047.           using the PASCAL  keyword for a function  declaration does not make  a
  2048.           function  pass  parameters by reference.  I read  about the purpose of
  2049.           the PASCAL  keyword  in an  article  but I  really  should have  tried
  2050.           passing parameters using  the PASCAL keyword  before claiming that  it
  2051.           works.  I apologize for any confusion that it may have caused.
  2052.  
  2053.                ...and now back to blowing things up.
  2054.  
  2055.                In  this article,  we'll at  least get  something moving.   We're
  2056.           going  to put the missile base on the screen and make it move when you
  2057.           press the cursor  keys.   Let's start with  what we'll need  to put  a
  2058.           missile base on the screen and make it move:
  2059.  
  2060.           1. A missile base
  2061.           2. A way to put the missile base in the window.
  2062.           3. A way to link the keyboard to the drawing of the missile base.
  2063.  
  2064.                The first  requirement is pretty easy.  We need to define all the
  2065.           properties that a missile base has:
  2066.  
  2067.           1. Its appearance
  2068.           2. Its location
  2069.           3. How many lives it has left.
  2070.           4.  Whether it can shoot or not. (We're only going to let it shoot one
  2071.           missile at a time.)
  2072.           5. Has it been hit by  an invader's missile? (so we know if  we should
  2073.           draw an explosion instead of a missile base)
  2074.  
  2075.                We'll work on the first two  properties now and save the rest for
  2076.           later.
  2077.  
  2078.                The first property,  position, is its x and y coordinates.  We'll
  2079.           use  the variables x  and y  to represent the  missile base's  x and y
  2080.           coordinates.   Before  we use  the variables  x and  y, we'll  need to
  2081.           declare them.  Declaring a variable  tells the C compiler what type of
  2082.           variable is  going to be  used.   We should pick  a data type  for our
  2083.           variables x and y that is big enough to handle the largest values that
  2084.           might be assigned to x and y.
  2085.  
  2086.                So how big are the data types that we have to work with?
  2087.  
  2088.           Data Type           Size
  2089.           char                -128 to 127
  2090.           int                 -32,768 to 32,767
  2091.           unsigned char       0 to 255
  2092.           unsigned int        0 to 65,535
  2093.  
  2094.                                           - 34 -
  2095.  
  2096.  
  2097.  
  2098.           unsigned int        0 to 4,294,967,295
  2099.           float                    3.4E38
  2100.           double              1.7xE308
  2101.           void                nothing
  2102.  
  2103.                There are a lot of other data types supported by C compilers such
  2104.           as long int and  short int but considering that Win32  and NT are just
  2105.           around  the corner you probably  don't need the  confusion of learning
  2106.           X86  segmentation now. (Especially since this IS a beginner's column.)
  2107.           Windows  has its  own data  types as  well.   For example  HBITMAP and
  2108.           HINSTANCE are two data types that  are specific to Windows.  You would
  2109.           declare a variable as HBITMAP  if the variable was going to  contain a
  2110.           handle to a bitmap.  A handle is a number that a program uses to refer
  2111.           to a file, bitmap, or  some other object.  You don't normally  have to
  2112.           care  about  the  size of  Windows'  own data  types  like  HBITMAP or
  2113.           HINSTANCE  because Windows' own data types are normally used as return
  2114.           values  or parameters for Windows' own functions.   For example, if we
  2115.           used a function called LoadBitmap() it would return a variable of type
  2116.           HBITMAP.   The HBITMAP variable would then  be used where ever we want
  2117.           to refer to the actual bitmap.
  2118.  
  2119.           EXAMPLE:
  2120.  
  2121.           MyFunction()
  2122.           { 
  2123.           /* declare a variable called mybitmap which is data type HBITMAP */
  2124.           HBITMAP mybitmap;
  2125.           /* Window's function LoadBitmap() returns an HBITMAP variable */
  2126.           mybitmap = LoadBitmap(hInstance, "bitmap");
  2127.           }
  2128.  
  2129.                Actually,  Windows doesn't have its  own data types.   Data types
  2130.           like HBITMAP are defined in the file windows.h as an unsigned integer.
  2131.           You  declare  the  variable as  HBITMAP  instead  of unsigned  integer
  2132.           because the size of HBITMAP might  (and will) change under NT or maybe
  2133.           even in  future versions of  Windows like  Windows 4.0.   The size  of
  2134.           HBITMAP could even change  for each CPU  that the program is  compiled
  2135.           for (this is an NT consideration again).
  2136.  
  2137.                Now that we  know what data  types are available,  we can pick  a
  2138.           data type for x  and y.  Since the  window is 400 pixels high  and 400
  2139.           pixels  wide, we should  define x and  y as type  int.  We'll probably
  2140.           have to use the position of the missile base in many places throughout
  2141.           our  code so it's best  that we define x and  y as global variables by
  2142.           defining them in the header file "invid.h".
  2143.  
  2144.                The  second property, its appearance, is defined by a bitmap that
  2145.           we'll have represent the missile  base.  You can create the  bitmap of
  2146.           the missile base using any program that can create .bmp  files.  We're
  2147.           going  to use 16  color bitmaps so  be sure to  save the file  in that
  2148.           format.    Take a  look at  the bitmap,  base.bmp,  that I  created to
  2149.           represent the missile base.  
  2150.  
  2151.                Notice  that the bitmap has  black pixels to  its right and left.
  2152.           Since the missile base only moves  right and left, and the  background
  2153.           is solid  black, we can  cheat in creating  the bitmap animation.   We
  2154.           don't have  to use a bitmap mask to draw the bitmap without disturbing
  2155.           the background.  We also don't have to bother redrawing the background
  2156.  
  2157.                                           - 35 -
  2158.  
  2159.  
  2160.  
  2161.           when the base is  moved.  As long as we move the  base to the right or
  2162.           left 5 pixels or  less at a  time (There's a 5  pixel black border  on
  2163.           each side  of the base.),  drawing the base  covers over  the previous
  2164.           base  bitmap that was on  the screen.   This way we can  just draw the
  2165.           bitmap to the screen directly.  
  2166.  
  2167.                To use a bitmap in a Windows  program you first need to define it
  2168.           in your  program's .rc  file.   Here is the  contents of  the invid.rc
  2169.           file:
  2170.  
  2171.           #include "windows.h"
  2172.           invid     ICON      invid.ico
  2173.           base BITMAP    base.bmp
  2174.  
  2175.                We've  added the missile base's  bitmap (base.bmp) to  the end of
  2176.           the .rc file after the icon file.  The first entry, base, will be what
  2177.           we call  the bitmap  file in  the program's source  code.   The second
  2178.           entry,  BITMAP,  tells the  resource compiler  that  this is  a bitmap
  2179.           resource.   The last entry, base.bmp,  is the name of  the bitmap file
  2180.           that we want  to load.  Now  our program will contain the  bitmap as a
  2181.           resource  that can  be loaded and  used by  our program.   To  use the
  2182.           resource, you call  the function LoadBitmap(Program_Instance,  "bitmap
  2183.           name").   The first  parameter to  LoadBitmap() is  the handle  of the
  2184.           program instance.  So  we'll need to save the hInstance  variable that
  2185.           is passed to  WinMain() by assigning  it to our  own variable hInst.  
  2186.           That way we can use hInst when we call the LoadBitmap() function.  The
  2187.           second parameter  is the name of the bitmap that we defined in the .rc
  2188.           file.  The LoadBitmap function returns a variable of type HBITMAP that
  2189.           we  use whenever we want to  refer to the bitmap.   I added a variable
  2190.           called hbBase of type HBITMAP in our header file invid.h.
  2191.  
  2192.  
  2193.           Fast and Friendly Animation
  2194.  
  2195.                Now that we've  got our missile base, how do we display it on the
  2196.           screen? There are two ways we can go about it:
  2197.  
  2198.           1)  Display the missile base at its position every time we get a Timer
  2199.           message. 
  2200.  
  2201.           2)  Display the missile base at its position whenever it moves.
  2202.  
  2203.                The first  method would  be ideal for  multitasking friendliness.
  2204.           However, this method  is too slow.  At best,  windows can only provide
  2205.           18 timer messages per second.  I originally thought that this would be
  2206.           fast enough.   After  all, TV is  at 30 frames/sec,  movies run  at 24
  2207.           frames/sec  and Microsoft  Video  files look  fine  at 15  frames/sec.
  2208.           Unfortunately it  didn't work out that  way.  18 frames  per second is
  2209.           just  too slow for arcade game speed.   We want the animation to be as
  2210.           smooth as  possible.   That means  that if the  user tapped  the right
  2211.           cursor key  quickly, the missile base would move 1 pixel to the right.
  2212.           If  the user  held  the  cursor  key  down, the  base  would  move  18
  2213.           pixels/second across the screen.  However, our window is currently 400
  2214.           pixels wide.  So it would take  over 22 seconds to move the base  from
  2215.           one end of the screen to the other.  Twenty-two seconds is a LONG time
  2216.           in  arcade style  action  games.   This method  just  won't work  with
  2217.           Windows only sending  18 timer messages  per second.   Well, we  could
  2218.           make it work if we added motion blur but I'd rather not try and tackle
  2219.  
  2220.                                           - 36 -
  2221.  
  2222.  
  2223.  
  2224.           motion blur  ...yet.  We could  make this method work if  we could get
  2225.           Windows to send us more than 18  timer messages per second.  There are
  2226.           ways  of doing this using the  multimedia timer services but they seem
  2227.           fairly complicated to me.  (The real reason is that I've been too lazy
  2228.           to upgrade from Quick C which only supports the Win 3.0 API.)
  2229.  
  2230.                The second method is much easier to deal with but creates its own
  2231.           problems.  Instead of having our program only do something in response
  2232.           to   a  message,  we  can  write  our  program  to  run  continuously.
  2233.           Unfortunately we need to  be careful with this method  because we want
  2234.           our  program to  be friendly  to other  Windows programs and  not stop
  2235.           every other  Windows program just because ours is running.  To do this
  2236.           we  can  use Windows'  PeekMessage() function  to  see if  Windows has
  2237.           anything  else to do.  If it does,  we'll let Windows take care of the
  2238.           other programs  before  coming back  to  our program.    This way  our
  2239.           program will run continuously as  long as no other program  has things
  2240.           to do.   An obstacle to  using PeekMessage is that  you shouldn't keep
  2241.           your  program  in a  PeekMessage loop  because  it keeps  Windows from
  2242.           posting idle messages.  Another problem with this method is that since
  2243.           our program runs as fast as the CPU will allow, we will have to do our
  2244.           own  timing to  keep the  animation rate constant  no matter  what CPU
  2245.           we're running on.
  2246.  
  2247.                I  created two  sample programs  that demonstrate  the difference
  2248.           between timer based animation  and peek message based animation.   The
  2249.           code in the two programs is essentially the same.  Tball.exe creates a
  2250.           timer that sends 18 timer  messages per second (the maximum  under Win
  2251.           3.1).  
  2252.  
  2253.           Condensed code:
  2254.  
  2255.           int  PASCAL  WinMain(HANDLE  hInstance,  HANDLE  hPrevInstance,  LPSTR
  2256.           lpCmdLine, int nCmdShow)
  2257.           {
  2258.                MSG msg;
  2259.                if (!hPrevInstance) InitApplication(hInstance)); 
  2260.           InitInstance(hInstance, nCmdShow)
  2261.                while (GetMessage(&msg, NULL, NULL,NULL))
  2262.                {
  2263.                     TranslateMessage(&msg);
  2264.                     DispatchMessage(&msg);
  2265.                }
  2266.                return (msg.wParam);    
  2267.           }
  2268.  
  2269.           LONG FAR PASCAL WndProc(HWND hWnd, unsigned message, 
  2270.           WORD wParam, LONG lParam) 
  2271.           {
  2272.                switch (message)
  2273.                {
  2274.                     case WM_CREATE:
  2275.                     // create a timer 
  2276.                          SetTimer (hWnd, 1, 1, NULL);
  2277.                          // get handle to bitmap
  2278.                          hbBall=LoadBitmap(hInst, "ball");
  2279.                          break;
  2280.  
  2281.                     case WM_TIMER:
  2282.  
  2283.                                           - 37 -
  2284.  
  2285.  
  2286.  
  2287.           /* if a timer message arrives, draw the ball */
  2288.                          DrawBall();
  2289.                          break;
  2290.  
  2291.                     case WM_DESTROY:
  2292.                          KillTimer(hWnd, 1);
  2293.                          DeleteObject(hbBall);
  2294.                          PostQuitMessage(0);
  2295.                          break;
  2296.  
  2297.                     default:
  2298.                          return (DefWindowProc(hWnd, message, wParam, lParam)); 
  2299.                }
  2300.                return (NULL);
  2301.           }
  2302.  
  2303.           void DrawBall(void)
  2304.           {
  2305.                MoveBall();
  2306.                DrawBitmap(hbBall, x, y); 
  2307.                EraseOldBall();
  2308.                return;
  2309.           }
  2310.  
  2311.  
  2312.                If  you run Tball.exe, you'll  notice that the  ball moves fairly
  2313.           quickly and smoothly across the screen.  Unfortunately this is as fast
  2314.           as you can get with timer based animation without making the animation
  2315.           jerky.   The  menu selection  Speed allows  you to  control change  in
  2316.           position  of the ball  per timer message.   AnimeStep=1 means that the
  2317.           ball moves 1  pixel on every  timer message.  AnimeStep=50  means that
  2318.           the ball moves 50 pixels on every timer message.  Changing how far the
  2319.           object moves per  timer message is the only way to effectively control
  2320.           the speed of an object in timer based animation.
  2321.  
  2322.                In contrast,  if  you  run  Pball.exe,  you'll  notice  that  the
  2323.           animation is  smoother and faster.  The code is different in one area,
  2324.           the message  loop.  Instead of the while(GetMessage()) loop, we have a
  2325.           loop  that  runs  as long  as  a  WM_QUIT  message  is  not  received.
  2326.           PeekMessage() checks  to see if there  are any messages waiting  to be
  2327.           dispatched  by Windows.   If  there aren't  any messages  waiting, the
  2328.           function  DrawBall() is run.   Notice that WndProc()  doesn't do much.
  2329.           The  Setup, Control, and Exiting of the  Window is in WndProc, but all
  2330.           the animation is performed whenever Windows isn't doing anything else.
  2331.  
  2332.           int PASCAL WinMain(HANDLE hInstance, HANDLE hPrevInstance, 
  2333.           LPSTR lpCmdLine, int nCmdShow)
  2334.           {
  2335.                MSG msg;        
  2336.                if (!hPrevInstance) InitApplication(hInstance)); 
  2337.           InitInstance(hInstance, nCmdShow))
  2338.                while (TRUE)
  2339.                {
  2340.                     if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
  2341.                     {
  2342.                          if (msg.message == WM_QUIT) break ;
  2343.                          TranslateMessage (&msg) ;
  2344.                          DispatchMessage (&msg) ;
  2345.  
  2346.                                           - 38 -
  2347.  
  2348.  
  2349.  
  2350.                     }
  2351.                     else
  2352.                     {
  2353.           /* if no messages waiting, run DrawBall() */
  2354.                          DrawBall() ;
  2355.                     }
  2356.                }
  2357.                return (msg.wParam);
  2358.           }
  2359.  
  2360.           LONG FAR PASCAL WndProc(HWND hWnd, unsigned message, WORD wParam, LONG
  2361.           lParam) 
  2362.           {
  2363.                switch (message)
  2364.                {
  2365.                     case WM_CREATE:
  2366.                          hbBall=LoadBitmap(hInst, "ball");
  2367.                          break;
  2368.  
  2369.                     case WM_DESTROY:
  2370.                          DeleteObject(hbBall);
  2371.                          PostQuitMessage(0);
  2372.                          break;
  2373.  
  2374.                     default:
  2375.                          return (DefWindowProc(hWnd, message, wParam, lParam)); 
  2376.                }
  2377.                return (NULL);
  2378.           }
  2379.  
  2380.           void DrawBall(void)
  2381.           {
  2382.                MoveBall();
  2383.                DrawBitmap(hbBall, x, y); 
  2384.                EraseOldBall();
  2385.                return;
  2386.           }
  2387.  
  2388.  
  2389.                The menu item Speed works  the same in Pball as it did  in Tball.
  2390.           AnimeStep  controls how  many  pixels  the  ball  is  moved  in  every
  2391.           animation step.   But instead of  18 animation steps  per second,  you
  2392.           have as many animation steps as your CPU can crank  out before Windows
  2393.           has a message to process.   If Windows has  a message to process,  the
  2394.           ball stops moving while the message is handled.  The ball will then go
  2395.           back to  moving as soon as  there are no more  messages.  Fortunately,
  2396.           messages  are handled  so quickly  that you  don't see  any noticeable
  2397.           pause in the animation of the bouncing ball.
  2398.  
  2399.                So let's apply this PeekMessage animation technique to The Game:
  2400.  
  2401.           /* invid.c */
  2402.           #include <windows.h>
  2403.           #include "invid.h"
  2404.  
  2405.           /*---------- Global Variables --------*/
  2406.           /* In the real source code I keep my global variables in "invid.h" but
  2407.           it's easier for you to read the source code if I put them here. */ 
  2408.  
  2409.                                           - 39 -
  2410.  
  2411.  
  2412.  
  2413.           /* Declare a structure called decodeWord  that will be used by WinProc
  2414.           to run a function in response to a message */
  2415.           struct decodeWord {
  2416.                     WORD Code;
  2417.                     LONG (*Fxn)(HWND, WORD, WORD, LONG); };
  2418.           /*  An array  called  messages made  up  of decodeWord  structures  is
  2419.           created. The array  lists the  Windows Message and  the function  that
  2420.           will  be  called  when  that message  is  received.    I've  added two
  2421.           messages, WM_KEYDOWN  and WM_KEYUP.   When WM_KEYDOWN  or WM_KEYUP  is
  2422.           received, the function DoKeydown or DoKeyUp is run. */
  2423.           struct decodeWord messages[] = {
  2424.                WM_KEYDOWN, DoKeydown,
  2425.                WM_KEYUP, DoKeyup,
  2426.                WM_CREATE, DoCreate,
  2427.                WM_DESTROY, DoDestroy,} ;
  2428.  
  2429.           /* Declare a character string that contains the name of the program.*/
  2430.           char szAppName [] = "Invid";
  2431.  
  2432.           /*  Declare  a variable  called  hInst that  contains  the data  for a
  2433.           particular instance of the program. */
  2434.           HANDLE hInst;
  2435.  
  2436.           /*  Declare  a variable  called  hWnd that  contains a  handle  to the
  2437.           programs window. */
  2438.           HWND hWnd;
  2439.  
  2440.           /* Declare an integer  called AnimeStep.  AnimeStep is how many pixels
  2441.           a bitmap should be moved during animation. */
  2442.           int AnimeStep = 2;
  2443.  
  2444.           /* Declare two variables of type  BOOL (boolean).  That means that the
  2445.           variables can  be  a 1  or  a 0.    The variables  fRightKeyState  and
  2446.           fLeftKeyState will be set by the functions DoKeyUp and DoKeyDown.  The
  2447.           variables will then  be used by other functions so  that they can know
  2448.           the state of the right  and left cursor keys.  (I could  have used the
  2449.           Windows function GetAsyncKeyState() instead  of waiting for a keypress
  2450.           message to be sent by Windows.  But I wanted the program to be written
  2451.           in the standard Windows  programming style of waiting for  a message.)
  2452.           */
  2453.           BOOL fRightKeyDown = FALSE, fLeftKeyDown = FALSE;
  2454.  
  2455.           /*  Decalaring  a  structure   called  BaseStruct  that  will  contain
  2456.           everything  about the  missile base.    I could  have  just as  easily
  2457.           declared  x, y, and  Bmp as separate  variables.  Putting  them into a
  2458.           structure groups related variables  under one title and makes  it easy
  2459.           to remember what variable does what. */
  2460.           struct BaseStruct {
  2461.           /*  Declare  an  integer  called x  that  will  contain  the base's  x
  2462.           coordinate */
  2463.                int x ;
  2464.           /*  Declare  an  integer  called y  that  will  contain  the  base's y
  2465.           coordinate */
  2466.                int y ;
  2467.           /* Declare a variable called Bmp that contains a handle  to the bitmap
  2468.           of the missile base. */
  2469.                HBITMAP Bmp ;
  2470.                };
  2471.  
  2472.                                           - 40 -
  2473.  
  2474.  
  2475.  
  2476.           /* Declare a Structure called Base of type BaseStruct (defined above).
  2477.           You can now access the missile base's  variables x, y and Bmp by using
  2478.           Base.x, Base.y, and Base.Bmp. */
  2479.           struct BaseStruct Base;
  2480.  
  2481.  
  2482.           /*------------ WinMain ------------*/
  2483.           int  PASCAL  WinMain (HANDLE  hInstance,  HANDLE  hPrevInstance, LPSTR
  2484.           lpszCmdParam, int nCmdShow)
  2485.           {
  2486.             MSG msg ;
  2487.             hInst = hInstance;
  2488.             if (!hPrevInstance) InitApplication(hInstance);
  2489.             InitInstance(hInstance, nCmdShow);
  2490.  
  2491.           /* This is the old message loop from the last program. 
  2492.                while (GetMessage (&msg, NULL, 0, 0))
  2493.                {
  2494.                     TranslateMessage (&msg) ;
  2495.                     DispatchMessage (&msg) ;
  2496.                }
  2497.           */
  2498.  
  2499.           /* This  is the new message  loop (from Petzold).   Instead of calling
  2500.           GetMessage() to retrieve  a message  from Windows,  this message  loop
  2501.           calls PeekMessage  to see if there  are any messages waiting.   If the
  2502.           waiting message  is a WM_QUIT message  then break out of  the loop and
  2503.           end our program.  If the waiting message is not a WM_QUIT message then
  2504.           translate and dispatch the message.   If there are no waiting messages
  2505.           then  run our function  DoInv().  This  message loop  will allow other
  2506.           Windows programs to run at  the same time but  it will also keep  idle
  2507.           messages  from being  generated.   We'll have  to fix  it in  a future
  2508.           article to make it more multitasking friendly.  */
  2509.           while (TRUE)
  2510.                {
  2511.                if (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
  2512.                {
  2513.                     if (msg.message == WM_QUIT) break ;
  2514.                     TranslateMessage (&msg) ;
  2515.                     DispatchMessage (&msg) ; }
  2516.                }
  2517.                else
  2518.                     {
  2519.                     DoInv() ;
  2520.                     }
  2521.                return (msg.wParam) ;
  2522.           }
  2523.  
  2524.           /*---------- InitApplication ---------*/
  2525.           BOOL InitApplication(HANDLE hInstance)
  2526.           {
  2527.                WNDCLASS wndclass;
  2528.                wndclass.style = 0 ;
  2529.                wndclass.lpfnWndProc = WndProc ; 
  2530.                wndclass.cbClsExtra = 0 ;
  2531.                wndclass.cbWndExtra = 0 ;
  2532.                wndclass.hInstance = hInstance ; 
  2533.                wndclass.hIcon = LoadIcon (hInstance, szAppName) ;
  2534.  
  2535.                                           - 41 -
  2536.  
  2537.  
  2538.  
  2539.                wndclass.hCursor = LoadCursor (hInstance, IDC_ARROW);
  2540.                wndclass.hbrBackground = GetStockObject (BLACK_BRUSH);
  2541.                wndclass.lpszMenuName = NULL ;
  2542.                wndclass.lpszClassName = szAppName ;
  2543.                return(RegisterClass (&wndclass));
  2544.           }
  2545.  
  2546.           /*------------ InitInstance ----------*/
  2547.           BOOL InitInstance(HANDLE hInstance, WORD nCmdShow)
  2548.           {
  2549.                hWnd = CreateWindow (szAppName,
  2550.                     "Invid",
  2551.                     WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU,
  2552.                     CW_USEDEFAULT, CW_USEDEFAULT,
  2553.                     WINDOW_WIDTH, WINDOW_HEIGHT ,
  2554.                     NULL,
  2555.                     NULL,
  2556.                     hInstance,
  2557.                     NULL) ;
  2558.                ShowWindow (hWnd, nCmdShow) ;
  2559.                UpdateWindow (hWnd) ;
  2560.                return (TRUE);
  2561.           }
  2562.  
  2563.           /*------------- WndProc ------------*/
  2564.           long  FAR  PASCAL WndProc  (HWND hWnd,  WORD  wMsg, WORD  wParam, LONG
  2565.           lParam)
  2566.           {
  2567.                int i;
  2568.                for(i=0; i < dim(messages); i++)
  2569.                {
  2570.                if(wMsg == messages[i].Code)
  2571.                     return((*messages[i].Fxn)(hWnd, wMsg, wParam, lParam));
  2572.                }
  2573.                return(DefWindowProc(hWnd, wMsg, wParam, lParam));
  2574.           }
  2575.  
  2576.           /*------------- DoCreate -----------*/
  2577.           /* DoCreate() is called  when our window receives a  WM_CREATE message
  2578.           at startup. */
  2579.           LONG DoCreate(HWND hWnd, WORD wMsg, WORD wParam, LONG lParam)
  2580.           {
  2581.           /*  Call a function called BaseInit that will initialize the variables
  2582.           in the missile base structure Base. */
  2583.                BaseInit();
  2584.                return 0;
  2585.           }
  2586.  
  2587.           /*------------ BaseInit -----------*/
  2588.           /*  This function  initializes  the variables  and  loads the  missile
  2589.           base's bitmap. */
  2590.           void BaseInit(void)
  2591.           {
  2592.                Base.x = 40;
  2593.                Base.y = WINDOW_HEIGHT - 80;
  2594.                Base.Bmp = LoadBitmap(hInst, "base");
  2595.           }
  2596.  
  2597.  
  2598.                                           - 42 -
  2599.  
  2600.  
  2601.  
  2602.           /*------------ DoDestroy ----------*/
  2603.           /* DoDestroy is called when our program receives a WM_QUIT message */
  2604.           LONG DoDestroy(HWND hWnd, WORD wMsg, WORD wParam, LONG lParam)
  2605.           {
  2606.           // Delete the handle to the missile base's bitmap 
  2607.           // before exiting. (otherwise Windows system 
  2608.           // resource's won't be released. 
  2609.                DeleteObject(Base.Bmp);
  2610.                PostQuitMessage (0) ;
  2611.                return 0 ;
  2612.           }
  2613.  
  2614.           /*----------- DoKeyDown ---------*/
  2615.           /*  DoKeyDown is  run  whenever  our  program  receives  a  WM_KEYDOWN
  2616.           message. */
  2617.           LONG DoKeydown(HWND hWnd, WORD wMsg, WORD wParam, LONG lParam)
  2618.           {
  2619.                switch (wParam) {
  2620.                     case VK_RIGHT :
  2621.                          fRightKeyDown = TRUE ;
  2622.                          break  ;
  2623.                     case VK_LEFT :
  2624.                          fLeftKeyDown = TRUE ;
  2625.                          break  ;
  2626.                }
  2627.                     return 0 ;
  2628.           }
  2629.  
  2630.           /*---------- DoKeyUp ------------*/
  2631.           /* DoKeyUp is run whenever our program receives a WM_KEYUP message.*/
  2632.           LONG DoKeyUp(HWND hWnd, WORD wMsg, WORD wParam, LONG lParam)
  2633.           {
  2634.                switch (wParam) {
  2635.                     case VK_RIGHT :
  2636.                          fRightKeyDown = FALSE ;
  2637.                          break  ;
  2638.                     case VK_LEFT :
  2639.                          fLeftKeyDown = FALSE ;
  2640.                          break  ;
  2641.                }
  2642.                return 0 ;
  2643.           }
  2644.  
  2645.           /*----------- DoInv -------------*/
  2646.           /* This function runs whenever Windows isn't doing anything else */
  2647.           void DoInv(void)
  2648.           {
  2649.                BaseAnimation (hWnd);
  2650.                return ;
  2651.           }
  2652.  
  2653.           /*----------- BaseAnimation -------*/
  2654.           /* This  function draws the bitmap of the base at a new position based
  2655.           on whether the cursor keys are pressed */
  2656.           void BaseAnimation (void)
  2657.           {
  2658.           /* Move the x coordinate of the missile base based on which cursor key
  2659.           is  pressed.  Base.x +=  AnimStep is  shorthand  forBase.x =  Base.x +
  2660.  
  2661.                                           - 43 -
  2662.  
  2663.  
  2664.  
  2665.           AnimeStep */
  2666.                if (fRightKeyDown) Base.x += AnimeStep; 
  2667.                if (fLeftKeyDown) Base.x -= AnimeStep;
  2668.           /* Keep the missile base on the screen */
  2669.                if (Base.x < 5)  Base.x = 5;
  2670.                if (Base.x > (WINDOW_WIDTH-50)) Base.x = WINDOW_WIDTH-50;
  2671.           /*  Another Petzold function slightly  modified to make  it simpler to
  2672.           use. (If you havn't bought Programming Windows by Charles Petzold yet,
  2673.           stop reading this and go to your local bookstore and buy it now!)  The
  2674.           function takes three arguments.  The first argument is a handle to the
  2675.           bitmap that will be drawn.  The second and third parameters are the  x
  2676.           and y position of the bitmap to be drawn. */
  2677.                DrawBitmap (Base.Bmp, Base.x, Base.y);
  2678.           }
  2679.  
  2680.           /*----------- DrawBitmap ------------*/
  2681.           /* This is where all the action takes place.  It's great for getting a
  2682.           bitmap on  your window as  simply as  possible.  Unfortunately  it has
  2683.           performance  problems when you  try and use  it for  all your graphics
  2684.           work. (This  might be why Windows doesn't have this function built in)
  2685.           */
  2686.           void DrawBitmap (HBITMAP hBitmap, int xStart, int yStart)
  2687.           {
  2688.           /* Declare  a handle  to a  device  context.   A handle  for a  Device
  2689.           Context is needed to draw to a window. */
  2690.                HDC hdc;
  2691.           /*  Declare  a  variable bm  of  type BITMAP.    It is  used  to store
  2692.           information about a bitmap such as its size and colordepth. */
  2693.                BITMAP bm ;
  2694.           /*  Declare a handle to  a device context.  This  handle will be for a
  2695.           block of memory that will be used as storage  for the bitmap that will
  2696.           be drawn. */
  2697.                HDC hdcMem ;
  2698.           /* Get a handle for a device context for our window. */
  2699.                hdc = GetDC(hWnd);
  2700.           /* Get a handle for a device context to a block of memory.  The memory
  2701.           context will be based on the device context of our Window. */
  2702.                hdcMem = CreateCompatibleDC (hdc) ;
  2703.           /* Put the bitmap into the block of memory that was created above. */
  2704.                SelectObject (hdcMem, hBitmap) ;
  2705.           /* Get information about the bitmap and put it in the structure bm */
  2706.                GetObject (hBitmap, sizeof (BITMAP), (LPSTR) &bm) ;
  2707.           /* The  BitBlt function copies  the bitmap in  memory (hdcMem) to  the
  2708.           screen (hdc) */
  2709.                BitBlt (hdc, xStart, yStart, bm.bmWidth, bm.bmHeight, 
  2710.                     hdcMem, 0, 0, SRCCOPY) ;
  2711.           /* Free up the resources we used before leaving */
  2712.                DeleteDC (hdcMem) ;
  2713.                ReleaseDC (hWnd, hdc);
  2714.           }
  2715.  
  2716.                When  you run  the program,  it will  put a  small bitmap  on the
  2717.           screen that moves back and  forth when you press the cursor keys.   If
  2718.           you're running it on  a fast 486, you'll notice that  it's too fast to
  2719.           be  controllable in  a game.   Fortunately  slowing down a  program is
  2720.           always easier than speeding one up.  
  2721.  
  2722.                You now  have the basics of  an arcade game:   Fast graphics with
  2723.  
  2724.                                           - 44 -
  2725.  
  2726.  
  2727.  
  2728.           user interaction.  In the next issue, we'll add the invaders and cover
  2729.           BitBlt'ing in detail.  
  2730.  
  2731.                I hope you  enjoyed my second article.  I spent  a lot of time on
  2732.           formatting details for both the .hlp  file and the .txt file.   In the
  2733.           .hlp  file  I  added bitmaps  and  linked  bitmaps to  run  the sample
  2734.           programs while you're  reading the .hlp file.   I also spent  a lot of
  2735.           time on the format of the source code with details such  as using bold
  2736.           for the code and blue highlights  for the start of functions.  If  you
  2737.           like this  formatting, (or  don't like  it!)  send me  or the  editors
  2738.           feedback.    
  2739.  
  2740.  
  2741.           About the author:
  2742.  
  2743.           (short version)
  2744.           SWM 25 ISO SWF.  PC Compatible, no experience necessary.
  2745.  
  2746.           (long version)
  2747.           Bernard  Andrys is a engineer  at CSI, an  ISDN networking development
  2748.           company.   He holds a bachelor's degree in Mechanical Engineering from
  2749.           the University of Maryland at College Park.  He can be reached through
  2750.           the  Internet  at  andrys@csisdn.com  or on  the  Windows  Programming
  2751.           Journal BBS.
  2752.  
  2753.           (longer version)
  2754.           I was born in the house my  father built.   ...No wait, that's someone
  2755.           else.  umm... Ok...  I bet you're wondering what a Mechanical Engineer
  2756.           is  doing working for a networking company and writing Windows program
  2757.           in his spare time.   Well I'm  wondering too.  If you find out,  write
  2758.           me.   ...and another  question.  Is  it the  company I keep  or do all
  2759.           programmers  like  The  Hitchhiker's Guide  to  the  Galaxy  and Monty
  2760.           Python?
  2761.  
  2762.  
  2763.  
  2764.  
  2765.  
  2766.  
  2767.  
  2768.  
  2769.  
  2770.  
  2771.  
  2772.  
  2773.  
  2774.  
  2775.  
  2776.  
  2777.  
  2778.  
  2779.  
  2780.  
  2781.  
  2782.  
  2783.  
  2784.  
  2785.  
  2786.  
  2787.                                           - 45 -
  2788.  
  2789.  
  2790.  
  2791.                                        Windows Hooks
  2792.                                     By David S. Browne
  2793.  
  2794.           What is a Windows Hook ? 
  2795.  
  2796.                Ever  want  to  get  control just  when  Windows  processes  that
  2797.           keydown,  or at that mouse move ? Well,  Windows Hooks are for you ! A
  2798.           hook is really  nothing more than  Windows making a  call to a DLL  at
  2799.           various points  in Kernel, User, GDI,  etc.   There  are some gotchas,
  2800.           though, in using a hook and keeping Windows alive to process work, and
  2801.           that's what I  hope to  show here.   There are  three types of  common
  2802.           hooks in Windows applications  these days; 3.0 style hooks,  3.1 style
  2803.           hooks and Dynamic Intercept of Windows API functions for your own evil
  2804.           doings.   The first two  of these are  somewhat documented  in various
  2805.           Microsoft and OA ('Other Authors') books.  The last is not  documented
  2806.           by Microsoft at all, but has been shown in various trade publications.
  2807.           We will cover all  three of these, in reverse order. We will present a
  2808.           real application usage of hooks when we look at the documented 3.0/3.1
  2809.           hook functions.  The application  we will develop is a windows  pop-up
  2810.           (I know, I'm still stuck in DOS !)  that will allow you to activate it
  2811.           with any user defined hotkey and it will allow you to  END or KILL any
  2812.           running window  in your system.   The problem with PROGMAN  is that in
  2813.           many cases its  END-TASK simply doesn't do the job.  The target window
  2814.           must  be reading its  message queue to  see the WM_QUIT  message.  The
  2815.           KILL  function will  call a  nice new  TOOLHELP debugging  function to
  2816.           force kill any window, even if its not got its ear to the queue, so to
  2817.           speak.
  2818.  
  2819.  
  2820.           Dynamic Intercept Of Windows API functions
  2821.  
  2822.                Dynamic  Intercept of Windows API functions is not the main basis
  2823.           of this  article but  I wanted to  touch on the  subject a  bit before
  2824.           getting  on  with the  real  meat.  When  you  issue a  GetMessage  or
  2825.           BeginPaint API call in your Windows program,  it's nothing more than a
  2826.           call to a Windows system DLL to process this function.  It is possible
  2827.           to  intercept this to do any front  end processing (or for that matter
  2828.           back  end after  the real  API has  finished  playing) that  you wish.
  2829.           Windows provides  a function  ("GetProcAddress") that will  return the
  2830.           memory address of any function call you  wish to look at.   With  this
  2831.           information you  can store  a jump  at the DLL  routine entry  to your
  2832.           routine.
  2833.  
  2834.                It's actually a bit more complicated  than this as Windows has  a
  2835.           habit  of discarding  code  segments when  it's  short of  memory  and
  2836.           reloading  fresh copies  from disk  as needed.   So  for this  kind of
  2837.           dynamic  intercept  it's also  necessary to  use  the TOOLHELP  DLL to
  2838.           install a  NotifyRegister function so that you see the segment reloads
  2839.           to  re-apply your hook.  If this is  not done, then all will work fine
  2840.           until Windows decides that a bit  more memory is in order, purges some
  2841.           code segments (that your hook  is in !) then reloads a fresh copy and,
  2842.           Presto!  Chango! NoHookO !  It's really not  as complicated  as it all
  2843.           sounds; a very good example  of this was used in the May  1993 edition
  2844.           of Windows/DOS Developer's Journal.  If you want to start intercepting
  2845.           Windows  API functions then I  recommend you read,  then re-read, then
  2846.           sleep read the article ! Now to the beef !
  2847.  
  2848.  
  2849.  
  2850.                                           - 46 -
  2851.  
  2852.  
  2853.  
  2854.           Windows 3.0 and 3.1 Hooks 
  2855.  
  2856.                There  are  two  API's  to  set  a  hook  in   Windows,  the  3.0
  2857.           SetWindowsHook  and  the 3.1  SetWindowsHookEx.  The  basic change  is
  2858.           that  the 3.0  SetWindowsHook is  system wide,  while the  3.1 version
  2859.           allows a hook  to be JUST for  a given instance or task.   This allows
  2860.           you to develop  that Keyboard Hook you always wanted,  but were afraid
  2861.           of because of the overhead of getting called for EVERY SYSTEM KEYBOARD
  2862.           EVENT.    The example code  we present here though  uses a system-wide
  2863.           keyboard hook (in for a penny, in for a pound)  so no matter where the
  2864.           current focus  is we see  the keydown and  keyup.  In  addition to the
  2865.           hook function  ID the main  param you  must provide on  both of  these
  2866.           calls is the address of the function that you are providing to process
  2867.           these hooks.   This function must  reside in a DLL.   As with  the 3.0
  2868.           hook  or a system wide 3.1  hook, it can be called  in the instance of
  2869.           any task in the  system.  A DLL is  required so as to be  available to
  2870.           not just the API calling task.
  2871.  
  2872.                There are twelve hooks you can set with these functions:
  2873.  
  2874.                WH_CALLWNDPROC  -  WNDPROC Filter  Hook  for  all SendMessage API
  2875.           Call's
  2876.                WH_CBT - Computer Based Training Filter        
  2877.                WH_DEBUG - Called before any other hook 
  2878.                WH_GETMESSAGE -  Called when  an application issues  a GetMessage
  2879.           API 
  2880.                WH_HARDWARE   Called for  any non-keyboard or  non-mouse hardware
  2881.           message
  2882.                WH_JOURNALPLAYBACK - Allows user to substitute mouse and keyboard
  2883.           input
  2884.                WH_JOURNALRECORD  -  Allows  user  to  save  mouse  and  keyboard
  2885.           messages
  2886.                WH_KEYBOARD - Called when any key is struck. 
  2887.                WH_MOUSE  - Called for EVERY  mouse movement in  the system. (Did
  2888.           someone say Pentium ?)
  2889.                WH_MSGFILTER  - Called for user  messages to any  Dialog, Menu or
  2890.           Scroll Bar Window
  2891.                WH_SHELL - Called whenever a  new top level window is  created or
  2892.           destroyed.
  2893.                WH_SYSMSGFILTER - Called for system messages to any Dialog,  Menu
  2894.           or Scroll Bar Window
  2895.  
  2896.                Some documentation I have seen does not list the WH_SHELL hook as
  2897.           valid for SetWindowsHookEx.  It  is, it works, I've used it.   Now for
  2898.           the actual API calls:
  2899.  
  2900.           3.0
  2901.                STATIC  HHOOK  hook1 = NULL;
  2902.                                   .
  2903.                                   .
  2904.                                   .
  2905.                hook1 =  SetWindowsHook(WH_KEYBOARD,  (HOOKPROC)KeyHook);
  2906.                                   .
  2907.                                   .
  2908.                DWORD  CALLBACK  KeyHook(int iCode, WPARAM wParam, LPARAM lParam)
  2909.                {
  2910.                WhoKnowsOrCares();
  2911.                ?????????
  2912.  
  2913.                                           - 47 -
  2914.  
  2915.  
  2916.  
  2917.                }
  2918.  
  2919.  
  2920.           3.1
  2921.                STATIC HHOOK hook1 =  NULL;
  2922.                                    .
  2923.                                    .
  2924.                                    .
  2925.                hook1    =     SetWindowsHookEx(WH_KEYBOARD,   (HOOKPROC)KeyHook,
  2926.           hInstance, NULL);
  2927.                                    .
  2928.                                    .
  2929.                DWORD  CALLBACK  KeyHook(int iCode, WPARAM wParam, LPARAM lParam)
  2930.                {
  2931.                WeNowKnowAndCare();
  2932.                CallNextHookEx(hook1, iCode, wParam, lParam);
  2933.                return(0);
  2934.                }
  2935.  
  2936.                Simple right ? Really it is ! Honest ! Have I ever lied to you ?
  2937.  
  2938.                Now to discuss an actual application usage for all this.
  2939.  
  2940.                As I said, when you pop up Windows TASKMAN with the ever faithful
  2941.           Ctrl-Esc we have an END-TASK  Button.  Ever had a case where it didn't
  2942.           end that misbehaving  task? Well that's because all it  does is send a
  2943.           WM_CLOSE message to the application.  If it is processing its messages
  2944.           normally it will see this message then terminate.  I  don't know about
  2945.           you  but  whenever  I use  that  button  the  application is  normally
  2946.           stalled!   If  I could  end the  application I  would have done  it by
  2947.           selecting  close from the Application  itself!  OK,  now we understand
  2948.           the problem.  Solution time.   We will implement a WH_KEYBOARD hook to
  2949.           scan for any ALT-S  key sequence and if we see it pop up a window that
  2950.           will list all active top level windows on it and allow the user to END
  2951.           the  task (same as the  dreaded TASKMAN does!)  or KILL the  task by a
  2952.           call to the new 3.1 TOOLHELP DLL TerminateApp() function.  The program
  2953.           is implemented  in  two  separate modules  WINSWAT,  the  main  window
  2954.           procedure and SWATDLL the DLL with the keyboard hook function.  If you
  2955.           look at WINSWAT it will look as any normal Windows  C program does, it
  2956.           has a WinMain function that sets up a main window  for the application
  2957.           and  it  has a  WndProc  for  all the  real  processing.   During  the
  2958.           WM_CREATE processing, we make  a call to InitialiseSwat to  SWATDLL to
  2959.           setup the keyboard hook.  The code in SWATDLL looks like this:
  2960.  
  2961.                void WINAPI _export InitialiseSwat(BOOL fAction, HWND hwnd)
  2962.                {
  2963.                if (fAction)
  2964.                     {
  2965.                     OutputDebugString("SWATDLL: Pre--SetHook\n");
  2966.                     hKhook     =    SetWindowsHookEx(WH_KEYBOARD,     (HOOKPROC)
  2967.           KeyboardHook,
  2968.                          hInstance, NULL);
  2969.                     OutputDebugString("SWATDLL: Post-SetHook\n");
  2970.                     hTASK = GetWindowTask(hwnd);
  2971.                     hHWND = hwnd;
  2972.                     }
  2973.                else
  2974.                     {
  2975.  
  2976.                                           - 48 -
  2977.  
  2978.  
  2979.  
  2980.                     UnhookWindowsHookEx(hKhook);
  2981.                     hKhook  = NULL;
  2982.                     hHWND = NULL;
  2983.                     hTASK = NULL;
  2984.                     }
  2985.                }
  2986.  
  2987.                This  sets up a hook callback to KeyboardHook for keyboard events
  2988.           from any task in the system  (The last NULL param says all tasks,  but
  2989.           you could  substitute a task  value here if  you ONLY wanted  keyboard
  2990.           events for that  task).  The HWND param we save  here is so that later
  2991.           in the  actual keyboard hook code  we can PostMessage to  that window.
  2992.           The  last part  of the  code is  for termination  to remove  our hook.
  2993.           After  this processing is complete, WINSWAT simply hides itself with a
  2994.           ShowWindow call and waits to be woken up with a message from SWATDLL.
  2995.  
  2996.                If you look at the KeyboardHook code in SWATDLL you will see that
  2997.           we check for our key, the S key, then we make sure the ALT key is  on.
  2998.           If all this  is true we  PostMessage a message  to WINSWAT so  that it
  2999.           will wake up.  Take a look:
  3000.  
  3001.  
  3002.           DWORD CALLBACK KeyboardHook(int nCode, WPARAM wParam, LPARAM lParam)
  3003.                {
  3004.                static BOOL fPost;
  3005.                BYTE iBit;
  3006.                WORD iWord;
  3007.                static UINT iQky = 83;
  3008.  
  3009.                if (nCode >= 0)
  3010.                     if (wParam == iQky)
  3011.                          {
  3012.                          OutputDebugString("SWATDLL: Key Hook Hit\n");
  3013.                          iWord = HIWORD(lParam);
  3014.                          iBit  = HIBYTE(iWord);
  3015.                          if (iBit & 0x20)
  3016.                               {
  3017.                               fPost  = PostMessage(hHWND,  WM_COMMAND, WAKESIGL,
  3018.           0);
  3019.                               return 1;
  3020.                               }
  3021.                          }
  3022.  
  3023.                CallNextHookEx(hKhook, nCode, wParam, lParam);
  3024.                return 0;
  3025.                }
  3026.  
  3027.                When the WAKESIGL message is  received in the WndProc  WM_COMMAND
  3028.           processing  section, it simply changes the Main Window to SHOW status,
  3029.           updates the listbox with all top level window names and then waits for
  3030.           a command.  If a  KILLBUTN is received, it is used  the TerminateApp()
  3031.           function to really SWAT the thing out of Windows!
  3032.  
  3033.                hTask = GetWindowTask(WorkHwnd);
  3034.                TerminateApp(hTask, NO_UAE_BOX);
  3035.  
  3036.                As  you can see it's really  not at all hard to  use hooks.  They
  3037.           can  add functionality  to any  application when  used in  the correct
  3038.  
  3039.                                           - 49 -
  3040.  
  3041.  
  3042.  
  3043.           manner.   Don't  substitute hooks,  though, for  things that  could be
  3044.           easily  done with normal Windows messages like WH_KEYDOWN, etc.  Hooks
  3045.           can add  overhead to your  system, and that's something  no one wants.
  3046.           Used correctly, however, they can add a multitude of features to  your
  3047.           application.
  3048.  
  3049.                All the source  code, H  files, Icons, Definition  file etc.  are
  3050.           included with this WPJ edition, take a look at it and get Hooking!
  3051.  
  3052.  
  3053.  
  3054.  
  3055.  
  3056.  
  3057.  
  3058.  
  3059.  
  3060.  
  3061.  
  3062.  
  3063.  
  3064.  
  3065.  
  3066.  
  3067.  
  3068.  
  3069.  
  3070.  
  3071.  
  3072.  
  3073.  
  3074.  
  3075.  
  3076.  
  3077.  
  3078.  
  3079.  
  3080.  
  3081.  
  3082.  
  3083.  
  3084.  
  3085.  
  3086.  
  3087.  
  3088.  
  3089.  
  3090.  
  3091.  
  3092.  
  3093.  
  3094.  
  3095.  
  3096.  
  3097.  
  3098.  
  3099.  
  3100.  
  3101.  
  3102.                                           - 50 -
  3103.  
  3104.  
  3105.  
  3106.                                    Shared Global Memory
  3107.                                       By Dennis Chuah
  3108.  
  3109.                In  this  article I  will present  a  way to  implement shareable
  3110.           global memory in Windows 3.1.  It  is also the aim of this article  to
  3111.           demonstrate two different methods  of detecting whether an application
  3112.           has been loaded or not.  Although the code is written in C, aiming for
  3113.           the Borland  C++ compiler, the  binaries produced  and the  associated
  3114.           header  file can  be  used  by  any other  C  compiler  that  supports
  3115.           programming in Windows.  It is also possible to use the libraries with
  3116.           Turbo Pascal or  Borland Pascal with the proper import definitions.  I
  3117.           am not an expert  in Pascal and I do not own a  Pascal compiler.  So I
  3118.           hope  someone who  is better versed  in Pascal  can write  a unit that
  3119.           allows the libraries to be used in Pascal programs.
  3120.  
  3121.                First,  let me introduce a  few concepts of  memory allocation in
  3122.           Windows 3.1.  Windows  3.1 only runs in  protected mode and  therefore
  3123.           most of  the memory allocation practices  that were once used  in real
  3124.           mode are now obsolete.  Prior to version 3.0[1], Windows  runs in real
  3125.           mode.  The  API inherits a number of real  mode features from previous
  3126.           version of  Windows.  In  real mode, Windows  needs to perform  memory
  3127.           moves  to accommodate  a multitasking environment.   This  is achieved
  3128.           through handles.   When a  block of memory  is allocated, a  handle is
  3129.           assigned to  it.  The  owner can request  that the block  of memory be
  3130.           locked  in memory  (meaning it  will not  be moved  by Windows).   The
  3131.           locking process also retrieves a pointer to the block of memory.  When
  3132.           the owner no longer needs  to access the block of memory, it  can tell
  3133.           Windows to  unlock it.   Windows  is then  free to  move the  block of
  3134.           memory around.   The next time the owner needs  to access the block of
  3135.           memory, it locks it and  retrieve another pointer.  The value  of this
  3136.           pointer is not necessary the same as the previous pointer it retrieved
  3137.           (Windows  may have  moved  the  block  of  memory  elsewhere).    This
  3138.           complicated process is necessary because  in real mode, pointers point
  3139.           to  physical  addresses.   The  only  way  for  Windows to  inform  an
  3140.           application  that a  block of  memory  it owns  has been  moved is  by
  3141.           passing a new pointer to it.  The locking process ensures that Windows
  3142.           does not move memory that is still being actively used.
  3143.  
  3144.                In  protected mode  however, pointers  point to  logical address.
  3145.           Each  pointer, instead  of  having  a  segment:offset address,  has  a
  3146.           selector value and an offset address.  The selector points to an entry
  3147.           in a  global table (The Global Descriptor  Table -- GDT) maintained by
  3148.           Windows.   When a memory access occurs, the CPU translates the logical
  3149.           address in a pointer to physical  address using the GDT.  This process
  3150.           is  transparent to software.   When Windows running  in protected mode
  3151.           needs  to move  blocks of  memory, it  only needs  to update  the GDT.
  3152.           Locked memory  can also be  moved as the CPU  translates memory access
  3153.           via  the GDT  transparently.   Therefore the  process of  allocating a
  3154.           handle, locking it and unlocking it becomes obsolete.  The  owner of a
  3155.           block of memory only needs to maintain a pointer to it.  The following
  3156.           table illustrates the differences in  the process of allocating memory
  3157.           under different modes.
  3158.  
  3159.           Real mode
  3160.           Protected mode
  3161.  
  3162.           Allocating Memory
  3163.           handle = GlobalAlloc...
  3164.  
  3165.                                           - 51 -
  3166.  
  3167.  
  3168.  
  3169.           handle = GlobalAlloc...
  3170.           ptr = GlobalLock (handle);
  3171.  
  3172.           Accessing memory
  3173.           ptr = GlobalLock (handle);
  3174.           *ptr = ...
  3175.           GlobaalUnlock (handle);
  3176.           *ptr = ...
  3177.  
  3178.           Freeing memory
  3179.           GlobalFree (handle);
  3180.           handle = LOWORD (GlobalHandle
  3181.              (SELECTOROF (ptr)));
  3182.           GlobalUnlock (handle);
  3183.           GlobalFree (handle);
  3184.  
  3185.  
  3186.                The  extra work  in maintaining  handles in  protected mode  is a
  3187.           legacy  inherited  from  real   mode.    It  is  there   for  backward
  3188.           compatibility.   For  a  more detailed  discussion  of Windows  memory
  3189.           allocation I  suggest  C. Petzold,  Programming  Windows --  2nd  Ed.,
  3190.           (1990)  Microsoft  Press.   For more  information  in the  Intel 80x86
  3191.           protected  mode  memory  management,  consult  Intel s,  80386  System
  3192.           Software  Writer's Guide , (1988)  Intel, Intel's,  80386 Programmer's
  3193.           Reference  Manual,  (1988)  Intel,  and  J Crawford  &  P.  Gelsinger,
  3194.           Programming the 80386, (1987) SYBEX Inc.
  3195.  
  3196.  
  3197.           Shared Memory
  3198.  
  3199.                Normally global memory allocated to an application belongs to the
  3200.           application  and no other application can access it.  Should any other
  3201.           application  happen  to  access  it,  a  GP  fault  will  occur.    To
  3202.           accommodate DDE, Windows 3.0 enables blocks of global memory allocated
  3203.           with  the  GMEM_DDESHARE  flag  to  be  shared  by  all  applications.
  3204.           According to the API documentation, memory allocated with this flag is
  3205.           automatically freed when the owner terminates.
  3206.  
  3207.                Now, suppose  we want to allocate  a block of memory  that can be
  3208.           shared between two or more applications.  One application can allocate
  3209.           it  with  the GMEM_DDESHARE  set and  pass  the handle/pointer  to the
  3210.           others.   This  is  fine, provided  the  owning application  does  not
  3211.           terminate  before any  of the  others that  make use  of the  block of
  3212.           global memory.   In the   user friendly  environment  of Windows,  the
  3213.           programmer cannot guarantee the life of  an application.  In fact, the
  3214.           programmer  should  not  write  code  that  exhibits  this  behaviour.
  3215.           Imagine the  frustration of  the user  of not being  able to  close an
  3216.           application if it was the first one in a group to be opened!
  3217.  
  3218.                To  get around  this problem,  we must allocate  shareable global
  3219.           memory  that can  live  longer than the application that allocated it.
  3220.           I  wrote a  .DLL and  a  background   application that  only allocates
  3221.           shareable  global memory.    It involves  writing an  application that
  3222.           always remain  active, but is hidden  from the user.   All requests to
  3223.           allocate shareable memory  is made through the .DLL and carried out by
  3224.           the hidden application.   This  way, every block  of shareable  memory
  3225.           that  is allocated remains  allocated until  explicitly freed  or when
  3226.           Windows is terminated.  This means that an application can request the
  3227.  
  3228.                                           - 52 -
  3229.  
  3230.  
  3231.  
  3232.           .DLL to  allocate a block of memory on its behalf, pass the pointer to
  3233.           other applications and  terminate.  When  the block  of memory is  not
  3234.           required anymore, it can be freed.
  3235.  
  3236.                More  specifically, a  .DLL when  called for  the first  time can
  3237.           allocate shareable  memory and  store the  pointer away.   When it  is
  3238.           called again (probably  from another  application), it  can reuse  the
  3239.           block of memory, without causing a GP fault.
  3240.  
  3241.  
  3242.           The hidden application
  3243.  
  3244.                Included is a  file named SHAREMEM.ZIP.   The source code of  the
  3245.           hidden  application  (_MALLOC.EXE) is  found  in  the root  directory.
  3246.           Unzip this file with the -d flag to preserve its directory  structure.
  3247.           The binaries, ie. the .DLL and the .EXE must be placed in  the Windows
  3248.           directory, the System directory or any other directories pointed to by
  3249.           the PATH environment variable.
  3250.  
  3251.                There are two sections in _MALLOC.EXE.  The first initialises and
  3252.           registers the hidden window class.  It also checks if there is another
  3253.           instance of itself in memory.  It will not  load if it found one.  The
  3254.           second  section  contains  a hidden  window  (a  message  target) that
  3255.           accepts requests to allocate shareable memory.
  3256.  
  3257.                _MALLOC.EXE check whether there is another instance of itself via
  3258.           the  call to  RegisterClass.   RegisterClass  fails if  an application
  3259.           registers more  than once across instances.   This is a  simple way to
  3260.           check if another instance exist.  The hidden window is registered as a
  3261.           global  class  with  the  CS_GLOBALCALSS style.    When  RegisterClass
  3262.           attempts to register  a second class  instance of a  global class,  it
  3263.           will  fail.  This prevents  the user from  accidently running a second
  3264.           instance of MALLOC.EXE.
  3265.  
  3266.  
  3267.           The .DLL
  3268.  
  3269.                The .DLL (MALLOC.DLL) interfaces MALLOC.EXE to  applications that
  3270.           make  use   of  it.    It  is  located  in  the  MALLOC  directory  of
  3271.           SHAREMEM.ZIP.   Like _MALLOC.EXE, _MALLOC.DLL  contains two  sections.
  3272.           The first  section performs initialisations and  checks if _MALLOC.EXE
  3273.           has been loaded.  If it not, it will be loaded.   If MALLOC.DLL cannot
  3274.           load MALLOC.EXE, the  initialisation will  fail.   The second  section
  3275.           contains the shareable memory allocation API.
  3276.  
  3277.                MALLOC.DLL  determines whether  _MALLOC.EXE  is  loaded by  first
  3278.           registering  a pivate Windows message.   It then creates a hidden test
  3279.           window  and broadcasts  the message  to  all top  level windows.   The
  3280.           wParam of the message contains the handle of the test window.
  3281.  
  3282.           HWND hWnd;
  3283.           HINSTANCE hInst;
  3284.           #define MAL_ACK 1
  3285.           #define TEST_ID 0x41A5
  3286.             .
  3287.             .
  3288.             .
  3289.           static void _gethwnd (void)
  3290.  
  3291.                                           - 53 -
  3292.  
  3293.  
  3294.  
  3295.             {HWND hwndTest;
  3296.  
  3297.              hwndTest = CreateWindow (TEST_CLASS, "", WS_OVERLAPPED,
  3298.                 0, 0, 0, 0, NULL, NULL, hInst, NULL);
  3299.              if (hwndTest == NULL) return;
  3300.              ShowWindow (hwndTest, SW_HIDE);
  3301.              SendMessage (HWND_BROADCAST, uTestMsg, (WPARAM) hwndTest,
  3302.                 MAKELPARAM (0, TEST_ID));
  3303.              DestroyWindow (hwndTest);
  3304.  
  3305.              if (hWnd == NULL)
  3306.                {WinExec ("_MALLOC.EXE", SW_HIDE);
  3307.                 hwndTest = CreateWindow (TEST_CLASS, "", WS_OVERLAPPED,
  3308.                    0, 0, 0, 0, NULL, NULL, hInst, NULL);
  3309.                 if (hwndTest == NULL) return;
  3310.                 ShowWindow (hwndTest, SW_HIDE);
  3311.                 SendMessage (HWND_BROADCAST, uTestMsg, (WPARAM) hwndTest,
  3312.                    MAKELPARAM (0, TEST_ID));
  3313.                 DestroyWindow (hwndTest);
  3314.                } // endif
  3315.             } // end gethwnd
  3316.  
  3317.                The  _gethwnd  function  first tests  to  see  if  _MALLOC.EXE is
  3318.           loaded.  If not, it will attempt to  load it.  It then tests again  to
  3319.           see  if _MALLOC.EXE is loaded.  If  the second check fails, it assumes
  3320.           that it has failed  to load _MALLOC.EXE and the  initialisation fails.
  3321.           Every time the .DLL is called it checks whether the initialisation was
  3322.           successful.  If it was not, the .DLL will return with error.
  3323.  
  3324.                _MALLOC.EXE also registers the same Windows message.  Its message
  3325.           procedure contains the following code extract.
  3326.  
  3327.           UINT uRegMessage;
  3328.           #define MAL_ACK 1
  3329.             .
  3330.             .
  3331.             .
  3332.           if (msg == uRegMessage)
  3333.             {return SendMessage ((HWND) wParam, WM_COMMAND, HIWORD (lParam),
  3334.                 MAKELPARAM (hWnd, MAL_ACK));
  3335.             } // endif
  3336.  
  3337.                Where    uRegMessage    is   the    value    returned    by   the
  3338.           RegisterWindowsMessage function.
  3339.  
  3340.                When it receives  the registered message,  it sends a  WM_COMMAND
  3341.           message  to the  sender with its  hWnd in  the loword of  lParam.  The
  3342.           following code extract is from the hidden test window of MALLOC.DLL:
  3343.  
  3344.           LRESULT CALLBACK TestProc (HWND hWndTest, UINT msg, WPARAM wParam, 
  3345.                                      LPARAM lParam)
  3346.             {if (msg == WM_COMMAND)
  3347.                {if (wParam == TEST_ID && HIWORD (lParam) == MAL_ACK)
  3348.                   {hWnd = (HWND) LOWORD (lParam);
  3349.                   } // endif
  3350.                 return 0;
  3351.                } // endif
  3352.              return DefWindowProc (hWndTest, msg, wParam, lParam);
  3353.  
  3354.                                           - 54 -
  3355.  
  3356.  
  3357.  
  3358.             } // end TestProc
  3359.  
  3360.                The test  window  then saves  the  hWnd of  _MALLOC.EXE s  hidden
  3361.           window to the  variable hWnd.   As hWnd is  initialised to NULL  every
  3362.           time the .DLL  is loaded, a NULL value indicates  that the test window
  3363.           did not receive the WM_COMMAND message.  This implies that _MALLOC.EXE
  3364.           has not been loaded.
  3365.  
  3366.                The second section of MALLOC.DLL contains these four API function
  3367.           calls.
  3368.  
  3369.           API function name
  3370.           Description/function
  3371.  
  3372.           gmalloc
  3373.           Allocates a block of shareable global memory.
  3374.  
  3375.           grealloc
  3376.           Changes the size of an already allocated block of global memory.
  3377.  
  3378.           gfree
  3379.           Frees a block of shareable global memory.
  3380.  
  3381.           gisptr
  3382.           Checks  if a  given  pointer points  to  a block  of shareable  global
  3383.           memory.
  3384.  
  3385.  
  3386.                The gmalloc  function first  checks if  _MALLOC.EXE is  loaded by
  3387.           calling _gethwnd.   It fails if _MALLOC.EXE cannot be loaded.  It then
  3388.           sends the MAL_ALLOC message to the hidden window of _MALLOC.EXE.   The
  3389.           return  value of  the  message is  a  pointer to  the  newly allocated
  3390.           shareable global  memory.  If there  was an error in  the process, the
  3391.           return  value will  be  NULL.   The  following  code  extract is  from
  3392.           _MALLOC.EXE.
  3393.  
  3394.           void far *lpPtr;
  3395.           HGLOBAL handle;
  3396.             .
  3397.             .
  3398.             .
  3399.           case MAL_ALLOC:
  3400.              handle = GlobalAlloc (GMEM_DDESHARE | GHND, (DWORD) lParam);
  3401.              if (handle == NULL) return NULL;
  3402.              lpPtr = GlobalLock (handle);
  3403.              return (LRESULT) lpPtr;
  3404.  
  3405.                This code simply allocates shareable global memory,  locks it and
  3406.           returns a pointer to it.
  3407.  
  3408.                The  grealloc   function  checks   if  initialisation  has   been
  3409.           successful.    It then  sends the  MAL_REALLOC  message to  the hidden
  3410.           window  of _MALLOC.EXE.    It fills  the  REALLOCSTRUCT structure  and
  3411.           passes a pointer in the lParam of the message.  The process is similar
  3412.           to gmalloc s.   When _MALLOC.EXE receives the MAL_REALLOC message, the
  3413.           following code extract processes it:
  3414.  
  3415.           typedef struct tagREALLOCSTRUCT
  3416.  
  3417.                                           - 55 -
  3418.  
  3419.  
  3420.  
  3421.             {DWORD dwSize;
  3422.              void far *lpPtr;
  3423.             } REALLOCSTRUCT, far *LPREALLOCSTRUCT;
  3424.  
  3425.           LPREALLOCSTRUCT lpRa;
  3426.             .
  3427.             .
  3428.             .
  3429.           case MAL_REALLOC:
  3430.              lpRa = (LPREALLOCSTRUCT) lParam;
  3431.              handle = (HANDLE) LOWORD (GlobalHandle (SELECTOROF (lpRa->lpPtr)));
  3432.              if (handle == NULL) return NULL;
  3433.              if (GlobalUnlock (handle)) return NULL;
  3434.              handle = GlobalReAlloc (handle, lpRa->dwSize, GMEM_ZEROINIT);
  3435.              if (handle == NULL) return NULL;
  3436.              lpPtr = GlobalLock (handle);
  3437.              return (LRESULT) lpPtr;
  3438.  
  3439.                The handle to the allocated memory block is first retrieved via a
  3440.           call  to   GlobalHandle.     The  memory   block  is   then  unlocked.
  3441.           GlobalUnlock returns the number of times a handle has been locked.  If
  3442.           the return  value is  greater than zero  (meaning the memory  is still
  3443.           locked),   this  message   returns  NULL.     Otherwise,  a   call  to
  3444.           GlobalRealloc is then issued  to reallocate the memory.  The  block of
  3445.           memory is then locked and the pointer returned.
  3446.  
  3447.                The gfree function  sends the  MAL_FREE to the  hidden window  of
  3448.           _MALLOC.EXE.  The  process is  similar to grealloc s  except that  the
  3449.           block of memory is freed instead of reallocated.
  3450.  
  3451.                The following code extract shows how gisptr determines whether  a
  3452.           pointer points to shareable global memory:
  3453.  
  3454.           BOOL WINAPI gisptr (void far *lpPtr)
  3455.             {HGLOBAL handle;
  3456.  
  3457.              handle = (HGLOBAL) LOWORD (GlobalHandle ((UINT) FP_SEG (lpPtr)));
  3458.              if (handle == NULL) return FALSE;
  3459.              if (FP_OFF (lpPtr) != NULL) return FALSE;
  3460.              return TRUE;
  3461.             } // end gisptr
  3462.  
  3463.                First,  it tries to retrieve  the handle to  the block of memory.
  3464.           If that fails the pointer  is not valid.  It then checks if the offset
  3465.           of the  pointer is 0 (NULL).   Windows 3.x GlobalLock  function always
  3466.           return  pointers with  0 offset.   This  is not  documented so  it may
  3467.           change.   The aim  of this  function is to  provide an  alternative to
  3468.           Windows 3.x IsBadHugeReadPtr and IsBadHugeWritePtr API functions.
  3469.  
  3470.  
  3471.           My methods:
  3472.  
  3473.                You  may argue  that  you have  a  better method  of  determining
  3474.           whether  an   application  is   loaded  (i.e.,  Using   FindWindow  or
  3475.           GetModule).  Well, you may be right.  I justify my methods because;
  3476.  
  3477.           1.   they are different,
  3478.           2.   they  serve to illustrate the fact  that there are more ways than
  3479.  
  3480.                                           - 56 -
  3481.  
  3482.  
  3483.  
  3484.           one to achieve something in software, and
  3485.           3.   they  are  meant  to demonstrate  alternative  (and interesting?)
  3486.           ways.
  3487.  
  3488.                _MALLOC.EXE  creates a  hidden window,  which only purpose  is to
  3489.           receive  messages.   I call  this kind  of windows   Message Targets .
  3490.           They  serve only  as a  method of  communications.   Windows does  not
  3491.           provide an object  that an  application can use  to receive  messages.
  3492.           The only way  is to use a  window that is always hidden.   _MALLOC.EXE
  3493.           has no other  window.  It  is a hidden  application, meaning the  user
  3494.           will never see  it on the screen.   In a way,  it is like a  .DLL.  It
  3495.           provides services that  other application can use.  You  might ask why
  3496.           don t  I use  a .DLL  instead.   The problem  is any  shareable global
  3497.           memory allocated by a .DLL is owned by the  application that requested
  3498.           it.  Our aim  is to allocate global memory that  has a longer lifespan
  3499.           than the  application.   To achieve  that, the global  memory must  be
  3500.           allocated   by  an  application  that  is  always  active.    As  this
  3501.           contradicts with  the notion that the  user should have the  choice of
  3502.           terminating applications, _MALLOC.EXE is made to be hidden.
  3503.  
  3504.  
  3505.           Notes:
  3506.  
  3507.           [1]  -  Although prior  to  WIndows 3.0,  there were  Windows  286 and
  3508.           Windows 386, these were relatively unpopular and hence this discussion
  3509.           excludes them.
  3510.  
  3511.  
  3512.           Other compilers/languages:
  3513.  
  3514.                The source code  provided will compile  with Borland C++  version
  3515.           3.1.   To use the  binaries with other  C compilers, use  the included
  3516.           wmalloc.h (This is  located in the INCLUDE  directory of SHAREMEM.ZIP)
  3517.           header  file,  the  associated binaries  (_MALLOC.EXE  and  MALLOC.DLL
  3518.           --These can be  found in the BIN directory of  SHAREMEM.ZIP. ) and the
  3519.           import  library, MALLOC.LIB (This is  located in the  LIB directory of
  3520.           SHAREMEM.ZIP).  Windows.h  must be included  before wmalloc.h.   Turbo
  3521.           Pascal users may  want to create external  referenced import functions
  3522.           to link up with MALLOC.DLL.   Visual Basic and Word for  Windows users
  3523.           can  use   the  declare  function/sub  statements  to   link  up  with
  3524.           MALLOC.DLL.
  3525.  
  3526.                Next article  we will look  at ways to  use the shareable  global
  3527.           memory blocks.
  3528.  
  3529.  
  3530.           The author:
  3531.  
  3532.                I am currently doing  a PhD. degree in Electrical  And Electronic
  3533.           Engineering  at   the  University  Of  Canterbury.     My  programming
  3534.           experience dates back to  the days where  real programmers   code with
  3535.           Z80 machine code.  For the past two years  I have taken an interest in
  3536.           Windows programming.
  3537.  
  3538.                Please send any comments, questions, criticisms to me via
  3539.           email: chuah@elec.canterbury.ac.nz
  3540.           or post mail:  Dennis Chuah
  3541.           c/o The Electronic and Electrical Engineering Department
  3542.  
  3543.                                           - 57 -
  3544.  
  3545.  
  3546.  
  3547.           University of Canterbury
  3548.           Private Bag
  3549.           Christchurch
  3550.           New Zealand.
  3551.  
  3552.                All mail (including  hate mail)  is appreciated.   I will try  to
  3553.           answer  all questions personally, or if the answer has general appeal,
  3554.           in  the next issue.  If you are sending me email, please make sure you
  3555.           provide me  with an address that I can reach (most internet and bitnet
  3556.           nodes are reachable, and so is compuserve).
  3557.  
  3558.  
  3559.  
  3560.  
  3561.  
  3562.  
  3563.  
  3564.  
  3565.  
  3566.  
  3567.  
  3568.  
  3569.  
  3570.  
  3571.  
  3572.  
  3573.  
  3574.  
  3575.  
  3576.  
  3577.  
  3578.  
  3579.  
  3580.  
  3581.  
  3582.  
  3583.  
  3584.  
  3585.  
  3586.  
  3587.  
  3588.  
  3589.  
  3590.  
  3591.  
  3592.  
  3593.  
  3594.  
  3595.  
  3596.  
  3597.  
  3598.  
  3599.  
  3600.  
  3601.  
  3602.  
  3603.  
  3604.  
  3605.  
  3606.                                           - 58 -
  3607.  
  3608.  
  3609.  
  3610.                            Customizing FileDialog in Visual C++
  3611.                                         By Tony Lee
  3612.  
  3613.           Overview
  3614.  
  3615.                I  have developed two application with Visual  C++.  Both of them
  3616.           are fairly good size applications with > 300K lines of C++ source code
  3617.           each.  I use  common dialogs  extensively in  my projects.     In this
  3618.           article,  I  will  try  to  show you  how  to  use  and  customize the
  3619.           CFileDialog  class in the Visual C++ environment.    I would also like
  3620.           to  share with  you why  I think  C++ and object  oriented development
  3621.           environment is  such a great tool  for developing Windows programs.   
  3622.           Since I  have limited time  to write  this article, I  want to   refer
  3623.           directly to the WinSDK help for most of the  basic information related
  3624.           to  Common  Dialog.     My  background:   I  have  been doing  Windows
  3625.           programming for  4 months now.   One of  the applications I wrote is a
  3626.           diagnostic  utility   for  a   complicated  instrument.     The  other
  3627.           application I wrote  is a C/C++ cross reference utility  that lets you
  3628.           cross   reference  C/C++  source  code.    As  you  can  see,  Windows
  3629.           programming with C++ is not hard at all.  
  3630.  
  3631.  
  3632.           What is Common Dialog?
  3633.  
  3634.                Common dialog is a set of dialog box functions.   These functions
  3635.           simplify the programming for  the common dialogs such as  File, Color,
  3636.           Font  and Print  Dialogs for Windows.   You can  find more information
  3637.           about them in the  "Common Dialog Box OverView" section of the Windows
  3638.           SDK  Help.   [Editor's note:  The common  dialog routines  are in  the
  3639.           COMMDLG.DLL file - mfw]
  3640.  
  3641.  
  3642.           Why C++?
  3643.  
  3644.                One  of  the best  reasons for  using  C++ in  developing Windows
  3645.           programs  is encapsulation.  The  class structure of  the C++ langauge
  3646.           hides the most  implicit details from you.   For example, to implement
  3647.           the FileDialog using C and SDK, you have to  write about 40 lines of C
  3648.           code.    (You  can count them  yourself in the  "Filename Dialog Boxes
  3649.           (3.1)" part of the Windows 3.1 SDK help.)  You also need to understand
  3650.           the "OPENFILENAME" structure and the "GetOpenFileName" functions. 
  3651.  
  3652.                To  implement the same open  file dialog function  in C++, you do
  3653.           the following:
  3654.  
  3655.                Void OnOpenFile() {
  3656.                     CFileDialog  dlg(TRUE,  "txt",  NULL,   OFN_FILEMUSTEXIST  |
  3657.           OFN_HIDEREADONLY,  "Text Files (*.txt)  | *.txt  All Files  (*.*) |*.*
  3658.           ||");
  3659.  
  3660.                     if (dlg.DoModal() != IDOK) return ;
  3661.                     CString sFilename = dlg.GetPathName();
  3662.                     // You can use the filename now.
  3663.                }
  3664.  
  3665.                As you  can see, the  C++ implementation  of the same  feature is
  3666.           much  more elegant and simpler than the C implementation.  CFileDialog
  3667.           encapsulates all of the structures and function initializations.   
  3668.  
  3669.                                           - 59 -
  3670.  
  3671.  
  3672.  
  3673.  
  3674.           Drawback of C++
  3675.  
  3676.                What  is the drawback of  using C++ in  implementing your Windows
  3677.           project?  In my honest opinion, the best reason  for using C++ is also
  3678.           the  biggest drawback.     If your  project's features  fit  the class
  3679.           library  very well,  you can  use the  class directly  or with  little
  3680.           modifications.    However, if  you want to  differentiate your product
  3681.           from  what's  avaiable in  the  market, you  will  have to  modify the
  3682.           existing class library  by creating inheritance  from the base  class,
  3683.           understanding the class interface  and implementing the needed virtual
  3684.           functions of the  inherited class.   The benefit  of the encapsulation
  3685.           in a class library also hide a lot  of details from you.   You have to
  3686.           spend  time to peel the layers  off the class hierarchy and understand
  3687.           each  layer's functionalities so your  new class adds  features to the
  3688.           existing  class in an optimized  fashion. From my  experience of doing
  3689.           C++ projects, to do  a reasonable job in  deriving a new class from  a
  3690.           class framework you should do the following:
  3691.  
  3692.                1. You  need to understand the existing  class hiearchy.  In this
  3693.           example,  you should know that the "CFileDialog" class is derived from
  3694.           the "CDialog" class.  CDialog is derived from the "CWnd" class, and so
  3695.           on.   You can find out  the MFC class hierarchy  relationship from the
  3696.           MFC Help's "Hierarchy Button".
  3697.  
  3698.                2.  You need  to  understand what  are the  public/protected data
  3699.           members and methods for every classes in that particular hiearchy.
  3700.  
  3701.                3.  You need to understand  the virtual functions in the hiearchy
  3702.           and  how  these  virtual  functions  interact  between layers  of  the
  3703.           classes.     For  example,  you should  know  that  the Windows  SDK's
  3704.           subclass mechanism  is inherited  in the "virtual  LRESULT WindowProc(
  3705.           UINT message, WPARAM wParam, LPARAM lParam ); " function.   
  3706.  
  3707.                As  you can see, encapsulation really  complicates the process of
  3708.           understanding the  class library.   In my project, in  order to extend
  3709.           the existing class successfully, I have to spend about five times  the
  3710.           amount of time to learn  the existing C++ code  than C code.  After  I
  3711.           understand the existing  C++ code,  I spend about  the same amount  of
  3712.           time writing  the new code.    The biggest benefit  of object oriented
  3713.           design  is that the  interface to that particular  object is very well
  3714.           defined by the class methods.   Almost all of the modifications I made
  3715.           were internal to the class.   The interface between this object to the
  3716.           rest  of  the  system  changes  very little.    Thus,  after  I finish
  3717.           modifying the  class, with only minor  change to the interface,  I can
  3718.           easily  test the  new system.      Testing of  the new  system is much
  3719.           easier.  
  3720.  
  3721.  
  3722.           Why customize the Common Dialog?
  3723.  
  3724.                The simplest form Common dialog is great, if  it fits your needs.
  3725.           With just six line of  C++ code, your users can get the professionally
  3726.           designed dialog box for open  and save file.  Most   important benefit
  3727.           of all,  there are  fewer chance for  errors.    However, most  of the
  3728.           time,  getting a  single file  name  from the  dialog box  may not  be
  3729.           exactly what you'd like to do.   Also properly designed and customized
  3730.           dialog box  make your  program easier  to use.   It adds  value and  a
  3731.  
  3732.                                           - 60 -
  3733.  
  3734.  
  3735.  
  3736.           professional look to your final product.
  3737.  
  3738.                 For  example,  in my  Interactive  Cross  Reference for  Windows
  3739.           (IXFW)  program,  the FileDialog has two additional controls: 
  3740.  
  3741.                1) Add a listbox to the dialog.
  3742.  
  3743.                2) Add an "Add File" button to the dialog box.  
  3744.  
  3745.                When an user pushs the "Add File" button, the dialog box will add
  3746.           the selected files in  the file listbox to my  custom listbox.    This
  3747.           way,  the user  can  select  all the  files  he  wants from  different
  3748.           directories  before  the file  dialog box  is  closed and  the program
  3749.           continues.  The user can save a lot of  tedious mouse/keyboard actions
  3750.           when  he tries to  gather files from  different directories.    A very
  3751.           good value is added to the final product.
  3752.  
  3753.  
  3754.           How to customize the common dialog in C?
  3755.  
  3756.                The easiest way learn how to customize the file dialog box with C
  3757.           is  by studying  the excellent article  "Using and  Customizing Common
  3758.           Dialogs", written by Kraig Brockschmidt of Microsoft System Developers
  3759.           Relations.  You can download this file from CompuServe.  Basically, to
  3760.           customize the file dialog, you need to do the following:
  3761.  
  3762.                1)  You need  to  edit  the dialog  template  by adding  the  new
  3763.           controls to the default template.
  3764.  
  3765.                2)  You have to hook  the dialog message  processing procedure by
  3766.           subclassing the dialog control.
  3767.  
  3768.                3) You need to initalize the proper data structure.
  3769.  
  3770.                4) You have  to write  message handler  code to  support the  new
  3771.           controls in your dialog template.
  3772.  
  3773.  
  3774.           How to customize the Common Dialog in C++?
  3775.  
  3776.                You  customize  the  File  dialog  in  a  very  similar  fashion.
  3777.           However, it's much more desirable  to use all the Visual C++  's tools
  3778.           in the customization process.   Here is how to do that:
  3779.  
  3780.                1) Use the "App Studio" to customize the template.
  3781.                  I did this by  inserting the default file dialog  template into
  3782.           my .rc file.   You also include the "dlgs.h" in the include section of
  3783.           the .rc file,  because most  of the resource IDs defined for the  file
  3784.           dialog box are located in the "dlgs.h" file.    
  3785.  
  3786.                2)  Derive a  new class  from the  CFileDialog class  with "Class
  3787.           Wizard".  
  3788.                The best  way to do this  is to run the Class  Wizard from inside
  3789.           the "App Studio". Since I can't tell  the Class Wizard to derive a new
  3790.           class from the CFileDialog directly, I  have to tell the Class  Wizard
  3791.           to  derive the  new class  for the  dialog template  from the  CDialog
  3792.           class.    After the Class  Wizard created the necessary  files for the
  3793.           new class,   I edited the  parent class from CDialog  to CFileDialog. 
  3794.  
  3795.                                           - 61 -
  3796.  
  3797.  
  3798.  
  3799.           (You   need   to   change   the  constructor   for   this   particular
  3800.           implementation.)
  3801.  
  3802.                3) Subclass the new dialog box.
  3803.                I  didn't  do  it  here,  since  the  dialog  box  is  subclassed
  3804.           automatically  in the MFC class  hiearchy.   However,   if you want to
  3805.           use any of the fancy features  like customizing the Common Dialog, you
  3806.           really  should  understand how  MFC subclasses  a  window and  how the
  3807.           messages are  routed to your handlers.    Since the subclass mechanism
  3808.           is in the MFC's class hiearchy already, I  can use the Class Wizard to
  3809.           add handler functions to the dialog controls' messages.   The handlers
  3810.           I added  are: a) OnOK()  -  to handle  the OK button  from the default
  3811.           actions to the new action of adding selected files to my listbox.   b)
  3812.           OnClickButton1() - for adding files to the listbox.  c) OnClickButton2
  3813.           - for deleting files from the listbox.  
  3814.  
  3815.                I also added several  data members which map  to the new  control
  3816.           IDs with Class  Wizard.  This way, I can use  the class methods of the
  3817.           CButton or  CListBox  instead of  trying  to figure  out how  to  send
  3818.           control messages to those IDs.  Isn't C++ wonderful?
  3819.  
  3820.                One  last data  member I  added to  this particular class  is the
  3821.           "CStringArray" for  storing the collection  of filenames after  the OK
  3822.           button  is pushed.     You  see,  when you  push  the OK  button,  the
  3823.           filedialog will  destoy the dialog  box with all  the controls  in it.
  3824.           You  have to  copy the  strings  from the  listbox control  to a  safe
  3825.           location before the listbox control is destroyed.
  3826.  
  3827.                4) Properly initialize the new class in the constructor.
  3828.                You  have to  tell the  "OPENFILENAME" structure  to use  the new
  3829.           template.  This is done in the constructor of this particular class.
  3830.  
  3831.                5)  Add methods to retrieve the collections of filenames from the
  3832.           dialog class.
  3833.  
  3834.                Since the  CStringArray data member  is public,  I  choose to let
  3835.           the  outside world  have access to  the filelist  directly.    Not the
  3836.           purest form of C++, but.... 
  3837.  
  3838.                OK, all of the files needed to compile and run the new FileDialog
  3839.           Box are in the filedia.zip file.  Have fun!
  3840.  
  3841.  
  3842.           * If you  have any comments, questions, suggestions, feel free to send
  3843.           email to Tony Lee at CompuServe 72064,1235. 
  3844.  
  3845.           ** "Interactive  Cross Reference for  Windows" is a  shareware utility
  3846.           that lets you build a  database from existing source code so  that you
  3847.           can  quickly browse  megabytes of source  code.  You  can download the
  3848.           program from IBMPRO forum of the CIS.
  3849.  
  3850.  
  3851.  
  3852.  
  3853.  
  3854.  
  3855.  
  3856.  
  3857.  
  3858.                                           - 62 -
  3859.  
  3860.  
  3861.  
  3862.                                    Installing Windows NT
  3863.                               (As Done by a non-Windows Guru)
  3864.                                       By Kurt Simmons
  3865.  
  3866.                A couple of  months ago at a company meeting  it was decided that
  3867.           we  should  get in  on the  ground  floor of  software  developing for
  3868.           Windows NT,  and that we  would order  the preliminary release  of the
  3869.           SDK.  When I called Microsoft, I found that the CD was $69.00 and that
  3870.           the documentation  would be more than $300, if you wanted a hard copy.
  3871.           I figured that if it was  on the CD that would be sufficient,  even if
  3872.           it was in  PostScript format (though in fact some  is in Windows Write
  3873.           format).
  3874.  
  3875.                After breathlessly  waiting a week,  the box arrived;  finally we
  3876.           too  would  enter the  next step  in giving  a  PC a  "real" operating
  3877.           system.  I had moved many of our files around to make over 200Meg free
  3878.           on  a  drive  so that  even  Windows  NT  could  not  complain  during
  3879.           installation.  Then I found that we can't run Stacker with Windows NT,
  3880.           nor could we run  the current drivers for our LAN (LBL).   NT requires
  3881.           that you  use 32bit drivers.  While  this will improve performance, it
  3882.           made  life difficult at  the current time.   OK, back  to moving files
  3883.           around and freeing up a drive  that is unStacked.  Two hours later,  I
  3884.           am back on my way to installing NT.
  3885.  
  3886.                NTFS:  would I like  to convert my drive to that format?  Options
  3887.           are available to convert with or without destroying the existing files
  3888.           or to simply keep the  existing FAT format.  The first time  through I
  3889.           figured I would keep  the existing format (an option exists to convert
  3890.           later  if we  desire).   After  some  trial and  error  of getting  NT
  3891.           configured to work with the  Ethernet cards, I decided to give  up and
  3892.           completely re-install as I have changed so many of the settings (you'd
  3893.           think  after doing  this sort  of  thing for  this long  I would  have
  3894.           learned to  keep notes!)   So, here I go  again installing it  for the
  3895.           second time.   By now I  have read about  the advantages of using  the
  3896.           NTFS for reasons  of security and  data integrity;  I figured that  it
  3897.           would be a good idea  to convert the drive to the  format that Windows
  3898.           NT will be most comfortable with.
  3899.  
  3900.                Everything  went along fine  until I  found out  that "Converting
  3901.           while leaving your files intact" doesn't mean you can boot off of that
  3902.           drive anymore or even access it from  MS-DOS!  Well, since I needed to
  3903.           be able to run DOS as my primary  OS at this point, I realized I  must
  3904.           reformat the drive.  Except  I couldn't do that from within NT.   So I
  3905.           booted  from floppy and found   that my D: drive  was now listed as my
  3906.           C:, and  C: was nowhere  to be  found.  The  installation program  for
  3907.           Windows  NT was  kind enough to  reformat the  drive in  MS-DOS for me
  3908.           after my partner got done performing CPR.
  3909.  
  3910.                The moral of the above story is that taking a day or so to search
  3911.           the  on-disk  documentation might  have done  us  some good.    In the
  3912.           DOC\ENDUSER\WRITE  (this  is   the  first  place  I   would  look  for
  3913.           information  on  "How to  plan  your installation")  directory  were a
  3914.           series of files on installing Windows NT.  I found that I had become a
  3915.           little too used to the standard OS  of the PC's being more than a  bit
  3916.           lax in security, I should be able to destroy my system from the inside
  3917.           if I want to, right?
  3918.  
  3919.                The  first eight  hours of NT  seemed to  have drained  me to the
  3920.  
  3921.                                           - 63 -
  3922.  
  3923.  
  3924.  
  3925.           point  of  actually considering  giving up  caffeine  as a  major food
  3926.           group, but after that life  got easier.  I hope to be able to continue
  3927.           my adventures in NT next month.  The articles will be written from the
  3928.           point of a programmer but not a Windows NT guru, as I think many of us
  3929.           fit that description.
  3930.  
  3931.                Kurt A. Simmons
  3932.                Systems Administrator
  3933.                Millennium Technologies, Inc.
  3934.                Suite 100
  3935.                160 Edgehill Rd.
  3936.                Glenside, Pa 19038-3004
  3937.                Voice (215) 886-7366   Fax (215) 886-8602
  3938.  
  3939.  
  3940.  
  3941.  
  3942.  
  3943.  
  3944.  
  3945.  
  3946.  
  3947.  
  3948.  
  3949.  
  3950.  
  3951.  
  3952.  
  3953.  
  3954.  
  3955.  
  3956.  
  3957.  
  3958.  
  3959.  
  3960.  
  3961.  
  3962.  
  3963.  
  3964.  
  3965.  
  3966.  
  3967.  
  3968.  
  3969.  
  3970.  
  3971.  
  3972.  
  3973.  
  3974.  
  3975.  
  3976.  
  3977.  
  3978.  
  3979.  
  3980.  
  3981.  
  3982.  
  3983.  
  3984.                                           - 64 -
  3985.  
  3986.  
  3987.  
  3988.                                  Software Development '93
  3989.                                        by Pete Davis
  3990.  
  3991.                Mike and I  went to  the Software Development  '93 conference  in
  3992.           Boston. The  conference ran the week  of the August 23-27.  Mike and I
  3993.           were  there  from  the  24th  to the  26th.  This  main  point  of the
  3994.           conference was to let  Software Developers strut their stuff.  All the
  3995.           big boys were there, Microsoft, Borland, Miller-Freeman and others. 
  3996.  
  3997.                So,  what were  people  showing off?  Well,  I think  the  single
  3998.           product that impressed Mike and I  the most, and this is strictly from
  3999.           a demo  point-of-view, but Symantec's C++  development environment won
  4000.           hands  down for best demo. They  also won in the  best "Star Trek: The
  4001.           Next Generation"  rip-off. Basically they  did a big  show as  part of
  4002.           their  demo. It  was cute,  but  the demo  was really  impressive. The
  4003.           development is a lot like Visual C++, but better. You  can create your
  4004.           dialog  boxes,  menus,  and  other resources  from  within  Symantec's
  4005.           Integrated Development  and Debugging Environment (IDDE).  You tie the
  4006.           buttons  to dialog boxes and dialog boxes to menu items, and so on and
  4007.           so on. All the code will be  generated by the IDDE. You can even throw
  4008.           in  things like tool-bars. Yep,  that's right, just  throw in the tool
  4009.           bar, make your  own icons for it  and bang, all  the code you need  is
  4010.           there.  It also allows you  to go in and modify  the code and have new
  4011.           generations  of the design take  on your existing  code changes. We're
  4012.           hoping to get a review copy of this soon. It's a great product  and it
  4013.           deserves some good press. (That's a hint  to any of you who happen  to
  4014.           work for Symantec!!!).
  4015.  
  4016.                Another product that really blew  peoples socks off was Nu-Mega's
  4017.           Bounds  Checker   2.0,  which  was  officially   released  during  the
  4018.           conference.  This   new  version   of  Bounds  Checker   has  terrific
  4019.           improvements  on version 1. More checking is done and the best feature
  4020.           is the new event tracing feature.  Essentially, it will trace all  API
  4021.           and  library  calls made  by  your  program  and  keep track  of  what
  4022.           parameters were passed  to each. It also  keeps track of messages  and
  4023.           other  things.  Really, it's  just  a  plain  fantastic product.  Look
  4024.           forward to a review in the next issue.
  4025.  
  4026.                Microsoft is working on  MFC 2.5. One of the main improvements is
  4027.           encapsulation of  OLE 2.0. A lot of people have agreed that OLE 2.0 is
  4028.           just too big and  bulky for most people. MFC 2.5 is  going to fix that
  4029.           problem. What about us C programmers? Well, the best thing to do might
  4030.           be to  do all the  OLE 2.0 work  in C++ and then  do the rest  of your
  4031.           coding in C. Really, doing OLE  2.0 in C is just about  impossible. In
  4032.           fact,  we went  to  a  class taught  by  a  Microsoft employee,  Nigel
  4033.           Thompson. Nigel  humorously described  his 4.5  months learning to  do
  4034.           simple OLE (as he says,  Objectionable Learning Experience) 2.0 things
  4035.           in C,  like  containers. We're  talking  about a  guy  who has,  as  a
  4036.           resource,  the people who wrote OLE 2.0.  Obviously if it took him 4.5
  4037.           months to do this,  most of us would probably  be better off doing  it
  4038.           with MFC 2.5, which is  supposed to cut that 4.5 month  learning curve
  4039.           down to 30 minutes or so. A slight improvement!
  4040.  
  4041.                Borland announced OWL 2.0 which is planned to be a cross-platform
  4042.           class  library. We'll see how far that goes. Microsoft released Visual
  4043.           C++ for Win32. That was all pretty public, though.
  4044.  
  4045.                We got  to meet some of  our readers who recognized  the names on
  4046.  
  4047.                                           - 65 -
  4048.  
  4049.  
  4050.  
  4051.           our  badges. I  have to say,  I was  awfully surprised  to have people
  4052.           recognize me at  the conference. Anyway, the  entire conference seemed
  4053.           to be  a big success and Mike and I certainly enjoyed meeting everyone
  4054.           there, especially the readers that made it.
  4055.  
  4056.                We were  able to get some review copies of software and books, so
  4057.           you'll be seeing  some reviews of some really good  products and books
  4058.           in the near future. Hopefully Mike  and I will have enough warning for
  4059.           the  next conference  that we  can let  you all  know we'll  be there.
  4060.           Hopefully  we'll  be able  to  run  into some  more  of  you at  other
  4061.           conferences.  It's nice to meet  the readers and  hear first-hand what
  4062.           they have to say about the magazine.
  4063.  
  4064.                I'd like to thank all the people who made SD '93  so much fun for
  4065.           me. Especially, Michael Liben,  Matt Pietrek and  all the guys at  Nu-
  4066.           Mega,  Dave Thielen,  Paul  Yao, Julianne  Sharer  and the  others  at
  4067.           WexTech,  all the  guys at  ProtoView Development  for being  so cool,
  4068.           Richard  Hale Shaw,  and Matt  Trask. Those  are the  only ones  I can
  4069.           remember off the top of my head.  
  4070.  
  4071.  
  4072.  
  4073.  
  4074.  
  4075.  
  4076.  
  4077.  
  4078.  
  4079.  
  4080.  
  4081.  
  4082.  
  4083.  
  4084.  
  4085.  
  4086.  
  4087.  
  4088.  
  4089.  
  4090.  
  4091.  
  4092.  
  4093.  
  4094.  
  4095.  
  4096.  
  4097.  
  4098.  
  4099.  
  4100.  
  4101.  
  4102.  
  4103.  
  4104.  
  4105.  
  4106.  
  4107.  
  4108.  
  4109.  
  4110.                                           - 66 -
  4111.  
  4112.  
  4113.  
  4114.                                  Getting In Touch With Us
  4115.  
  4116.           Internet: 71644.3570@compuserve.com
  4117.  
  4118.           GEnie: P.DAVIS5 (Pete)
  4119.  
  4120.           CompuServe: 71141,2071 (Mike)   71644,3570 (Pete)
  4121.  
  4122.           WPJ BBS (703) 503-3021 (Mike and Pete)
  4123.  
  4124.           You can also send paper mail to:
  4125.  
  4126.           Windows Programmer's Journal
  4127.           9436 Mirror Pond Drive
  4128.           Fairfax, VA   22032
  4129.                 U.S.A.
  4130.  
  4131.                In   future  issues  we  will  be  posting  e-mail  addresses  of
  4132.           contributors  and   columnists  who  don't  mind   you  knowing  their
  4133.           addresses. We will also  contact any writers from previous  issues and
  4134.           see  if they  want  their mail  addresses  made available  for  you to
  4135.           respond  to them. For now, send your  comments to us and we'll forward
  4136.           them.
  4137.  
  4138.  
  4139.  
  4140.  
  4141.  
  4142.  
  4143.  
  4144.  
  4145.  
  4146.  
  4147.  
  4148.  
  4149.  
  4150.  
  4151.  
  4152.  
  4153.  
  4154.  
  4155.  
  4156.  
  4157.  
  4158.  
  4159.  
  4160.  
  4161.  
  4162.  
  4163.  
  4164.  
  4165.  
  4166.  
  4167.  
  4168.  
  4169.  
  4170.  
  4171.  
  4172.  
  4173.                                           - 67 -
  4174.  
  4175.  
  4176.  
  4177.                                        The Last Page
  4178.                                       by Mike Wallace
  4179.  
  4180.                
  4181.           "The first thing we do, we kill all the OS/2 programmers."
  4182.                          -William Shakespeare
  4183.  
  4184.                No, this isn't an OS/2-bashing column.  Pete and I spent a couple
  4185.           of days last week at the Software Development '93 convention in Boston
  4186.           meeting  people  and  going   to  conferences  (see  Pete's  "Software
  4187.           Development  '93" article  elsewhere in  this issue).   Here  are some
  4188.           notes:
  4189.  
  4190.                One  of the speakers I saw  (from Borland) gave a presentation on
  4191.           an informal  survey he took of  programmers.  One of  the questions he
  4192.           asked was whether they thought programming was an art or a science.  A
  4193.           majority said  "an art",  and I  agreed with this  at first,  but then
  4194.           after  thinking about it for  a while I  had to change my  mind.  Take
  4195.           physics,  for  example.   Most people  would  consider that  a science
  4196.           (versus an art).  What's involved with a physicist's work?  Usually it
  4197.           starts off with an idea or an observation about the way  things are in
  4198.           nature.  He then takes that idea and applies the laws of physics to it
  4199.           in  an effort to understand  what's happening.   He's "doing Physics."
  4200.           For  instance, in 1960, Edward Lorenz created a machine that simulated
  4201.           weather (the  "Royal McBee") and after  staring at it for  a while, he
  4202.           thought he  noticed a  pattern to the  cloud formations.   He  started
  4203.           working on  the math, and  along the  way helped create  the field  of
  4204.           chaos, an unusual and fascinating branch of physics.  My point is that
  4205.           he  had an  initial  burst  of  creative  thought  that  preceded  the
  4206.           remainder of his  work in  the field, which  sounds like  programming.
  4207.           When I start on a program,  I usually first think about how  I'm going
  4208.           to approach the  problem and then I use what  I know about programming
  4209.           to implement my solution, so maybe programming is as much of a science
  4210.           as Physics, and not so much an art.  
  4211.  
  4212.                Another question  this  speaker asked  in  his survey  was  about
  4213.           favorite  foods for  programmers.   The winners  were pizza,  beer and
  4214.           hamburgers.  The loser, coming  in as the favorite food of 1% of those
  4215.           programmers surveyed, was fresh vegetables.  I'm glad I'm not the only
  4216.           one favoring beef over beets.
  4217.  
  4218.                The  speaker also  asked  about favorite  outside  hobbies.   The
  4219.           winner  was "Watching Star Trek reruns".   How did I know he was going
  4220.           to say that?  The  audience gave a very  loud cheer when he  announced
  4221.           that.    Am  I  the  only  programmer  who  doesn't  watch  "The  Next
  4222.           Generation"?  [Pete's Note: Don't buy that for a second. Mike reminded
  4223.           me it  was on the other night!  Obviously he keeps better  track of it
  4224.           than me!]  I went  to Symantec's party where they announced  their C++
  4225.           compiler,  and it  was a 30-minute  skit straight out  of "Star Trek".
  4226.           They  talked  about  getting  to  the  planet  C++  and  fighting  the
  4227.           "Borland-ians" and  "Microsoft-ians" from  their space  ship.   It was
  4228.           pretty corny.
  4229.  
  4230.                I spent  a good while  talking to the  president of a  well-known
  4231.           software company about what albums are good for prgramming to (you can
  4232.           tell  I did  a lot of  work at  this convention).   We agreed  on Pink
  4233.           Floyd's "The Wall".  I'd  also have to nominate Pearl Jam's  "Ten" and
  4234.           Joe Satriani's "Surfing with the Alien".  I want your responses.  What
  4235.  
  4236.                                           - 68 -
  4237.  
  4238.  
  4239.  
  4240.           music do  you listen to when you're pounding out code?  Van Halen with
  4241.           Visual Basic?  "Purple Haze" with  Pascal?  Send me your comments, and
  4242.           I'll reprint the best ones after I get enough  for a column (so it may
  4243.           take a while).  Hope you enjoy this issue.  Talk to you next month.
  4244.  
  4245.  
  4246.  
  4247.  
  4248.  
  4249.  
  4250.  
  4251.  
  4252.  
  4253.  
  4254.  
  4255.  
  4256.  
  4257.  
  4258.  
  4259.  
  4260.  
  4261.  
  4262.  
  4263.  
  4264.  
  4265.  
  4266.  
  4267.  
  4268.  
  4269.  
  4270.  
  4271.  
  4272.  
  4273.  
  4274.  
  4275.  
  4276.  
  4277.  
  4278.  
  4279.  
  4280.  
  4281.  
  4282.  
  4283.  
  4284.  
  4285.  
  4286.  
  4287.  
  4288.  
  4289.  
  4290.  
  4291.  
  4292.  
  4293.  
  4294.  
  4295.  
  4296.  
  4297.  
  4298.  
  4299.                                           - 69 -
  4300.  
  4301.