home *** CD-ROM | disk | FTP | other *** search
/ Arawak OS/2 Shareware / PAKLED.ISO / docs / edm / edmi2-11.inf (.txt) < prev    next >
Encoding:
OS/2 Help File  |  1994-12-05  |  239.7 KB  |  3,209 lines

  1.  
  2. ΓòÉΓòÉΓòÉ 1. Dec 1994 Title Page ΓòÉΓòÉΓòÉ
  3.  
  4.                                       EDM/2
  5.  
  6.                   The Electronic Developer's Magazine for OS/2
  7.  
  8.                    Portions copyright (c) by Larry Salomon Jr.
  9.                                Volume 2, issue 11
  10.  
  11. Copyright Notice and Other Stuff 
  12.  
  13. The Editor-in-Chief of this electronic magazine is Larry Salomon, Jr. 
  14.  
  15. Portions of EDM/2 are copyrighted by the editors.  This publication may be 
  16. freely distributed in electronic form provided that all parts are present in 
  17. their original unmodified form.  A reasonable fee may be charged for the 
  18. physical act of distribution; no fee may be charged for the publication itself. 
  19.  
  20. All articles are copyrighted by their authors. No part of any article may be 
  21. reproduced without permission from the original author. 
  22.  
  23. Neither this publication nor the editors are affiliated with International 
  24. Business Machines Corporation. 
  25.  
  26. OS/2 is a registered trademark of International Business Machines Corporation. 
  27. Other trademarks are property of their respective owners.  Any mention of a 
  28. product in this publication does not constitute an endorsement or affiliation 
  29. unless specifically stated in the text. 
  30.  
  31. Happy Holidays 
  32.  
  33. I hesitate to say "Merry Christmas", since this is an international magazine 
  34. whose readers have international religious philosophies, but I do hope everyone 
  35. is enjoying this time of the year.  Only in December can you spend more than 
  36. you earn on someone else, and we all know how enjoyable that is.  <grin>  This 
  37. is especially true in my house because my wife is an accountant by profession 
  38. and she wants to buy a house (sometime this decade, so she says), so I can't 
  39. spend a penny more than I need to on anything. 
  40.  
  41. Okay, maybe it's not that bad. 
  42.  
  43. "Honey, can I have my lunch money for next week?" 
  44.  
  45. Game of the Year 
  46.  
  47. I need to make a plug.  This is not a plug made out of obligation, but one made 
  48. because I truly think every one of us needs to know about this method of 
  49. frustration-release.  The thing for which I am making this plug is Roids 
  50. (currently at version 2.2).  It is the reincarnation of the Asteroids game that 
  51. we loved so much in the arcades of yesteryear, complete with vectorized 
  52. graphics and flying rocks that will smash your ship to pieces if you are not 
  53. careful.  But wait(!) for there is more.  This new version, produced by Hungry 
  54. Man Productions, also has more things to get you and is fully configurable. 
  55. Better yet, it has full support for MMPM/2, so you can not only watch yourself 
  56. get blown to pieces, but you can hear it also. 
  57.  
  58. Roids 2.2 can be retrieved from hobbes.nmsu.edu; it is shareware with a $10 
  59. (U.S.) registration fee. 
  60.  
  61. New Icon 
  62.  
  63. I finally decided to make the icon more presentable.  While the first icon 
  64. accurately depicted what I envisioned EDM/2 to be - a newsletter - it has grown 
  65. beyond that to become more of a magazine.  So, the new icon displays that.  I 
  66. hope you like it. 
  67.  
  68. In Search Of... 
  69.  
  70. We are looking to expand our content by adding new columnists.  While I have a 
  71. few people in mind, I would be more than happy to receive recommendations for 
  72. people to write columns on the following topics: 
  73.  
  74. o Graphics 
  75. o REXX Language 
  76. o Multimedia 
  77. o Client/Server Programming 
  78.  
  79. All of these are with respect to development on the OS/2 platform, of course. 
  80. If you know of someone who knows any of these topics quite well and would 
  81. consider writing a monthly column, please send their name and email address to 
  82. os2man@panix.com (Internet). 
  83.  
  84. Before I Forget (Cause I Already Forgot Once) 
  85.  
  86. Gordon Zeglinski's C++ Corner will be missing from this issue as he rushes to 
  87. finish his soon-to-be-released InterCOMM product.  He will be missed and we 
  88. wish him a speedy completion of his product. 
  89.  
  90. Reader's Choice Awards 
  91.  
  92. This month marks the end of our second calendar year in existance.  And that 
  93. means that it is time once again for the Reader's Choice Awards!  While I 
  94. haven't started my annual fishing expedition for the prizes, that doesn't mean 
  95. that you can refrain from voting.  Choose from the list below the three best 
  96. articles, in your opinion, and send your choices to os2man@panix.com (Internet) 
  97. by January 15.  The votes will be tallied and the results, as well as the 
  98. prizes, will be revealed in the February issue. 
  99.  
  100. o A Review of Personal REXX for OS/2 (Issue 2) 
  101. o Adding Sounds to Your OS/2 Application (Issue 1) 
  102. o /dev/EDM/BookReview (Column) 
  103. o Coding for Dollars:  Copy Protection and Prevention 
  104. o Controlling Yourself:  A Framework for Configurable Options 
  105. o C++ Corner (Column) 
  106. o Debugging Classes in Borland C++ (Issue 4) 
  107. o Introduction to PM Programming (Column) 
  108. o Making Noise with MMPM/2 - Parts 1 and 2 (Issues 1 and 3) 
  109. o Porting STEP02 to ICLUI (Issue 3) 
  110. o Resources and Decompiling Them (Issue 6) 
  111. o Scratch Patch (Column) 
  112. o Sprites and Animation - Parts 1-4 (Issues 5-8) 
  113. o TCP/IP Socket Programming in REXX (Issue 2) 
  114. o The Design and Implementation of VIOWIN - Parts 1-4 (Issues 8-11) 
  115. o Using SYSLEVEL Files in Your Applications (Issue 5) 
  116. o Utilizing Hooks for Added Capabilities (Issue 1) 
  117. o Visual REXX Faceoff - Parts 1-3 (Issues 6, 7, and 9) 
  118. o WPS Programming the Easy Way - Parts 1 and 2 (Issues 8 and 9) 
  119. o Workplace Shell Development 101 (Issue 3) 
  120.  
  121. Title Page - EDM/2 - Dec 1994 - Volume 2, Issue 11 
  122.  
  123.  
  124. ΓòÉΓòÉΓòÉ 2. Coding for Dollars:  Copy Protection and Prevention ΓòÉΓòÉΓòÉ
  125.  
  126.  
  127. ΓòÉΓòÉΓòÉ 2.1. Introduction ΓòÉΓòÉΓòÉ
  128.  
  129. Coding for Dollars:  Copy Protection and Prevention 
  130.  
  131. Written by Larry Salomon, Jr. 
  132.  
  133. Introduction 
  134.  
  135. Have you ever written a wonderful application, decided to distribute it 
  136. commercially, get little money for it, but find out that many people have a 
  137. copy nonetheless?  Ask Gordon Zeglinski; or ask Describe; or ask any of the 
  138. many commercial developers in the world what it feels like to see your revenue 
  139. lost due to piracy. 
  140.  
  141. I am reminded specifically the story of the brother of an ex-girlfriend of 
  142. mine:  he wrote a really good cribbage program for DOS but received only 
  143. approximately 40 orders for the program.  He lived in Raleigh, where the 
  144. National Cribbage Tournament is held annually and everyone at the next 
  145. tournament told him how good his program was.  He was ready, though; he had 
  146. memorized the names of all of the people that had ordered the program and 
  147. confronted those that had not on the spot. 
  148.  
  149. Of course, this isn't considered the best way to handle this situation, 
  150. especially since he didn't receive any more money from the people who he had 
  151. "caught with their hands in the cookie jar."  No, the best way is to follow the 
  152. age-old adage, "an ounce of prevention is worth a pound of cure."  This article 
  153. will attempt to do just that - develop code that will enable an application to 
  154. keep track of the number of licenses available and prevent unauthorized copying 
  155. of the application. 
  156.  
  157. The reader should be forewarned:  if a software pirate wants something bad 
  158. enough, they will get it.  I can remember the days when I bought the COPYIIPC 
  159. hardware adapter which copied the analog representation of a diskette's data to 
  160. another.  This hardware-assisted "archival copy enabler" ("Yeah, that's the 
  161. ticket...") could copy anything, including software that used the infamous 
  162. weak-bit protection scheme.  The point is that no protection scheme is 100% 
  163. secure, much less this one which I conceived, designed, and coded in two hours 
  164. one morning while suffering from ennui. 
  165.  
  166. Coding for Dollars:  Copy Protection and Prevention - EDM/2 - Dec 1994 - Volume 
  167. 2, Issue 11 
  168.  
  169.  
  170. ΓòÉΓòÉΓòÉ 2.2. Magic Cookies and Other Nonsense ΓòÉΓòÉΓòÉ
  171.  
  172. Magic Cookies and Other Nonsense 
  173.  
  174. The idea behind any protection scheme is that of the software license, i.e. the 
  175. developer or company allows you to use a preset number of copies of the 
  176. software.  The user should not be allowed to exceed this number in any 
  177. situation.  If software were something tangible then this might be enforcable, 
  178. but since software is really a collection of analog signals transferred via 
  179. magnetic media enforcing this is more than a trivial matter. 
  180.  
  181. What the now infamous dongle did was make a part of the software tangible 
  182. through a hardware device which attaches to the parallel port of your PC and is 
  183. software addressable, to insure its presence.  If the dongle isn't there the 
  184. software displays an appropriate message and stops. 
  185.  
  186. Illusions of Association 
  187.  
  188. What makes the dongle successful (and annoying and the same time) is the bond, 
  189. the association between the dongle and the software - if the dongle isn't there 
  190. then the software will not continue.  It then becomes our challenge to discover 
  191. some method of binding the software with something that is unchangeable (which 
  192. implies a hardware-assisted solution) or something that is very difficult to 
  193. change (which can be done entirely through software). 
  194.  
  195. So what shall we use as our magic cookie, the item which needs to be present in 
  196. order to allow our application to run?  The answer to this was nothing obvious 
  197. and was discovered only by following a hunch I had one morning when pondering 
  198. the CmnFilFormatDiskette() code that I had written some time ago. 
  199.  
  200. Try this exercise in futility: 
  201.  
  202.  1. Format a diskette and give it a label. 
  203.  
  204.  2. DISKCOPY the formatted diskette so that you now have two blank, 
  205.     just-formatted diskettes. 
  206.  
  207.  3. Examine both diskettes and discover the one difference between them that 
  208.     can be exploited under OS/2. 
  209.  
  210. [E:\]dir a:
  211.  
  212.  Volume in drive A is TESTDISK
  213.   The Volume Serial Number is 25C2:5415
  214.    Directory of A:\
  215.  
  216. TEST     DAT       70  11-14-94  10:09a
  217.          1 file(s)         70 bytes used
  218.                       1455616 bytes free
  219.  
  220. [E:\]dir a:
  221.  
  222.  Volume in drive A is TESTDISK
  223.   The Volume Serial Number is 226D:EC14
  224.    Directory of A:\
  225.  
  226. TEST     DAT       70  11-14-94  10:09a
  227.          1 file(s)         70 bytes used
  228.                       1455616 bytes free
  229.  
  230. [E:\]
  231.  
  232. Sleight of Hand 
  233.  
  234. The answer, for those who cannot see it, is the volume serial number (VSN). 
  235. Even if you use DISKCOPY to copy one diskette to another, the VSN is changed on 
  236. the target diskette.  As you will see, this is the key to the entire solution. 
  237.  
  238. Coding for Dollars:  Copy Protection and Prevention - EDM/2 - Dec 1994 - Volume 
  239. 2, Issue 11 
  240.  
  241.  
  242. ΓòÉΓòÉΓòÉ 2.3. The Principle of Contagion ΓòÉΓòÉΓòÉ
  243.  
  244. The Principle of Contagion 
  245.  
  246. "Once together, always together," wrote Lyn Hardy in the series Master of the 
  247. Five Magics and this is what we want to accomplish using the VSN as our magic 
  248. cookie.  Obviously, this means we have to tuck the VSN somewhere where the 
  249. potential hacker cannot find it.  And, while we're at it, we might as well put 
  250. the number of available licenses remaining and the volume label of the diskette 
  251. in our holding place also. But where do we put this information?  An ASCII file 
  252. wouldn't work because it is too easily cracked; a binary file would be better, 
  253. but an inquisitive hacker could decypher the meaning of the data, once the 
  254. purpose of the data was discovered.  What we need is a way to store this 
  255. important information in such a way that the data is never noticed, invoking 
  256. the "Axiom of Forgetfulness" - "out of sight, out of mind." <grin> 
  257.  
  258. Bag of Holding 
  259.  
  260. Like the Advanced Dungeons and Dragons item, we need a place to put this 
  261. information that is out of sight of any hackers.  What better place to put it 
  262. than in the extended attributes of a file?  This is the best place, that I 
  263. could think of, to put it. 
  264.  
  265. Note:  originally, I thought I could attach EAs to the root-directory of the 
  266. diskette, like you can a subdirectory, but that returned ERROR_ACCESS_DENIED, 
  267. so I had to settle on attaching them to the EAs of a hidden file instead. 
  268.  
  269. Coding for Dollars:  Copy Protection and Prevention - EDM/2 - Dec 1994 - Volume 
  270. 2, Issue 11 
  271.  
  272.  
  273. ΓòÉΓòÉΓòÉ 2.4. Hackers, Start Your Keyboards ΓòÉΓòÉΓòÉ
  274.  
  275. Hackers, Start Your Keyboards 
  276.  
  277. We now have enough information to write a complete function to handle 
  278. installation diskettes.  Let us look at the four actions that can be performed 
  279. and the logic for each. 
  280.  
  281. Create an Installation Diskette 
  282.  
  283.  1. Use DosQueryFSInfo() to get the VSN and the volume label of the diskette to 
  284.     be initialized. 
  285.  2. Write the VSN, volume label, and the initial number of licenses to the EAs 
  286.     of a file on the diskette. 
  287.  
  288. Decrementing the Number of Licenses 
  289.  
  290.  1. Use DosQueryFSInfo() to get the VSN and the volume label of the "original" 
  291.     installation diskette. 
  292.  2. Read the EA data for the file on the diskette. 
  293.  3. Compare the information returned from DosQueryFSInfo() with that from the 
  294.     EA data and return an error if there is a mismatch. 
  295.  4. If there is are no more available licenses, return an error. 
  296.  5. Decrement the license count and write the EA data back to the file. 
  297.  
  298. Incrementing the Number of Licenses 
  299.  
  300.  1. Use DosQueryFSInfo() to get the VSN and the volume label of the "original" 
  301.     installation diskette. 
  302.  2. Read the EA data for the file on the diskette. 
  303.  3. Compare the information returned from DosQueryFSInfo() with that from the 
  304.     EA data and return an error if there is a mismatch. 
  305.  4. Increment the license count and write the EA data back to the file. 
  306.  
  307. Determing the Number of Licenses 
  308.  
  309.  1. Use DosQueryFSInfo() to get the VSN and the volume label of the "original" 
  310.     installation diskette. 
  311.  2. Read the EA data for the file on the diskette. 
  312.  3. Compare the information returned from DosQueryFSInfo() with that from the 
  313.     EA data and return an error if there is a mismatch. 
  314.  4. Return the number of available licenses. 
  315.  
  316. Easy, huh?  The code corresponding to the above is shown below, is provided in 
  317. cpyprt.zip, and has also been integrated into Common/2 which will eventually be 
  318. sent to hobbes.nmsu.edu. 
  319.  
  320. typedef struct FCIINSTALLINFO {
  321.    ULONG ulSzStruct;
  322.    CHAR achVolLabel[12];
  323.    ULONG ulVolSerial;
  324.    ULONG ulNumLicenses;
  325. } FCIINSTALLINFO, *PFCIINSTALLINFO;
  326.  
  327. ULONG EXPENTRY CmnFilInstallDisk(PCHAR pchFile,
  328.                                  PCHAR pchVendor,
  329.                                  PCHAR pchApp,
  330.                                  PCHAR pchName,
  331.                                  PULONG pulNumLicenses,
  332.                                  ULONG ulRequest)
  333. //-------------------------------------------------------------------------
  334. // This function performs one of the available actions on a diskette
  335. // which is intended to be an install diskette for an application.  The
  336. // action to be performed is specified in ulRequest:
  337. //
  338. //    CFID_REQ_CREATE - initialize the install diskette with a specified
  339. //                      number of licenses
  340. //    CFID_REQ_DECREMENT - decrement the number of licenses
  341. //    CFID_REQ_INCREMENT - increment the number of licenses
  342. //    CFID_REQ_QUERY - query the number of licenses
  343. //
  344. // On all actions but create, the diskette is checked to insure that
  345. // it is the original diskette initialized with the create action and
  346. // an error code is returned if not.
  347. //
  348. // Input:  pchFile - points to the name of a file to contain the
  349. //                   installation information.
  350. //         pchVendor - points to the name of the company producing the
  351. //                     product.
  352. //         pchApp - points to the name of the application.
  353. //         pchName - points to an arbitrary name.
  354. //         pulNumLicenses - if ulRequest=CFID_REQ_CREATE, this points
  355. //                          to a variable specifying the initial number
  356. //                          of licenses for this installation diskette.
  357. //                          Otherwise, it points to a variable to receive
  358. //                          the updated number of available licenses or
  359. //                          is NULL meaning that the information isn't
  360. //                          needed.
  361. //         ulRequest - one of the CFID_REQ_* constants
  362. // Output:  pulNumLicenses - if non NULL, this contains the updated number
  363. //                           of licenses after the request was performed.
  364. // Returns:  CFID_ERR_NOERROR if successful, a CFID_ERR_* constant otherwise.
  365. //-------------------------------------------------------------------------
  366. {
  367.    APIRET arRc;
  368.    CHAR achFullFile[CCHMAXPATH];
  369.    CHAR chDrive;
  370.    FSINFO fsiInfo;
  371.    BYTE bNumDrives;
  372.    ULONG ulVolSerial;
  373.    USHORT usAttr;
  374.    USHORT usSzData;
  375.    FCIINSTALLINFO fiiInstall;
  376.    BOOL bReturn;
  377.  
  378.    //----------------------------------------------------------------------
  379.    // Get the fully qualified name of the file so that we can get the
  380.    // drive letter.  FIL_QUERYFULLNAME does require the diskette to be in
  381.    // the drive so that OS/2 can query the current directory on the drive.
  382.    //----------------------------------------------------------------------
  383.    DosError(FERR_DISABLEHARDERR);
  384.  
  385.    arRc=DosQueryPathInfo(pchFile,
  386.                          FIL_QUERYFULLNAME,
  387.                          achFullFile,
  388.                          sizeof(achFullFile));
  389.    if (arRc!=0) {
  390.       DosError(FERR_ENABLEHARDERR);
  391.       return CFID_ERR_DRIVENOTREADY;
  392.    } /* endif */
  393.  
  394.    DosError(FERR_ENABLEHARDERR);
  395.  
  396.    chDrive=toupper(achFullFile[0])-'A'+1;
  397.  
  398.    //----------------------------------------------------------------------
  399.    // Verify that the drive specified is a diskette drive.
  400.    //----------------------------------------------------------------------
  401.    DosDevConfig(&bNumDrives,DEVINFO_FLOPPY);
  402.  
  403.    if (chDrive>bNumDrives) {
  404.       return CFID_ERR_NOTFLOPPY;
  405.    } /* endif */
  406.  
  407.    //----------------------------------------------------------------------
  408.    // Query the file system information on the diskette.
  409.    //----------------------------------------------------------------------
  410.    if (DosQueryFSInfo(chDrive,
  411.                       FSIL_VOLSER,
  412.                       &fsiInfo,
  413.                       sizeof(fsiInfo))!=0) {
  414.       return CFID_ERR_READFAILED;
  415.    } /* endif */
  416.  
  417.    //----------------------------------------------------------------------
  418.    // Get the volume serial number of the diskette.
  419.    //----------------------------------------------------------------------
  420.    ulVolSerial=*((PULONG)&fsiInfo.fdateCreation);
  421.  
  422.    usAttr=EAT_BINARY;
  423.    usSzData=sizeof(fiiInstall);
  424.  
  425.    switch (ulRequest) {
  426.    case CFID_REQ_CREATE:
  427.       //-------------------------------------------------------------------
  428.       // pulNumLicenses cannot be NULL if we are initializing the diskette.
  429.       //-------------------------------------------------------------------
  430.       if (pulNumLicenses==NULL) {
  431.          return CFID_ERR_BADPARM;
  432.       } /* endif */
  433.  
  434.       //-------------------------------------------------------------------
  435.       // Setup the intended EA data and write it to the file.
  436.       //-------------------------------------------------------------------
  437.       fiiInstall.ulSzStruct=sizeof(fiiInstall);
  438.       strcpy(fiiInstall.achVolLabel,fsiInfo.vol.szVolLabel);
  439.       fiiInstall.ulVolSerial=ulVolSerial;
  440.       fiiInstall.ulNumLicenses=*pulNumLicenses;
  441.  
  442.       bReturn=CmnFilSetExtAttribute(achFullFile,
  443.                                     usAttr,
  444.                                     pchVendor,
  445.                                     pchApp,
  446.                                     pchName,
  447.                                     (PCHAR)&fiiInstall,
  448.                                     usSzData);
  449.       if (!bReturn) {
  450.          return CFID_ERR_WRITEFAILED;
  451.       } /* endif */
  452.       break;
  453.    case CFID_REQ_DECREMENT:
  454.       //-------------------------------------------------------------------
  455.       // Get the current information from the file.
  456.       //-------------------------------------------------------------------
  457.       bReturn=CmnFilQueryExtAttribute(achFullFile,
  458.                                       pchVendor,
  459.                                       pchApp,
  460.                                       pchName,
  461.                                       &usAttr,
  462.                                       (PCHAR)&fiiInstall,
  463.                                       &usSzData);
  464.       if (!bReturn) {
  465.          return CFID_ERR_READFAILED;
  466.       } /* endif */
  467.  
  468.       //-------------------------------------------------------------------
  469.       // If the data is incorrect or the volume serial numbers do not
  470.       // match, then this isn't the original install disk.
  471.       //-------------------------------------------------------------------
  472.       if ((fiiInstall.ulSzStruct!=sizeof(fiiInstall)) ||
  473.           (strcmp(fiiInstall.achVolLabel,fsiInfo.vol.szVolLabel)!=0) ||
  474.           (fiiInstall.ulVolSerial!=ulVolSerial)) {
  475.          return CFID_ERR_NOTINSTALLDISK;
  476.       } /* endif */
  477.  
  478.       //-------------------------------------------------------------------
  479.       // Insure that we have at least one more license left.
  480.       //-------------------------------------------------------------------
  481.       if (fiiInstall.ulNumLicenses==0) {
  482.          return CFID_ERR_NOMORELICENSES;
  483.       } /* endif */
  484.  
  485.       //-------------------------------------------------------------------
  486.       // Update and write the new information back to the diskette.
  487.       //-------------------------------------------------------------------
  488.       fiiInstall.ulNumLicenses--;
  489.  
  490.       if (pulNumLicenses!=NULL) {
  491.          *pulNumLicenses=fiiInstall.ulNumLicenses;
  492.       } /* endif */
  493.  
  494.       bReturn=CmnFilSetExtAttribute(achFullFile,
  495.                                     usAttr,
  496.                                     pchVendor,
  497.                                     pchApp,
  498.                                     pchName,
  499.                                     (PCHAR)&fiiInstall,
  500.                                     usSzData);
  501.       if (!bReturn) {
  502.          return CFID_ERR_WRITEFAILED;
  503.       } /* endif */
  504.       break;
  505.    case CFID_REQ_INCREMENT:
  506.       //-------------------------------------------------------------------
  507.       // Get the current information from the file.
  508.       //-------------------------------------------------------------------
  509.       bReturn=CmnFilQueryExtAttribute(achFullFile,
  510.                                       pchVendor,
  511.                                       pchApp,
  512.                                       pchName,
  513.                                       &usAttr,
  514.                                       (PCHAR)&fiiInstall,
  515.                                       &usSzData);
  516.       if (!bReturn) {
  517.          return CFID_ERR_READFAILED;
  518.       } /* endif */
  519.  
  520.       //-------------------------------------------------------------------
  521.       // If the data is incorrect or the volume serial numbers do not
  522.       // match, then this isn't the original install disk.
  523.       //-------------------------------------------------------------------
  524.       if ((fiiInstall.ulSzStruct!=sizeof(fiiInstall)) ||
  525.           (strcmp(fiiInstall.achVolLabel,fsiInfo.vol.szVolLabel)!=0) ||
  526.           (fiiInstall.ulVolSerial!=ulVolSerial)) {
  527.          return CFID_ERR_NOTINSTALLDISK;
  528.       } /* endif */
  529.  
  530.       //-------------------------------------------------------------------
  531.       // Update and write the new information back to the diskette.
  532.       //-------------------------------------------------------------------
  533.       fiiInstall.ulNumLicenses++;
  534.  
  535.       if (pulNumLicenses!=NULL) {
  536.          *pulNumLicenses=fiiInstall.ulNumLicenses;
  537.       } /* endif */
  538.  
  539.       bReturn=CmnFilSetExtAttribute(achFullFile,
  540.                                     usAttr,
  541.                                     pchVendor,
  542.                                     pchApp,
  543.                                     pchName,
  544.                                     (PCHAR)&fiiInstall,
  545.                                     usSzData);
  546.       if (!bReturn) {
  547.          return CFID_ERR_WRITEFAILED;
  548.       } /* endif */
  549.       break;
  550.    case CFID_REQ_QUERY:
  551.       //-------------------------------------------------------------------
  552.       // Get the current information from the file.
  553.       //-------------------------------------------------------------------
  554.       bReturn=CmnFilQueryExtAttribute(achFullFile,
  555.                                       pchVendor,
  556.                                       pchApp,
  557.                                       pchName,
  558.                                       &usAttr,
  559.                                       (PCHAR)&fiiInstall,
  560.                                       &usSzData);
  561.       if (!bReturn) {
  562.          return CFID_ERR_READFAILED;
  563.       } /* endif */
  564.  
  565.       //-------------------------------------------------------------------
  566.       // If the data is incorrect or the volume serial numbers do not
  567.       // match, then this isn't the original install disk.
  568.       //-------------------------------------------------------------------
  569.       if ((fiiInstall.ulSzStruct!=sizeof(fiiInstall)) ||
  570.           (strcmp(fiiInstall.achVolLabel,fsiInfo.vol.szVolLabel)!=0) ||
  571.           (fiiInstall.ulVolSerial!=ulVolSerial)) {
  572.          return CFID_ERR_NOTINSTALLDISK;
  573.       } /* endif */
  574.  
  575.       if (pulNumLicenses!=NULL) {
  576.          *pulNumLicenses=fiiInstall.ulNumLicenses;
  577.       } /* endif */
  578.       break;
  579.    default:
  580.       return CFID_ERR_BADPARM;
  581.    } /* endswitch */
  582.  
  583.    return CFID_ERR_NOERROR;
  584. }
  585.  
  586. Coding for Dollars:  Copy Protection and Prevention - EDM/2 - Dec 1994 - Volume 
  587. 2, Issue 11 
  588.  
  589.  
  590. ΓòÉΓòÉΓòÉ 2.5. Summary ΓòÉΓòÉΓòÉ
  591.  
  592. Summary 
  593.  
  594. What has been presented is simply an enabler.  The code to call this function 
  595. is still the responsibility of the application developer. 
  596.  
  597. o When you are installing the application, call CmnFilInstallDiskette() 
  598.   specifying CFID_REQ_DECREMENT to decrement the license count and check the 
  599.   return code. 
  600.  
  601. o When you are uninstalling the application, call CmnFilInstallDiskette() 
  602.   specifying CFID_REQ_INCREMENT to increment the license count and check the 
  603.   return code. 
  604.  
  605. Limitation 
  606.  
  607. You probably have already noted a major limitation of this scheme: since the 
  608. VSN of the install diskette is stored in the EAs, the preparation of the 
  609. install diskette cannot be done before duplication, but must be done - diskette 
  610. by diskette - after duplication has been completed.  The makes distribution of 
  611. commercial software using this scheme much more cumbersome, but you did want 
  612. that money, didn't you? 
  613.  
  614. Also, it is fairly easy to hack the VSN of a diskette if the pirate does enough 
  615. research to find out where on the diskette it is stored.  This is the weakest 
  616. point in the scheme, but given that the data could be stored anywhere, there is 
  617. nothing to indicate that it is this scheme which is being used, so your 
  618. application should still be fairly safe. 
  619.  
  620. Feedback on this article will be greatly appreciated. 
  621.  
  622. Coding for Dollars:  Copy Protection and Prevention - EDM/2 - Dec 1994 - Volume 
  623. 2, Issue 11 
  624.  
  625.  
  626. ΓòÉΓòÉΓòÉ 3. Controlling Yourself:  A Framework for Configurable Options ΓòÉΓòÉΓòÉ
  627.  
  628.  
  629. ΓòÉΓòÉΓòÉ 3.1. Introduction ΓòÉΓòÉΓòÉ
  630.  
  631. Controlling Yourself:  A Framework for Configurable Options 
  632.  
  633. Written by John Holt 
  634.  
  635. Introduction 
  636.  
  637. Most programs have a set of persistent options that are under user control. 
  638. These options must be managed.  This article discusses an approach to uniform 
  639. management of option sets that may reside in a profile (INI file) or on a 
  640. file's extended attributes (EA's). 
  641.  
  642. The home for user specified general application defaults varies by system. In a 
  643. UNIX system, environment variables or entries in .mwmrc  (the Motif resource 
  644. file) are used for system-wide defaults.  In Windows and OS/2, INI files are 
  645. used to hold the general application defaults.  The OS/2 file system also 
  646. provides an additional means of keeping file specific default settings. 
  647.  
  648. Problem and Environment Description 
  649.  
  650. Any general framework of classes must have certain attributes.  Two attributes 
  651. that are germane to this article are programmer-extensibility and harmony with 
  652. the external environment.  The facility for programmer extensions is simply 
  653. providing a capability for the programmer to add new classes to be stored in 
  654. the profile or extended attributes.  The attribute of harmony with the external 
  655. environment in this case is using profiles and extended attributes in a way 
  656. that facilitates the use of existing tools. 
  657.  
  658. Several base and implementation classes were developed.  The task of managing 
  659. persistent option sets is common to many programs, and therefore is an 
  660. excellent candidate for reusable objects.  The classes developed for the 
  661. article represent an attempt to divide the implementation into classes so as to 
  662. reuse common behaviors.  The model for implementation is individual objects 
  663. grouped into a set.  The set is then stored on either a profile or in EAs. 
  664.  
  665. Controlling Yourself:  A Framework for Configurable Options - EDM/2 - Dec 1994 
  666. - Volume 2, Issue 11 
  667.  
  668.  
  669. ΓòÉΓòÉΓòÉ 3.2. Class Relationship Structure ΓòÉΓòÉΓòÉ
  670.  
  671. Class Relationship Structure 
  672.  
  673. Overview 
  674.  
  675. The class structure is depicted in the diagram below.  The relationship marked 
  676. IS-A denotes class derivation.  The relationship marked HAS-A shows that the 
  677. class contains an object of the class.  The KeyedOptionSet class is the result 
  678. of a template generation using the KeyedSequentialSet class in the compiler 
  679. collection classes library. 
  680.  
  681.        ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ                       ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
  682.        Γöé BOptionSet Γöé                       Γöé BOptionItem Γöé
  683.        ΓööΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÿ                       ΓööΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
  684.          Γöé        Γöé                           Γöé
  685.        ISΓöéA    HASΓöéA                        ISΓöéA
  686.          Γöé        Γöé                           Γöé
  687.          Γöé        ΓöéΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ         ΓöéΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
  688.          Γöé        ΓööΓöñ KeyedOptionSet Γöé         Γö£Γöñ StrOptionItem Γöé
  689.          Γöé         Γöé    <IKeySet>   Γöé         ΓöéΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
  690.          Γöé         ΓööΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ         ΓöéΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
  691.          Γöé           Γöé                        ΓööΓöñ Programmer Item Γöé
  692.          Γöé        HASΓöéA                        ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
  693.          Γöé           ΓöéΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
  694.          Γöé           ΓööΓöñ OptionSetElem Γöé
  695.          Γöé            ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
  696.          ΓöéΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
  697.          Γö£Γöñ EAOptionSet Γöé
  698.          ΓöéΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
  699.          ΓöéΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
  700.          ΓööΓöñ INIOptionSet Γöé
  701.           ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
  702. Set Classes 
  703.  
  704. The option set behaviors have been factored into a base class (BOptionSet) and 
  705. two implementation classes (INIOptionSet and EAOptionSet).  The behaviors 
  706. specific to manipulation of EAs or profiles is in EAOptionSet and INIOptionSet 
  707. respectively. 
  708.  
  709. BOptionSet is concerned with the maintenance of the list of items to be stored 
  710. or retrieved.  The following public methods are implemented: 
  711.  
  712. addItem             Adds an item in the form of a pointer to a BOptionItem and 
  713.                     a name for the option 
  714. delItem             Removes an item from the set 
  715. applName            The character string used as the high order name qualifier 
  716.                     for EAs or the application name parameter for INI calls. 
  717. ~BOptionSet         The virtual destructor used to clean up. 
  718.  
  719. The BOptionSet object implements several protected methods used by the derived 
  720. classes to access and manipulate the private data members. Those behaviors are: 
  721.  
  722. firstItem           Position the set cursor to the first item 
  723. nextItem            Advance the set cursor to the next item in the set 
  724. item                Returns the pointer to an item in the set that is at the 
  725.                     current cursor position. 
  726. copy                Makes this set a copy of another set. 
  727. keySpace            Returns the number of bytes needed to store the item key 
  728.                     names. 
  729. flatSpace           Returns the number of bytes needed to store a flat version 
  730.                     of the set of items. 
  731. itemCount           Returns the number of items in the set. 
  732. BOptionSet          The object constructors.  There is a copy constructor and a 
  733.                     constructor taking the application name and producing an 
  734.                     empty set. 
  735.  
  736. The KeyedOptionSet is generated from the IKeySet template class which is a 
  737. keyed sequential set class in the IBM collection classes.  A cursor is provided 
  738. by the template.  The object is a set of OptionSetElem objects. 
  739.  
  740. The OptionSetElem is a simple object holding only the name of the option and a 
  741. pointer to a BOptionItem object. 
  742.  
  743. The derived classes EAOptionSet and INIOptionSet provide the implementations 
  744. for the loadSet, storeSet, and resetSet as well as the usual expected 
  745. complement of constructors and an equal operator. 
  746.  
  747. The loadSet method updates each item with the value stored for that item's 
  748. name.  For the case of the items stored as EAs, the name is <application 
  749. name>.<item name> and is mono-case.  For objects stored in profiles, the name 
  750. is forced to be in uppercase for consistency. The storeSet method is the 
  751. reverse of the loadSet. 
  752.  
  753. The resetSet method performs two operations.  First, the set of names in the 
  754. profile or EAs is made to correspond with the set of names in the set.  Any 
  755. names with a prefix other than <application name> are ignored. The second 
  756. operation is to perform a storeSet. 
  757.  
  758. Item Classes 
  759.  
  760. The BOptionItem class provides some default implementations for the behaviors. 
  761. The class interface is primarily intended to be the communication portal 
  762. between an option set and the real data objects that are the elements of that 
  763. set.  The interface is: 
  764.  
  765. itemType       Returns an enum indicating that the object is a character string 
  766.                or is binary data 
  767. itemSize       Returns the size of the item in bytes 
  768. exportData     Returns a flattened version of the item consisting of a length 
  769.                and a pointer to a string of bytes. 
  770. importData     Accepts a flattened version of the item for updating the real 
  771.                data object. 
  772.  
  773. The StrOptionItem is a class definition for a string data object. The 
  774. definition provides for the normal string behaviors. 
  775.  
  776. Interface to Programmer Supplied Objects 
  777.  
  778. The interface used by the option set to manipulate the items in memory is 
  779. BOptionItem.  Because all option items are derived from BOptionItem, the 
  780. correct method is invoked. 
  781.  
  782. When an addItem method is invoked on a set, the set obtains a BOptionItem type 
  783. pointer to that item.  When the set needs the size of the item, e.g. to 
  784. allocate a buffer, the pointer to a BOptionItem is used to invoke the correct 
  785. method.  Similarly, the importData and exportData methods are invoked to move 
  786. the value to and from the persistent store. 
  787.  
  788. Adding New Object Classes 
  789.  
  790. There are two different strategies that can be employed for extending the range 
  791. of types of items that can be used in option sets.  The programmer may 
  792. implement interface behaviors directly in the object to be added, or the 
  793. programmer can implement a wrapper class.  If the class is under control of the 
  794. programmer, direct implementation is usually the best approach.  If the object 
  795. to be used is provided by a third party class library, or is not a first class 
  796. object (e.g., an INT) then a wrapper class is the right approach. The sample 
  797. program uses the later approach because it is the most non-obvious of the two 
  798. approaches.  The reader will notice that the example is contrived. 
  799.  
  800. Controlling Yourself:  A Framework for Configurable Options - EDM/2 - Dec 1994 
  801. - Volume 2, Issue 11 
  802.  
  803.  
  804. ΓòÉΓòÉΓòÉ 3.3. Walkthrough the Sample/Test Program ΓòÉΓòÉΓòÉ
  805.  
  806. Walkthrough the Sample/Test Program 
  807.  
  808. The sample/test program illustrates how to create a set, add option items to 
  809. that set, and store the set of option items.  To exercise the code, these tasks 
  810. were performed for both profile and EA based option sets.  The file test.cpp 
  811. [see test_src.zip within options.zip - Editor] should be followed for this 
  812. discussion. You may also want look at BOptionSet.cpp and StrOptionItem.cpp [see 
  813. source.zip within options.zip - Editor] for additional detail. 
  814.  
  815.  
  816.  Housekeeping           test.cpp               Initialize several variables,
  817.                                                get an anchor block, and open
  818.                                                the test profile.
  819.  
  820.  create empty set       test.cpp               Declare object of type
  821.                                                INIOptionSet with a string and
  822.                                                handle for the profile
  823.  
  824.                         INIOptionSet.cpp       Constructor for string and
  825.                                                handle pass string to the
  826.                                                BOptionSet for the application
  827.                                                name, then hold onto the
  828.                                                handle for subsequent file
  829.                                                operations.
  830.  
  831.  create items           test.cpp               Declares for a StrOptionItem
  832.                                                and two Option items. Option
  833.                                                is a contrived class defined
  834.                                                in the file option.h.  Both of
  835.                                                these objects take a value and
  836.                                                a string used as the name of
  837.                                                the option.  The name is
  838.                                                passed on to the BOptionItem
  839.                                                parent class.
  840.  
  841.  Add items to the       test.cpp               The addItem method is invoked
  842.  set                                           for each item.
  843.  
  844.                         BOptionSet.cpp         The addItem method allocates
  845.                                                an OptionSetElem object to
  846.                                                hold the BOptionItem * and the
  847.                                                name of the item.  The element
  848.                                                is then added to the set or
  849.                                                replaces a previous item of
  850.                                                the same name.  The size of
  851.                                                the item name is recorded.
  852.  
  853.  store the set          INIOptionSet.cpp       The items are stored on the
  854.                                                profile test.ini.  The set is
  855.                                                traversed and each item is
  856.                                                individually written to the
  857.                                                profile.  The exportData
  858.                                                method is invoked for the
  859.                                                BOptionItem involved. In the
  860.                                                case of Item3,
  861.                                                StrOptionItem::exportData is
  862.                                                invoked.
  863.  
  864.                         StrOptionItem.cpp      A FlatItem data structure is
  865.                                                allocated along with a buffer
  866.                                                to hold the string value.  The
  867.                                                pointer to this is returned.
  868.                                                It will be the callers
  869.                                                responsibility to free the
  870.                                                areas.
  871.  
  872.  change the values      test.cpp               The item values are changed
  873.  of the items                                  via the operator= method
  874.  
  875.  reset the items        test.cpp               The loadSet method is invoked
  876.  from the store                                to update the items from the
  877.                                                values held in the INI file.
  878.                                                This restores them to their
  879.                                                original values
  880.  
  881.                         INIOptionSet.cpp       For each item in the option
  882.                                                set:  the size of the item is
  883.                                                obtained via the
  884.                                                PrfQueryProfileItemSize()
  885.                                                call; the item is read from
  886.                                                the profile; the importData
  887.                                                method for that item is
  888.                                                invoked to update the item
  889.                                                value in memory.
  890.  
  891.                         StrOptionItem.cpp      The importData method receives
  892.                                                a FlatItem structure. The
  893.                                                current string data is freed,
  894.                                                and the new value is kept.
  895.  
  896.  delete some items      test.cpp               Items 1 and 2 are deleted from
  897.                                                the set, and the version of
  898.                                                the set on the profile is
  899.                                                updated via resetSet so that
  900.                                                only Item3 remains.
  901.  
  902.  Housekeeping           test.cpp               The profile data set is
  903.                                                closed, and a regular
  904.                                                sequential file is opened for
  905.                                                the subsequent EA object
  906.                                                testing.
  907.  
  908.  Create an              test.cpp               The EAOptionSet constructor is
  909.  EAOptionSet                                   invoked, using the
  910.                                                INIOptionSet object from
  911.                                                above, and the file handle for
  912.                                                the sequential dataset opened
  913.                                                above.
  914.  
  915.                         EAOptionSet.cpp        The constructor sends the set
  916.                                                name to the BOptionSet
  917.                                                constructor for later use as
  918.                                                the application name.  The
  919.                                                handle of the file is retained
  920.  
  921.  reset EAs to known     test.cpp               The resetSet operation is
  922.  values                                        invoked which eliminates all
  923.                                                option items currently stored
  924.                                                as EAs, and replaces them with
  925.                                                the current set contents, in
  926.                                                this case, Item3.
  927.  
  928.  Exercise the EA        test.cpp               Items are added to the set;
  929.  methods                                       the set is stored; values are
  930.                                                changed and reset in a manner
  931.                                                similar to the tests for the
  932.                                                INIOptionSet.
  933.  
  934. There are several things worth keeping in mind.  The flow for the above example 
  935. is designed to test the logic, and not how the objects would be used in a real 
  936. application.  In a real application, the steps for use would be: 
  937.  
  938.  1. The data objects for all of the program options are declared, and initial 
  939.     values are given.  All of these items must be derived from BOptionItem. The 
  940.     object may be derived from more than one class. 
  941.  2. The INIOptionSet item is declared using either the handle to an INI file 
  942.     just for this application, or the handle to the user ini file 
  943.     (HINI_USERPROFILE). 
  944.  3. The loadSet operation is invoked to pick up any defaults established by the 
  945.     user. 
  946.  
  947. If there are file specific options, then the EAOptionSet object is used as 
  948. well.  The EAOptionSet object would be created from the INIOptionSet object, 
  949. and if there were any non-file related options, those option items would be 
  950. removed from the EAOptionSet via the delItem operation. 
  951.  
  952. At an appropriate time, the option sets would be written to either the profile 
  953. or the EAs or both. 
  954.  
  955. Controlling Yourself:  A Framework for Configurable Options - EDM/2 - Dec 1994 
  956. - Volume 2, Issue 11 
  957.  
  958.  
  959. ΓòÉΓòÉΓòÉ 3.4. Implementation notes ΓòÉΓòÉΓòÉ
  960.  
  961. Implementation notes 
  962.  
  963. This section discusses some of the less obvious aspects of the set classes. To 
  964. warrant the appellation of "less obvious," it had to be something I needed some 
  965. effort to figure out.  Which of course means that they may be completely 
  966. obvious to you. 
  967.  
  968. Profiles 
  969.  
  970. The API for the profile was just a joy to use (compared to EAs), and the 
  971. documentation makes everything pretty clear. 
  972.  
  973. EAs 
  974.  
  975. The EAs are not at all the same story.  I used two books, and the 
  976. documentation, to puzzle my way through how EAs worked.  The books used were 
  977. The Art of OS/2 2.1 C Programming, by Panov, Salomon, and Panov; the other book 
  978. was OS/2 2.1 Application Programmers Guide, by Kelly et. al. 
  979.  
  980. The EAOptionSet operations work on all of the EAs at once.  That way there are 
  981. no windows where a a particular EA may change, or the ordinal positions of the 
  982. EAs change. 
  983.  
  984. The layout of the EA buffer is as follows: 
  985.  
  986. FEA2           The head for each item in the buffer.  The cbName contains the 
  987.                length of the name field (NULL terminated) and cbValue contains 
  988.                the length of the data portion.  The offset to the next item is 
  989.                sizeof(FEA2) + cbValue + cbName + this position + enough to get 
  990.                you to the next mod 4 offset. The value of the cbValue element 
  991.                is the size of the flat data item + two SHORTs. 
  992. ItemType       At FEA2 + cbName + 1 is the EAT value.  In our case it is either 
  993.                EAT_BINARY or EAT_ASCII.  There are other options available, but 
  994.                the remainder of this discussion would not apply. 
  995. Size           The is the size in bytes of the flattened data item 
  996.  
  997. Controlling Yourself:  A Framework for Configurable Options - EDM/2 - Dec 1994 
  998. - Volume 2, Issue 11 
  999.  
  1000.  
  1001. ΓòÉΓòÉΓòÉ 3.5. Wrap-up ΓòÉΓòÉΓòÉ
  1002.  
  1003. Wrap-up 
  1004.  
  1005. This article has present a set of classes that can be used to manage an 
  1006. application's set of options items.  The basic model is that there exists a set 
  1007. of default values for the options of an application; the user is permitted to 
  1008. modify those options in a persistent manner; and that there may be some sub-set 
  1009. of options that are associated with particular files. 
  1010.  
  1011. Industrial Strength. 
  1012.  
  1013. There are several obvious features lacking which are required for an industrial 
  1014. strength implementation.  In the case of EAs, there are no checks for exclusive 
  1015. control while writing or reading the set of EAs from the file. In the realm of 
  1016. memory management, there are no safeguards against bad pointers. 
  1017.  
  1018. One extension worthwhile making is that BOptionItems could have reference 
  1019. counts and perhaps references lists of the option sets where they are members. 
  1020.  
  1021. Useful Test Tools 
  1022.  
  1023. There are a couple of REXX scripts included in the zip file.  In addition, 
  1024. there are several very useful tools at ftp-os2 such as EABrowse. The REXX 
  1025. script to print EAs only works when the the test program has closed the file, 
  1026. but I have no idea why I could not get it to share read access. 
  1027.  
  1028. Controlling Yourself:  A Framework for Configurable Options - EDM/2 - Dec 1994 
  1029. - Volume 2, Issue 11 
  1030.  
  1031.  
  1032. ΓòÉΓòÉΓòÉ 4. The Design and Implementation of VIOWIN:  Part 4 ΓòÉΓòÉΓòÉ
  1033.  
  1034.  
  1035. ΓòÉΓòÉΓòÉ 4.1. Introduction ΓòÉΓòÉΓòÉ
  1036.  
  1037. The Design and Implementation of VIOWIN:  Part 4 
  1038.  
  1039. Written by Larry Salomon, Jr. 
  1040.  
  1041. Introduction 
  1042.  
  1043. For my job, I once had to write an application that ran only when OS/2 booted 
  1044. from the floppy diskettes.  Because I had no access to the functionality PM 
  1045. provides, I resorted to a line-oriented interface, where messages were 
  1046. displayed on the screen and scrolled up when necessary.  It was a good 
  1047. interface, I thought; it was fully NLS enabled and had intelligent defaults so 
  1048. the user basically only had to type in the name of the application. 
  1049. Unfortunately, the Quality Assurance team didn't concur with my opinion.  "We 
  1050. want a nice interface!" one exclaimed.  "Yeah, one with different windows and 
  1051. such!" another shouted. 
  1052.  
  1053. I was backed into a corner that I could only get out of one way. 
  1054.  
  1055. This series describes the design and implementation of VIOWIN, a library that 
  1056. implements a subset of the Win APIs provided by PM for fullscreen sessions. 
  1057. The reasoning behind writing this series is that it provided me and will 
  1058. hopefully provide you with some unique insights into how a windowing system is 
  1059. developed; and since it is based on PM, your familiarity with the already 
  1060. defined interface will increase your capability to fully understand what is 
  1061. being described. 
  1062.  
  1063. Obviously, this series assumes you have PM application development experience, 
  1064. but it isn't required. 
  1065.  
  1066. Last Month 
  1067.  
  1068. Last month, we finished looking at the remaining functions that are called from 
  1069. within the main() function, and realized that everything so far was quite 
  1070. trivial in nature.  This month, we will delve into the window management 
  1071. functions including vwSendMsg() and vwPostMsg(). 
  1072.  
  1073. The Design and Implementation of VIOWIN:  Part 4 - EDM/2 - Dec 1994 - Volume 2, 
  1074. Issue 11 
  1075.  
  1076.  
  1077. ΓòÉΓòÉΓòÉ 4.2. Window Management ΓòÉΓòÉΓòÉ
  1078.  
  1079. Window Management 
  1080.  
  1081. Window management functions are, in my opinion, functions which act upon 
  1082. windows in some fashion.  The list of functions within VIOWIN that fall into 
  1083. this category are listed below: 
  1084.  
  1085. o (MRESULT)vwDefWindowProc(hwndWnd,ulMsg,mpParm1,mpParm2); 
  1086. o (BOOL)vwEnableWindow(hwndWnd,bEnable); 
  1087. o (BOOL)vwInSendMsg(VOID); 
  1088. o (BOOL)vwIsWindow(hwndWnd); 
  1089. o (BOOL)vwIsWindowEnabled(hwndWnd); 
  1090. o (BOOL)vwPostMsg(hwndWnd,ulMsg,mpParm1,mpParm2); 
  1091. o (LONG)vwQueryBackColor(hwndWnd); 
  1092. o (HVWWND)vwQueryFocus(VOID); 
  1093. o (LONG)vwQueryForeColor(hwndWnd); 
  1094. o (HVWWND)vwQueryWindow(hwndWnd,lCmd); 
  1095. o (BOOL)vwQueryWindowPos(hwndWnd,pswpSwp); 
  1096. o (PVOID)vwQueryWindowPtr(hwndWnd,ulPtr); 
  1097. o (BOOL)vwQueryWindowRect(hwndWnd,prclRect); 
  1098. o (BOOL)vwQueryWindowText(hwndWnd,ulSzBuf,pchBuf); 
  1099. o (ULONG)vwQueryWindowTextLength(hwndWnd); 
  1100. o (LONG)vwQueryWindowULong(hwndWnd,lIndex); 
  1101. o (SHORT)vwQueryWindowUShort(hwndWnd,lIndex); 
  1102. o (MRESULT)vwSendMsg(hwndWnd,ulMsg,mpParm1,mpParm2); 
  1103. o (LONG)vwSetBackColor(hwndWnd,lColor); 
  1104. o (BOOL)vwSetFocus(hwndWnd); 
  1105. o (BOOL)vwSetForeColor(hwndWnd,lColor); 
  1106. o (BOOL)vwSetWindowPtr(hwndWnd,ulPtr,pvData); 
  1107. o (BOOL)vwSetWindowText(hwndWnd,pchText); 
  1108. o (BOOL)vwSetWindowULong(hwndWnd,lIndex,ulValue); 
  1109. o (BOOL)vwSetWindowUShort(hwndWnd,lIndex,usValue); 
  1110. o (BOOL)vwUpdateWindow(hwndWnd); 
  1111. o (HVWWND)vwWindowFromID(usId); 
  1112.  
  1113. What a list!  Fortunately, many of these functions are quite small in their 
  1114. implementation, so they will not occupy much space or take up too much time in 
  1115. their discussion. 
  1116.  
  1117. Note! 
  1118.  
  1119. It needs to be pointed out again that color support differs greatly from PM, 
  1120. due to the lack of a Gpi component. 
  1121.  
  1122. The Design and Implementation of VIOWIN:  Part 4 - EDM/2 - Dec 1994 - Volume 2, 
  1123. Issue 11 
  1124.  
  1125.  
  1126. ΓòÉΓòÉΓòÉ 4.3. Out Comes Our Spectacles ΓòÉΓòÉΓòÉ
  1127.  
  1128. Out Comes Our Spectacles 
  1129.  
  1130. Let us take a closer look at each of the functions. 
  1131.  
  1132. vwDefWindowProc 
  1133.  
  1134. This is probably one of the more interesting functions, since its corresponding 
  1135. function in PM is not well-documented.  Notice that only three messages are 
  1136. actually processed and the rest are simply ignored.  Another important note is 
  1137. that this function actually handles the allocation of the window text in the 
  1138. WM_SETWINDOWPARAMS message. 
  1139.  
  1140. vwEnableWindow 
  1141.  
  1142. VIOWIN does nothing with the WS_DISABLED bit, though it probably should check 
  1143. this before allowing keystrokes to go to the window. 
  1144.  
  1145. vwInSendMsg 
  1146.  
  1147. One of the many trivial functions. 
  1148.  
  1149. vwIsWindow 
  1150.  
  1151. Another of the many trivial functions. 
  1152.  
  1153. vwIsWindowEnabled 
  1154.  
  1155. Another of the many trivial functions. 
  1156.  
  1157. vwPostMsg 
  1158.  
  1159. This function initializes the VWQMSG structure directly in the message queue, 
  1160. after first checking that there is space to store the message. In the first 
  1161. part of this series, you saw that the size of the queue was defined to be 
  1162. VW_SIZEQUEUE.  This can be anything, and it is currently defined to be 50. 
  1163.  
  1164. vwQueryBackColor 
  1165.  
  1166. Another of the many trivial functions. 
  1167.  
  1168. vwQueryFocus 
  1169.  
  1170. Another of the many trivial functions. 
  1171.  
  1172. vwQueryForeColor 
  1173.  
  1174. Another of the many trivial functions. 
  1175.  
  1176. vwQueryWindow 
  1177.  
  1178. Another of the many trivial functions, if you are familiar with the linked-list 
  1179. routines of Common/2. 
  1180.  
  1181. vwQueryWindowText 
  1182.  
  1183. This function is how I envisioned PM performing the same task. 
  1184.  
  1185. vwQueryWindowPos 
  1186.  
  1187. Another of the many trivial functions. 
  1188.  
  1189. vwQueryWindowPtr 
  1190.  
  1191. Another of the many trivial functions.  Note the restriction on the index of 
  1192. the pointer to be retrieved. 
  1193.  
  1194. vwQueryWindowRect 
  1195.  
  1196. Another of the many trivial functions. 
  1197.  
  1198. vwQueryWindowTextLength 
  1199.  
  1200. This function is how I envisioned PM performing the same task. 
  1201.  
  1202. vwQueryWindowULong 
  1203.  
  1204. Note that only QWL_STYLE is accepted.  I probably could have added QWL_HMQ and 
  1205. QWL_USER, but I was lazy. 
  1206.  
  1207. vwQueryWindowUShort 
  1208.  
  1209. Note that only QWS_ID is accepted.  I probably could have added QWS_USER here 
  1210. also. 
  1211.  
  1212. vwSendMsg 
  1213.  
  1214. This is fairly trivial, once you see what it is doing, but I never really 
  1215. thought about this function until I had to implement it.  PM's version of this 
  1216. function is significantly more complicated, since I has to address sending 
  1217. across processes, etc. 
  1218.  
  1219. vwSetBackColor 
  1220.  
  1221. Another of the many trivial functions. 
  1222.  
  1223. vwSetFocus 
  1224.  
  1225. Note that I do not send the WM_SETSELECTION nor the WM_ACTIVATE message. 
  1226.  
  1227. vwSetForeColor 
  1228.  
  1229. Another of the many trivial functions. 
  1230.  
  1231. vwSetWindowPtr 
  1232.  
  1233. Another of the many trivial functions. 
  1234.  
  1235. vwSetWindowText 
  1236.  
  1237. This function is how I envisioned PM performing the same task, with the 
  1238. exception that PM uses heaps for memory allocation and I use the memory 
  1239. management routines within Common/2. 
  1240.  
  1241. vwSetWindowULong 
  1242.  
  1243. Note that only QWL_STYLE is accepted. 
  1244.  
  1245. vwSetWindowUShort 
  1246.  
  1247. This is a stub in case I decide to accept QWS_USER in the future. 
  1248.  
  1249. vwUpdateWindow 
  1250.  
  1251. This is an interesting function.  If the window is the desktop, we cannot 
  1252. simply send it a WM_PAINT message, since it will erase its children in the 
  1253. process.  So, we send the desktop first and then the children each a WM_PAINT 
  1254. message. 
  1255.  
  1256. vwWindowFromID 
  1257.  
  1258. Another of the many trivial functions. 
  1259.  
  1260. The Design and Implementation of VIOWIN:  Part 4 - EDM/2 - Dec 1994 - Volume 2, 
  1261. Issue 11 
  1262.  
  1263.  
  1264. ΓòÉΓòÉΓòÉ 4.4. Summary ΓòÉΓòÉΓòÉ
  1265.  
  1266. Summary 
  1267.  
  1268. This month, much code was presented (see viowin4.zip) and the interesting 
  1269. details were discussed.  I do realize that I have probably glossed over a lot 
  1270. of things; if there is something more you would like me to discuss, please send 
  1271. me email.  I must add, though, that a lot of the "way it fits together" didn't 
  1272. actually occur to me until I needed to use the functions that I wrote, which 
  1273. resulted in modifications to the original attempts at the implementation of 
  1274. these functions. 
  1275.  
  1276. Next month, we will look at the remaining sections of the base library: 
  1277. timers, cursors, drawing, rectangles, and anything else I have missed.  The 
  1278. following parts will then begin to look at the implementation of the window 
  1279. classes:  buttons, entryfields, and static controls. 
  1280.  
  1281. The Design and Implementation of VIOWIN:  Part 4 - EDM/2 - Dec 1994 - Volume 2, 
  1282. Issue 11 
  1283.  
  1284.  
  1285. ΓòÉΓòÉΓòÉ <hidden> vwDefWindowProc ΓòÉΓòÉΓòÉ
  1286.  
  1287. vwDefWindowProc 
  1288.  
  1289. MRESULT EXPENTRY vwDefWindowProc(HVWWND hwndWnd,
  1290.                                  ULONG ulMsg,
  1291.                                  MPARAM mpParm1,
  1292.                                  MPARAM mpParm2)
  1293. //-------------------------------------------------------------------------
  1294. // This is the default window procedure for windows.  It processes certain
  1295. // messages only.  Others, it passes to the desktop window, unless it is
  1296. // the desktop window which is calling this function.
  1297. //-------------------------------------------------------------------------
  1298. {
  1299.    HVWWND hwndDesktop;
  1300.  
  1301.    if (!vwIsWindow(hwndWnd)) {
  1302.       return MRFROMLONG(0);
  1303.    } /* endif */
  1304.  
  1305.    hwndDesktop=vwWindowFromID(VWWID_DESKTOP);
  1306.  
  1307.    switch (ulMsg) {
  1308.    case WM_QUERYWINDOWPARAMS:
  1309.       {
  1310.          PWNDPARAMS pwpParms;
  1311.  
  1312.          pwpParms=(PWNDPARAMS)PVOIDFROMMP(mpParm1);
  1313.  
  1314.          switch (pwpParms->fsStatus) {
  1315.          case WPM_CCHTEXT:
  1316.             pwpParms->cchText=strlen(hwndWnd->pchText)+1;
  1317.             return MRFROMLONG(TRUE);
  1318.          case WPM_TEXT:
  1319.             *pwpParms->pszText=0;
  1320.             strncat(pwpParms->pszText,hwndWnd->pchText,pwpParms->cchText-1);
  1321.             return MRFROMLONG(TRUE);
  1322.          default:
  1323.             break;
  1324.          } /* endswitch */
  1325.       }
  1326.       break;
  1327.    case WM_SETWINDOWPARAMS:
  1328.       {
  1329.          PWNDPARAMS pwpParms;
  1330.  
  1331.          pwpParms=(PWNDPARAMS)PVOIDFROMMP(mpParm1);
  1332.  
  1333.          switch (pwpParms->fsStatus) {
  1334.          case WPM_TEXT:
  1335.             {
  1336.                PCHAR pchText;
  1337.  
  1338.                CmnMemAllocate(habAnchor->hcmWork,
  1339.                               pwpParms->cchText,
  1340.                               (PPVOID)&pchText);
  1341.                if (pchText==NULL) {
  1342.                   return FALSE;
  1343.                } /* endif */
  1344.  
  1345.                strcpy(pchText,pwpParms->pszText);
  1346.  
  1347.                if (hwndWnd->pchText!=NULL) {
  1348.                   CmnMemFree(habAnchor->hcmWork,hwndWnd->pchText);
  1349.                } /* endif */
  1350.  
  1351.                hwndWnd->pchText=pchText;
  1352.                return MRFROMLONG(TRUE);
  1353.             }
  1354.          default:
  1355.             break;
  1356.          } /* endswitch */
  1357.       }
  1358.       break;
  1359.    case WM_CLOSE:
  1360.       vwPostMsg(hwndWnd,WM_QUIT,0,0);
  1361.       return MRFROMLONG(0);
  1362.    default:
  1363.       break;
  1364.    } /* endswitch */
  1365.  
  1366.    if (hwndWnd!=hwndDesktop) {
  1367.       //-------------------------------------------------------------------
  1368.       // If the message wasn't processed and the window isn't the desktop,
  1369.       // pass the message up the chain.  Some messages get sent; the rest
  1370.       // get posted.
  1371.       //-------------------------------------------------------------------
  1372.       switch (ulMsg) {
  1373.       case WM_COMMAND:
  1374.       case WM_CONTROL:
  1375.       case WM_CHAR:
  1376.          if (vwInSendMsg()) {
  1377.             return vwSendMsg(hwndDesktop,ulMsg,mpParm1,mpParm2);
  1378.          } else {
  1379.             vwPostMsg(hwndDesktop,ulMsg,mpParm1,mpParm2);
  1380.             return MRFROMLONG(0);
  1381.          } /* endif */
  1382.       default:
  1383.          return MRFROMLONG(0);
  1384.       } /* endswitch */
  1385.    } else {
  1386.       return MRFROMLONG(0);
  1387.    } /* endif */
  1388. }
  1389.  
  1390. The Design and Implementation of VIOWIN:  Part 4 - EDM/2 - Dec 1994 - Volume 2, 
  1391. Issue 11 
  1392.  
  1393.  
  1394. ΓòÉΓòÉΓòÉ <hidden> vwEnableWindow ΓòÉΓòÉΓòÉ
  1395.  
  1396. vwEnableWindow 
  1397.  
  1398. BOOL EXPENTRY vwEnableWindow(HVWWND hwndWnd,BOOL bEnable)
  1399. //-------------------------------------------------------------------------
  1400. // This function sets or resets the WS_DISABLED bit for the window.
  1401. //
  1402. // Input:  hwndWnd - specifies the window handle
  1403. //         bEnable - flag indicating the desired enabled state
  1404. // Returns:  TRUE if successful, FALSE otherwise
  1405. //-------------------------------------------------------------------------
  1406. {
  1407.    if (!vwIsWindow(hwndWnd)) {
  1408.       return FALSE;
  1409.    } /* endif */
  1410.  
  1411.    if (bEnable) {
  1412.       hwndWnd->ulStyle&=~WS_DISABLED;
  1413.    } else {
  1414.       hwndWnd->ulStyle|=WS_DISABLED;
  1415.    } /* endif */
  1416.  
  1417.    return TRUE;
  1418. }
  1419.  
  1420. The Design and Implementation of VIOWIN:  Part 4 - EDM/2 - Dec 1994 - Volume 2, 
  1421. Issue 11 
  1422.  
  1423.  
  1424. ΓòÉΓòÉΓòÉ <hidden> vwInSendMsg ΓòÉΓòÉΓòÉ
  1425.  
  1426. vwInSendMsg 
  1427.  
  1428. BOOL EXPENTRY vwInSendMsg(VOID)
  1429. //-------------------------------------------------------------------------
  1430. // This function determines if the system is in the middle of processing
  1431. // a vwSendMsg() call.
  1432. //
  1433. // Returns:  TRUE if in vwSendMsg(), FALSE otherwise
  1434. //-------------------------------------------------------------------------
  1435. {
  1436.    if (hmqQueue==NULL) {
  1437.       return FALSE;
  1438.    } /* endif */
  1439.  
  1440.    return habAnchor->bIsSendMsg;
  1441. }
  1442.  
  1443. The Design and Implementation of VIOWIN:  Part 4 - EDM/2 - Dec 1994 - Volume 2, 
  1444. Issue 11 
  1445.  
  1446.  
  1447. ΓòÉΓòÉΓòÉ <hidden> vwIsWindow ΓòÉΓòÉΓòÉ
  1448.  
  1449. vwIsWindow 
  1450.  
  1451. BOOL EXPENTRY vwIsWindow(HVWWND hwndWnd)
  1452. //-------------------------------------------------------------------------
  1453. // This function determines if the specified handle is a valid window
  1454. // handle or not.
  1455. //
  1456. // Input:  hwndWnd - handle to query
  1457. // Returns:  TRUE if the handle is valid, FALSE otherwise
  1458. //-------------------------------------------------------------------------
  1459. {
  1460.    ULONG ulNumItems;
  1461.    ULONG ulIndex;
  1462.    HVWWND hwndList;
  1463.  
  1464.    if (hmqQueue==NULL) {
  1465.       return FALSE;
  1466.    } /* endif */
  1467.  
  1468.    ulNumItems=CmnLstQueryRecordCount(habAnchor->hclWindows);
  1469.  
  1470.    for (ulIndex=0; ulIndex<ulNumItems; ulIndex++) {
  1471.       if (ulIndex==0) {
  1472.          hwndList=(HVWWND)CmnLstQueryRecord(habAnchor->hclWindows,0);
  1473.       } else {
  1474.          hwndList=(HVWWND)CmnLstQueryRelative(hwndList,LQR_NEXT);
  1475.       } /* endif */
  1476.  
  1477.       if (hwndList==hwndWnd) {
  1478.          return TRUE;
  1479.       } /* endif */
  1480.    } /* endfor */
  1481.  
  1482.    return FALSE;
  1483. }
  1484.  
  1485. The Design and Implementation of VIOWIN:  Part 4 - EDM/2 - Dec 1994 - Volume 2, 
  1486. Issue 11 
  1487.  
  1488.  
  1489. ΓòÉΓòÉΓòÉ <hidden> vwIsWindowEnabled ΓòÉΓòÉΓòÉ
  1490.  
  1491. vwIsWindowEnabled 
  1492.  
  1493. BOOL EXPENTRY vwIsWindowEnabled(HVWWND hwndWnd)
  1494. //-------------------------------------------------------------------------
  1495. // This function determines if the window is enabled or not.
  1496. //
  1497. // Input:  hwndWnd - handle to query
  1498. // Returns:  TRUE if the window is enabled, FALSE otherwise
  1499. //-------------------------------------------------------------------------
  1500. {
  1501.    if (!vwIsWindow(hwndWnd)) {
  1502.       return FALSE;
  1503.    } /* endif */
  1504.  
  1505.    return ((hwndWnd->ulStyle & WS_DISABLED)==0);
  1506. }
  1507.  
  1508. The Design and Implementation of VIOWIN:  Part 4 - EDM/2 - Dec 1994 - Volume 2, 
  1509. Issue 11 
  1510.  
  1511.  
  1512. ΓòÉΓòÉΓòÉ <hidden> vwPostMsg ΓòÉΓòÉΓòÉ
  1513.  
  1514. vwPostMsg 
  1515.  
  1516. BOOL EXPENTRY vwPostMsg(HVWWND hwndWnd,
  1517.                         ULONG ulMsg,
  1518.                         MPARAM mpParm1,
  1519.                         MPARAM mpParm2)
  1520. //-------------------------------------------------------------------------
  1521. // This function sends a message to the window.  This is done by
  1522. // building a VWQMSG structure and placing it in the queue.
  1523. //
  1524. // Input:  hwndWnd - specifies the window handle
  1525. //         ulMsg - specifies the message
  1526. //         mpParm1, mpParm2 - specify the message parameters
  1527. // Returns:  TRUE if successful, FALSE otherwise
  1528. //-------------------------------------------------------------------------
  1529. {
  1530.    PVWQMSG pqmMsg;
  1531.  
  1532.    if (hwndWnd==VWHWND_DESKTOP) {
  1533.       hwndWnd=vwWindowFromID(VWWID_DESKTOP);
  1534.    } /* endif */
  1535.  
  1536.    if (!vwIsWindow(hwndWnd)) {
  1537.       return FALSE;
  1538.    } /* endif */
  1539.  
  1540.    if (((hmqQueue->ulHead==0) && (hmqQueue->ulTail==VW_SIZEQUEUE)) ||
  1541.        (hmqQueue->ulHead==hmqQueue->ulTail+1)) {
  1542.       return FALSE;
  1543.    } /* endif */
  1544.  
  1545.    hmqQueue->ulTail++;
  1546.    if (hmqQueue->ulTail==VW_SIZEQUEUE) {
  1547.       hmqQueue->ulTail=0;
  1548.    } /* endif */
  1549.  
  1550.    pqmMsg=&hmqQueue->aqmMsgs[hmqQueue->ulTail];
  1551.  
  1552.    pqmMsg->hwndWnd=hwndWnd;
  1553.    pqmMsg->ulMsg=ulMsg;
  1554.    pqmMsg->mpParm1=mpParm1;
  1555.    pqmMsg->mpParm2=mpParm2;
  1556.    return TRUE;
  1557. }
  1558.  
  1559. The Design and Implementation of VIOWIN:  Part 4 - EDM/2 - Dec 1994 - Volume 2, 
  1560. Issue 11 
  1561.  
  1562.  
  1563. ΓòÉΓòÉΓòÉ <hidden> vwQueryBackColor ΓòÉΓòÉΓòÉ
  1564.  
  1565. vwQueryBackColor 
  1566.  
  1567. LONG EXPENTRY vwQueryBackColor(HVWWND hwndWnd)
  1568. //-------------------------------------------------------------------------
  1569. // This function returns the background color of the window.
  1570. //
  1571. // Input:  hwndWnd - specifies the window handle
  1572. // Returns:  VWCLR_* constant if successful, -1 otherwise
  1573. //-------------------------------------------------------------------------
  1574. {
  1575.    if (hwndWnd==VWHWND_DESKTOP) {
  1576.       hwndWnd=vwWindowFromID(VWWID_DESKTOP);
  1577.    } /* endif */
  1578.  
  1579.    if (!vwIsWindow(hwndWnd)) {
  1580.       return -1;
  1581.    } /* endif */
  1582.  
  1583.    return hwndWnd->lBackClr;
  1584. }
  1585.  
  1586. The Design and Implementation of VIOWIN:  Part 4 - EDM/2 - Dec 1994 - Volume 2, 
  1587. Issue 11 
  1588.  
  1589.  
  1590. ΓòÉΓòÉΓòÉ <hidden> vwQueryFocus ΓòÉΓòÉΓòÉ
  1591.  
  1592. vwQueryFocus 
  1593.  
  1594. HVWWND EXPENTRY vwQueryFocus(VOID)
  1595. //-------------------------------------------------------------------------
  1596. // This function returns the handle of the window currently with the
  1597. // focus.
  1598. //
  1599. // Returns:  window handle or NULLHANDLE if no window has the focus
  1600. //-------------------------------------------------------------------------
  1601. {
  1602.    //----------------------------------------------------------------------
  1603.    // Make sure we were initialized
  1604.    //----------------------------------------------------------------------
  1605.    if (hmqQueue==NULL) {
  1606.       return NULL;
  1607.    } /* endif */
  1608.  
  1609.    return habAnchor->hwndFocus;
  1610. }
  1611.  
  1612. The Design and Implementation of VIOWIN:  Part 4 - EDM/2 - Dec 1994 - Volume 2, 
  1613. Issue 11 
  1614.  
  1615.  
  1616. ΓòÉΓòÉΓòÉ <hidden> vwQueryForeColor ΓòÉΓòÉΓòÉ
  1617.  
  1618. vwQueryForeColor 
  1619.  
  1620. LONG EXPENTRY vwQueryForeColor(HVWWND hwndWnd)
  1621. //-------------------------------------------------------------------------
  1622. // This function returns the foreground color of the window.
  1623. //
  1624. // Input:  hwndWnd - specifies the window handle
  1625. // Returns:  VWCLR_* constant if successful, -1 otherwise
  1626. //-------------------------------------------------------------------------
  1627. {
  1628.    if (hwndWnd==VWHWND_DESKTOP) {
  1629.       hwndWnd=vwWindowFromID(VWWID_DESKTOP);
  1630.    } /* endif */
  1631.  
  1632.    if (!vwIsWindow(hwndWnd)) {
  1633.       return -1;
  1634.    } /* endif */
  1635.  
  1636.    return hwndWnd->lForeClr;
  1637. }
  1638.  
  1639. The Design and Implementation of VIOWIN:  Part 4 - EDM/2 - Dec 1994 - Volume 2, 
  1640. Issue 11 
  1641.  
  1642.  
  1643. ΓòÉΓòÉΓòÉ <hidden> vwQueryWindow ΓòÉΓòÉΓòÉ
  1644.  
  1645. vwQueryWindow 
  1646.  
  1647. HVWWND EXPENTRY vwQueryWindow(HVWWND hwndWnd,LONG lCmd)
  1648. //-------------------------------------------------------------------------
  1649. // This function returns the handle of a window relative to the specified
  1650. // window.
  1651. //
  1652. // Input:  hwndWnd - specifies the window handle
  1653. //         lCmd - specifies a QW_* constant
  1654. // Returns:  window handle if successful, NULLHANDLE otherwise
  1655. //-------------------------------------------------------------------------
  1656. {
  1657.    ULONG ulNumRecs;
  1658.    HVWWND hwndReturn;
  1659.  
  1660.    if (!vwIsWindow(hwndWnd)) {
  1661.       return NULL;
  1662.    } /* endif */
  1663.  
  1664.    ulNumRecs=CmnLstQueryRecordCount(habAnchor->hclWindows);
  1665.  
  1666.    switch (lCmd) {
  1667.    case QW_NEXT:
  1668.       hwndReturn=CmnLstQueryRelative(hwndWnd,LQR_NEXT);
  1669.       break;
  1670.    case QW_PREV:
  1671.       hwndReturn=CmnLstQueryRelative(hwndWnd,LQR_PREVIOUS);
  1672.  
  1673.       if (hwndReturn==vwWindowFromID(VWWID_DESKTOP)) {
  1674.          hwndReturn=NULL;
  1675.       } /* endif */
  1676.       break;
  1677.    case QW_TOP:
  1678.       hwndReturn=CmnLstQueryRecord(habAnchor->hclWindows,1);
  1679.       break;
  1680.    case QW_BOTTOM:
  1681.       hwndReturn=CmnLstQueryRecord(habAnchor->hclWindows,ulNumRecs-1);
  1682.       break;
  1683.    default:
  1684.       return NULL;
  1685.    } /* endswitch */
  1686.  
  1687.    return hwndReturn;
  1688. }
  1689.  
  1690. The Design and Implementation of VIOWIN:  Part 4 - EDM/2 - Dec 1994 - Volume 2, 
  1691. Issue 11 
  1692.  
  1693.  
  1694. ΓòÉΓòÉΓòÉ <hidden> vwQueryWindowText ΓòÉΓòÉΓòÉ
  1695.  
  1696. vwQueryWindowText 
  1697.  
  1698. BOOL EXPENTRY vwQueryWindowText(HVWWND hwndWnd,ULONG ulSzBuf,PCHAR pchBuf)
  1699. //-------------------------------------------------------------------------
  1700. // This function returns the window text by sending the window a
  1701. // WM_QUERYWINDOWPARAMS message.
  1702. //
  1703. // Input:  hwndWnd - specifies the window handle
  1704. //         ulSzBuf - specifies the size of the buffer
  1705. //         pchBuf - points to the buffer to receive the text
  1706. // Output:  pchBuf - points to the buffer containing the text
  1707. // Returns:  TRUE if successful, FALSE otherwise
  1708. //-------------------------------------------------------------------------
  1709. {
  1710.    WNDPARAMS wpParms;
  1711.  
  1712.    if (!vwIsWindow(hwndWnd)) {
  1713.       return FALSE;
  1714.    } /* endif */
  1715.  
  1716.    if (pchBuf==NULL) {
  1717.       return FALSE;
  1718.    } /* endif */
  1719.  
  1720.    wpParms.fsStatus=WPM_TEXT;
  1721.    wpParms.cchText=ulSzBuf;
  1722.    wpParms.pszText=pchBuf;
  1723.  
  1724.    if (SHORT1FROMMP(vwSendMsg(hwndWnd,
  1725.                               WM_QUERYWINDOWPARAMS,
  1726.                               MPFROMP(&wpParms),
  1727.                               0))) {
  1728.       return TRUE;
  1729.    } else {
  1730.       return FALSE;
  1731.    } /* endif */
  1732. }
  1733.  
  1734. The Design and Implementation of VIOWIN:  Part 4 - EDM/2 - Dec 1994 - Volume 2, 
  1735. Issue 11 
  1736.  
  1737.  
  1738. ΓòÉΓòÉΓòÉ <hidden> vwQueryWindowPos ΓòÉΓòÉΓòÉ
  1739.  
  1740. vwQueryWindowPos 
  1741.  
  1742. BOOL EXPENTRY vwQueryWindowPos(HVWWND hwndWnd,PVWSWP pswpSwp)
  1743. //-------------------------------------------------------------------------
  1744. // This function returns the size and position of the window.
  1745. //
  1746. // Input:  hwndWnd - specifies the window handle
  1747. //         pswpSwp - points to the VWSWP structure to receive the result
  1748. // Output:  pswpSwp - points to the VWSWP structure containing the result
  1749. // Returns:  TRUE if successful, FALSE otherwise
  1750. //-------------------------------------------------------------------------
  1751. {
  1752.    if (!vwIsWindow(hwndWnd)) {
  1753.       return FALSE;
  1754.    } /* endif */
  1755.  
  1756.    (*pswpSwp)=hwndWnd->swpSwp;
  1757.    return TRUE;
  1758. }
  1759.  
  1760. The Design and Implementation of VIOWIN:  Part 4 - EDM/2 - Dec 1994 - Volume 2, 
  1761. Issue 11 
  1762.  
  1763.  
  1764. ΓòÉΓòÉΓòÉ <hidden> vwQueryWindowPtr ΓòÉΓòÉΓòÉ
  1765.  
  1766. vwQueryWindowPtr 
  1767.  
  1768. PVOID EXPENTRY vwQueryWindowPtr(HVWWND hwndWnd,ULONG ulPtr)
  1769. //-------------------------------------------------------------------------
  1770. // This function returns the specified window pointer.
  1771. //
  1772. // Input:  hwndWnd - specifies the window handle
  1773. //         ulPtr - specifies 0 or 1
  1774. // Returns:  specified window pointer if successful, NULL otherwise
  1775. //-------------------------------------------------------------------------
  1776. {
  1777.    if (!vwIsWindow(hwndWnd)) {
  1778.       return NULL;
  1779.    } /* endif */
  1780.  
  1781.    if (ulPtr>1) {
  1782.       return NULL;
  1783.    } /* endif */
  1784.  
  1785.    return hwndWnd->pvData[ulPtr];
  1786. }
  1787.  
  1788. The Design and Implementation of VIOWIN:  Part 4 - EDM/2 - Dec 1994 - Volume 2, 
  1789. Issue 11 
  1790.  
  1791.  
  1792. ΓòÉΓòÉΓòÉ <hidden> vwQueryWindowRect ΓòÉΓòÉΓòÉ
  1793.  
  1794. vwQueryWindowRect 
  1795.  
  1796. BOOL EXPENTRY vwQueryWindowRect(HVWWND hwndWnd,PRECTL prclRect)
  1797. //-------------------------------------------------------------------------
  1798. // This function returns the size of the window.
  1799. //
  1800. // Input:  hwndWnd - specifies the window handle
  1801. //         prclRect - points to the RECTL structure to receive the result
  1802. // Output:  prclRect - points to the RECTL structure containing the result
  1803. // Returns:  TRUE if successful, FALSE otherwise
  1804. //-------------------------------------------------------------------------
  1805. {
  1806.    if (!vwIsWindow(hwndWnd)) {
  1807.       return FALSE;
  1808.    } /* endif */
  1809.  
  1810.    prclRect->xLeft=0;
  1811.    prclRect->yBottom=0;
  1812.    prclRect->xRight=hwndWnd->swpSwp.ulCx;
  1813.    prclRect->yTop=hwndWnd->swpSwp.ulCy;
  1814.    return TRUE;
  1815. }
  1816.  
  1817. The Design and Implementation of VIOWIN:  Part 4 - EDM/2 - Dec 1994 - Volume 2, 
  1818. Issue 11 
  1819.  
  1820.  
  1821. ΓòÉΓòÉΓòÉ <hidden> vwQueryWindowTextLength ΓòÉΓòÉΓòÉ
  1822.  
  1823. vwQueryWindowTextLength 
  1824.  
  1825. ULONG EXPENTRY vwQueryWindowTextLength(HVWWND hwndWnd)
  1826. //-------------------------------------------------------------------------
  1827. // This function returns the length of the window text.
  1828. //
  1829. // Input:  hwndWnd - specifies the window handle
  1830. // Returns:  length if successful, 0 otherwise
  1831. //-------------------------------------------------------------------------
  1832. {
  1833.    WNDPARAMS wpParms;
  1834.  
  1835.    if (!vwIsWindow(hwndWnd)) {
  1836.       return FALSE;
  1837.    } /* endif */
  1838.  
  1839.    wpParms.fsStatus=WPM_CCHTEXT;
  1840.  
  1841.    if (SHORT1FROMMP(vwSendMsg(hwndWnd,
  1842.                               WM_QUERYWINDOWPARAMS,
  1843.                               MPFROMP(&wpParms),
  1844.                               0))) {
  1845.       return wpParms.cchText;
  1846.    } else {
  1847.       return 0;
  1848.    } /* endif */
  1849. }
  1850.  
  1851. The Design and Implementation of VIOWIN:  Part 4 - EDM/2 - Dec 1994 - Volume 2, 
  1852. Issue 11 
  1853.  
  1854.  
  1855. ΓòÉΓòÉΓòÉ <hidden> vwQueryWindowULong ΓòÉΓòÉΓòÉ
  1856.  
  1857. vwQueryWindowULong 
  1858.  
  1859. LONG EXPENTRY vwQueryWindowULong(HVWWND hwndWnd,LONG lIndex)
  1860. //-------------------------------------------------------------------------
  1861. // This function returns the value of the specified window variable.
  1862. //
  1863. // Input:  hwndWnd - specifies the window handle
  1864. //         lIndex - specifies a QWL_* constant
  1865. // Returns:  value of the variable if successful, -1 otherwise
  1866. //-------------------------------------------------------------------------
  1867. {
  1868.    if (!vwIsWindow(hwndWnd)) {
  1869.       return -1;
  1870.    } /* endif */
  1871.  
  1872.    switch (lIndex) {
  1873.    case QWL_STYLE:
  1874.       return hwndWnd->ulStyle;
  1875.    default:
  1876.       return -1;
  1877.    } /* endswitch */
  1878. }
  1879.  
  1880. The Design and Implementation of VIOWIN:  Part 4 - EDM/2 - Dec 1994 - Volume 2, 
  1881. Issue 11 
  1882.  
  1883.  
  1884. ΓòÉΓòÉΓòÉ <hidden> vwQueryWindowUShort ΓòÉΓòÉΓòÉ
  1885.  
  1886. vwQueryWindowUShort 
  1887.  
  1888. SHORT EXPENTRY vwQueryWindowUShort(HVWWND hwndWnd,LONG lIndex)
  1889. //-------------------------------------------------------------------------
  1890. // This function returns the value of the specified window variable.
  1891. //
  1892. // Input:  hwndWnd - specifies the window handle
  1893. //         lIndex - specifies a QWS_* constant
  1894. // Returns:  value of the variable if successful, -1 otherwise
  1895. //-------------------------------------------------------------------------
  1896. {
  1897.    if (!vwIsWindow(hwndWnd)) {
  1898.       return -1;
  1899.    } /* endif */
  1900.  
  1901.    switch (lIndex) {
  1902.    case QWS_ID:
  1903.       return hwndWnd->usId;
  1904.    default:
  1905.       return -1;
  1906.    } /* endswitch */
  1907. }
  1908.  
  1909. The Design and Implementation of VIOWIN:  Part 4 - EDM/2 - Dec 1994 - Volume 2, 
  1910. Issue 11 
  1911.  
  1912.  
  1913. ΓòÉΓòÉΓòÉ <hidden> vwSendMsg ΓòÉΓòÉΓòÉ
  1914.  
  1915. vwSendMsg 
  1916.  
  1917. MRESULT EXPENTRY vwSendMsg(HVWWND hwndWnd,
  1918.                            ULONG ulMsg,
  1919.                            MPARAM mpParm1,
  1920.                            MPARAM mpParm2)
  1921. //-------------------------------------------------------------------------
  1922. // This function sends a message to the window.  This is done by
  1923. // dereferencing the class and calling the window class procedure
  1924. // directly.
  1925. //
  1926. // Input:  hwndWnd - specifies the window handle
  1927. //         ulMsg - specifies the message
  1928. //         mpParm1, mpParm2 - specify the message parameters
  1929. // Returns:  value specific to the message sent
  1930. //-------------------------------------------------------------------------
  1931. {
  1932.    BOOL bOldSendMsg;
  1933.    MRESULT mrRc;
  1934.  
  1935.    if (hwndWnd==VWHWND_DESKTOP) {
  1936.       hwndWnd=vwWindowFromID(VWWID_DESKTOP);
  1937.    } /* endif */
  1938.  
  1939.    if (!vwIsWindow(hwndWnd)) {
  1940.       return MRFROMLONG(0);
  1941.    } /* endif */
  1942.  
  1943.    bOldSendMsg=habAnchor->bIsSendMsg;
  1944.    habAnchor->bIsSendMsg=TRUE;
  1945.  
  1946.    mrRc=(*(hwndWnd->pciClass->pfnWndProc))(hwndWnd,ulMsg,mpParm1,mpParm2);
  1947.  
  1948.    habAnchor->bIsSendMsg=bOldSendMsg;
  1949.    return mrRc;
  1950. }
  1951.  
  1952. The Design and Implementation of VIOWIN:  Part 4 - EDM/2 - Dec 1994 - Volume 2, 
  1953. Issue 11 
  1954.  
  1955.  
  1956. ΓòÉΓòÉΓòÉ <hidden> vwSetBackColor ΓòÉΓòÉΓòÉ
  1957.  
  1958. vwSetBackColor 
  1959.  
  1960. LONG EXPENTRY vwSetBackColor(HVWWND hwndWnd,LONG lColor)
  1961. //-------------------------------------------------------------------------
  1962. // This function sets the background color of the window.
  1963. //
  1964. // Input:  hwndWnd - specifies the window handle
  1965. //         lColor - specifies the new color
  1966. // Returns:  TRUE if successful, FALSE otherwise
  1967. //-------------------------------------------------------------------------
  1968. {
  1969.    if (hwndWnd==VWHWND_DESKTOP) {
  1970.       hwndWnd=vwWindowFromID(VWWID_DESKTOP);
  1971.    } /* endif */
  1972.  
  1973.    if (!vwIsWindow(hwndWnd)) {
  1974.       return FALSE;
  1975.    } /* endif */
  1976.  
  1977.    hwndWnd->lBackClr=lColor;
  1978.    return TRUE;
  1979. }
  1980.  
  1981. The Design and Implementation of VIOWIN:  Part 4 - EDM/2 - Dec 1994 - Volume 2, 
  1982. Issue 11 
  1983.  
  1984.  
  1985. ΓòÉΓòÉΓòÉ <hidden> vwSetFocus ΓòÉΓòÉΓòÉ
  1986.  
  1987. vwSetFocus 
  1988.  
  1989. BOOL EXPENTRY vwSetFocus(HVWWND hwndWnd)
  1990. //-------------------------------------------------------------------------
  1991. // This function sets the focus to another window.
  1992. //
  1993. // Input:  hwndWnd - specifies the handle of the window to receive the
  1994. //                   focus
  1995. // Returns:  TRUE if successful, FALSE otherwise
  1996. //-------------------------------------------------------------------------
  1997. {
  1998.    HVWWND hwndLose;
  1999.  
  2000.    //----------------------------------------------------------------------
  2001.    // Validate the window handle
  2002.    //----------------------------------------------------------------------
  2003.    if ((hwndWnd!=NULL) && !vwIsWindow(hwndWnd)) {
  2004.       return FALSE;
  2005.    } /* endif */
  2006.  
  2007.    hwndLose=habAnchor->hwndFocus;
  2008.  
  2009.    //----------------------------------------------------------------------
  2010.    // Send a WM_SETFOCUS message to the window losing the focus.  Also,
  2011.    // send a WM_PAINT message (this deviates from PM).
  2012.    //----------------------------------------------------------------------
  2013.    if (hwndLose!=NULL) {
  2014.       habAnchor->hwndFocus=NULL;
  2015.       vwSendMsg(hwndLose,WM_SETFOCUS,MPFROMHWND(hwndWnd),MPFROMLONG(FALSE));
  2016.       vwSendMsg(hwndLose,WM_PAINT,0,0);
  2017.    } /* endif */
  2018.  
  2019.    //----------------------------------------------------------------------
  2020.    // Send a WM_SETFOCUS message to the window gaining the focus.  Again,
  2021.    // also send a WM_PAINT message (deviation).
  2022.    //----------------------------------------------------------------------
  2023.    if (hwndWnd!=NULL) {
  2024.       vwSendMsg(hwndWnd,
  2025.                 WM_SETFOCUS,
  2026.                 MPFROMHWND(hwndLose),
  2027.                 MPFROMLONG(TRUE));
  2028.       habAnchor->hwndFocus=hwndWnd;
  2029.       vwSendMsg(hwndWnd,WM_PAINT,0,0);
  2030.    } else {
  2031.       habAnchor->hwndFocus=NULL;
  2032.    } /* endif */
  2033.  
  2034.    return TRUE;
  2035. }
  2036.  
  2037. The Design and Implementation of VIOWIN:  Part 4 - EDM/2 - Dec 1994 - Volume 2, 
  2038. Issue 11 
  2039.  
  2040.  
  2041. ΓòÉΓòÉΓòÉ <hidden> vwSetForeColor ΓòÉΓòÉΓòÉ
  2042.  
  2043. vwSetForeColor 
  2044.  
  2045. BOOL EXPENTRY vwSetForeColor(HVWWND hwndWnd,LONG lColor)
  2046. //-------------------------------------------------------------------------
  2047. // This function sets the foreground color of the window.
  2048. //
  2049. // Input:  hwndWnd - specifies the window handle
  2050. //         lColor - specifies the new color
  2051. // Returns:  TRUE if successful, FALSE otherwise
  2052. //-------------------------------------------------------------------------
  2053. {
  2054.    if (hwndWnd==VWHWND_DESKTOP) {
  2055.       hwndWnd=vwWindowFromID(VWWID_DESKTOP);
  2056.    } /* endif */
  2057.  
  2058.    if (!vwIsWindow(hwndWnd)) {
  2059.       return FALSE;
  2060.    } /* endif */
  2061.  
  2062.    hwndWnd->lForeClr=lColor;
  2063.    return TRUE;
  2064. }
  2065.  
  2066. The Design and Implementation of VIOWIN:  Part 4 - EDM/2 - Dec 1994 - Volume 2, 
  2067. Issue 11 
  2068.  
  2069.  
  2070. ΓòÉΓòÉΓòÉ <hidden> vwSetWindowPtr ΓòÉΓòÉΓòÉ
  2071.  
  2072. vwSetWindowPtr 
  2073.  
  2074. BOOL EXPENTRY vwSetWindowPtr(HVWWND hwndWnd,ULONG ulPtr,PVOID pvData)
  2075. //-------------------------------------------------------------------------
  2076. // This function set the specified window pointer.
  2077. //
  2078. // Input:  hwndWnd - specifies the window handle
  2079. //         ulPtr - specifies 0 or 1
  2080. //         pvData - specifies the window pointer
  2081. // Returns:  TRUE if successful, FALSE otherwise
  2082. //-------------------------------------------------------------------------
  2083. {
  2084.    if (!vwIsWindow(hwndWnd)) {
  2085.       return FALSE;
  2086.    } /* endif */
  2087.  
  2088.    if (ulPtr>1) {
  2089.       return FALSE;
  2090.    } /* endif */
  2091.  
  2092.    hwndWnd->pvData[ulPtr]=pvData;
  2093.    return TRUE;
  2094. }
  2095.  
  2096. The Design and Implementation of VIOWIN:  Part 4 - EDM/2 - Dec 1994 - Volume 2, 
  2097. Issue 11 
  2098.  
  2099.  
  2100. ΓòÉΓòÉΓòÉ <hidden> vwSetWindowText ΓòÉΓòÉΓòÉ
  2101.  
  2102. vwSetWindowText 
  2103.  
  2104. BOOL EXPENTRY vwSetWindowText(HVWWND hwndWnd,PCHAR pchText)
  2105. //-------------------------------------------------------------------------
  2106. // This function attempts to set the window text by sending the window
  2107. // a WM_SETWINDOWPARAMS message.  It is the responsibility of the window
  2108. // class to copy the text to its own workspace.
  2109. //
  2110. // Input:  hwndWnd - specifies the window handle
  2111. //         pchText - points to the new text
  2112. // Returns:  TRUE if successful, FALSE otherwise
  2113. //-------------------------------------------------------------------------
  2114. {
  2115.    WNDPARAMS wpParms;
  2116.  
  2117.    if (!vwIsWindow(hwndWnd)) {
  2118.       return FALSE;
  2119.    } /* endif */
  2120.  
  2121.    if (pchText!=NULL) {
  2122.       wpParms.pszText=pchText;
  2123.    } else {
  2124.       wpParms.pszText="";
  2125.    } /* endif */
  2126.  
  2127.    wpParms.fsStatus=WPM_TEXT;
  2128.    wpParms.cchText=strlen(pchText)+1;
  2129.  
  2130.    if (SHORT1FROMMP(vwSendMsg(hwndWnd,
  2131.                               WM_SETWINDOWPARAMS,
  2132.                               MPFROMP(&wpParms),
  2133.                               0))) {
  2134.       return TRUE;
  2135.    } else {
  2136.       return FALSE;
  2137.    } /* endif */
  2138. }
  2139.  
  2140. The Design and Implementation of VIOWIN:  Part 4 - EDM/2 - Dec 1994 - Volume 2, 
  2141. Issue 11 
  2142.  
  2143.  
  2144. ΓòÉΓòÉΓòÉ <hidden> vwSetWindowULong ΓòÉΓòÉΓòÉ
  2145.  
  2146. vwSetWindowULong 
  2147.  
  2148. BOOL EXPENTRY vwSetWindowULong(HVWWND hwndWnd,LONG lIndex,ULONG ulValue)
  2149. //-------------------------------------------------------------------------
  2150. // This function sets the value of the specified window variable.
  2151. //
  2152. // Input:  hwndWnd - specifies the window handle
  2153. //         lIndex - specifies a QWL_* constant
  2154. //         ulValue - specifies the new value
  2155. // Returns:  TRUE if successful, FALSE otherwise
  2156. //-------------------------------------------------------------------------
  2157. {
  2158.    if (!vwIsWindow(hwndWnd)) {
  2159.       return FALSE;
  2160.    } /* endif */
  2161.  
  2162.    switch (lIndex) {
  2163.    case QWL_STYLE:
  2164.       hwndWnd->ulStyle=ulValue;
  2165.       break;
  2166.    default:
  2167.       return FALSE;
  2168.    } /* endswitch */
  2169.  
  2170.    return TRUE;
  2171. }
  2172.  
  2173. The Design and Implementation of VIOWIN:  Part 4 - EDM/2 - Dec 1994 - Volume 2, 
  2174. Issue 11 
  2175.  
  2176.  
  2177. ΓòÉΓòÉΓòÉ <hidden> vwSetWindowUShort ΓòÉΓòÉΓòÉ
  2178.  
  2179. vwSetWindowUShort 
  2180.  
  2181. BOOL EXPENTRY vwSetWindowUShort(HVWWND hwndWnd,LONG lIndex,USHORT usValue)
  2182. //-------------------------------------------------------------------------
  2183. // This function sets the value of the specified window variable.
  2184. //
  2185. // Input:  hwndWnd - specifies the window handle
  2186. //         lIndex - specifies a QWS_* constant
  2187. //         usValue - specifies the new value
  2188. // Returns:  TRUE if successful, FALSE otherwise
  2189. //-------------------------------------------------------------------------
  2190. {
  2191.    if (!vwIsWindow(hwndWnd)) {
  2192.       return FALSE;
  2193.    } /* endif */
  2194.  
  2195.    switch (lIndex) {
  2196.    default:
  2197.       return FALSE;
  2198.    } /* endswitch */
  2199.  
  2200.    return TRUE;
  2201. }
  2202.  
  2203. The Design and Implementation of VIOWIN:  Part 4 - EDM/2 - Dec 1994 - Volume 2, 
  2204. Issue 11 
  2205.  
  2206.  
  2207. ΓòÉΓòÉΓòÉ <hidden> vwUpdateWindow ΓòÉΓòÉΓòÉ
  2208.  
  2209. vwUpdateWindow 
  2210.  
  2211. BOOL EXPENTRY vwUpdateWindow(HVWWND hwndWnd)
  2212. //-------------------------------------------------------------------------
  2213. // This function updates the specified window.  If the window is the
  2214. // desktop, it updates all of the children also.
  2215. //
  2216. // Input:  hwndWnd - specifies the window handle
  2217. // Returns:  TRUE if successful, FALSE otherwise
  2218. //-------------------------------------------------------------------------
  2219. {
  2220.    if (hwndWnd==VWHWND_DESKTOP) {
  2221.       hwndWnd=vwWindowFromID(VWWID_DESKTOP);
  2222.    } /* endif */
  2223.  
  2224.    if (!vwIsWindow(hwndWnd)) {
  2225.       return FALSE;
  2226.    } /* endif */
  2227.  
  2228.    if (vwQueryWindowUShort(hwndWnd,QWS_ID)==VWWID_DESKTOP) {
  2229.       //-------------------------------------------------------------------
  2230.       // Since the desktop is first in the list, traverse the list and
  2231.       // send each window a WM_PAINT message.
  2232.       //-------------------------------------------------------------------
  2233.       CmnLstTraverseList(habAnchor->hclWindows,(PFNRECFUNC)paintWindow);
  2234.    } else {
  2235.       paintWindow(hwndWnd);
  2236.    } /* endif */
  2237.  
  2238.    return TRUE;
  2239. }
  2240.  
  2241. The Design and Implementation of VIOWIN:  Part 4 - EDM/2 - Dec 1994 - Volume 2, 
  2242. Issue 11 
  2243.  
  2244.  
  2245. ΓòÉΓòÉΓòÉ <hidden> vwWindowFromID ΓòÉΓòÉΓòÉ
  2246.  
  2247. vwWindowFromID 
  2248.  
  2249. HVWWND EXPENTRY vwWindowFromID(USHORT usId)
  2250. //-------------------------------------------------------------------------
  2251. // This function returns the handle of the window with the specified id.
  2252. //
  2253. // Input:  usId - specifies the id to search for
  2254. // Returns:  handle of the window with the specified id if found, NULLHANDLE
  2255. //           otherwise
  2256. //-------------------------------------------------------------------------
  2257. {
  2258.    HVWWND hwndFound;
  2259.  
  2260.    if (hmqQueue==NULL) {
  2261.       return NULL;
  2262.    } /* endif */
  2263.  
  2264.    //----------------------------------------------------------------------
  2265.    // If the id is -1, then the caller doesn't care about the window id,
  2266.    // so we should return NULL to allow for multiple windows with this
  2267.    // id can be used multiple times.
  2268.    //----------------------------------------------------------------------
  2269.    if (usId==(USHORT)-1) {
  2270.       return NULL;
  2271.    } /* endif */
  2272.  
  2273.    hwndFound=(HVWWND)CmnLstQueryRecord(habAnchor->hclWindows,0);
  2274.    hwndFound=(HVWWND)CmnLstSearchRecord(hwndFound,
  2275.                                         &usId,
  2276.                                         (PFNRECCOMP)_findWindowId);
  2277.    return hwndFound;
  2278. }
  2279.  
  2280. The Design and Implementation of VIOWIN:  Part 4 - EDM/2 - Dec 1994 - Volume 2, 
  2281. Issue 11 
  2282.  
  2283.  
  2284. ΓòÉΓòÉΓòÉ 5. /dev/EDM/BookReview ΓòÉΓòÉΓòÉ
  2285.  
  2286.  
  2287. ΓòÉΓòÉΓòÉ 5.1. Introduction ΓòÉΓòÉΓòÉ
  2288.  
  2289. /dev/EDM2/BookReview 
  2290.  
  2291. Written by Carsten Whimster 
  2292.  
  2293. Introduction 
  2294.  
  2295. /dev/EDM2/BookReview is a monthly column which focuses on development oriented 
  2296. books and materials.  The column is from a beginning PM programmer, but 
  2297. intermediate REXX programmer's eyes.  Pick up whichever book strikes your 
  2298. fancy, and join the growing group of people following our PM programming 
  2299. columns.  I will review books aimed at beginners for a while, and then move on 
  2300. from there. 
  2301.  
  2302. Please send me your comments and thoughts so that I can make this column as 
  2303. good as possible.  All mail gets read and responded to. 
  2304.  
  2305. OS/2 Presentation Manager Programming is perhaps the most awaited book in the 
  2306. history of OS/2.  Charles Petzold wrote a version of this book way back when, 
  2307. but for a long time it looked like he might never write another, perhaps due to 
  2308. pressure from Microsoft.  Well, all the hopefuls will have to revise their 
  2309. opinion now as the updated version is finally out.  This is a PM programming 
  2310. book for beginners. 
  2311.  
  2312. /dev/EDM2/BookReview - EDM/2 - Dec 1994 - Volume 2, Issue 11 
  2313.  
  2314.  
  2315. ΓòÉΓòÉΓòÉ 5.2. Errata ΓòÉΓòÉΓòÉ
  2316.  
  2317. Errata 
  2318.  
  2319. I am still waiting for OS/2 Warp 3.0, and I can hardly contain myself 
  2320. (well...).  I have heard much good about it, but also that it is less stable 
  2321. than OS/2 2.11.  I'll just have to wait until it arrives before I judge. 
  2322. Meanwhile, I unfortunately sold my OS/2 2.1, so I am running on OS/2 Warp Beta 
  2323. II, which has had its down-sides.  I purchased a Promise DC4030VL-2 with 2Mb of 
  2324. cache RAM, but due to driver bugs, I lost my Warp.  Later on the card went 
  2325. AWOL, so I had to return it.  When the replacement finally came, it was also 
  2326. broken, but I noticed that the packaging had been opened, so I am wondering 
  2327. whether it was the same card.  Back to the store it goes, but this time I wrote 
  2328. down the serial number, so I can tell whether the supplier really is giving me 
  2329. a new one.  Sigh. 
  2330.  
  2331. The format I have been using so far in reviewing books requires that I read 
  2332. every line in the whole book, which is very time-consuming, and difficult, 
  2333. considering that I am not an expert in every field.  It makes it very difficult 
  2334. for me to evaluate material with which I am not familiar, so I am going to 
  2335. change format slightly.  I will concentrate a bit more on filling in the tables 
  2336. at the end of the review accurately, and slightly less on writing a detailed 
  2337. review of every section in every chapter.  This lets me skim the material a 
  2338. little more, but still lets you find what you are looking for. Expect the 
  2339. tables to grow a fair amount, and perhaps even change format a bit from time to 
  2340. time. 
  2341.  
  2342. /dev/EDM2/BookReview - EDM/2 - Dec 1994 - Volume 2, Issue 11 
  2343.  
  2344.  
  2345. ΓòÉΓòÉΓòÉ 5.3. OS/2 Presentation Manager Programming ΓòÉΓòÉΓòÉ
  2346.  
  2347. OS/2 Presentation Manager Programming 
  2348.  
  2349. OS/2 Presentation Manager Programming is a very thorough introductory PM 
  2350. programming book.  Many programmers have been clamouring for this book for a 
  2351. long time, knowing that the author covers his material very well, and writes 
  2352. very lucidly.  Well, it is finally here, and these are the chapters: 
  2353.  
  2354.  1. OS/2 and the Presentation Manager 
  2355.  2. Welcome to Presentation Manager Programming 
  2356.  3. More Fun with Windows 
  2357.  4. An Exercise in Text Output 
  2358.  5. The Five GPI Primitives 
  2359.  6. Bitmaps and BitBlts 
  2360.  7. Advanced Graphics 
  2361.  8. Tapping into the Keyboard 
  2362.  9. Taming the Mouse 
  2363. 10. Setting the Timer 
  2364. 11. Control Windows: Putting the Children to Work 
  2365. 12. Bitmaps, Icons, Pointers, and Strings 
  2366. 13. Menus and Keyboard Accelerators 
  2367. 14. Dialog Boxes 
  2368. 15. Cut, Copy, and Paste: The Clipboard 
  2369. 16. Dynamic Link Libraries 
  2370. 17. Multithreaded Programming Techniques 
  2371. 18. Printing Graphics and Text 
  2372. 19. Appendix A: About the Accompanying Disk 
  2373.  
  2374. The book is divided into sections as follows: 
  2375.  
  2376.  1. Basic Concepts - chapters one through three 
  2377.  2. Painting the Client Window - chapters four through seven 
  2378.  3. Getting Input - chapters eight through eleven 
  2379.  4. Using Resources - chapters twelve through fourteen 
  2380.  5. Miscellaneous Topics - chapters fifteen through eighteen 
  2381.  
  2382. Before I get started I have to express dissatisfaction that the author chose 
  2383. only to support the IBM and Borland compilers (and then there is the cheesy 
  2384. install program, ie. PKZIP).  I use the Watcom compiler which is better quality 
  2385. than the Borland compiler, and which has been around for quite a while.  It 
  2386. really shouldn't take that much time for an author to ask a couple of friends 
  2387. who use the Watcom compiler if they can come up with a .CMD file to set the 
  2388. Watcom options.  With that off my chest, here is the review... 
  2389.  
  2390. In addition to a brief history of OS/2, the first three chapters have the 
  2391. obligatory "this is a window, this is how you open it" introduction.  What sets 
  2392. it apart from other books is the depth in which the WinCreateWindow call is 
  2393. treated.  Many, if not all of the options to this API are listed and explained, 
  2394. which is unusual.  The treatment is usually biased towards WinCreateStdWindow 
  2395. instead, so this change is refreshing and educational.  By the way, Petzold 
  2396. seems to have made it a habit to pull out the constants when he describes a new 
  2397. call, and explain them as well, including the ones he doesn't actually use. 
  2398. This is a good thing in my opinion, and only some books do that, not all. 
  2399.  
  2400. Section two describes some of the GPI calls commonly used to do fairly mundane 
  2401. tasks, and also gets into a bit of advanced GPI programming.  Those of you who 
  2402. intend to write the next Doom (Copyright 1994 iD Software) should probably wait 
  2403. for the new version of OS/2 Presentation Manager GPI, Winn, or take a look at 
  2404. the new DIVE API, however.  The basic lines, curves, and so on are described, 
  2405. and some of the more advanced calls like Bezier splines, paths, and transforms, 
  2406. but do not expect to be able to write the next graphing program after reading 
  2407. this section.  Surprisingly, however, he does go into mathematics to explain 
  2408. the various functions, and this makes this advanced section a good GPI 
  2409. introduction for intermediate and advanced programmers.  This is probably a 
  2410. section you'll want to come back to when you have mastered more of the rest of 
  2411. the book.  Many of the compromises inherent to both raster (pixel) and vector 
  2412. (line) graphics are explained in detail, and you will learn a lot about the 
  2413. basics of graphics programming even you don't actually do any. 
  2414.  
  2415. Fonts are treated in the GPI section as well, and they are explained in fairly 
  2416. good depth.  You should be able to find a solution to most text problems here. 
  2417. The various shears, rotations, and so on are all explained patiently, with many 
  2418. little sample programs.  In addition, many different types of shadows and 
  2419. fillings are demonstrated in quite a bit of detail, as is text justification. 
  2420.  
  2421. Part three covers the keyboard and the mouse, and getting input from them. The 
  2422. keyboard is covered first, not surprisingly.  A program called TYPEAWAY is used 
  2423. to demonstrate these concepts.  It is basically a brain-damaged editor with a 
  2424. few crucial design flaws, so if you ever wanted to write a word-processor or an 
  2425. editor, this would be a good place to start.  The flaws, by the way, are 
  2426. intended. 
  2427.  
  2428. The mouse material is introduced with a neat program called WEB, which draws 
  2429. lines from the corner of the window to the current mouse position.  It is a 
  2430. little gimmicky, but quite a fun little program.  It is followed up by another 
  2431. neat little program called CHECKER1 (CHECKER2 for the keyboard enabled 
  2432. version).  These programs are basic, but demonstrate how to use the mouse 
  2433. position and buttons to do various things in your client window.  CHECKER3 then 
  2434. demonstrates how to use the divide and conquer strategy in your window, and 
  2435. divide the window into sections which each handle their own input.  These are 
  2436. followed by programs which keep track of mouse movement, and do something at 
  2437. the same time, i.e.  draw blocks the size of a click-and-drag area, and then by 
  2438. a sketching program which draws when you click-and-drag.  These are all 
  2439. educational programs which really drive home the point of certain types of 
  2440. interaction. 
  2441.  
  2442. The last chapter in the input section is the chapter on the timer.  Petzold 
  2443. shows both how to set a repeating timer, how to get around the basic timer 
  2444. functionality to set one-time timers, and how to get around the 65.5 second 
  2445. maximum timer duration limit.  The logic necessary to distinguish between the 
  2446. timers of cursor blinkers and clock and so on are also demonstrated.  A little 
  2447. program which uses a timer to display free memory at fixed intervals is shown, 
  2448. and finally a plain digital clock and an analog clock are shown.  The analog 
  2449. clock makes me think that he had a hand in the design of the OS/2 system clock 
  2450. :) 
  2451.  
  2452. Chapter eleven seems a little out of place in the input section, but since many 
  2453. of the controls are aimed at getting input from the user, it isn't so odd after 
  2454. all.  This chapter is fairly basic, and goes through a number of small programs 
  2455. which create buttons, sliders, text, and other small windows.  It skips a 
  2456. detailed treatment of the MLE, as well as lists, drop-down lists, text-entry 
  2457. fields and others.  Some of these are introduced along the way in the book, but 
  2458. not in any real depth.  In fact, now that I look closer at the book, this is 
  2459. the only really serious short-coming of the book that isn't just a matter of 
  2460. opinion.  Yes, many of these controls are shown in various examples here and 
  2461. there, but there is no real in-depth treatment anywhere. There really should be 
  2462. a good thick chapter on these things, and that chapter should have been chapter 
  2463. eleven, but isn't.  This is the bread and butter type stuff that applications 
  2464. are made of.  There is a large section on GPI that most people will never use, 
  2465. but no collection of controls?  Hmm, well, onwards we go... 
  2466.  
  2467. Part four is on using resources such as bitmaps, icons, accelerator keys and so 
  2468. on.  It starts out by showing what the various resources are, and how they get 
  2469. into the EXE via the different stages of compilation.  This entails explaining 
  2470. resource scripts, which is then promptly done.  A couple of examples are given, 
  2471. but without much detail pertaining to the different options. 
  2472.  
  2473. The next chapter is devoted to menus and accelerator keys.  Regular menus, 
  2474. checked/unchecked items, enabled/disabled items, help menus, and accelerators 
  2475. are all demonstrated, but the first example shows how to add items to the 
  2476. system menu.  This is called the poor person's menu :)  The next example is a 
  2477. nice introductory pop-up menu.  This is a nicely thought-out piece with a few 
  2478. items with more than one level to them.  The third example has an amusing 
  2479. bitmap as the last item.  This bitmap is a cheesy bitmap of a children's 
  2480. handwriting rendition of the word help :)  A game called TAQUIN follows (more 
  2481. commonly known as PUZZLE).  This game is a good demonstration of a combination 
  2482. of the techniques learned earlier in the book, as it combines menus, graphics, 
  2483. and mouse tracking programming techniques.  Finally, a decent implementation of 
  2484. The Game of Life is given.  These programs are both interesting and give good 
  2485. real-life examples of menu usage.  This is how it should be done. 
  2486.  
  2487. Chapter fourteen discusses dialog boxes.  Dialog boxes aren't the most 
  2488. interesting of PM possibilities, but they are probably the most ubiquitous. 
  2489. There is probably at least one dialog box in almost every PM program ever 
  2490. written!  This chapter gives a nice variety of the possible features of a 
  2491. dialog box. 
  2492.  
  2493. The final section in the book consists of various topics which didn't fit the 
  2494. neat, boxed labels that the other chapters did.  An introduction to the 
  2495. clipboard, dynamic link libraries, multi-threaded programs and printing can be 
  2496. found here.  Given the importance of multi-threading, I would have liked to see 
  2497. a larger chapter on it, and perhaps the same goes for printing. Multi-threading 
  2498. is a topic which even an introductory volume should have, since it is the key 
  2499. area which differentiates PM from the more mundane Windows API.  There is a 
  2500. nice section with a multi-tasking explanation, including the differences 
  2501. between Windows' and OS/2's way of doing things, however. 
  2502.  
  2503. /dev/EDM2/BookReview - EDM/2 - Dec 1994 - Volume 2, Issue 11 
  2504.  
  2505.  
  2506. ΓòÉΓòÉΓòÉ 5.4. Summary ΓòÉΓòÉΓòÉ
  2507.  
  2508. Summary 
  2509.  
  2510. OS/2 Presentation Manager Programming is a very thorough beginners' book with a 
  2511. lot of emphasis on the comprehension of basic PM calls, and their parameters. 
  2512. It has a good introduction to almost every API call that you are likely to come 
  2513. across initially when learning to program PM, and in addition it has an 
  2514. introduction to some advanced techniques.  The advanced material is frequently 
  2515. not covered in any real depth, but some examples are given for the usage of 
  2516. various APIs, and you will learn what type of problems you can solve with the 
  2517. different parts of the PM API.  I especially like the little motivational 
  2518. sections given at the start of each chapter, and wished that he had done 
  2519. similarly for each subsection.  They really help you understand why you are 
  2520. doing this, and the meat of the chapter then ensures that you really understand 
  2521. how the nuts and bolts work. 
  2522.  
  2523. I like the fact that the book is divided into sections as well as chapters. It 
  2524. makes it easier to get a good overview, or to skip the heavier sections if you 
  2525. are getting bogged down in something you don't need.  Overall, the book is very 
  2526. well organized indeed, and very easy to jump around in.  The index is quite 
  2527. good if not perfect, but it is probably the best of any PM programming book I 
  2528. have seen, so guess I'll have to say that this is the mark for potential 
  2529. authors to aim for. 
  2530.  
  2531. Many of the programs were a little mundane, but in a beginners' book that is 
  2532. inevitable, so I really can't fault it for that.  The overriding impression of 
  2533. this book is that of lucid, accurate, and thorough coverage.  It isn't as 
  2534. humorous as some, but not as dry as others.  It is simply competent, as you 
  2535. would expect from a master of the trade. 
  2536.  
  2537. The only things I could fault were the use of Windows graphics instead of PM 
  2538. graphics here and there in the figures of the book, and the numerous references 
  2539. to OS/2 2.0 instead of OS/2 2.1.  This book is updated for OS/2 2.1, but 
  2540. sometimes you wouldn't know it.  MINMAX3 didn't work for me, but that is 
  2541. probably attributable to Warp Beta II which has the occasional glitch in the 
  2542. graphics subsystem.  Apart from that, only the lack of in-depth treatment of 
  2543. control windows and common dialog boxes keep it from being perfect. 
  2544.  
  2545. Overall, this book is rock-solid, and a good start for anyone who hasn't seen 
  2546. or tried GUI programming before.  I would rate this a solid A if it wasn't for 
  2547. the lack of depth in the above mentioned fields. Therefore it gets an A-. 
  2548.  
  2549. /dev/EDM2/BookReview - EDM/2 - Dec 1994 - Volume 2, Issue 11 
  2550.  
  2551.  
  2552. ΓòÉΓòÉΓòÉ 5.5. Books Reviewed ΓòÉΓòÉΓòÉ
  2553.  
  2554. Books Reviewed 
  2555.  
  2556. ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
  2557. ΓöéBOOK                              ΓöéAUDIENCE    ΓöéMARKΓöéCOMMENTS                               Γöé
  2558. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2559. ΓöéReal-World Programming for OS/2   ΓöéIntermediateΓöéB+  ΓöéLots of good code examples, but        Γöé
  2560. Γöé2.1, Blain, Delimon, and English, Γöéto Advanced Γöé    Γöésometimes it is too complex for        Γöé
  2561. ΓöéSAMS Publishing.  ISBN            ΓöéPM C        Γöé    Γöénovices. Accurate.  Well organized.    Γöé
  2562. Γöé0-672-30300-0.  US$40, CAN$50.    Γöéprogrammers Γöé    ΓöéThe index needs a little beefing up.   Γöé
  2563. Γöé                                  Γöé            Γöé    ΓöéGood, but not entirely complete how-to Γöé
  2564. Γöé                                  Γöé            Γöé    Γöéreference.  Good purchase.             Γöé
  2565. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2566. ΓöéLearning to Program OS/2 2.0      ΓöéBeginning PMΓöéB-  ΓöéThis book can be both frustrating and  Γöé
  2567. ΓöéPresentation Manager by Example,  ΓöéC           Γöé    Γöévery rewarding.  It is not very large, Γöé
  2568. ΓöéKnight, Van Nostrand Reinhold.    ΓöéProgrammers Γöé    Γöéand a bit pricey, but has some         Γöé
  2569. ΓöéISBN 0-442-01292-6.  US$40,       Γöé            Γöé    Γöéexcellent chapters on certain beginningΓöé
  2570. ΓöéCAN$50.                           Γöé            Γöé    Γöétopics, such as messages, resources,   Γöé
  2571. Γöé                                  Γöé            Γöé    ΓöéIPF, and dialog boxes.  Strictly for   Γöé
  2572. Γöé                                  Γöé            Γöé    Γöébeginners.  This book has only one     Γöé
  2573. Γöé                                  Γöé            Γöé    Γöé(large) sample program!                Γöé
  2574. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2575. ΓöéWriting OS/2 2.1 Device Drivers inΓöéAdvanced C  ΓöéA-  ΓöéThe only thing a device driver         Γöé
  2576. ΓöéC, 2nd Edition, Mastrianni, Van   ΓöéProgrammers,Γöé    Γöéprogrammer would not find in here is   Γöé
  2577. ΓöéNostrand Reinhold.  ISBN          Γöéfamiliar    Γöé    Γöéhow to write SCSI, ADD, and IFS        Γöé
  2578. Γöé0-442-01729-4.  US$35, CAN$45.    Γöéwith        Γöé    Γöédrivers.  Most everything else is in   Γöé
  2579. Γöé                                  Γöéhardware    Γöé    Γöéhere, along with skeleton examples.  AnΓöé
  2580. Γöé                                  Γöéprogramming Γöé    Γöéoptional DevHlp library of C-callable  Γöé
  2581. Γöé                                  Γöé            Γöé    Γöéfunctions can be purchased by those whoΓöé
  2582. Γöé                                  Γöé            Γöé    Γöédon't have time to write their own.    Γöé
  2583. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2584. ΓöéOS/2 Presentation Manager GPI,    ΓöéIntermediateΓöéC+  ΓöéThis book needs updating for OS/2 2.x. Γöé
  2585. ΓöéWinn, Van Nostrand Reinhold. ISBN Γöéto advanced Γöé    ΓöéIt is a well-written in-depth coverage Γöé
  2586. Γöé0-442-00739-6.  US$35, CAN$45.    ΓöéPM C        Γöé    Γöéof the OS/2 way of programming for     Γöé
  2587. Γöé                                  Γöéprogrammers Γöé    Γöégraphics.  It is not an introductory PMΓöé
  2588. Γöé                                  Γöé            Γöé    Γöéor graphics programming book.  You     Γöé
  2589. Γöé                                  Γöé            Γöé    Γöéshould know the basics of PM           Γöé
  2590. Γöé                                  Γöé            Γöé    Γöéprogramming already.                   Γöé
  2591. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2592. ΓöéThe Art of OS/2 2.1 C Programming,ΓöéBeginning   ΓöéB+  ΓöéThis is a great introductory PM        Γöé
  2593. ΓöéPanov, Salomon, and Panov,        ΓöéOS/2 and PM Γöé    Γöéprogramming book.  It covers basic OS/2Γöé
  2594. ΓöéWiley-QED.  ISBN 0-471-58802-4.   Γöéprogrammers Γöé    Γöéissues like threads before it jumps    Γöé
  2595. ΓöéUS$40, CAN$50.                    Γöé            Γöé    Γöéinto PM programming.  The coverage is  Γöé
  2596. Γöé                                  Γöé            Γöé    Γöéquite thourough, with just enough      Γöé
  2597. Γöé                                  Γöé            Γöé    Γöéreference material to make it useful   Γöé
  2598. Γöé                                  Γöé            Γöé    Γöéafter you read it through the first    Γöé
  2599. Γöé                                  Γöé            Γöé    Γöétime.  The upcoming revised edition    Γöé
  2600. Γöé                                  Γöé            Γöé    Γöéshould be a killer.                    Γöé
  2601. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2602. ΓöéMastering OS/2 REXX, Gargiulo,    ΓöéIntermediateΓöéB   ΓöéThis book is very easy to understand.  Γöé
  2603. ΓöéWiley-QED.  ISBN 0-471-51901-4.   ΓöéOS/2 users  Γöé    ΓöéIf you program with any regularity,    Γöé
  2604. ΓöéUS$40, CAN$50.                    Γöéand         Γöé    Γöélook elsewhere, but if you need an     Γöé
  2605. Γöé                                  Γöébeginning   Γöé    Γöéeasily read, well-explained beginner's Γöé
  2606. Γöé                                  Γöéprogrammers Γöé    Γöébook, look no further.  Some more      Γöé
  2607. Γöé                                  Γöé            Γöé    Γöédetailed, and complex real-world       Γöé
  2608. Γöé                                  Γöé            Γöé    Γöéexamples might be useful as you learn  Γöé
  2609. Γöé                                  Γöé            Γöé    Γöéthe material.  Good coverage of REXX's Γöé
  2610. Γöé                                  Γöé            Γöé    Γöécapabilities.                          Γöé
  2611. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2612. ΓöéREXX Reference Summary Handbook,  ΓöéBeginning toΓöéA   ΓöéThis little handbook is packed full of Γöé
  2613. ΓöéGoran, C F S Nevada.  ISBN        Γöéadvanced    Γöé    Γöéuseful information.  Includes chapters Γöé
  2614. Γöé0-9639854-1-8.  US$20, CAN$25     ΓöéREXX        Γöé    Γöéon both built-in and some popular      Γöé
  2615. Γöé                                  Γöéprogrammers Γöé    Γöécommercial libraries.  Well-written andΓöé
  2616. Γöé                                  Γöé            Γöé    Γöécomprehensively indexed.  A must for   Γöé
  2617. Γöé                                  Γöé            Γöé    ΓöéREXX programmers.                      Γöé
  2618. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2619. ΓöéApplication Development Using OS/2ΓöéIntermediateΓöéA-  ΓöéExcellent coverage of everything REXX, Γöé
  2620. ΓöéREXX, Rudd, Wiley-QED.  ISBN      Γöéto advanced Γöé    Γöéwith the occasional sparse section. It Γöé
  2621. Γöé0-471-60691-X.  US$40, CAN$50     ΓöéREXX        Γöé    Γöéis a decent reference book, and has    Γöé
  2622. Γöé                                  Γöéprogrammers Γöé    Γöéenough unusual material that it will   Γöé
  2623. Γöé                                  Γöé            Γöé    Γöéprobably find its way into many REXX   Γöé
  2624. Γöé                                  Γöé            Γöé    Γöéprogrammers' libraries.                Γöé
  2625. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2626. ΓöéOS/2 Presentation Manager         ΓöéBeginning PMΓöéA-  ΓöéThis book has the most thorough        Γöé
  2627. ΓöéProgramming, Petzold, Ziff-Davis  ΓöéC           Γöé    Γöéintroduction to PM basics in any book IΓöé
  2628. ΓöéPress. ISBN 1-56276-123-4.  US$30,Γöéprogrammers Γöé    Γöéhave read so far.  It leaves the field Γöé
  2629. ΓöéCAN$42                            Γöé            Γöé    Γöéwide open for a more thorough advanced Γöé
  2630. Γöé                                  Γöé            Γöé    ΓöéPM book later.  Very well written, veryΓöé
  2631. Γöé                                  Γöé            Γöé    Γöéthorough, and very understandable. OnlyΓöé
  2632. Γöé                                  Γöé            Γöé    Γöéa lack of in-depth treatment of controlΓöé
  2633. Γöé                                  Γöé            Γöé    Γöéwindows and common dialog boxes keep itΓöé
  2634. Γöé                                  Γöé            Γöé    Γöéfrom being perfect.                    Γöé
  2635. ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
  2636.  
  2637. This table contains all books I have reviewed, so that you can find what you 
  2638. are looking for at a glance.  I will be careful to rate books fairly.  If I 
  2639. feel a need to adjust ratings, I will adjust all of them at the same time, and 
  2640. write a note explaining why I felt this necessary.  Please note that books 
  2641. aimed at different audiences should only be compared with great care, if at 
  2642. all.  I intend to concentrate on the strong points of the books I review, but I 
  2643. will point out any weaknesses in a constructive manner. 
  2644.  
  2645. LEGEND: 
  2646.  
  2647. BOOK:  The name of the book, author(s), publishing company, ISBN, and 
  2648. approximate price. 
  2649.  
  2650. AUDIENCE:  This is a description of the audience I think the book targets best. 
  2651. This is not intended as gospel, just a guideline for people not familiar with 
  2652. the book. 
  2653.  
  2654. MARK:  My opinion of the success of the book's presentation, and how well it 
  2655. targets its audience.  Technical content, accuracy, organization, readability, 
  2656. and quality of index all weigh heavily here, but the single most important item 
  2657. is how well the book covers what it says it covers.  Many books try to cover 
  2658. too much, and get a lower mark as a result. 
  2659.  
  2660. ΓöîΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
  2661. ΓöéA+ ΓöéGround-breaking, all-around outstanding book                               Γöé
  2662. Γö£ΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2663. ΓöéA  ΓöéExcellent book.  This is what I want to see happen a lot                   Γöé
  2664. Γö£ΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2665. ΓöéA- ΓöéExcellent book with minor flaws                                            Γöé
  2666. Γö£ΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2667. ΓöéB+ ΓöéVery good book with minor flaws or omissions                               Γöé
  2668. Γö£ΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2669. ΓöéB  ΓöéGood book with some flaws and omissions                                    Γöé
  2670. Γö£ΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2671. ΓöéB- ΓöéGood book, but in need of improvement                                      Γöé
  2672. Γö£ΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2673. ΓöéC+ ΓöéMediocre book with some potential, but in need of some updating            Γöé
  2674. Γö£ΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2675. ΓöéC  ΓöéMediocre book with some good sections, but badly in need of fixing         Γöé
  2676. Γö£ΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2677. ΓöéC- ΓöéMediocre book, little good material, desperately in need of an overhaul    Γöé
  2678. Γö£ΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2679. ΓöéD  ΓöéDon't buy this book unless you need it, and nothing else exists            Γöé
  2680. Γö£ΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2681. ΓöéF  ΓöéDon't buy this book.  Period                                               Γöé
  2682. ΓööΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
  2683.  
  2684. COMMENTS:  This is a very brief summary of the review proper. 
  2685.  
  2686. /dev/EDM2/BookReview - EDM/2 - Dec 1994 - Volume 2, Issue 11 
  2687.  
  2688.  
  2689. ΓòÉΓòÉΓòÉ 5.6. Content Index ΓòÉΓòÉΓòÉ
  2690.  
  2691. Content Index 
  2692.  
  2693. This Content Index is designed to let you find the book that covers the topics 
  2694. you need to learn about.  It will eventually have a lot of categories, with 
  2695. each book being rated along each row.  This table will be quite large, and will 
  2696. continually grow, so please give me your feedback regarding what categories you 
  2697. would like to see, and which you don't.  It may take me a while to fill it, so 
  2698. have a little patience. 
  2699.  
  2700. NOTE:  Books which cover the same material can look similar in this table, but 
  2701. be different in real life.  The style of a book, for example, can not be seen 
  2702. from a quick table, so make sure that you follow up by reading the reviews of 
  2703. the books you find here.  Finally, be sure that the books you are comparing are 
  2704. aimed at the same audiences. 
  2705.  
  2706. PM BOOKS: 
  2707.  
  2708. ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
  2709. ΓöéBOOK ΓöéMARK ΓöéPM     ΓöéKernel ΓöéGPI    ΓöéFonts  ΓöéDevice ΓöéPrint  Γöé
  2710. Γöé     Γöé     ΓöéIntro  ΓöéBasics Γöé       Γöé       ΓöéDriver Γöé       Γöé
  2711. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2712. ΓöéRWP  ΓöéB+   Γöé4      Γöé2      Γöé4      Γöé4      Γöé0      Γöé3      Γöé
  2713. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2714. ΓöéPME  ΓöéB-   Γöé2      Γöé1      Γöé2      Γöé2      Γöé0      Γöé0      Γöé
  2715. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2716. ΓöéODD  ΓöéA    Γöé0      Γöé0      Γöé1      Γöé0      Γöé5      Γöé1      Γöé
  2717. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2718. ΓöéGPI  ΓöéC+   Γöé0      Γöé0      Γöé5      Γöé2      Γöé0      Γöé3      Γöé
  2719. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2720. ΓöéTAO  ΓöéB+   Γöé4      Γöé3      Γöé1      Γöé2      Γöé0      Γöé2      Γöé
  2721. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2722. ΓöéPMP  ΓöéA-   Γöé5      Γöé1      Γöé3      Γöé4      Γöé0      Γöé2      Γöé
  2723. ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
  2724.  
  2725. REXX BOOKS: 
  2726.  
  2727. ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
  2728. ΓöéBOOK ΓöéMARK ΓöéREXX     ΓöéWPS      ΓöéReferenceΓöé
  2729. Γöé     Γöé     ΓöéIntro    Γöé         Γöé         Γöé
  2730. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2731. ΓöéMOR  ΓöéB    Γöé4        Γöé0        Γöé2        Γöé
  2732. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2733. ΓöéRSH  ΓöéA    Γöé1        Γöé2        Γöé5        Γöé
  2734. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2735. ΓöéADO  ΓöéA-   Γöé3        Γöé0        Γöé4        Γöé
  2736. ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
  2737.  
  2738. BOOK LEGEND: 
  2739.  
  2740. ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
  2741. ΓöéReal-World Programming for OS/2 2.1                                ΓöéRWP  Γöé
  2742. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2743. ΓöéLearning to Program OS/2 2.0 Presentation Manager by Example       ΓöéPME  Γöé
  2744. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2745. ΓöéWriting OS/2 2.1 Device Drivers in C, 2nd Edition                  ΓöéODD  Γöé
  2746. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2747. ΓöéOS/2 Presentation Manager GPI                                      ΓöéGPI  Γöé
  2748. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2749. ΓöéThe Art of OS/2 2.1 C Programming                                  ΓöéTAO  Γöé
  2750. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2751. ΓöéMastering OS/2 REXX                                                ΓöéMOR  Γöé
  2752. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2753. ΓöéREXX Reference Summary Handbook                                    ΓöéRSH  Γöé
  2754. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2755. ΓöéApplication Development Using OS/2 REXX                            ΓöéADO  Γöé
  2756. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2757. ΓöéOS/2 Presentation Manager Programming                              ΓöéPMP  Γöé
  2758. ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
  2759.  
  2760. RATINGS LEGEND: 
  2761.  
  2762. ΓöîΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
  2763. Γöé0ΓöéNo coverage           Γöé
  2764. Γö£ΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2765. Γöé1ΓöéVery light coverage   Γöé
  2766. Γö£ΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2767. Γöé2ΓöéIntroductory coverage Γöé
  2768. Γö£ΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2769. Γöé3ΓöéGood Coverage         Γöé
  2770. Γö£ΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2771. Γöé4ΓöéIn-depth coverage     Γöé
  2772. Γö£ΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2773. Γöé5ΓöéAuthoritative         Γöé
  2774. ΓööΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
  2775.  
  2776. /dev/EDM2/BookReview - EDM/2 - Dec 1994 - Volume 2, Issue 11 
  2777.  
  2778.  
  2779. ΓòÉΓòÉΓòÉ 5.7. Coming Up ΓòÉΓòÉΓòÉ
  2780.  
  2781. Coming Up 
  2782.  
  2783. Next month I will be looking at Designing OS/2 Applications, Reich.  There is a 
  2784. new version of this coming out in the new year, but the author requested that I 
  2785. review this now, and the new one when it comes out, so that is what I am going 
  2786. to try to do.  The books I intend to review are (not necessarily in this 
  2787. order): 
  2788.  
  2789. o Designing OS/2 Applications, Reich 
  2790. o OS/2 Unleashed, Moskowitz and Kerr 
  2791. o The Design of OS/2, 2nd Edititon, Kogan and Deitel 
  2792.  
  2793. I am considering reviewing the IBM OS/2 Redbooks, since they are readily and 
  2794. cheaply available, and look like good reference. 
  2795.  
  2796. If anyone has a book they want to see reviewed, I will be happy to oblige, as 
  2797. long as I can afford it.  Publishers can send me books at the address on my 
  2798. personal page at the end of the magazine, and I will review all OS/2 
  2799. development-related and advanced user books I receive. 
  2800.  
  2801. /dev/EDM2/BookReview - EDM/2 - Dec 1994 - Volume 2, Issue 11 
  2802.  
  2803.  
  2804. ΓòÉΓòÉΓòÉ 6. Introduction to PM Programming ΓòÉΓòÉΓòÉ
  2805.  
  2806.  
  2807. ΓòÉΓòÉΓòÉ 6.1. Introduction ΓòÉΓòÉΓòÉ
  2808.  
  2809. Introduction to PM Programming 
  2810.  
  2811. Written by Larry Salomon, Jr. 
  2812.  
  2813. Introduction 
  2814.  
  2815. The purpose of this column is to provide the readers out there who are not 
  2816. familiar with PM application development the information necessary to satisfy 
  2817. their curiousity, educate themselves, and give them an advantage over the 
  2818. documentation supplied by IBM.  Of course, much of this stuff could probably be 
  2819. found in one of the many books out there, but the problem with books in general 
  2820. is that they don't answer the questions you have after you read the book the 
  2821. first time through. 
  2822.  
  2823. I will gladly entertain feedback from the readers about what was "glossed over" 
  2824. or what was detailed well, what tangential topics need to be covered and what 
  2825. superfluous crap should have been removed.  This feedback is essential in 
  2826. guaranteeing that you get what you pay for.  :) 
  2827.  
  2828. It should be said that you must not depend solely on this column to teach you 
  2829. how to develop PM applications; instead, this should be viewed as a supplement 
  2830. to your other information storehouses (books, the network conferences, etc.). 
  2831. Because this column must take a general approach, there will be some topics 
  2832. that you would like to see discussed that really do not belong here.  Specific 
  2833. questions can be directed to the Scratch Patch, where an attempt to answer them 
  2834. will be made. 
  2835.  
  2836. Last Month 
  2837.  
  2838. Last month, we looked at the ownerdraw concept and how it specifically applies 
  2839. to the WC_BUTTON class.  This month, we will begin looking at a new window 
  2840. class, the listbox (WC_LISTBOX). 
  2841.  
  2842. Introduction to PM Programming - EDM/2 - Dec 1994 - Volume 2, Issue 11 
  2843.  
  2844.  
  2845. ΓòÉΓòÉΓòÉ 6.2. Purpose and Variations on a Theme ΓòÉΓòÉΓòÉ
  2846.  
  2847. Purpose and Variations on a Theme 
  2848.  
  2849. The purpose of the listbox is to store a list of items; no surprise there. 
  2850. Unfortunately, the original design (which had to be adhered to, for backward 
  2851. compatibility) only allowed for textual items to be stored.  It is possible 
  2852. through "black magic" (read as ownerdraw) to display other items, however, but 
  2853. there is a lot more work involved, as we will see in the near future. 
  2854.  
  2855. The styles of the listbox are described below: 
  2856.  
  2857. LS_EXTENDEDSEL      Selection of items follows the CUA '91 specification. 
  2858. LS_HORZSCROLL       A horizontal scrollbar should be provided, which is managed 
  2859.                     by the listbox. 
  2860. LS_MULTIPLESEL      One or more items may be selected at any time.  See below 
  2861.                     for more information. 
  2862. LS_NOADJUSTPOS      The listbox should not adjust its height to be a integral 
  2863.                     multiple of the item height. 
  2864. LS_OWNERDRAW        The listbox should allow the application to draw each item. 
  2865.  
  2866. The difference between LS_MULTIPLESEL and LS_EXTENDEDSEL is that the former is 
  2867. brain-dead and utterly useless, while the latter is only somewhat useless. 
  2868. Seriously, try changing the sample in this issue to use the former and use it 
  2869. some.  You will agree that it is quite cumbersome and that the latter is much 
  2870. easier from a usability point-of-view.  The reasoning for the "(somewhat) 
  2871. useless" labelling will be seen later; I alluded to something similar last 
  2872. month with the WC_BUTTON class. 
  2873.  
  2874. More Bad Design 
  2875.  
  2876. So far we have... 
  2877.  
  2878.  1. Text only allowed 
  2879.  
  2880.  2. (Somewhat) useless selection strategies 
  2881.  
  2882. What else is there? 
  2883.  
  2884. IBM and Microsoft originally designed the listbox to hold modest amounts of 
  2885. data, but its ease-of-use quickly encouraged programmers to "push it to the 
  2886. limit."  They quickly found out that the listbox will hold only so much.  How 
  2887. much exactly depends on the length of each item, but it is generally accepted 
  2888. that you cannot - without some hokey-pokey - get more than 32K of items in a 
  2889. listbox, and that is only if every item is 1 character in length.  The answer 
  2890. to why this is true deals with memory allocation within the original 16-bit PM 
  2891. subsystem and is beyond the scope of this article (as well as the hokey-pokey 
  2892. needed to circumvent it). 
  2893.  
  2894. Don't Get Me Wrong 
  2895.  
  2896. In spite of all of these shortcomings, there is no doubt that the listbox gives 
  2897. you the most "bang for the buck."  It is very easy to use for most purposes and 
  2898. it is used quite frequently, so this ease-of-use saves you lots of heartache. 
  2899.  
  2900. Final Notes 
  2901.  
  2902. Each item within a listbox can have associated with it a ULONG. Typically, this 
  2903. is cast to a pointer to an application-specific structure for application 
  2904. housekeeping.  These are referred to as item handles. 
  2905.  
  2906. It is also important to note that all indices are 0-based. 
  2907.  
  2908. Introduction to PM Programming - EDM/2 - Dec 1994 - Volume 2, Issue 11 
  2909.  
  2910.  
  2911. ΓòÉΓòÉΓòÉ 6.3. The Days of Yesteryear ΓòÉΓòÉΓòÉ
  2912.  
  2913. The Days of Yesteryear 
  2914.  
  2915. Let's go back to the HELLO.C program that was presented in volume 2, issue 3. 
  2916.  
  2917. Figure 1)  HELLO in action 
  2918.  
  2919. If you'll remember, whenever a name was selected from the listbox, it appeared 
  2920. in the entryfield beneath it.  We will finish the dangling ends of this 
  2921. application in the next issue or two, so keep this handy. 
  2922.  
  2923. But First 
  2924.  
  2925. First, let's list the listbox messages that can be sent to the control. 
  2926.  
  2927. LM_DELETEALL        Deletes all of the items within the listbox. 
  2928. LM_DELETEITEM       Deletes a single item within the listbox. 
  2929. LM_INSERTITEM       Inserts a signle item within the listbox. 
  2930. LM_QUERYITEMCOUNT   Returns the number of items within the listbox. 
  2931. LM_QUERYITEMHANDLE  Returns a ULONG associated with an item within the listbox. 
  2932. LM_QUERYITEMTEXT    Returns the text of an item within the listbox. 
  2933. LM_QUERYITEMTEXTLENGTH Returns the length of the text of an item within the 
  2934.                     listbox. 
  2935. LM_QUERYSELECTION   Returns the index of the first item selected within the 
  2936.                     listbox after the one specified. 
  2937. LM_QUERYTOPINDEX    Returns the index of the item shown at the top of the 
  2938.                     listbox. 
  2939. LM_SEARCHSTRING     Searches for a string in the item text. 
  2940. LM_SELECTITEM       Sets the selection state of an item within the listbox. 
  2941. LM_SETITEMHANDLE    Associates a ULONG with an item within the listbox. 
  2942. LM_SETITEMHEIGHT    Never implemented by IBM, but never removed from the 
  2943.                     Toolkit header files. 
  2944. LM_SETITEMTEXT      Sets the text of an item within the listbox. 
  2945. LM_SETTOPINDEX      Specifies the item to be displayed at the top of the 
  2946.                     listbox. 
  2947.  
  2948. A Closer Look, Please 
  2949.  
  2950. Let's now take a closer look at the more frequently used messages. 
  2951.  
  2952. LM_DELETEALL 
  2953.  
  2954. Purpose 
  2955.  
  2956. Deletes all of the items within the listbox. 
  2957.  
  2958. Parameters 
  2959.  
  2960. This message accepts no parameters. 
  2961.  
  2962. Returns 
  2963.  
  2964. SHORT1FROMMR() 
  2965.  
  2966. TRUE                Success 
  2967. FALSE               Failure 
  2968.  
  2969. ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ 
  2970.  
  2971. LM_DELETEITEM 
  2972.  
  2973. Purpose 
  2974.  
  2975. Deletes a single item within the listbox. 
  2976.  
  2977. Parameters 
  2978.  
  2979. SHORT1FROMMP(mpParm1) Index of the item to delete. 
  2980.  
  2981. Returns 
  2982.  
  2983. SHORT1FROMMR() 
  2984.  
  2985. Number of items remaining. 
  2986.  
  2987. ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ 
  2988.  
  2989. LM_INSERTITEM 
  2990.  
  2991. Purpose 
  2992.  
  2993. Inserts a single item within the listbox. 
  2994.  
  2995. Parameters 
  2996.  
  2997. SHORT1FROMMP(mpParm1) Index at which to insert the item. 
  2998. PVOIDFROMMP(mpParm2) Pointer to the item text. 
  2999.  
  3000. Notes 
  3001.  
  3002. The insertion point can also be one of the following values: 
  3003.  
  3004. LIT_END             Insert the item at the end of the listbox. 
  3005. LIT_SORTASCENDING   Insert the item in ascending sorted order. 
  3006. LIT_SORTDESCENDING  Insert the item in descending sorted order. 
  3007.  
  3008. ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ 
  3009.  
  3010. LM_QUERYITEMCOUNT 
  3011.  
  3012. Purpose 
  3013.  
  3014. Returns the number of items within the listbox. 
  3015.  
  3016. Parameters 
  3017.  
  3018. This message accepts no parameters. 
  3019.  
  3020. Returns 
  3021.  
  3022. SHORT1FROMMR() 
  3023.  
  3024. Number of items. 
  3025.  
  3026. Introduction to PM Programming - EDM/2 - Dec 1994 - Volume 2, Issue 11 
  3027.  
  3028.  
  3029. ΓòÉΓòÉΓòÉ 6.4. Cleanliness ΓòÉΓòÉΓòÉ
  3030.  
  3031. Cleanliness 
  3032.  
  3033. If you modify HELLO.C to insert "Tom" 100 times, you'll see that the 
  3034. initialization phase of the listbox looks quite ugly.  It would be nice if the 
  3035. listbox would "pop-up" with everything already in it. The way to do this is to 
  3036. disable the update of the listbox before we begin and re-enable it after we 
  3037. finish the initialization.  This brings us to our next API. 
  3038.  
  3039. (BOOL)WinEnableWindowUpdate(hwndWnd,bEnable);
  3040.  
  3041. hwndWnd is the window to enable or disable.  bEnable is the new enable state of 
  3042. the window.  The function returns a success indicator. 
  3043.  
  3044. Introduction to PM Programming - EDM/2 - Dec 1994 - Volume 2, Issue 11 
  3045.  
  3046.  
  3047. ΓòÉΓòÉΓòÉ 6.5. Summary ΓòÉΓòÉΓòÉ
  3048.  
  3049. Summary 
  3050.  
  3051. This month, we looked at our next conquest - the listbox.  We saw the various 
  3052. styles it has and the design limitations that accompany it.  We also began 
  3053. looking at the listbox messages, and it is with this that we will continue next 
  3054. month. 
  3055.  
  3056. Introduction to PM Programming - EDM/2 - Dec 1994 - Volume 2, Issue 11 
  3057.  
  3058.  
  3059. ΓòÉΓòÉΓòÉ 7. Contributors to this Issue ΓòÉΓòÉΓòÉ
  3060.  
  3061. Are You a Potential Author? 
  3062.  
  3063. We are always looking for (new) authors.  If you have a topic about which you 
  3064. would like to write, send a brief description of the topic electronically to 
  3065. any of the editors, whose addresses are listed below, by the 15th of the month 
  3066. before the month in which your article will appear.  This alerts us that you 
  3067. will be sending an article so that we can plan the issue layout accordingly. 
  3068. After you have done this, get the latest copy of the Article Submission 
  3069. Guidelines from ftp.cdrom.com in the /pub/os2/2_x/program/newsltr directory. 
  3070. (The file is artsub.zip.)  The completed text of your article should be sent to 
  3071. us no later than five days prior to the last day of the month; any articles 
  3072. received after that time may be pushed to the next issue. 
  3073.  
  3074. The editors can be reached at the following email addresses: 
  3075.  
  3076. o Larry Salomon - os2man@panix.com (Internet). 
  3077. o Carsten Whimster - bcrwhims@undergrad.math.uwaterloo.ca (Internet). 
  3078.  
  3079. The following people contributed to this issue in one form or another (in 
  3080. alphabetical order): 
  3081.  
  3082. o John Holt 
  3083. o Larry Salomon, Jr. 
  3084. o Carsten Whimster 
  3085. o Network distributors 
  3086.  
  3087. Contributors - EDM/2 - Dec 1994 - Volume 2, Issue 11 
  3088.  
  3089.  
  3090. ΓòÉΓòÉΓòÉ 7.1. John Holt ΓòÉΓòÉΓòÉ
  3091.  
  3092. John Holt 
  3093.  
  3094. John Holt is a graduate student at Wright State University, and is employed by 
  3095. LEXIS/NEXIS in Dayton, Ohio as a software engineer.  John develops distributed 
  3096. systems in both an MVS and UNIX environment.  He can be reached via the 
  3097. internet at either johnh@meaddata.com or jholt@cs.wright.edu; or via Compuserve 
  3098. at 71324,3400. 
  3099.  
  3100.  
  3101. ΓòÉΓòÉΓòÉ 7.2. Larry Salomon, Jr. ΓòÉΓòÉΓòÉ
  3102.  
  3103. Larry Salomon, Jr. 
  3104.  
  3105. Larry Salomon, Jr. wrote his first Presentation Manager application for OS/2 
  3106. version 1.1 in 1989.  Since that time, he has written numerous VIO and PM 
  3107. applications, including the Scramble applet included with OS/2 and the 
  3108. I-Brow/Magnify/Screen Capture trio being distributed by IBM with the 
  3109. Professional Developers Kit CD-ROM.  Currently, he works for Cheyenne Software 
  3110. in Roslyn, New York and resides in Bellerose, New York with his wife Lisa. 
  3111.  
  3112. Larry can be reached electronically via the Internet at os2man@panix.com. 
  3113.  
  3114. Contributors - EDM/2 - Dec 1994 - Volume 2, Issue 11 
  3115.  
  3116.  
  3117. ΓòÉΓòÉΓòÉ 7.3. Carsten Whimster ΓòÉΓòÉΓòÉ
  3118.  
  3119. Carsten Whimster 
  3120.  
  3121. Carsten is an undergraduate Computer Science student at the University of 
  3122. Waterloo.  He is currently in third year, and is enjoying it immensely. 
  3123.  
  3124. Carsten is a beginning OS/2 PM programmer with a few projects on the go. He 
  3125. uses Watcom C/C++ 10.0 and Watcom VX-REXX 2.0b. 
  3126.  
  3127. Carsten is the author of POV-Panel/2, a popular shareware dashboard-like 
  3128. front-end for the POV-Ray 2.x compilers, and some commandline utilities. 
  3129. POV-Panel/2 can be found on ftp-os2.cdrom.com in pub/os2/32bit/graphics and 
  3130. some of the other major OS/2 ftp sites.  Carsten is also a TEAM-OS/2 member, 
  3131. and has adopted a little store in Waterloo. 
  3132.  
  3133. You may reach Carsten... 
  3134.  
  3135. ...via email: 
  3136.  
  3137. bcrwhims@undergrad.math.uwaterloo.ca - Internet 
  3138.  
  3139. ...Web homepage (I am just setting it up, so it may or may not be on-line): 
  3140.  
  3141. http://www.undergrad.math.uwaterloo.ca/~bcrwhims - WWW 
  3142.  
  3143. ...via snail mail (notice the changed address): 
  3144.  
  3145. Carsten Whimster
  3146. 318A Spruce Street
  3147. Waterloo, Ontario
  3148. Canada
  3149. N2L 3M7
  3150.  
  3151. Contributors - EDM/2 - Dec 1994 - Volume 2, Issue 11 
  3152.  
  3153.  
  3154. ΓòÉΓòÉΓòÉ 7.4. Network distributors ΓòÉΓòÉΓòÉ
  3155.  
  3156. Network Distributors 
  3157.  
  3158. These people are part of our distribution system to provide EDM/2 on networks 
  3159. other than the Internet.  Their help to provide access to this magazine for 
  3160. others is voluntary and we appreciate them a lot! 
  3161.  
  3162. o Paul Hethmon (hethmon@apac.ag.utk.edu) - Compuserve 
  3163. o Gess Shankar (gess@knex.mind.org) - Internet 
  3164. o David Singer (singer@almaden.ibm.com) - IBM Internal 
  3165. o Andre Asselin (ASSELIN AT RALVM12) - IBM Internal 
  3166.  
  3167. If you would like to become a "network distributor", be sure to contact the 
  3168. editors so that we can give you the credit you deserve! 
  3169.  
  3170. Contributors - EDM/2 - Dec 1994 - Volume 2, Issue 11 
  3171.  
  3172.  
  3173. ΓòÉΓòÉΓòÉ 8. How Do I Get EDM/2? ΓòÉΓòÉΓòÉ
  3174.  
  3175. How Do I Get EDM/2? 
  3176.  
  3177. EDM/2 can be obtained in any of the following ways: 
  3178.  
  3179. On the Internet 
  3180.  
  3181. o All back issues are available via anonymous FTP from the following sites: 
  3182.  
  3183.    - ftp.cdrom.com in the /pub/os2/2_x/program/newsltr directory. 
  3184.    - ftp.luth.se in the /pub/os2/programming/newsletter directory. 
  3185.    - generalhq.pc.cc.cmu.edu in the /pub/newsletters/edm2 directory. 
  3186.  
  3187. o The EDM/2 mailing list.  Send an empty message to edm2-info@knex.mind.org to 
  3188.   receive a file containing (among other things) instructions for subscribing 
  3189.   to EDM/2.  This is a UUCP connection, so be patient please. 
  3190. o IBM's external gopher/WWW server in Almaden. The address is 
  3191.   index.almaden.ibm.com and it is in the "Non-IBM-Originated" submenu of the 
  3192.   "OS/2 Information" menu; the URL is 
  3193.   "gopher://index.almaden.ibm.com/1nonibm/os2nonib.70". 
  3194.  
  3195. On Compuserve 
  3196.  
  3197. All back issues are available in the OS/2 Developers Forum 2. 
  3198.  
  3199. IBM Internal 
  3200.  
  3201. o IBM's internal gopher/WWW server in Almaden. The address is 
  3202.   n6tfx.almaden.ibm.com and it is in the "Non-IBM-Originated Files" menu; the 
  3203.   URL is "gopher://n6tfx.almaden.ibm.com/1!!nonibm/nonibm.70". 
  3204. o IBM's REQUEST command on all internal VM systems.  Enter the VM command 
  3205.   REQUEST LIST FROM ASSELIN AT RALVM12 and a list of the requestable packages 
  3206.   will be sent to you; in this list are the names of the packages containing 
  3207.   the EDM/2 issues. 
  3208.  
  3209. How do I Get EDM/2? - EDM/2 - Dec 1994 - Volume 2, Issue 11