





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

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

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

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

     CompuServe: 71141,2071 (Mike)

     GEnie: P.DAVIS5

     or you can send paper mail to:

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

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

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

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










                                     LEGAL STUFF


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

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

     - WordPerfect is a registered trademark of WordPerfect Corporation.

     -  Other trademarks mentioned herein  are the property  of their respective
     owners.

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

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

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









                         Table of Contents

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

     Beginner's Column  ............................  6   Dave Campbell

     Creating Windows Text File Editors  ........... 15   Eric Grass

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

     WDASM - Review of a Windows Disassembler  ..... 23   Pete Davis

     Hypertext, Sex and Winhelp  ................... 25   Loewy Ron

     Enhancing the WINSTUB Module  ................. 30   Rodney Brown

     Microsoft Developers Network .................. 33   Dave Campbell

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

     Last Page ....................................  36   Mike Wallace


     Windows Programmer's Journal Staff:

     Publishers ......................... Pete Davis and Mike Wallace
     Editor-in-Chief .................... Pete Davis
     Managing Editor .................... Mike Wallace
     Contributing Editor ................ Dave Campbell
                                          
     Contributing Writer ................ Dave Campbell
     Contributing Writer ................ Eric Grass
     Contributing Writer ................ Loewy Ron
     Contributing Writer ................ Rodney Brown











                                       WPJ.INI
                                    By Pete Davis

          Another  month, another  issue. Wow,  up to  number 5.  I guess  I say
     something  along those  lines  every month,  but  the response  just  keeps
     getting  better  and better  and  I  guess I'm  just  surprised  that we're
     actually getting these things out every month.

          I received  some questions from our readers about some of the articles
     in the magazine.  Their concern was  that we seem  to try to  fit a lot  of
     information into  short  articles  and  that sometimes  it's  not  in-depth
     enough.  Well,  to those  who think  that, let  me  explain. When  we write
     articles for the magazine (and I can only speak for me and Mike), we do try
     to fit the  information into rather short  articles and we don't go  into a
     great deal of depth a lot of the time. The reason for this is that Mike and
     I both work  full-time. On top of that, we do  several other things outside
     of work and the magazine. Writing in-depth  articles means lots of research
     and time. We can't afford that kind  of time, unfortunately. It is my  hope
     that, someday, I'll have more time to spend writing more in-depth articles.
     Until that day,  we can  only offer what  we can.  We try to  give as  much
     information as  is  applicable  and  when  a readers  asks  us  to  clarify
     something, we  try our best to  do that. If  you ever feel like  an article
     needs  clarification, let us  know and we'll  either do  that directly, via
     mail, or we'll do an addendum in a future issue.

          Next subject: Me!  After the last paragraph,  now's the time  to spill
     the  beans about why  things are going  to be changing  a bit for  me. I am
     going  to  be  writing a  book  for  Addison-Wesley  publishing on  Windows
     programming  (NT and  the 32-bit  stuff). Unlike  the writing  I do  in the
     magazine, the  stuff in the book will be  the kind of writing that requires
     months of research and lots  of checking and double-checking (like  I said,
     we don't have the time to do that for the magazine). Anyway, the book is on
     Win32 programming and it will be in the Andrew Schulman Programming Series.
     I have  to say  I'm awfully excited  about the  whole thing and  I'm really
     looking forward to it. I couldn't possibly thank Andrew Schulman enough for
     the  chance he's giving  me. So, that's  what's up with me.  What does this
     mean? Well, for  one thing, all  of my articles  are going  to be on  Win32
     programming  for the next few months. I've got to do this just to help keep
     my mind trained on the topic, if nothing else. Also, I might not be able to
     do quite as much writing for the magazine as I'd like. 

          Also, nothing firm here, but it looks like I might be doing an article
     with Ron Burk of Windows/DOS Developer's Journal. This, like the book, is a
     sort of first for me and I'm really excited about that. I'll give more info
     on this next month, when I know more.

          So, that's  what's going on with  me. After the book  is done, perhaps
     I'll start writing about things other  than Win32, but hopefully, by  then,
     EVERYONE  will be  programming in  Win32. It  could happen!  By the  way, I
     expect each and every one of you to buy my book when it's done.

          We've got an article by a guy named Eric Grass in this issue. I've got

                                        - 4 -








     to talk about how I met Eric  for a minute. Eric writes a shareware Windows
     disassembler called WDASM. It's a  really nice product and I felt  I had to
     review it in this issue. Anyway, when I registered WDASM (registration is a
     mere $10, I felt obligated  to pay $20, because it's worth at least that) I
     sent  in  some  suggestions for  expanding  WDASM.  In  our ensuing  e-mail
     volleys, I  asked Eric if he'd  write an article for  the magazine. Anyway,
     read his article  on writing an editor in Windows and read my review of his
     disassembler  and   then  get  his   disassembler!  You'll  thank   me  for
     recommending it and you'll thank Eric for writing it.

          The last  thing I  want to  talk about is  submissions. The  number of
     article  submissions seems to  be on the  decline. Unless  we start getting
     more articles, this  magazine might have  to become a bi-monthly  affair. I
     know that I won't have as much time as I'd like to do all the work required
     to keep it monthly. Mike, I'm sure, will  do his best to write for it,  but
     unless we get  more submissions, I can't see us staying  at a monthly pace.
     The final decision  will have to be  Mike's since he  is going to be  stuck
     with doing most of the work for the next few months.

          Remember, we don't do this just for us, we do it for you, the readers.
     If  you've felt like  you've gotten something  out of the  magazine, try to
     return the favor and give  a little of your time to help keep  it a monthly
     magazine. I  know a lot of  you want to see  it stay monthly. I  know I do.
     Writing  an article can  take as little  as a few  hours. Most of  you have
     something  you've learned somewhere that  could help a  lot of other people
     out. We're counting on you to share that information.

          Until next month, peace.

     P.S. I'm writing this the day of the release of this  issue to apologize to
     our readers.  Things have been incredibly  hectic lately for  me because of
     the book and now  this possible article  with Ron Burk.  The timing on  the
     magazine was just off a bit. Mike has also  been really busy, so neither of
     us had  the  time this  month  that we'd  normally  like to  put  into  the
     magazine. Please accept our apologies. 









                                        - 5 -







                                  Beginner's Column
                                   By Dave Campbell

     Don't you just hate it  when you look at a ton of Windows Applications, and
     they  all look alike,  and they all do  the same things  in response to the
     same keystrokes, and YOU DON'T HAVE ANY IDEA HOW THEY DID IT?  That's how I
     was about file open dialog boxes. I knew I  could do it, I just didn't want
     to. Yeah, right. So, this month, I am going to give one possible example of
     a way to do  one (the hard way), and along the way, we'll learn some things
     about list boxes.

     First I want to pass along a few  things. The code out of which I  stripped
     the file  open dialog  box  uses the  Borland classes  for  the dialog  box
     displays. I have been trying my best to stay away from any compiler-
     particular code for  my articles.  Consequently, I was  mildly taken  aback
     when my dialog box came up with the Borland background in place. How  could
     this be?  Well...I run a background menu processor  that is in Beta at this
     time,  (waiting on documentation), and it  uses the Borland classes.  Since
     that is all instantiated in memory, when our new Hello program came in, and
     tried using  the Borland  calls,  it all  worked.  Basically I  got  lucky.
     Usually it works the  other way. You forget to  put it in, and then  try to
     call it.

     My  reason for mentioning  all this is:  if you are  in a debug  cycle, and
     going into and out of Windows relatively often, remember that if you hold a
     shift key down during Windows start-up, you won't get your "run=", "load=",
     or "Startup" files. All you'll get  is Windows, and you can then make  sure
     that if you need a DLL, you load it yourself. 

     The second thing is to be sure to read my review of the Microsoft
     Developer's Network  program in this issue. This is like the mother-load of
     Windows help.

     Now on to a file open box. What  I want to do is display a dialog  box that
     has a file spec  in it, for example  "*.EXE", and various areas to  display
     paths, and file names,  and directory names, and a way to  select a file or
     quit. That will just about solve  all the requirements (not counting  drag-
     and -drop). How this is implemented in my other application is: in a dialog
     box is a text box requesting a file  name. If OK is selected, and the  file
     name is NULL, then I pop up the file open box. In  Hello, we already have a
     File Open menu choice, and are going to use it.

     First let me display what this dlg code looks like:

     From Hello.DLG
                   

     FILEOPEN DIALOG 80, 60, 148, 101
     STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION
     CAPTION "Find File"
     FONT 8, "Helv"
     BEGIN

                                        - 6 -







          CONTROL "File name:", -1, "STATIC" 
           SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP, 2, 4, 144, 10
          EDITTEXT IDD_FNAME, 2, 14, 144, 12, ES_AUTOHSCROLL
          CONTROL "Files in", -1, "STATIC" 
           SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP, 2, 30, 38, 12
          CONTROL "", IDD_FPATH, "STATIC" 
           SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP, 48, 30, 98, 12
          LISTBOX IDD_FLIST, 2, 43, 82, 58, WS_TABSTOP | WS_VSCROLL
          CONTROL "Select", IDOK, "BUTTON", 
           BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_GROUP | WS_TABSTOP,    
                                                         90,    52,    56,    14
          CONTROL "Cancel", IDCANCEL, "BUTTON", WS_GROUP, 90, 76, 56, 14

     The  first new item to  us is the  EDITTEXT line. EDITTEXT  creates a child
     window of  the EDIT class which  gives us a place  to allow a  user to type
     some text for us to  capture. The user may move from edit box  to edit box,
     and  get the focus by clicking the mouse inside it, or we as developers can
     place  the user there. In  our example, we will place  the user in the box.
     When the user sees  a flashing prompt in the  text box, it is safe  to type
     and edit the  typing. As with any Windows text box,  the mouse may be used,
     along with  the backspace key to provide editing features. All this for one
     line in the dialog box!! Isn't it simple to program in Windows?

     As with most Windows features,  the parameters are many. The only  one used
     in this example is  ES_AUTOHSCROLL. This does exactly what  it sounds like.
     If the text scrolls beyond the right-hand margin during input, the box will
     scroll with it. The default options are: ES_LEFT | WS_BORDER | WS_TABSTOP.


     LISTBOX
            

     The next new control  is LISTBOX. A listbox is a  child window containing a
     list of character strings, allowing the user to scroll and make selections.
     When a  string is selected with  a mouse click, the  string is highlighted,
     and a message sent to the parent  window. I have chosen to allow a vertical
     scrollbar  on our listbox, and adding it to  the style list is all it takes
     to do that.

     Invoking the dialog  box is the easy part. In place of the message handling
     of last month in response to the File Open menu, we'll do:

     if (DoFileOpenDlg(hInst, hWnd, &of))
        lstrcpy(OutMsg, (char far *)pof->szPathName);

     Notice the  file open dialog  box is embedded in  an if statement.  In this
     circumstance, we  are going  to check  the return value  of the  dialog box
     ourselves, and  if we return  TRUE, we know  we have a  good value  for the
     file, and we display it in the same manner the other data is displayed from
     last month.

     Don't be confused as to the parameters passed in this statement.

                                        - 7 -








     'DoFileOpenDlg ' is  NOT a dialog box  call. This is simply a  launcher for
     the dialog box that we are setting up  with some parameters, and from which
     we are going to get a return value.

     The real dialog  box code inside DoFileOpenDlg looks like  our dialog boxes
     from the last two issues:

     lpfnFileOpenDlgProc  =  MakeProcInstance((FARPROC)FileOpenDlgProc,  hInst);
     iReturn = DialogBox(hInst, "FileOpen", hWnd, lpfnFileOpenDlgProc);
     FreeProcInstance(lpfnFileOpenDlgProc);

     The only difference here is picking up the return value from 'DialogBox'. 


     OFSTRUCT
             

     At this point, however, I need to discuss one of the parameters to
     DoFileOpenDlg, and that is  &of. of is  listed way up  in the beginning  of
     Hello.C as being of type OFSTRUCT. OFSTRUCT is defined as:

     typedef struct tagOFSTRUCT {    /* of */
        BYTE  cBytes;
        BYTE  fFixedDisk;
        UINT  nErrCode;
        BYTE  reserved[4];
        BYTE  szPathName[128];
     } OFSTRUCT;

     This  structure is  the result  of opening  a file.  As you  can  see, this
     structure tells us  if the file is  on a hard disk,  if there was an  error
     opening the file, and the complete pathspec for the file.


     DoFileOpenDlg
                  

     Inside DoFileOpenDlg, a few global variables are set:

     pof = pofIn;
     lstrcpy(szFileSpec, "*.EXE");
     lstrcpy(szDefExt,   "EXE");
     wFileAttr = DDL_DRIVES | DDL_DIRECTORY;

     1) 'pof' is defined above with of as an LPOFSTRUCT 
                                             (or Long Pointer  to an  OFSTRUCT).
     2) *.EXE is picked as the default filespec (to begin with).  
     3) the default file extension of EXE is set.  
     4) the global file attribute variable, wFileAttr is set to 
        DDL_DRIVES | DDL_DIRECTORY  which declares that we are interested  in   
     files that are drive or directory names (to begin with).


                                        - 8 -








     Windows message Loop
                         

     With all that out of the way, let's start with the WM_INITDIALOG case:

        case WM_INITDIALOG :
           SendDlgItemMessage(hDlg, IDD_FNAME, EM_LIMITTEXT, 80, 0L);
           DlgDirList(hDlg, szFileSpec, IDD_FLIST, IDD_FPATH,  wFileAttr);      
     SetDlgItemText(hDlg, IDD_FNAME, szFileSpec);
           return TRUE;

     Remember,  this is the  first thing that  happens to the  dialog box. Right
     away we jump  into three commands  that haven't been discussed  before. Two
     are related, and I'll discuss them first:

        SendDlgItemMessage(hDlg, IDD_FNAME, EM_LIMITTEXT, 80, 0L);

     SendDlgItemMessage  sends a message to a  specific control in a dialog box,
     identified by, in this case, the IDD_FNAME control ID. 
     SendDlgItemMessage does not return until the message has been
     processed. 'EM_LIMITTEXT, 80' sets  the control up for  receiving up to  80
     characters of text, and the 0L is an empty lParam value for the
     call. The bottom line is that this call did one thing: it set the
     length of the text box to be 80 characters.

        SetDlgItemText(hDlg, IDD_FNAME, szFileSpec);

     SetDlgItemText sets the  title or text of a control in a dialog box. In our
     case, it is the same control as the last call, and this time we send it the
     "*.EXE" default file spec from above.

     Now the fun one:

        DlgDirList(hDlg, szFileSpec, IDD_FLIST, IDD_FPATH, wFileAttr);

     DlgDirList  fills a list  box with a  file or directory  listing. What gets
     displayed is controlled by the parameters passed in: szFileSpec and
     wFileAttr, both discussed above. IDD_FLIST is the ID of the listbox control
     itself,  and IDD_FPATH is the  ID of a static control  that will be used by
     Windows to  display the  current  drive and  directory. If  this  is 0,  no
     display will be done.

     If the filespec is a directory name  (or a null string), the string will be
     changed to "*.*". After the box is filled, the filespec  is stripped of any
     drive or path information.

     At  this point,  the dialog  box is  up, and  the listbox  is full  of file
     listings, and the user can move around in there just like any other Windows
     file open box. 


     Mouse Messages

                                        - 9 -








                   

     This brings us to  the fun part of the  dialog box code. Of course  we want
     OUR dialog box  to be just  like everyone elses,  so double clicks  select,
     etc. But then we have to write that code! So here goes(taken liberally from
     Charles Petzold's Great book "Programming Windows"):

        case WM_COMMAND :
           switch (wParam)
              {
              case IDD_FLIST :
                 switch (HIWORD(Lparam))
                    {
                    case LBN_SELCHANGE :
                       ----code below----
                       return TRUE;

                    case LBN_DBLCLK :
                       ----code below----
                       return TRUE;
                    }
                 break;

     WM_COMMAND  is  the  workhorse message  of  Windows.  A  window receives  a
     WM_COMMAND  just about anytime the user does something. If the user selects
     a menu item, or a child control sends a note back to 'mom'. In our case, we
     are  concerned about  messages coming  from the  list box,  so we  look for
     IDD_FLIST, our ID for the listbox.

     If we receive a message from IDD_FLIST, we are likely to get two things: 1)
     we select an item by single-clicking it with the mouse, or 2) we
     double-click  something to  not only  select it,  but SELECT  it  (in other
     words, do something with it now,  I only have enough time to deal  with the
     mouse).

     This kind of information is passed to the window  in the high-order word of
     Lparam. This is officially called 'wNotifyCode'.  If the message is from  a
     control, this  will be a  Windows-type message. If  the message is  from an
     accelerator, wNotifyCode is 1, and if from a menu, it is 0.

     This is why  there is a secondary switch case inside  the outer one. Now we
     have to deal with what is going on with the listbox. Consequently, we  have
     our two messages: 1) LBN_SELCHANGE, and 2) LBN_DBLCLK. 


     LBN_SELCHANGE
                  

        if (DlgDirSelect(hDlg, szFileName, IDD_FLIST))
           lstrcat(szFileName, szFileSpec);
        SetDlgItemText(Hdlg, IDD_FNAME, szFileName);


                                        - 10 -








     DlgDirSelect assumes that the listbox  identified with the ID (in our  case
     IDD_FLIST), has  been filled with a DlgDirList  function (good thing we did
     that), and returns us the selection currently selected. The second
     parameter should be  a pointer to  a 128-byte character  string (to be  fer
     sure, fer sure). By the way, I will change this to 128 bytes before anybody
     else sees it, now that I have looked that one up.

     The  function returns  non-zero  if successful,  zero  otherwise. And,  the
     string stuffed into, in our case szFileName, will be a drive letter, or a
     directory name, with any brackets, etc. removed. The function appends a ':'
     to a drive letter and a '\' to a subdirectory name.

     So,  when we start out,  szFileSpec is *.EXE,   and if we  select [-c-] for
     example, in this loop we concat c: with *.EXE, and send C:*.EXE to
     IDD_FNAME with the SetDltItemText call that is next. Notice this doesn't do
     anything  more than save the names in  the global strings, and display them
     in the text box. However:


     LBN_DBLCLK
               

         if (DlgDirSelect(hDlg, szFileName, IDD_FLIST))
            {
            lstrcat(szFileName, szFileSpec);
            DlgDirList(hDlg, szFileName, IDD_FLIST, IDD_FPATH, wFileAttr);      
     SetDlgItemText(hDlg, IDD_FNAME, szFileSpec);
            }
         else
            {
            SetDlgItemText(hDlg, IDD_FNAME, szFileName);
            SendMessage(hDlg, WM_COMMAND, IDOK, 0L);
            }

     The first if message is identical. If we get a good return from
     DlgDirSelect,  concat the  name,  and then...oh  yeah,  now we  need  to do
     something,  since we double clicked!!  Ok, so what would we  like it to do?
     Most  of us  would agree  that leaving  the szFileSpec  alone,   *.EXE, and
     changing  to the drive  or directory, in  addition to changing  the text in
     IDD_FNAME would be nice. 

     So that's what we  do. The second half is  identical to what we did  in the
     LBN_SELCHANGE  message, just display, but the first  part is handled by re-
     executing  the DlgDirList  call,  only passing  it  the new  szFileName  we
     concatenated.

     This is  all well and  good if we  double click on  a directory or  a drive
     letter, but what if we double click a file name? That's what the  else case
     is for. Remember above I said that a good  return from DlgDirSelect gave us
     a drive or subdirectory name? Well, a FALSE return gives us a filename. So,
     if we  take the 'else'  clause, for FALSE, we  know that szFileName  is the
     filename that the user double-clicked, and we stuff that into the IDD_FNAME

                                        - 11 -








     box, followed by sending OURSELVES (!!) an IDOK message.


     IDD_FNAME
              

     IDD_FNAME is also user-modifiable, so if the user types something in there,
     we want to know what is going on, so we handle calls originated from there:

        if (HIWORD(lParam) == EN_CHANGE)
           EnableWindow(GetDlgItem(hDlg, IDOK),
             (BOOL) SendMessage(LOWORD(lParam), WM_GETTEXTLENGTH, 0, 0L));

     Remember  above  in talking  about messages  from  child controls,  how the
     message is passed in the high-order word of lParam?, well here we go again.
     And, this time we are looking for a changed text box, or EN_CHANGE. If this
     is the case, Windows has already updated the text in the box.

     The EnableWindow line  is a combination of three  windows functions, and as
     such is extremely messy. The EnableWindow call really looks like:

        EnableWindow(hwnd, fEnable), 

     where  hwnd is the handle of the window to be enabled or disabled according
     to the Boolean equivalence of fEnable (ie, TRUE/FALSE).

     The window  we want to enable/disable  is the one whose  handle is returned
     from  GetDlgItem(hDlg,  IDOK). hDlg  is the  handle to  our dialog  box, so
     that's easy, but  how about the IDOK? Go back to  the .DLG file and look at
     what  happens if the  user clicks the  button "Select". The  button returns
     IDOK. It works  the same  as an  OK button, but  has Select  written on  it
     instead. That is the window whose handle we are going after for the
     EnableWindow call.

     Now, what are we going to do with it? 

        SendMessage(LOWORD(lParam), WM_GETTEXTLENGTH, 0, 0L)

     SendMessage is going  to send  the command WM_GETTEXTLENGTH  to the  window
     whose handle is  the LOWORD of  lParam. The high  word was discussed  above
     (twice) as the  message sent,  the low word  is the handle  of the  control
     sending the message. Therefore, SendMessage is going to send a
     WM_GETTEXTLENGTH command to the IDD_FNAME box which will return the length,
     in bytes of the text in IDD_FNAME. 

     Whew...let's see now: if we square the result, oops wrong article.

     But, it's almost that  complicated. The bottom line is we are going to send
     an EnableWindow message to the Select  button only if there is text  in the
     IDD_FNAME  box after it has been modified. This will end up working its way
     back to us as an IDOK, and will select the filename. 


                                        - 12 -








     Now wasn't that fun?


     IDOK
         

     The IDOK message handler does the following:

        1) Gets the filename from IDD_FNAME. We put it there, remember?
        2) If szFileName ends in ':' or '\', append szFileSpec to  
           szFileName.
        3) If szFileName ends in a wildcard, execute DlgDirList with the new    
      szFileName, and clean up the displays appropriately:

           a) copy szFileName to szFileSpec (since it had a wildcard anyway)    
      b) write the new szFileSpec into IDD_FNAME
           c) if DlgDirList fails, beep to the user
           d) exit the message loop

        4) If szFileName does not end in a wild-card, concat '\szFileSpec' onto 
         szFileName, and re-execute DlgDirList with this new filename.

        5) If DlgDirList finishes successfully, displays are updated, and we    
       exit the message loop

        6) If DlgDirList does not finish successfully, it  means that the       
     szFileName did  not contain a  drive\directory structure. So,  it is       
     probably a file name. This  is our back-door, so to speak, to get       the
     file name.

        7) If we've come this far, we assume we have a full file-spec on our    
      hands.

        8) We take two tries at opening the file with OF_EXIST set. This will   
       open and close  the file just to  check for existence of  the file.      
     The OF_READ  for read-only was thrown in  as insurance. The first try      
     is with the  name we have  already. If that  fails, we try  with the       
     default file extension on it, just in case. If both  fail, we beep       to
     the user and exit.

        9) If we are successful  in opening and closing the file, we have       
     finished what we set out to do, and exit the dialog box. The 
           filename is copied from the OFSTRUCT way back up where the 
           adventure started with File/Open, and at the bottom of that loop,    
      we print  execute a MessageBox call  with the filename as  the child      
     window text.

     I did  gloss over one  of Petzold's  routines, and neglected  to mention  a
     couple of other things.  Petzold used an internal routine  called 'lstrchr'
     to find the existence of a character in a string. That's how we decided  if
     there was a wild-card in the filename. He also used lstrcat and 
     lstrcpy to let Windows handle the far pointer manipulation without 

                                        - 13 -








     messing up.

     I know I've gone over the last part pretty quickly, but I tried to cover it
     completely.  It's mostly  'C' code,  and  doesn't have  a  lot of  Windows-
     particular stuff  in it. I  think I'd rather  spend my time  explaining the
     hairy Windows parts than mess so much with the 'C' parts.


     EndDialog
              

     That's it for now.  As I have said, Please hang in there. If you are beyond
     the scope of this article, stick with me we are going to go places 
     together. If  you are  way beyond  this, write  us an article.  If you  are
     bogged down, just  compile it, and  stare at the source.  If you want  help
     with something, send me a note.

     Next month, unless I get mail not in  favor of it, we're going to put  this
     month's code into a  DLL so we can use it later. Feel free to contact me in
     any  of the  ways below.   Thanks  to those  of you  that have  e-mailed me
     questions, keep them coming.   Notice that I now have an  internet address,
     to make it real easy to get in touch  with me. I want to rat out the things
     other people are having questions about,  not just what I think people want
     to hear.

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























                                        - 14 -








           Programming Techniques For Creating Windows Text File Editors  
                                   by Eric Grass  
       
          This article  discusses advanced  programming techniques for  creating
     text editors under Windows.  The  Edit window class will not be implemented
     in order to focus on how to create more advanced types of editors.  We will
     thus be creating a text editor "from scratch", so to speak.  As an example,
     the source  code for a simple  text editor named EditPro  has been included
     with this  issue of WPJ.   EditPro is a  smaller version of  another editor
     named KeyPro which I wrote recently. 

          EditPro  simply loads and displays files using the Windows system font
     and does paging.  This article covers  these aspects of  file editing,  and
     then offers some tips on using the system caret. 

          The fundamental  portions of the program (i.e.,  the WinMain function,
     etc.) were created using QuickCase:W for Windows.  The source code contains
     some  in-line  assembly language  code in  order  to improve  the program's
     execution time in certain places where it is appropriate.  

          I compiled  EditPro using QuickC for  Windows. You may or  may not get
     the following warnings when you compile:  
       
          qcw /AS /G2w /Zp /W3 /D_WINDOWS /Gi /Od /Zi -f A:\EDITPRO.C 
          A:\EDITPRO.C(190) : warning C4059: segment lost in conversion
          A:\EDITPRO.C(191) : warning C4059: segment lost in conversion
          A:\EDITPRO.C(195) : warning C4059: segment lost in conversion
          A:\EDITPRO.C(198) : warning C4059: segment lost in conversion
          A:\EDITPRO.C(200) : warning C4059: segment lost in conversion
          A:\EDITPRO.C(208) : warning C4059: segment lost in conversion
          A:\EDITPRO.C(254) : warning C4059: segment lost in conversion
          linkw /ST:5120 /A:16 /CO @$$QCW$$.CRF
          rcw -t EDITPRO.RES EDITPRO.EXE
          cvpackw EDITPRO.EXE
          EDITPRO.EXE - 0 error(s), 7 warning(s)

     These  warnings can be ignored.  This  occurs because the code extracts the
     offset from global  memory pointers.  I  tried to figure  out a way to  use
     casting to avoid these warnings, but I couldn't.  
          The source code includes some helper routines (or, file routines) in a
     file named filerout.c.  The procedure FileRoutine() merely invokes the Open
     dialog box  and retrieves a  file path name.   The second  two routines are
     used to merely display message boxes.  
       
     Storing Text  
       
          The  most important step in  designing a file  editor is designing the
     data structure  for  storing the  lines  of text  of  the file  in  memory.
     EditPro uses the following Line structure to store a single line of
     text:
                _  
               |                          ...........22222222222222  

                                        - 15 -








     OFFSET:------|           111111111122...........44444444555555  
               |_   0123456789012345678901...........23456789012345  
               \/\/|\________________________________________/  
                \ \ \      \  
                 \ \ \      \__Text  
                  \ \ \________(BYTE) total number of bytes in line structure  
                   \ \_________(WORD) memory handle of next line  
                    \__________(WORD) memory handle of previous line  
       
       
          For each  line we allocate between  5 and 256 bytes  of global memory.
     If a  line contains just  N characters, then we  only have to  allocate N+5
     bytes.  Allocating more memory than this would be a waste  of memory. Thus,
     this type of structure allows at  most 251 characters per line.  The  first
     two bytes, bytes  0 and  1, store the  memory handle to  the previous  line
     proceeding the current line.  The next two bytes, bytes 2  and 3, store the
     memory  handle to the next  line following the current  line.  Thus, we may
     construct  a linked list of  lines.  The byte at  offset 4 stores the total
     number of bytes  constituting the memory block minus  1, and must therefore
     be  a value between 4  and 255.   Finally, bytes 5 through  255 are used to
     store the actual text  constituting the line.   The primary motivation  for
     selecting the  value 256 as the  maximum size of  the Line Structure  is so
     that we can  store the size  of the structure  (at byte  4) using just  one
     byte.  

          The  file loading procedure can be expressed in pseudocode as follows:


          .  
          .  
          .  
          Open the specified file;  
          if( size of file < MAXFILEBUFF)  
               Allocate temporary file buffer of size = size of file;  
          else  
               Allocate a temporary file buffer of size = MAXFILEBUFF;  
          Allocate and Lock a new Line Structure of size = 256 bytes;  
          Set Line Structure pointer to 0;  
          while( End of File not reached)  
          {    Read portion of file into temporary file buffer;  
               Set file buffer pointer to 0;  
               while( Not at the end of the temporary file buffer)  
               {    if( file buffer pointer points to Newline character)  
                    {    Allocate and Lock new Line Structure of size 256 bytes;


                         Store the handle of  the previous Line in the  new Line
                         Structure;  

                         Reallocate the  size of the previous  Line Structure to
                         N+5 bytes;  


                                        - 16 -








                         Store  the  value  N+4 at  offset  4  in previous  Line
                         Structure;  

                         Unlock the previous Line Structure;  
                    }  
                    else  
                    {    Copy the character pointed to by file buffer pointer to
                         the Line Buffer;  

                         Increment the  Line Buffer Pointer and  the file buffer
                         pointer;  
                    }  
               }  
          }  
          Reallocate the size of the new Line structure to N+5 bytes;  
          Store the size of  the new Line Structure (i.e.,  N+4) at offset 4  in
          Line Structure;  
          Unlock the new Line Structure;  
          .  
          .  
          .  
       
       
          The  corresponding source code is located in the file named editpro.c.
     For EditPro, the value of MAXFILEBUFF is 0x00003FFFL bytes.  This value can
     be made  larger, if desired, to  reduce the amount of  disk reads required.
     If you look  at the source code, you'll notice that when line number 32,767
     (7FFFH) is reached, EditPro displays a message notifying that the file will
     be truncated at that point, because of Windows' scroll bar  
     range limit of 32,767. Also,  when the user exits EditPro or  selects Close
     from the  menu, we must  free the  memory associated with  all of the  Line
     structures.
       
     Painting the Screen  
       
          Painting the screen involves drawing only a portion of the text in the
     window.   Which portion is a drawn  depends on the line  which the user has
     scrolled  to and  the  invalidated rectangle  specified by  the PAINTSTRUCT
     structure that is filled out  by the BeginPaint() function.  To  output the
     appropriate text  one  can  use either  the  TextOut()  or  TabbedTextOut()
     function.  EditPro uses  the TabbedTextOut() function since it  must expand
     the  tabs in  the file.  One thing  to  remember about  the TabbedTextOut()
     function is  that it uses  up a  lot of CPU  cycles whenever it  is called.
     Therefore,  you'll want  to call  this function  as few  times as  possible
     whenever you're painting/repainting the screen.  To keep  track of the line
     to which the  user has scrolled,  we use a  global variable,  hCurrentLine,
     which holds the  memory handle of the Line  Structure of the line  to which
     the user has scrolled.  This value is  set to the handle of the very  first
     line when the file is first opened.  

          The  variable xoffset holds a non-positive value between 0 and -32,767
     which determines the  text's displacement relative to the  x-axis.  This is

                                        - 17 -








     used  in case the window has been  horizontally scrolled by the user toward
     the right.   xoffset is changed  (decreased) whenever the user  scrolls the
     screen horizontally, and the window must be updated accordingly. 

          You'll notice  that the  text is  displayed in  the system  font. This
     choice is purely arbitrary and can actually be any font that you want.  
       
     Paging The Screen  
       
          To page up/down, whenever  the user clicks the vertical scroll bar, we
     merely  "chase pointers"  either  backwards or  forwards  from our  current
     position in the file until we reach the  line that is at the bottom of  the
     screen or at the top of the screen.  We use the client rectangle to help us
     find  the  desired line.    Every  time we  chase  a  pointer  to the  next
     consecutive line,  we  simply add  the  text height  of  that line  to  our
     variable, CurrentLine, which initially is set to zero.  When CurrentLine is
     greater  than the client rectangle's  top/bottom, then we  know we've found
     the right line.  

          For  horizontal  paging,  we  merely  change  increment/decrement  the
     xoffset variable and scroll the client window in the proper direction.  
       
     Tips On Using The Caret  
       
          The  topics covered thus far provide a minimum foundation for creating
     editors. The  resulting editor  merely loads and  displays files.   What is
     missing is the implemention of  the system caret into the program  and some
     basic editing operations, which are topics I cannot cover in  depth in this
     article due to  a limited amount of time. However, just  to get you started
     on using the caret, here's a few tips:  
       
          - The caret  is a shared resource. You must  create the caret whenever
          you're window receives the input focus, and destroy the caret whenever
          your  window loses input focus  (which is signaled  by the WM_SETFOCUS
          and WM_KILLFOCUS messages).  

          -  It is  important to  hide  the caret  before  erasing the  window's
          background. The WM_ERASEBKGND message signals when the background must
          be repainted. By  default (via the  DefWindowProc() function)  Windows
          paints the  background. If you  don't do  this, what  happens is  that
          occasionally the caret will leave an "imprint" on the window. That is,
          you'll  have two  carets appearing on  the screen, except  only one of
          them will actually be the caret and the other will be just an image of
          the caret.  

          The following code is  an example of how to hide  the caret before the
     background is repainted:  
       
               switch( wParam)  
               {   .  
                   .  
                   .  

                                        - 18 -








                   case WM_ERASEBKGND:  
                    // if window has input focus, hide the caret before
                    // erasing...  
                    if( hWnd == GetFocus())  
                    {    HideCaret( hWnd);  
                         dwTemp    =   DefWindowProc(hWnd,    Message,   wParam,
                                             lParam);  
                         ShowCaret( hWnd);  
                         return dwTemp;  
                    }  
                    else goto DEFAULTACTION;  
                   case WM_PAINT:  
                    .  
                    .  
                    .  
                   default:  
          DEFAULTACTION:   
     /* For  any message  for which you  don't specifically  provide a   */   /*
     service  routine, you should  return the message  to Windows    */   /* for
     default message processing.                             */              
     return DefWindowProc(hWnd, Message, wParam, lParam);  
               }  
               return 0L;  
       
          -  The  GetTextExtent() function  must be  used  to calculate  the new
          position of the caret whenever the user moves the caret.  
          - It's important to save the position of the caret before losing input
          focus  in  order  to know  where  to  display  the  caret again  after
          regaining input focus.  

       
     Closing Remarks  
       
          If time  permits,  I may  write another  article in  the future  which
     covers the caret and the implementation of basic editing operations.  
       
       
       















                                        - 19 -








                            Midlife Crisis: Windows at 32
                                    By Pete Davis

          I decided I  needed to back-track this month. Last month's article was
     done out of a sheer need for  me to know about threads and  synchronization
     without much thought given to you, the reader. I apologize. I'm going to do
     my best  to start from  the beginning on this  Win32 stuff and  explain the
     different  areas of Win32 programming. This month, in particular, I'm going
     to discuss  the differences between  the different environments,  Win32 for
     Windows 3.1, 'Chicago', and NT). This  difference is particularly important
     right  now as far as  the difference between NT and  Windows 3.1 when doing
     Win32 programming.

          One of  the  key  differences as  far  as environment  is  that  Win32
     applications  running  under  Windows  3.1 share  memory  with  Windows 3.1
     applications  and Win32  applications and  Win32 applications  share memory
     with other Win32 applications. This is from a virtual point of view, if you
     get  my meaning,  and it's  just what  we expect  from regular  Windows 3.1
     programs.  The difference is with  NT and later,  'Chicago'. Win32 programs
     running under  NT each have their  own virtual 4 GB  (that's gigabytes, for
     those of you not used to seeing  numbers of that magnitude) of RAM to  work
     in.  That means  that Program  Manager has  its own  4 GB  of RAM  and File
     Manager has its own 4 GB of RAM, even when they're both running at the same
     time. Actually, when I  say 4 GB, you're really  limited to a mere 2  GB of
     that memory.  The other  2 GB belongs  to the Kernel.  Also, keep  in mind,
     that's 'Virtual' memory,  not real memory. You're still limited by how much
     you have in your  machine and how big your swap file is. Under NT, you have
     only one swap file and its the permanent swap file.

          Anyway,  I  digress, the  point  is  that  without some  sort  of  IPC
     (interprocess  communication,  which  we'll  get  into  in  a  second),  NT
     applications wouldn't be  able to  see each other's  memory. This makes  it
     slightly  more difficult to share  memory between applications  but it pays
     off  in that one  process can't  damage another  process' memory  and cause
     everything to fall  to pieces. Instead, under NT, the  one process can hurt
     itself and die, but everything else goes on unaffected. 

          When  designing Win32  applications under  Windows 3.1,  however, it's
     important to keep in mind that the applications CAN wipe each other out. If
     you  want your applications to run under  both environments, you need to be
     sure to keep this in mind.  Under Windows 3.1, Win32 applications, like 16-
     bit  applications, share  the  same memory  with  all the  other  processes
     running. This can be  a problem when porting  Win32 applications to  Win32s
     that make assumptions about the memory.

          Well,  that's  enough of  that,  I think  you  get the  picture. Under
     Windows  3.1, 32-bit  apps share  the same memory,  under Windows  NT, they
     don't. Clear?

          Ok, so what if you want to share memory? Well, DDE is still there, but
     I wouldn't recommend it  for most situations. It's just too  much of a pain
     for most applications. The old method of using GlobalAlloc and then passing

                                        - 20 -








     the  handle  in  lParam  of  a  message  and the  other  application  using
     GlobalLock to access it no longer works because that 'Global'  memory isn't
     so  global anymore, it's not shared between applications (except in Win32s,
     but  we don't  want to  make that  assumption, right?).  In the  Windows NT
     environment, there are  memory-mapped files. These are  an excellent method
     of IPC  and the good news  is that Microsoft is working  on implementing it
     for Win32s  (that's 32-bit Windows  for Windows  3.1, as  opposed to  Win32
     which is  for NT  and Win32c for  'Chicago'). This  means there  will be  a
     simple, common method for sharing data between applications.

          I won't  go too in-depth into  it this month, but  memory-mapped files
     are essentially files that are treated as if they're regular memory, except
     a sort of temp file is created to  hold the data. The way it works is  that
     you have a file that you treat as if it's memory. You simply have a pointer
     that you can point  and move anywhere in the  file as if it were  a regular
     pointer to memory  and Win32 picks  up the tab on  the file I/O  work. It's
     basically just another type of virtual memory. All the other program has to
     know  about  is the  file mapping  object name.  There  are other  uses for
     memory-mapped files which I'll cover in a later column, likely one  devoted
     to memory-mapped files.

     Preemptive Multitasking vs. Non-Preemptive Multitasking

          Windows  NT  (and Windows  4.0,  when it  gets  here) is  a preemptive
     multitasking  operating   system.  You've  probably  seen  those  buzzwords
     mentioned in every review or discussion of NT. What does this mean exactly?
     Preemptive multitasking is where the CPU decides how much time your program
     has to run. After  that time is up,  your program is stopped,  the register
     contents  are saved and another program  is given control. And then another
     and  then another until it  gets back to the  first. Then its registers are
     reset and it is sent on its way for its particular time slice.

          Windows 3.x  use  a  non-preemptive  multitasking  system  where  each
     program is responsible for giving the other programs in the  system time to
     run. This  cooperation takes place in the  message queue where messages are
     passed around the system from one program to another until it finds its way
     to the program that needs it. In Windows 3.x it's simple to write a program
     that doesn't  release the CPU  to other  applications. Just try  writing an
     infinite loop and you've locked up the entire system.

          Under Windows  NT, the  message queue is  still used,  but the  system
     doesn't rely on  it to provide CPU time to other  programs. If your program
     has an  infinite loop,  then your  program will  lock up.  The rest of  the
     system will continue running with no effect from your program.

          I  should mention that Windows  3.x is not  an entirely non-preemptive
     multitasking  system.  There  are a  couple  parts  that  are, or  can  be,
     preemptive. The  DOS box, for example, is preempted by Windows. The DOS box
     receives its time slice to run  and that's it. Then Windows gets  some time
     for a while  and then it gives  another time slice to the  DOS box. Another
     place than can  be preemptive is VxDs  or .386 drivers. The  reason is that
     these  run at what is called  Ring 0. Ring 0 is  where the low-level system

                                        - 21 -








     control stuff takes  place in 386,  486 and now  Pentium chips. (There  are
     four  rings,  0-3. Rings  0  and 3  are  the  only ones  used  by Windows).
     Actually,  this  area  isn't  exactly  preemptively  multi-tasked  but   is
     available for it. The software running at Ring 0 has  complete control over
     the system, so it can really do whatever it wants.

          That's all for this month, I'm afraid. I'm really sorry if my articles
     seem a bit anemic for the next  few months. I'm afraid I have little choice
     in that. We'll see how much time I actually have over  the next few months,
     but this book is really going to take a lot of work.











































                                        - 22 -








                                        WDASM
                           Review of a Windows Disassembler
                                    By Pete Davis

          WDASM is the only Windows disassembler  I know of that's available for
     shareware. The product is amazing and well worth it's mere $10 registration
     fee. The  version I  have  been using  is 1.2,  which  is the  unregistered
     version. I am awaiting version 1.6 which is on it's way.

          WDASM is available from many sources,  including the CompuServe WINSDK
     forum, WSMR-SIMTEL20.ARMY.MIL in the PD:<MSDOS.WINDOWS> directory, and from
     FTP.CICA.INDIANA.EDU in the pub/pc/win3/programr directory. 

          When you run  WDASM you get a window with a small and simple menu. The
     menu structure is:

     File          Edit         View            Help
     ----          ----         ----            ----
     Open          Set Bytes    Segment         Index
     Save Text As               Goto            About
     ----                       Address Offset
     Exit                       Far Call Names


          WDASM will  disassemble both .EXEs and  .DLLs. As soon as  you open an
     executable,  the WDASM  window shows  the disassembly  of Segment 1  of the
     executable. To change  to different segments, simply go to  View and select
     the segment you want. 

          Set Bytes  allows you to set  a range of bytes  to data, instructions,
     labeled instructions.

          Goto, of course, takes you  to a certain address. Address Offset  is a
     nice  feature. Normally, the only address offsets  that show up in the code
     are the labels. All labels are in the form:

                          LxxxxH: where xxxx is the offset.

          Far  call names  toggles between  labeling far  calls and  showing the
     actual address of the call.

          When you're  done  you can  use  Save Text  As  to save  the  assembly
     language code generated by WDASM to a .ASM file.

          There are some things  I'd like to  see in WDASM  and, in fact,  after
     talking  to Eric,  it appears  some of  them are  there (in  the registered
     version).  These include  some minor  bug fixes and  some expansion  of the
     program. I'd like to see a little commenting in the code about what's going
     on. Sometimes it's a simple operation to find out  what some code is doing.
     It would be nice if the disassembler did some of those for you.

          Also, I'd like to be able to do some editing in WDASM. For example, if

                                        - 23 -








     I figure out what a certain routine does, I'd  like to name it in WDASM and
     have it go through and correct all references to that label. 

          I'd like support  for the PE file  structure, which, actually,  when I
     suggested it to Eric, he was more than happy  to get some information on PE
     file structures from me and plans to upgrade the software to handle them.

          Other upgrades  which I'm aware  of that are  in the works  (or done?)
     include support  for 386/486  instructions and  support for  floating point
     instructions.

          I have  a wish list of things  to be added and I  have passed a lot of
     them to Eric. This is where Eric's product sticks out the most. I've talked
     to other shareware authors  about improvements in their products.  Few have
     been as receptive to new ideas as  Eric. Eric does his product for the end-
     user, not himself. That's really not as common as you might think. A lot of
     shareware authors  ask for your  comments and when  you send them  in, just
     ignore them. Eric really wants  to do a product that will be useful for his
     users. He's already done that. 

          I  can't recommend  this  program highly  enough.  If you  do  Windows
     disassembly,  consider  this program.  For  the  price,  it's  an  absolute
     bargain. And if you like  it, PLEASE REGISTER it.  It's only $10, which  is
     inexpensive, even for shareware. It's worth every penny.

          Happy disassembling.

     [Ed. Note: Just prior to  the release of this issue I  received Version 1.6
     of the disassembler. Not  enough time to really use it a  lot or to rewrite
     the  review. I will only  say that 1.6 has  some major improvements. I also
     know Eric is in the process of  making more. Get this disassembler and  try
     it out.]





















                                        - 24 -








                              Hypertext, Sex and Winhelp
                                     By Loewy Ron

          Hi, this article  is about hypertext, help systems and winhelp. If you
     ask yourself what has this kind of article to do in a programming
     magazine,  and  tell yourself  (and  everyone around  you) that  you  are a
     programmer and not a technical writer, I can only tell you of a
     sentence I heard at  a recent conference I attended, from a  Rep. of a firm
     that markets a database development tool. The guy told us that
     in user surveys they learned that people that evaluated their
     product, gave the  quality of the documentation the same  weight they  gave
     to the functionality of the product. (Hearing this stuff, and 
     knowing I'm not as good as I should be at writing documentation, I 
     decided  I had to stop  while I'm at the  top - so this  is the end of this
     article, bye. Well,  no - this was  just a joke, we will  continue from now
     ..).

          An important part  of your application's documentation  is the on-line
     help. One of the advantages of programming for Windows, is that
     Windows includes a built-in help engine, with a (well?) documented
     interface. This engine is both easy for the users to use, standard
     among applications, and yet is powerful and extendible.

          This article  will talk a bit about  hypertext systems in general, and
     will try to focus on the Windows implementation.

          Hypertext is a term used to describe large amounts of textual data,
     that have embedded references to other textual elements of the
     database. These other  elements have  some connection to  the text  element
     that is currently read by the user, and might interest him. 
     The difference of hypertext systems from traditional documentation is  that
     reading a hypertext document, the  reader (user) can read in a non   linear
     order,  and  jump around  the  database  according  to  his will  (and  the
     references that were created in the document by the system's
     designer).

          We usually define  a piece of textual information that can be regarded
     as a single unit - a Topic. Our help "database" is a collection of
     Topics, that most (if not all) of them, have references (called
     Links) to other Topics, that have some connection to them.

          You have probably used some hypertext systems, such as the on-line
     help that came with your development package/language/compiler. (if
     you still use GW-BASIC as your main development tool - you are
     advised to upgrade, or you might find some of the other articles in
     this journal hard to follow).

          In order to incorporate help (using winhelp) into your application,
     you (roughly) have to go through the following steps :

     1. Design the layout of your hypertext help database.


                                        - 25 -








     2. Create the winhelp source files, and compile them to create the
     .HLP file(s) for use with the winhelp application.

     3. Create a help  menu/button/toolbar or other interface element  from your
     application to the winhelp database, and code the winhelp API
     calls to activate that database from your application.

     4. Optionally - If you want to create context-sensitive help (help
     that is activated when a user presses the F1 key for example, and
     receives help about the operation/options that he is currently
     doing/having), code the key interception and calls to the winhelp
     database.

          Step 1 is really something that should be left to people that know
     more about user interfaces, usability studies and these sort of
     things than me. I have included some references that might be of
     interest at the end of this article.

          Steps 3 and 4 will be discussed in a future article (If the editor
     will want me to write any more after some angry readers will e-mail
     him some threats, or knock on his door at the middle of the night).

          In order to  create .HLP files - that contain  your help database, you
     have  to create  source  files for  Microsoft's  help compiler.  There  are
     actually 2 help compilers - HC30.EXE that creates help topics in
     Windows 3.0 format,  and HC31.EXE that creates  help topics in  Windows 3.1
     format.  While  HC31 help  databases can  be  more sophisticated  than HC30
     created databases, you will not be able to use them on Windows
     3.0 platforms, while winhelp that came with Windows 3.1 can display
     help from .HLP files in both formats.

          The  input to the help compiler is  a project description (.HPJ) file,
     and a set of Rich Text Format (.RTF) files, with special code to
     describe Topics, Links, Search keywords etc..

          RTF is a document description standard that can describe (any?)
     word-processor file using  only 7  bit characters. Unfortunately  RTF is  a
     language that is very hard to read and maintain manually. You would
     probably want to use a  word processor that can output RTF files, or a tool
     that can translate plain readable ascii files into RTF output.
     If you have Word for Windows, several help authoring templates,
     styles and macros are available in the commercial and shareware
     market, that can help in the creation of help databases. At the end
     of this  article you will find a  list of the help  authoring packages I am
     aware of, I have  not tried or used all  of them, I'm just trying  to bring
     them to your attention.

          In the RTF file, use page breaks between Topics. Your Topics can be
     longer than one page, but when you use a page break - you tell the
     Help Compiler that this is the end of the text that belongs to this
     Topic. For each Topic include a # and $ footnotes to describe its
     name, and reference, so that other topics will be able to include

                                        - 26 -








     Links to it. Links are included using a text with double underline
     attribute, followed by a hidden Topic reference name. I will give a
     RTF example that will help you understand the mechanism.

          We will define 2 Topics - TOP1 and TOP2 and create Links between
     them. The following code example is part of a RTF file. 

     {\f2
      #{\footnote \pard\plain \fs20 # TOP1}
      ${\footnote \pard\plain \fs20 $ TOP1}
     }
     \pard{This is the first paragraph of Topic 1, with no Link here}
     \pard{This is the 2nd. paragraph of topic 1, we will include a
     {\f2\uldb Link}{\v\f2 TOP2} here to Topic 2}
     \page
     {\f2
      #{\footnote \pard\plain \fs20 # TOP2}
      ${\footnote \pard\plain \fs20 $ TOP2}
     }
     \pard{This is Topic 2, Click {\f2\uldb Here}{\v\f2 TOP1} to reach
     topic 1 }
     \page

          As can be seen - Each topic starts with a # and $ footnotes, and
     ends with  a page break (\page). Whenever we want  to create a Link, we use
     the \uldb (double underline) attribute with the text we want to
     display, and follow  it with a hidden  (\v) text with the reference  to the
     Topic it is linked to.

          It should be explained at this point - that this code is not enough
     to create a help database that contains only these 2 Topics, it is,
     however, beyond the scope of this article to describe all the RTF
     commands needed  to create a help  document. I have, however,  included the
     source to a simple help database with only 3 Topics, and Links
     between them. You will have to refer to the RTF specifications
     (available from Microsoft, or - if  you have the MSDN CD-ROM, you  can find
     it there), and the help compiler documentation that comes with
     the SDK or with your compiler.

          The next file that should be created for the help compiler is the
     project file. The following code is an example of such a file :

     [OPTIONS]
     INDEX=TOP1
     COMPRESS=TRUE
     TITLE=Demo Help Project

     [FILES]
     DEMOPROJ.RTF

     [MAP]


                                        - 27 -








     [BITMAPS]

          This is a file that have a structure that resembles a .INI file,  with
     sections and options in it. The [OPTIONS] section include general
     options that are used by the help compiler during the .HLP file
     generation.  In  the example  above our  Topic 1  is  defined as  the index
     topic - the one that winhelp will display first when it loads the
     help file, the compress entry tells  the help compiler to compress the help
     text, and the title entry instructs winhelp what to display in
     the  title of the help window. It is important to notice that the .WPJ file
     syntax is  different between HC30 and  HC31. The example above  is good for
     HC30, and in HC31 the index entry should be replaced by a
     contents entry to achieve the same results.

          The [FILES] section lists all  the RTF source files used to  build the
     help database. There are additional sections and entries in section
     that are used to create and set options to the help database. It is
     advised that you refer to the Microsoft SDK documentation, or the
     documentation that came with your compiler for a complete discussion of all
     the features and options of the help project file.

     Summary
     -------

          O.k, this was just an introduction that touched the surface of what
     it means to create help databases for use with winhelp, and this
     should probably get you started. I might add additional articles on
     this subject in the future. Good luck, and have fun helping others.

          If  you wonder about the title of the  article - hey, this is my first
     one, and I covered 2 of the 3 subjects - that is pretty good, don't
     you think? 

     References :
     ------------

     1.  Microsoft Press - Microsoft Windows 3.1 Programming Tools, Chapter    3
     and Appendix B. 

     2. Borland Pascal With Objects 7.0 - Tools and Utilities Guide,
        Chapter 6 and Appendix A.

     3. Windows Help Authoring Guide - A Microsoft publication available
        on the internet as WHAG.ZIP in CICA.

     4. RTF Specifications - I have mine from the Microsoft MSDN CD-ROM.

     Help Authoring Tools :
     ----------------------

          This is a list of tools I know that exist. I have not used all of
     them, nor do I know if they all work. I only included this list in

                                        - 28 -








     order to help you  get a jump start in creating help  databases. All of the
     opinions I express - are mine, and mine only.

     1.  Microsoft  Windows Help Authoring Tools  - a combination of  a help    
     project editor  and winWord templates  and macros.  Available on  the      
     internet in CICA as WHAT.ZIP.

     2.  SYNTAX.ZIP - A tool to convert WP documents to windows help.
         Available in the misc directory of CICA on the internet.

     3.  HELP.ZIP - A tool to convert OS/2 IPF files to RTF documents.
         Available in the nt directory of CICA on the internet.

     4.  HAG.ZIP, WHAG.ZIP - Windows help authoring guide in winword and
         .HLP formats. Available in the programr directory of CICA.

     5.  WFWHLP.ZIP - Info on Constructing Windows Help Files.
         Available on  the program directory  of CICA,  also in CIS,  WINSDK    
     forum, Lib. #16.

     6.   HWAB21.ZIP - Help  Writer's Assistant for Windows  (This is cute).    
     Available on the util directory of CICA.

     7.  QDHELP.ZIP, QDTOOL.ZIP - Use DOS Editors  to Create WinHelp Files.     
     This is a really nice tool. Available on the util directory of
         CICA. Also on CIS WINSDK Forum, Lib 16.

     8.  DRHELPEV.ZIP - Macro to translate Word files to WinHelp files.
         Available on the winword directory of CICA.

     9.  HLPDK30.ZIP, HLPDK40.ZIP - My own Help Development Kit. I'm biased so I
     will not say  any further  word. Available on  SIMTEL hypertext  directory,
     GARBO programming  directory, Compuserve  WINSDK  forum, and  an  undefined
     place (currently in uploads) on CICA.

     10.  Doc-To-Help  - A  commercial tool.  A press  release  can be  found in
     D2HPRS.TXT and D2HNEW.ZIP in library 3 of the WINSDK forum on COMPUSERVE.

     11. HELP.DOT - WFW 2.0 Template for Creating WINHELP RTF Files -
         Library 3, WINSDK Forum, CIS.

     12. RoboHELP - A  Commercial Tool (I have heard  some nice things about    
     this one). HELPTO.TXT on CIS WINSDK forum (Lib 3) has a reference to it.

     13. BOOKHL.ZIP - Developing Online Help for Windows - The Book.
         WINSDK forum, Lib 16, CIS.

     14. CH102.ZIP - CreateHelp! HLP authoring tool.
         WINSDK forum, Lib 16, CIS.

     15. HCMAC1.ARC - HC1.DOT template and support files to build help.
         WINSDK forum, Lib 16, CIS.

                                        - 29 -








          There are more tools and references in CIS WINSDK Forum, LIB 16, as
     well as other places.



















































                                        - 30 -








                             Enhancing the WINSTUB Module
                                   By Rodney Brown
                                         
          When I was reading over the first issue of WPJ, I came upon an article
     that was talking  about the possibilities of the WINSTUB  module.  This got
     me thinking and I started  working on enhancing WINSTUB.  WINSTUB is  a DOS
     program that is placed at the front  of your Windows application.  When you
     try to load  a Windows program from  DOS, it is the culprit  that gives you
     that nasty little  message "This program requires  Microsoft Windows," then
     drops you back to DOS to pay for your sin<g>.

          The first WINSTUB  program I designed automatically loaded  up Windows
     if the program was started from DOS.  I  then decided to change it a little
     bit because some users may not  like that.  The second WINSTUB module  asks
     the user whether they want to load  Microsoft Windows or exit to DOS.  This
     allows the user to back out of the program if they made a mistake in typing
     the program  name, etc..  The WINSTUB program  was written in Turbo C++ for
     DOS.  The source code and project file are provided in the WPJ archive, but
     the C++ code is discussed below for those with other C/C++ compilers.


     // Enhanced WINSTUB.EXE for Windows applications

     #include <process.h>

     The process.h header file  contains the information for the  spawn command,
     the spawn command allows you to call an external program.

     #include <stddef.h>

     This header file is used solely for the definition of the NULL statement.

     #include <iostream.h>

     This is the C++ version of stdio.h

     char answer;
     int returncode;

     These two  statements initialize an  answer variable  of type  char, and  a
     returncode variable of type int.

     int main(void)
     {
       cerr << "\nThis program requires Microsoft Windows to run.";
       cerr << "\nWould you like me to start Microsoft Windows for you? ";

     The cerr << "text" command is the C++ equivalent of sending text to stderr.

       cin >> answer;

     The  cin >> variable command is the  C++ equivalent of retrieving data from

                                        - 31 -








     stdin.

       if (answer == 'y' || answer == 'Y')
         {
           returncode = spawnlp(P_WAIT,"win.com","","myprog",NULL);

     The statement above  tries to call win.com  (Windows).  If the  call is not
     successful, the spawn  command returns a negative integer  number.  The "l"
     in spawnlp signifies that the  number of parameters sent to  called program
     is known.  The  "p" in spawnlp tells spawn  to search the DOS path  for the
     program being called.

           if (returncode<0)
             {
               cerr << "\n\nError, could not start Microsoft Windows.";
               cerr << "\nExiting back to DOS...";
             }
         }
       else
         cerr << "\n\nExiting back to DOS...";

       return returncode;
     }

     The purpose  of the  rest of  the program  should be  obvious, if  the user
     answered yes and the spanwnlp call  was not successful, an error message is
     displayed and the program drops back to DOS.  If the user answered  no, the
     program exits to DOS.


     Once you  have written and compiled  your DOS WINSTUB program,  you need to
     return to window and modify  your .def file to put everything together.   A
     sample .def file looks like this:


     NAME            MYPROG
     DESCRIPTION     'Description of program goes here...'
     EXETYPE         WINDOWS
     STUB            'WINSTUB.EXE'
     CODE            PRELOAD MOVEABLE
     DATA            PRELOAD MOVEABLE MULTIPLE
     HEAPSIZE        4096
     STACKSIZE       5120


     After reading the last 4 WPJ's, you should know what most of the above code
     does.  The main parameter we are concerned with  is the STUB parameter.  In
     Turbo C++ for Windows (and I assume Borland C++), the STUB parameter is not
     required unless you are using a WINSTUB module other than Borland's.  Turbo
     C++ for Windows also does not require the EXPORT parameter if you are using
     ObjectWindows.


                                        - 32 -








     There  are other possibilities  for WINSTUB, the  program above  was just a
     simple enhancement of  WINSTUB.  If you thought the  comments of the source
     code  above  was overkill,  I'm sorry,  but I  just  wanted to  explain the
     program to people who may not know C++, or are just starting out in C.

                                 About the author

     Rodney M. Brown is a civilian  employee at Charleston Air Force Base, South
     Carolina.  His job involves operating and maintaining minicomputers and POS
     Systems, programming and repairing microcomputers.   He also gives computer
     lessons to fellow employees.

     He can be reached through the following services:

     GEnie: R.BROWN141

     CompuServe: 72163,2165

     Internet: 72163.2165@compuserve.com

     If you  are connected  to more  than one  of these services,  communication
     through CompuServe is preferred.































                                        - 33 -








                             Microsoft Developers Network
                                    Dave Campbell

          How would you like to have all the latest information about Windows
     programming,  OLE, ODBC, etc. at your fingertips? This would include sample
     code from the people that wrote the compiler and defined the interface, and
     books  by leaders  in the industry.  So far  I'm talking  about 2-layers of
     documentation deep on my desk. 

          That's what I usually get  into when I get  rolling on my article,  or
     get  deep  into  a  development  sequence.  Sometimes  it   gets  downright
     embarrassing.

          There  is a solution, though. If you haven't heard about the Microsoft
     Developer's Network yet,  you're missing out on a valuable resource. I have
     had the pre-release CD since December, but didn't get a  CD-ROM drive until
     this month. This is the scenario: I got into Windows, then Word, and opened
     up  my article,  and my  source code.  Then back  to Resource  Manager, and
     opened  the CD. Now  for the rest of  the article, I  worked back and forth
     between  the two Windows applications, cutting  and pasting, and rewording,
     and basically saving me a ton of time.

          The Developer's Network  is a subscription program  in which Microsoft
     sends a CD out quarterly which uses a great user interface to search out
     information. The pre-release CD contains technical information and  example
     code to use in writing applications for C/C++, DDE, OLE, using the
     Clipboard.  NT and VB 2.0  sample code, VB  2.0 Professional documentation,
     Excel SDK, MAPI,  ODBC, and  RTF specifications.  Additionally you'll  find
     Charles  Petzold's Programming Windows 3.1 book  in its entirety, including
     code with hyperlinks out to the SDK. There's a few years worth of Microsoft
     Systems Journal  magazines, and Ray Duncan's  "Advanced MS-DOS Programming"
     book in its entirety.  When you open up a section of the CD for
     inspection,  you can view  the code, run  the executable, or  cut and paste
     directly into your application from the CD. 

          I think you get the idea.

          The installation  was  extremely  smooth...all  contained  on  the  CD
     itself. My CD-ROM  drive normally comes  alive as drive  I:, so I just  ran
     I:\SETUP.EXE. It is taking up about 10M of my hard disk because I let it do
     the most it wanted,  for speed of access  later. Once it was  finished, the
     whole thing was available for use. 

          The cost of the program is $200/yr now that the pre-release program is
     over. Petzold's  book alone is $50  if you buy high-dollar  retail, so that
     covered the cost of  the first one  for us in the  pre-release. I'm sure  I
     won't be disappointed with the remainder of them.

          Of course, you'll need a CD-ROM drive to play  the CDs, and that isn't
     cheap  yet. But, if you  know someone with a Gateway  2000 customer ID, you
     can buy a Sony drive, which is AX, CDI, multisession, and Kodak
     compatible, drive, cables, and interface card for $225 plus $5 shipping.  I

                                        - 34 -








     highly recommend finding someone that can order this for you. I got mine in
     5 days, and it took about 20 minutes to install. It plays audio and digital
     CDs, and works great with the Developer's network.

          If you have any questions, feel free to send me e-mail:

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










































                                        - 35 -








                              Getting in touch with us:

     Internet and Bitnet:

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

     GEnie: P.DAVIS5 (Pete)

     CompuServe: 71141,2071 (Mike)

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

     You can also send paper mail to:

     Windows Programmer's Journal
     9436 Mirror Pond Drive
     Fairfax, VA   22032
           U.S.A.

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





























                                        - 36 -








                                    The Last Page
                                   by Mike Wallace

          Some background:  Taoism is a  very old philosophy that originated  in
     China, and  one of its  major writers  was a  man named  Chuang-tse.   This
     month's column starts off with a story from his writings:

          Hui-tse said to Chuang-tse,  "I have a  large tree which no  carpenter
          can cut  into lumber.  Its  branches and trunk are  crooked and tough,
          covered with bumps and depressions.  No builder would turn his head to
          look at  it.  Your  teachings are the  same - useless,  without value.
          Therefore, no one pays attention to them."

          "As you know," Chuang-tse replied, "a cat is very skilled at capturing
          its prey.   Crouching  low, it  can leap  in  any direction,  pursuing
          whatever it  is after.    But when  its attention  is  focued on  such
          things, it can be easily caught with a net.  On the other hand, a huge
          yak  is not easily caught  or overcome.  It stands  like a stone, or a
          cloud in the sky.  But for all its strength, it cannot catch a mouse.

          "You complain that your tree is not valuable as lumber.  But you could
          make use of the shade it provides, rest under its sheltering branches,
          and stroll beneath it,  admiring its character and appearance.   Since
          it  would not  be  endangered  by  an axe,  what  could  threaten  its
          existence?  It is useless to you only because you want to make it into
          something else and do not want to use it in its proper way."


          Why am I writing about Taoism?  Well, I'm not, really.  I was reminded
     of this  story a couple of weeks  ago after reading a  review of IBM's OS/2
     2.1.  The  focus of the article  seemed to be  that OS/2 could run  Windows
     programs almost  as well as Windows.   Sounded like the  reviewer wanted to
     make OS/2 something it's not: Windows.  Thus, the story.  Now, I'm not here
     to  harp on  that useless  operating  system OS/2,  but if  I  want to  run
     Windows, I'm not going to buy a 486 with 12MB of RAM and a 100MB hard drive
     to support OS/2.  I've seen Windows run  fine on a 386 with 4MB and a  much
     smaller hard drive.  My computer could run OS/2, but I installed Windows NT
     on it, and it runs Win3.1 programs without a problem.  Windows NT has about
     the  same memory  requirement as  OS/2 but  needs less  space on  your hard
     drive.

          Windows and OS/2  are different  operating systems, and  I think  they
     were each designed with a different kind of end  user in mind.  If you want
     to run Windows, why buy OS/2?  OS/2 should have a better selling point than
     "it runs Windows apps almost as well as Windows."  Anyone buying OS/2 based
     on this  promise will only be  disappointed, I think.   An operating system
     should be able to  stand on what makes it  unique, or it will end  up being
     nothing more than a cheap imitation.

          Hey, enough ranting  and raving.  This  issue is already late  enough.
     Talk to you next month.


                                        - 37 -




