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

  1.  
  2. ΓòÉΓòÉΓòÉ 1. Title Page ΓòÉΓòÉΓòÉ
  3.  
  4.            Welcome to EDM/2 - The Electronic OS/2 Developers Magazine!
  5.                    Portions copyright (c) by Larry Salomon Jr.
  6.                                 Volume 2, issue 1
  7.  
  8. Copyright Notice and Other Stuff 
  9.  
  10. The editor of this electronic magazine is Larry Salomon, Jr. 
  11.  
  12. Portions of EDM/2 are copyrighted by the editors.  This publication may be 
  13. freely distributed in electronic form provided that all parts are present in 
  14. their original unmodified form.  A reasonable fee may be charged for the 
  15. physical act of distribution; no fee may be charged for the publication itself. 
  16.  
  17. All articles are copyrighted by their authors. No part of any article may be 
  18. reproduced without permission from the original author. 
  19.  
  20. Neither this publication nor the editors are affiliated with International 
  21. Business Machines Corporation. 
  22.  
  23. OS/2 is a registered trademark of International Business Machines Corporation. 
  24. Other trademarks are property of their respective owners.  Any mention of a 
  25. product in this publication does not constitute an endorsement or affiliation 
  26. unless specifically stated in the text. 
  27.  
  28. Administrivia 
  29.  
  30. Eesh.  After a one-week vacation, I returned home with a SoundBlaster 16 and a 
  31. pair of Koss speakers; delighted, I decided to install them as soon as I 
  32. returned, but only after I finished hooking up my Super Nintendo system that I 
  33. also received from my younger brother (isn't family great? :) . It's only when 
  34. you receive a new hardware card more than a year after buying your computer 
  35. that you really appreciate the value of the Microchannel architecture, 
  36. especially when you have an ISA machine. 
  37.  
  38. "Let's see, was that DMA channel 5 or 6 used by the Adaptec card, and address 
  39. 330h is used by my modem, correct?"  After two hours, I finally got the machine 
  40. to boot and was able to use the rest of the hardware in my system, but still 
  41. had trouble with my SoundBlaster.  Some email suggested that I change the IRQ 
  42. number from 7 to something else, because OS/2 reserves that for the printer. 
  43. You have to love computer conferencing; thanks to everyone who responded. 
  44.  
  45. Will 1994 see an explosion in MMPM/2 programming?  I certainly hope so.  Having 
  46. been a member at the initial requirements meeting in Atlanta while I was an 
  47. IBMer, I would like to learn how to develop applications to use this extension 
  48. to OS/2 that has been hyped by those who have used it.  While we are waiting 
  49. for the myriad of application, check out the two head starts in this issue. 
  50. Not that I'm complaining, but can we see a few on video, too? 
  51.  
  52. Well, considering all that has happened in 1993, I must say the greatest thing 
  53. was that EDM/2 had close to 1000 subscriptions  total at the end of December! 
  54. What a number!  While I keep receiving email saying how I'm doing such a great 
  55. job with this, and blah blah blah, I must turn it around and say thank you to 
  56. all of the authors who have kept this magazine alive (and special thanks for 
  57. making this issue possible!) and also to all of the readers who have given the 
  58. authors a reason to write.  Considering that everything is done voluntarily, I 
  59. must hand it to everyone - we all did a fantastic job for a pilot year... 
  60.  
  61. And now that we are in volume 2 (did you notice that?), I get the extreme 
  62. pleasure of announcing the winners from the Reader's Choice Awards.  By the end 
  63. of December, I had received a total of 14 sets of votes, which I consider a 
  64. pathetic number given the number of readers, but I was consoled by email where 
  65. I was told that Analog Magazine (a scientific publication) which has been in 
  66. existance for 30+ years has always had an extremely low votes/reader ratio.  I 
  67. shall digress, however.  The winning articles, their authors, and the prize 
  68. each author shall receive is listed below. 
  69.  
  70. 1st Place 
  71.  
  72. 1st place goes to Andre Asselin for the OS/2 Installable File Systems series. 
  73. For this, he will receive a copy of HockWare, Inc.'s VisPro/REXX Bronze version 
  74. 2.0 (when it becomes available in mid-January) and a 1 year subscription to the 
  75. OS/2 Developer magazine. 
  76.  
  77. 2nd Place 
  78.  
  79. 2nd place goes to Gordon Zeglinski for the Rexx-ercising Your Applications 
  80. series.  For this, he will receive a copy of HockWare Inc.'s VisPro/REXX Bronze 
  81. version 2.0. 
  82.  
  83. 3rd Place 
  84.  
  85. 3rd place was a tie between Gavin Baker's Intro to PM column and my Threads in 
  86. PM applications article.  While I realize that this looks fishy, I - in the 
  87. spirit of random distribution - flipped a coin for it.  (Boy, was I glad it 
  88. wasn't a three-way tie! :)  Fortunately, Gavin won, so he will receive a 1 year 
  89. subscription to the OS/2 Developer magazine...as soon as I can figure out how 
  90. to reach him!  Mail to his address gets bounced.  If I do not hear from him by 
  91. next issue, I will award the prize to someone else. 
  92.  
  93. A hearty congratulations to all of the winners from us here at EDM/2!  We would 
  94. also like to thank Dick Conklin, the editor of OS/2 Developer, and Dave Hock, 
  95. the president of HockWare, Inc., for their generous contributions. 
  96.  
  97. If you have a topic to write about, this should be enough incentive; see the 
  98. section Are You a Potential Author? for information on submitting articles for 
  99. publication. 
  100.  
  101. This just in from the network - IBM will be presenting on February 3 a seminar 
  102. on its C-Set++ product.  This will be in New York City at the Madison Avenue 
  103. location (590 Madison Avenue, between 56th and 57th streets), in room 610 
  104. (auditorium).  The agenda currently looks like so: 
  105.  
  106. 6:15-7:15           C-Set++ Class Library: Design and Use 
  107.  
  108.                     Rich Hickey, a C++ a C SIG Technical Leader and teacher of 
  109.                     Advanced C++ at the NYU School of Continuing Education will 
  110.                     be the speaker. 
  111.  
  112. 7:45-9:00           C-Set++ for OS/2 Discussion and Demonstration 
  113.  
  114.                     IBMers from the C-Set++ development group in Toronto, 
  115.                     Canada will be the speakers. 
  116.  
  117. Post partum         Dutch treat (i.e. you pay for yourself) dinner at a nearby 
  118.                     restaurant for good food and great conversation. 
  119.  
  120. Admission is free, but a voluntary contribution of $3 (US) to defray the cost 
  121. of the meetings, etc. is asked.  All are welcome to attend.  I will try to be 
  122. there so if you ever wanted to meet the editor of EDM/2, you know where to find 
  123. me (this, unfortunately, is subject to change without notice. :( ). 
  124.  
  125. EDM/2 officially welcomes its newest columnist with whom we should all be 
  126. familiar.  Gordon Zeglinski takes up his post as resident C++ columnist 
  127. beginning this issue.  See C++ Corner for a first taste. 
  128.  
  129. Select this to go to the next section 
  130.  
  131.  
  132. ΓòÉΓòÉΓòÉ <hidden> Votes ΓòÉΓòÉΓòÉ
  133.  
  134. The breakdown of the votes is as follows: 
  135.  
  136. o OS/2 Installable File Systems - 6 votes 
  137. o Rexx-ercising Your Applications - 5 votes 
  138. o Threads in PM applications - 4 votes 
  139. o Introduction to PM Programming (Gavin Baker) - 4 votes 
  140. o Getting started with EMX/GCC - 3 votes 
  141. o Programming the Container Control - 3 votes 
  142. o A Review of C++ Compliers - 2 votes 
  143. o Introduction to PM Programming (Larry Salomon) - 2 votes 
  144. o Writing a Direct Manipulation Spy - 1 vote 
  145. o C++ Encapsulation of PM - 1 vote 
  146. o The Unofficial Guide to the Palette Manager - 1 vote 
  147. o Advanced GPI:  Retained Segments and Transformations - 1 vote 
  148. o The Making of MineSweeper - 1 vote 
  149. o Development of a New Window Class - 1 vote 
  150. o Customizing the Enhanced Editor - 1 vote 
  151. o The Help Manager & Online Documentation - 1 vote 
  152.  
  153.  
  154. ΓòÉΓòÉΓòÉ <hidden> VisPro/REXX Bronze version 2.0 ΓòÉΓòÉΓòÉ
  155.  
  156. The following excerpts are taken from an advance copy of the press release soon 
  157. to be made available to trade publications, etc. 
  158.  
  159. VisPro/REXX Bronze is an easy-to-use visual programming tool that provides a 
  160. quick way to build OS/2 2.x GUI applications.  Some of the key features 
  161. include: 
  162.  
  163. o Drag and drop programming which provides the easiest and quickest way to 
  164.   develop programs.  It automatically creates REXX code that can be used as-is 
  165.   or modified. 
  166. o Interactive debugger with break points, animation, error reporting, tracing 
  167.   and graphical event browsing for quicker debugging. 
  168. o Support for REXX APIs including DB2/2, APPC, and EHLLAPI. 
  169. o Multiple views such as Settings, Layout, Event Tree and List for efficient 
  170.   development. 
  171. o Ability to create multiple-thread applications that allow some program tasks 
  172.   to run while one task is still available to the user. 
  173. o More CUA '91 objects than similar products in order to provide flexible user 
  174.   interface design. 
  175. o New and improved: macro support, SOM Toolkit, Q+E Database Library support, 
  176.   improved debugger, OS/2-style help, information line tips, support for adding 
  177.   pop-up menus to programs, enhanced print support, accelerator key support and 
  178.   more sample programs. 
  179.  
  180. Suggested retail pricing for VisPro/REXX Bronze version 2.0 is $99 US. Special 
  181. upgrade pricing is available until March 31, 1994...$39 US ($59 after 3/31). 
  182.  
  183. The VisPro/REXX products are available directly from HockWare or from software 
  184. resellers worldwide.  [It] is available from IBM Australia, Limited for the 
  185. following countries: Australia, Hong Kong, Indonesia, Korea, Malaysia, New 
  186. Zealand, Philippines, Sri Lanka, Republic of China, Taiwan and Thailand. 
  187.  
  188. HockWare, Incorporated, based in Cary, North Carolina, can be reached at by 
  189. voice at (919) 380-0616 and by fax at (919) 380-0757. 
  190.  
  191.  
  192. ΓòÉΓòÉΓòÉ <hidden> OS/2 Developer ΓòÉΓòÉΓòÉ
  193.  
  194. The following was composed by me, so any inaccuracies are my fault; however, 
  195. OS/2 Developer was asked to return some form of text describing what they are 
  196. about, of which I have received none. 
  197.  
  198. OS/2 Developer was established by IBM's Personal Systems Line of Business 
  199. division in the late 1980's.  Since then, they have grown to a very large 
  200. reader base in many areas of OS/2 development, from MIS to the hard-core 
  201. programmers and all areas in between.  The magazine is currently published 
  202. bi-monthly by Miller Freeman, Incorporated and costs $39.95 US for 6 issues (1 
  203. year).  Canada, Mexico, and international surface mail, add $16 US per year for 
  204. postage.  (Canadian GST number is 124513185.) For international air mail, add 
  205. $30 US for postage.  Foreign orders must be accompanied by payments in US 
  206. funds. 
  207.  
  208. OS/2 Developer can be ordered in the US by calling (800) WANT OS2 
  209. (800-926-8672) and outside of the US by calling (708) 647-5960.  Questions may 
  210. also be directed to those phone numbers or via the Internet at 
  211. os2mag@vnet.ibm.com. 
  212.  
  213.  
  214. ΓòÉΓòÉΓòÉ 2. This Issue's Features ΓòÉΓòÉΓòÉ
  215.  
  216. The following articles constitute this issue's features: 
  217.  
  218. o Adding Sound to Your OS/2 Application 
  219. o Making Noise with MMPM/2 - Part 1 
  220. o Utilizing Hooks for Added Capabilities 
  221.  
  222.  
  223. ΓòÉΓòÉΓòÉ 2.1. Adding Sound to Your OS/2 Application ΓòÉΓòÉΓòÉ
  224.  
  225. Written by Semir Patel 
  226.  
  227. Introduction 
  228.  
  229. As PC based systems become cheaper and cheaper, soundcards are fastly becoming 
  230. a standard peripheral just like your hard disk or keyboard.  Todays PC's, are 
  231. for the most part, multimedia capable complete with 16bit sound cards and a 
  232. cdrom drive.  What better way to take advantage of that 16 bit (OK...so some of 
  233. you have 8 bit) sound card than to jazz up your OS/2 application with some 
  234. stunning sound effects.  They will definitely give your application a certain 
  235. edge over your competitor's. 
  236.  
  237. First and foremost, this article is not  meant to be an introduction to MMPM/2 
  238. programming.  This article is aimed at those of you who are asking yourself the 
  239. question, "How can I add sound to my program in the shortest amount of time 
  240. possible without trudging through the enormous and very complex MMPM/2 
  241. subsystem?"  Hopefully, this article will succeed in guiding you from point A 
  242. to point B without wandering too far off track.  It may be a little lean on 
  243. concept and instructional in style, but it explains only what is necessary to 
  244. get the job done.  In the end, you will have a fully functional thead that you 
  245. can plug into your already existing application. 
  246.  
  247. This article assumes previous knowledge of C, Presentation Manager, and basic 
  248. sound terminology.  The following hardware and software prerequisites are also 
  249. needed: 
  250.  
  251. o OS/2 2.x w/ MMPM/2 support installed 
  252. o OS/2 compiler 
  253. o MMPM/2 Toolkit 
  254. o Sound Card supported by MMPM/2 
  255.  
  256. Select this to go to the next section 
  257.  
  258.  
  259. ΓòÉΓòÉΓòÉ 2.1.1. Introduction ΓòÉΓòÉΓòÉ
  260.  
  261. Written by Semir Patel 
  262.  
  263. As PC based systems become cheaper and cheaper, soundcards are fastly becoming 
  264. a standard peripheral just like your hard disk or keyboard.  Todays PC's, are 
  265. for the most part, multimedia capable complete with 16bit sound cards and a 
  266. cdrom drive.  What better way to take advantage of that 16 bit (OK...so some of 
  267. you have 8 bit) sound card than to jazz up your OS/2 application with some 
  268. stunning sound effects.  They will definitely give your application a certain 
  269. edge over your competitor's. 
  270.  
  271. First and foremost, this article is not  meant to be an introduction to MMPM/2 
  272. programming.  This article is aimed at those of you who are asking yourself the 
  273. question, "How can I add sound to my program in the shortest amount of time 
  274. possible without trudging through the enormous and very complex MMPM/2 
  275. subsystem?"  Hopefully, this article will succeed in guiding you from point A 
  276. to point B without wandering too far off track.  It may be a little lean on 
  277. concept and instructional in style, but it explains only what is necessary to 
  278. get the job done.  In the end, you will have a fully functional thead that you 
  279. can plug into your already existing application. 
  280.  
  281. This article assumes previous knowledge of C, Presentation Manager, and basic 
  282. sound terminology.  The following hardware and software prerequisites are also 
  283. needed: 
  284.  
  285. o OS/2 2.x w/ MMPM/2 support installed 
  286. o OS/2 compiler 
  287. o MMPM/2 Toolkit 
  288. o Sound Card supported by MMPM/2 
  289.  
  290. Select this to go to the next section 
  291.  
  292.  
  293. ΓòÉΓòÉΓòÉ 2.1.2. The MCI String and Command Interfaces ΓòÉΓòÉΓòÉ
  294.  
  295. The MMPM/2 subsystem serves as the layer of abstraction between you and your 
  296. multimedia peripherals.  Its sole purpose is to allow you to effectively 
  297. communicate with and use your multimedia peripherals to their fullest extent. 
  298. It offers a rich and complex set of standardized APIs with which you can 
  299. comminuicate with media devices ranging from CD/LaserDisc players to sound 
  300. cards. 
  301.  
  302. There are two approaches one can take from this point forward.  The first is by 
  303. using the Media Control Interface (MCI) String Interface. This approach is 
  304. delightfully simple, far from complex, requires the least amount of time to 
  305. learn, and can even be accessed directly from IBM's powerful REXX language. 
  306. The string interface's claim to fame is its amazing simplicity.  Playing a 
  307. sound file is as easy as issuing the folloing sequence of character strings via 
  308. the mciSendString(). function. 
  309.  
  310. open waveaudio alias soundcard        // open/acquire sound device and give it the alias "soundcard"
  311. load soundcard sample.wav             // load sound file onto device (read into memory)
  312. play soundcard                        // play whatever is loaded onto device
  313. close soundcard                       // close/release sound device
  314.  
  315. All other multimedia devices can be accessed in the same general way.  It has 
  316. one shortcoming with respect to waveaudio devices, though - only one sound file 
  317. per device can be kept in memory at any one point in time.  This approach is 
  318. clearly not suitable for applications that require a few sound files or 
  319. situations where sounds are expected to be played synchronously in conjunction 
  320. with events.  Having to constantly shuffle sounds in and out from the hard disk 
  321. can also dramatically increase CPU usage. 
  322.  
  323. As opposed to the MCI string interface, the MCI command interface centers 
  324. around sending individual commands to a particluar media device.  It is in many 
  325. aspects very similar to the message based scheme used in window procedures. 
  326. The big advantage and our main reason for using the MCI command interface is 
  327. its low CPU usage and its ability to play one or more sounds that reside in 
  328. memory rather than on the hard disk. Granted, the command interface is somewhat 
  329. more complicated and harder to grasp, but it's more than worth it for the 
  330. efficiency and quick response. 
  331.  
  332. The following list outlines what needs to be done using the MCI command 
  333. interface to accomplish what the string interface does in four lines.  All 
  334. points will be discussed in detail throughout the article. 
  335.  
  336.  1. Open sound file 
  337.  2. Read into memory buffers 
  338.  3. Get information about sound file 
  339.  4. Close sound file 
  340.  5. Create memory playlist 
  341.  6. Insert sound file into memory playlist 
  342.  7. Open sound device 
  343.  8. Mount memory play list onto sound device 
  344.  9. Adjust sound device to match characteristics of sound file 
  345. 10. Issue command to play memory playlist 
  346. 11. Free memory sound file memory buffers 
  347. 12. Close sound device 
  348.  
  349. Select this to go to the next section 
  350.  
  351.  
  352. ΓòÉΓòÉΓòÉ 2.1.3. The MCI Command Interface ΓòÉΓòÉΓòÉ
  353.  
  354. You can communicate with any media device by sending it specific pre-defined 
  355. commands.  Just like PM window messages have the WM_* prefix, MCI commands and 
  356. flags have a suffix of MCI_*.  You also might have noticed the family of MCI 
  357. functions is prefixed by mci. These commands coupled with flags of various 
  358. sorts give you a very high degree of control over a device.  For example, with 
  359. a sound device we can manipulate it - PLAY, STOP, REWIND, etc.) or set certain 
  360. qualities like samples per second, volume, and bits per sample.  The 
  361. mciSendCommand() function's sole purpose is to deliver your commands to a 
  362. specific media device and to return a message once the command is completed if 
  363. so specified. 
  364.  
  365. mciSendCommand(usDeviceID,      /* device ID of media device    (sound card, CD, ....               ) */
  366.                usMessage,       /* command to be performed      (MCI_PLAY, MCI_SEEK, MCI_STOP, .... ) */
  367.                ulParam1,        /* flags for this command       (MCI_WAIT, MCI_NOTIFY, MCI_, ....   ) */
  368.                uParam2,         /* ptr to info structure        (MCI_OPEN_PARMS, MCI_SEEK_PARMS, ...) */
  369.                usUserParm);     /* param returned if MCI_NOTIFY (user defined message WM_USER + x   ) */
  370.                                 /* is a flag in ulParam1                                              */
  371.  
  372. The following are MCI commands with some of their associated flags and 
  373. information structures that we will be using in our example program: 
  374.  
  375. ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
  376. ΓöéCommand        ΓöéDescription              ΓöéValid Flags                        ΓöéFlag Desc.               ΓöéInfo Structure           Γöé
  377. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  378. ΓöéMCI_CLOSE      ΓöéClose the sound device   ΓöéNone                               Γöé                         ΓöéNone                     Γöé
  379. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  380. ΓöéMCI_SET        ΓöéUse to set device        ΓöéMCI_WAVE_SET_SAMPLESPERSEC         ΓöéSet samples per second   ΓöéMCI_WAVE_SET_PARMS       Γöé
  381. Γöé               Γöéinformation              Γöé                                   Γöé                         Γöé                         Γöé
  382. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  383. Γöé(MCI_SET)      Γöé                         ΓöéMCI_WAVE_SET_BITSPERSAMPLE         ΓöéSet bits per sample (8 orΓöéMCI_WAVE_SET_PARMS       Γöé
  384. Γöé               Γöé                         Γöé                                   Γöé16)                      Γöé                         Γöé
  385. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  386. ΓöéMCI_STOP       ΓöéStop the audio playback  ΓöéNone                               Γöé                         ΓöéNone                     Γöé
  387. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  388. ΓöéMCI_SEEK       ΓöéChanges position in wave ΓöéMCI_TO                             ΓöéSeek to specified postionΓöéMCI_SEEK_PARMS           Γöé
  389. Γöé               Γöéfile                     Γöé                                   Γöé                         Γöé                         Γöé
  390. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  391. Γöé(MCI_SEEK)     Γöé                         ΓöéMCI_TO_START                       ΓöéSeek to beginning        ΓöéMCI_SEEK_PARMS           Γöé
  392. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  393. Γöé(MCI_SEEK)     Γöé                         ΓöéMCI_TO_END                         ΓöéSeek to end              ΓöéMCI_SEEK_PARMS           Γöé
  394. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  395. ΓöéMCI_PLAY       ΓöéSignals the sound device ΓöéMCI_FROM                           ΓöéPlay starting from this  ΓöéMCI_PLAY_PARMS           Γöé
  396. Γöé               Γöéto start playing         Γöé                                   Γöéposition                 Γöé                         Γöé
  397. Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  398. Γöé(MCI_PLAY)     Γöé                         ΓöéMCI_TO                             ΓöéPlay to this postition   ΓöéMCI_PLAY_PARMS           Γöé
  399. ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
  400.  
  401. Flags commmon to all Commands: 
  402.  
  403. MCI_WAIT            Wait for the operation to finish succesfully instead of 
  404.                     executing it asynchronously and returning from the function 
  405.                     call 
  406. MCI_NOTIFY          Send usUserParam back to calling window (via 
  407.                     mciOpenParameters.hwndCallBack more on this below) once the 
  408.                     operation has finished. 
  409.  
  410. It is also possible to logically OR flags together to complete two operations 
  411. in one function call.  Of course, all the OR'ed flags must be valid for the 
  412. particular command you are about to issue. 
  413.  
  414. So before calling mciSendCommand() you should: 
  415.  
  416. o Decide on the command 
  417. o OR together the appropriate flags for your desired command 
  418. o Fill in the appropriate info structure with only the relevant information 
  419.  
  420. Let's say we want to change the bits per sample and the samples per second of 
  421. our sound device and also specify that the function not return before 
  422. completing the command.  The follow code should produce the desired effect: 
  423.  
  424. MCI_WAVE_SET_PARMS mwspWaveFormParameters;     /* info struc for MCI_SET command*/
  425.  
  426. mwspWaveFormParameters.ulSamplesPerSec = 11025;
  427. mwspWaveFormParameters.usBitsPerSample = 8;
  428.  
  429. mciSendCommand (SoundDevice.usSoundDeviceID,   /* sound device id. more details on this later */
  430.                 MCI_SET,                       /* command to set device  */
  431.                 MCI_WAIT                   |   /* OR'ed flags            */
  432.                 MCI_WAVE_SET_SAMPLESPERSEC |
  433.                 MCI_WAVE_SET_BITSPERSAMPLE ,
  434.                 (PVOID)&mwspWaveFormParameters, /* info structure */
  435.                 0);                            /* no notification message*/
  436.  
  437. Select this to go to the next section 
  438.  
  439.  
  440. ΓòÉΓòÉΓòÉ 2.1.4. Wave Audio Structures ΓòÉΓòÉΓòÉ
  441.  
  442. As you might already know, wave files are not all the same.  First of all, they 
  443. can either be 8 or 16 bit.  Furthermore, they may vary in Khz from 11 to 44. 
  444. It is imperative that the characteristics of the sound device match those of 
  445. the wave file.  For example, if your wave file is recorded at 11025Hz and your 
  446. sound device is set at 22050Hz, your output will obviously be distorted. The 
  447. information we need to know about the wave file is stored in its header. 
  448. Calling the function mmioGetHeader() along with the following structure and a 
  449. file handle obtained throught mmioOpen() will extract that information for us. 
  450. Comments are beside the fields that are of interest to us. 
  451.  
  452. mmioGetHeader(hmmio,                    /* file handle returned by mmioOpen */
  453.               pHeader,                  /* pointer to header structure      */
  454.               usHeaderLength,           /* size of header stucture          */
  455.               plBytesRead,              /* returns # bytes read into header */
  456.               ulReserved,               /* reserved                         */
  457.               ulFlags);                 /* reserved                         */
  458.  
  459.        ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
  460.        Γöé  MMAUDIOHEADER                  Γöé
  461.        Γöé   ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ Γöé
  462.        Γöé   Γöé  MMXWAV_HEADER            Γöé Γöé
  463.        Γöé   Γöé   ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ Γöé Γöé
  464.        Γöé   Γöé   Γöé  WAVE_HEADER        Γöé Γöé Γöé
  465.        Γöé   Γöé   ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ Γöé Γöé
  466.        Γöé   Γöé   ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ Γöé Γöé
  467.        Γöé   Γöé   Γöé  XWAV_HEADERINFO    Γöé Γöé Γöé
  468.        Γöé   Γöé   ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ Γöé Γöé
  469.        Γöé   ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ Γöé
  470.        ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
  471.  
  472. typedef struct _MMAUDIOHEADER {
  473.         ULONG            ulHeaderLength;
  474.         ULONG            ulContentType;
  475.         ULONG            ulMediaType;
  476.         MMXWAV_HEADER    mmXWAVHeader;
  477.         } MMAUDIOHEADER;
  478.  
  479. typedef struct _MMXWAV_HEADER {
  480.         WAVE_HEADER        WAVEHeader;
  481.         XWAV_HEADERINFO    XWAVHeaderInfo;
  482.         } MMXWAV_HEADER;
  483.  
  484. typedef struct _WAVE_HEADER {
  485.         USHORT    usFormatTag;
  486.         USHORT    usChannels;           /* mono,stereo or quad*/
  487.         ULONG     ulSamplesPerSec;      /* samples per second */
  488.         ULONG     ulAvgBytesPerSec;
  489.         USHORT    usBlockAlign;
  490.         USHORT    usBitsPerSample;      /* bits per sample    */
  491.         } WAVE_HEADER;
  492.  
  493. typedef struct _XWAV_HEADERINFO {
  494.         ULONG    ulAudioLengthInMS;
  495.         ULONG    ulAudioLengthInBytes;  /* length of raw data */
  496.         ULONG    ulReserved;
  497.         } XWAV_HEADERINFO;
  498.  
  499. Once the header has been obtained, the next step would be to copy the commented 
  500. fields into an MCI_WAVE_SET_PARMS structure and issue a MCI_SET as in the 
  501. previous section. 
  502.  
  503. Select this to go to the next section 
  504.  
  505.  
  506. ΓòÉΓòÉΓòÉ 2.1.5. Memory Playlists ΓòÉΓòÉΓòÉ
  507.  
  508. A memory playlist is the glue that holds the whole thing together.  Without it, 
  509. all you would have is an open sound device and wave files sitting in memory but 
  510. no way to play them.  Memory playlists are simply a set of instructions.  Each 
  511. entry of a memory playlist consists of one command (the instruction) and up to 
  512. 3 operands. 
  513.  
  514. /* playlist structure */
  515. typedef struct pls
  516. {
  517.    ULONG ulCommand;
  518.    ULONG ulOperandOne;
  519.    ULONG ulOperandTwo;
  520.    ULONG ulOperandThree;
  521. } PLAY_LIST_STRUCTURE;
  522.  
  523. Memory playlists are very flexible and offer a variety of instructions. Here 
  524. are a few of interest with along with descriptions of the three operands. Pay 
  525. special attention to the DATA_OPERATION. 
  526.  
  527. BRANCH_OPERATION 
  528.  
  529. Transfers control to another instruction in the playlist. 
  530.  
  531. Operand 1           Ignored. 
  532. Operand 2           The absolute instruction number in the playlist to which 
  533.                     control is being transferred.  Because the playlist is 
  534.                     defined as an array of structures (instruction, operation, 
  535.                     and operand values) its first instruction is referenced as 
  536.                     array element, index 0. Therefore, the first instruction in 
  537.                     the list is 0, the second instruction is 1, and so on. 
  538. Operand 3           Ignored. 
  539.  
  540. DATA_OPERATION 
  541.  
  542. Specifies a data buffer to be played from or recorded into. 
  543.  
  544. Operand 1           Long pointer to a buffer in the application. 
  545. Operand 2           Length of the buffer pointed to by Operand 1. 
  546. Operand 3           Current position in the buffer.  This operand is updated by 
  547.                     the system during a recording or playback operation.  For a 
  548.                     playback operation, it is the number of bytes that have 
  549.                     been sent to the output device handler.  For a recording 
  550.                     operation, it is the number of bytes that have been placed 
  551.                     into a user buffer. 
  552.  
  553. The buffer indicated by the DATA instruction must only contain the raw data 
  554. bytes from the device and cannot include any header information. For example, a 
  555. buffer for a sequencer device can contain only MIDI multibyte messages, as 
  556. defined by the International MIDI Association. Therefore, the precise meaning 
  557. or format of the data is dependent on the current settings of the media device. 
  558. For example, a wave audio data element is assumed to have the format PCM or 
  559. ADPCM, number of bits per sample, and so on, that is indicated by the settings 
  560. of the audio device. 
  561.  
  562. The address range of a DATA statement cannot overlap the address range of any 
  563. another DATA statement.  However, the same DATA statement can be repeated. 
  564.  
  565. EXIT_OPERATION 
  566.  
  567. Indicates the end of the playlist. 
  568.  
  569. Operand 1           Ignored. 
  570. Operand 2           Ignored. 
  571. Operand 3           Ignored. 
  572.  
  573. Using playlist instructions, you can play audio objects in succession from one 
  574. or more memory buffers.  Instructions include branching to and returning from 
  575. subroutines within the playlist.  In addition, the playlist can be modified 
  576. dynamically by the application while it is being played. 
  577.  
  578. The MCI_OPEN_PLAYLIST flag is specified for the MCI_OPEN or MCI_LOAD command 
  579. message to indicate that the pszElementName field in the MCI_OPEN_PARMS or 
  580. MCI_LOAD_PARMS data structure is a pointer to a memory playlist. This has the 
  581. effect of mounting the playlist onto or linking the playlist to the sound 
  582. device. 
  583.  
  584. For example, if we wanted to play the contents of a memory buffer the command 
  585. would be DATA_OPERATION with the first and second operands being a pointer to a 
  586. memory buffer and the length of the memory buffer respectively.  When the 
  587. MMPM/2 subsystem receives a command to play a memory playlist, it simply goes 
  588. down the list and sequentially executes the commands. Changing the first and 
  589. second operands dynamically enables you to play any wave file loaded into a 
  590. memory buffer. 
  591.  
  592. /* example declaration for a memory playlist with 2 commands and */
  593. /* nulled out operands                                           */
  594. PLAY_LIST_STRUCTURE PlayList [NUMBER_OF_COMMANDS] =
  595. {
  596.         DATA_OPERATION,    0, 0, 0,       /* play command       */
  597.         EXIT_OPERATION,    0, 0, 0        /* terminate playlist */
  598. };
  599.  
  600. PlayList[0].ulOperandOne = (ULONG)&MemoryBufferWhereRawWaveDataIsStored;
  601. PlayList[0].ulOperandTwo = (ULONG)SizeofMemoryBuffer;
  602. Now an MCI_PLAY would play the playlist. 
  603.  
  604. mciSendCommand (SoundDevice.usSoundDeviceID,
  605.                 MCI_PLAY,
  606.                 MCI_WAIT,
  607.                 (PVOID)&mciOpenParameters,
  608.                 0);
  609.  
  610. Select this to go to the next section 
  611.  
  612.  
  613. ΓòÉΓòÉΓòÉ 2.1.6. Putting the pieces together ΓòÉΓòÉΓòÉ
  614.  
  615. Now that the groundwork has been laid out, let's work on building a fully 
  616. functional thread thread that can easily be plugged into your already existing 
  617. program and play waves when requested to do so.  We will need to: 
  618.  
  619.  1. Spawn a thread to act as a server for all sound operations 
  620.  2. Read the wave files in from disk to memory 
  621.  3. Open the sound device and mount playlist onto it 
  622.  4. Accept requests to play wave files until the server is instructed to 
  623.     terminate 
  624.  5. Close the sound device and free memory buffers 
  625.  6. End thread 
  626.  
  627. We'll skip the explanation for part one here since threads are a whole 
  628. different topic.  The code for this is of course in the accompanying zip file. 
  629.  
  630. Select this to go to the next section 
  631.  
  632.  
  633. ΓòÉΓòÉΓòÉ 2.1.7. Reading in the wave files from disk to memory ΓòÉΓòÉΓòÉ
  634.  
  635. We will use an array of the following structure to store information about each 
  636. individual sound file. 
  637.  
  638. struct SOUNDINFO{
  639.         char    szFileName[255];        /* filename..already initialized       */
  640.         LONG    *Address;               /* pointer to this wav's memory buffer */
  641.         ULONG   ulSize;                 /* size of the wave file               */
  642.         ULONG   ulSamplesPerSec;        /* samples per second                  */
  643.         USHORT  usBitsPerSample;        /* bits per sample                     */
  644.         };
  645.  
  646. struct SOUNDINFO SoundFiles[NUMBER_OF_WAVES];
  647.  
  648. We loop through the array of structures and do the following on each iteration: 
  649.  
  650. First we want to attach a file handle to the wave file so we can operate on it. 
  651.  
  652. hmmioFileHandle = mmioOpen (acFileName,         /* file name                   */
  653.                             (PMMIOINFO)NULL,    /* extra params not needed     */
  654.                             MMIO_READ);         /* opens file for reading only */
  655.  
  656. Next we obtain the header information by calling mmioGetHeader() which fills in 
  657. the mmAudioHeader structure. 
  658.  
  659. mmioGetHeader (hmmioFileHandle,                 /* file handle                  */
  660.                (PVOID)&mmAudioHeader,           /* info copied into this struc  */
  661.                sizeof (MMAUDIOHEADER),          /* size of header structure     */
  662.                (PLONG) &ulBytesRead,            /* # bytes read into header     */
  663.                (ULONG) NULL,                    /* reserved                     */
  664.                (ULONG) NULL);                   /* reserved                     */
  665.  
  666. Now we copy the information from the header structure into our own array of 
  667. soundfile information and then malloc and copy the raw data into memory. 
  668.  
  669. SoundFiles[usSoundFileID].ulSize          = mmAudioHeader.mmXWAVHeader.XWAVHeaderInfo.ulAudioLengthInBytes;
  670. SoundFiles[usSoundFileID].ulSamplesPerSec = mmAudioHeader.mmXWAVHeader.WAVEHeader.ulSamplesPerSec;
  671. SoundFiles[usSoundFileID].usBitsPerSample = mmAudioHeader.mmXWAVHeader.WAVEHeader.usBitsPerSample;
  672. SoundFiles[usSoundFileID].Address         = (LONG *) malloc(SoundFiles[usSoundFileID].ulSize);
  673.  
  674. mmioRead(hmmioFileHandle,                               /* handle of source     */
  675.          (PSZ)SoundFiles[usSoundFileID].Address,        /* ptr to destination   */
  676.          SoundFiles[usSoundFileID].ulSize);             /* amount to copy       */
  677.  
  678. Finally we release the file by calling mmioClose(). 
  679.  
  680. mmioClose(hmmioFileHandle,0 );
  681.  
  682. Select this to go to the next section 
  683.  
  684.  
  685. ΓòÉΓòÉΓòÉ 2.1.8. Opening the Sound Device ΓòÉΓòÉΓòÉ
  686.  
  687. Let's go through the 3 steps before calling mciSendCommand(). 
  688.  
  689.  1. Pick command 
  690.  
  691.     In order to open a sound device, an MCI_OPEN must be issued 
  692.  
  693.  2. OR flags together 
  694.  
  695.         ULONG    ulOpenFlags = MCI_WAIT           |          /* wait for function to return                                                 */
  696.                                MCI_OPEN_PLAYLIST  |          /* this flag says that you are mounting playlist onto the sound device         */
  697.                                                              /* the address of the playlist resides in the info structure                   */
  698.                                MCI_OPEN_TYPE_ID   |          /* you are specifying a device type in the info structure                      */
  699.                                MCI_OPEN_SHAREABLE;           /* if another process requests the sound device, you are willing to give it up */
  700.  
  701.  3. Pick appropriate info structure and fill in relevant info. In our case, 
  702.     MCI_OPEN_PARMS is the info structure 
  703.  
  704.         typedef struct _MCI_OPEN_PARMS {
  705.                 DWORD    hwndCallback       /* Window handle */
  706.                 WORD     usDeviceID;        /* Device ID     */
  707.                 WORD     usReserved0;        /* Reserved      */
  708.                 PSZ      pszDeviceType;     /* Device type   */
  709.                 PSZ      pszElementName;    /* Element name  */
  710.                 PSZ      pszAlias;          /* Device alias  */
  711.              } MCI_OPEN_PARMS;
  712.  
  713.         MCI_OPEN_PARMS       mciOpenParameters;
  714.  
  715.         mciOpenParameters.pszDeviceType = (PSZ) MAKEULONG ( MCI_DEVTYPE_WAVEFORM_AUDIO, 1 );  /* specifies that we want a waveform audio device */
  716.         mciOpenParameters.hwndCallback  = (HWND) NULL;                                        /* no callback window                          */
  717.         mciOpenParameters.pszAlias      = (CHAR) NULL;                                        /* no alias                                    */
  718.  
  719. Now this is the part that links the playlist and sound device together.  By 
  720. making the pszElementName of the MCI_OPEN_PARMS structure point to the base 
  721. address of the playlist, any operations you perform on that device (like 
  722. MCI_PLAY or MCI_STOP) are performed on the playlist.  The mciSendCommand() 
  723. essentially opens the sound device and moints the playlist onto it.  Note that 
  724. the device id is hardcoded in as a zero.  The actual device id will be returned 
  725. in mciOpenParameters.usDeviceID. 
  726.  
  727. mciOpenParameters.pszElementName = (PSZ)PlayList[0];
  728.  
  729. mciSendCommand(0,                           /* We don't know the device yet.        */
  730.                MCI_OPEN,                    /* MCI message.                         */
  731.                ulOpenFlags,                 /* Flags for the MCI message.           */
  732.                (PVOID) &mciOpenParameters,  /* Parameters for the message.          */
  733.                0);                          /* Parameter for notify message.        */
  734.  
  735. We're going to include one more structure to keep track of the current 
  736. characteristics of the sound device.  This way, if a wave files samples/sec or 
  737. bits/sample do not match those of the sound device, we know to adjust the sound 
  738. device. 
  739.  
  740. struct SOUNDDEVICE {
  741.         USHORT  usSoundDeviceID;        /* device id */
  742.         ULONG   ulSamplesPerSec;        /* samps/sec */
  743.         USHORT  usBitsPerSample;        /* bits/samp */
  744.         };
  745.  
  746. struct SOUNDDEVICE SoundDevice;
  747.  
  748. Let's copy the the device id into SoundDevice so we can use it on future calls 
  749. to mciSendCommand(). 
  750.  
  751. SoundDevice.usSoundDeviceID = mciOpenParameters.usDeviceID;
  752.  
  753. Now the sound device is open and ready to accept commands. 
  754.  
  755. Select this to go to the next section 
  756.  
  757.  
  758. ΓòÉΓòÉΓòÉ 2.1.9. Playing Wave Files ΓòÉΓòÉΓòÉ
  759.  
  760. This part of the MCI command interface works exactly like you would work your 
  761. VCR or tape player.  There are commands to PLAY, STOP, PAUSE, REWIND, FAST 
  762. FORWARD, etc. 
  763.  
  764. Given an index, usSoundFileID, into an array of SOUND_INFO structures, the 
  765. first step is to modify the playlist so that it points to that waves memory 
  766. buffer.  This is easily done by copying the address and memory buffer size to 
  767. operands one and two respectively. 
  768.  
  769. PlayList[0].ulOperandOne = (ULONG)SoundFiles[usSoundFileID].Address;
  770. PlayList[0].ulOperandTwo = SoundFiles[usSoundFileID].Size;
  771.  
  772. Next is the check to make sure that the number of bits per sample and the 
  773. number of samples per second match for the wave file and the sound device.  If 
  774. they are not identical, then the sound device is changed accordingly. 
  775.  
  776. MCI_WAVE_SET_PARMS   mwspWaveFormParameters;
  777.  
  778. if ((SoundFiles[usSoundFileID].ulSamplesPerSec != SoundDevice.ulSamplesPerSec) ||
  779.     (SoundFiles[usSoundFileID].usBitsPerSample != SoundDevice.usBitsPerSample))
  780. {
  781.          /* null out structure */
  782.          memset( &mwspWaveFormParameters, 0, sizeof(mwspWaveFormParameters));
  783.  
  784.          /* update sound device info */
  785.          SoundDevice.ulSamplesPerSec = SoundFiles[usSoundFileID].ulSamplesPerSec;
  786.          SoundDevice.usBitsPerSample = SoundFiles[usSoundFileID].usBitsPerSample;
  787.  
  788.          /* setup the info structire for an MCI_SET */
  789.          mwspWaveFormParameters.ulSamplesPerSec = SoundDevice.ulSamplesPerSec;
  790.          mwspWaveFormParameters.usBitsPerSample = SoundDevice.usBitsPerSample;
  791.  
  792.          mciSendCommand(SoundDevice.usSoundDeviceID,      /* Device to play the waves.     */
  793.                         MCI_SET,                          /* MCI message.                  */
  794.                         MCI_WAIT |
  795.                         MCI_WAVE_SET_SAMPLESPERSEC |      /* Flags for the MCI message.    */
  796.                         MCI_WAVE_SET_BITSPERSAMPLE ,
  797.                         (PVOID) &mwspWaveFormParameters,  /* Parameters for the message.   */
  798.                         0);                               /* Parameter for notify message. */
  799. }
  800.  
  801. Stop and rewind the playlist just in case a previous wave file is still 
  802. playing. 
  803.  
  804. mciSendCommand(SoundDevice.usSoundDeviceID,    /* Device to play the waves.     */
  805.                MCI_STOP,                       /* MCI message.                  */
  806.                MCI_WAIT,                       /* Flags for the MCI message.    */
  807.                (PVOID) &mciOpenParameters,     /* Parameters for the message.   */
  808.                0);                             /* Parameter for notify message. */
  809.  
  810. mciSendCommand(SoundDevice.usSoundDeviceID,    /* Device to play the waves.     */
  811.                MCI_SEEK,                       /* MCI message.                  */
  812.                MCI_WAIT | MCI_TO_START,        /* Flags for the MCI message.    */
  813.                (PVOID) &mciOpenParameters,     /* Parameters for the message.   */
  814.                0);                             /* Parameter for notify message. */
  815.  
  816. Now we're ready to issue an MCI_PLAY command 
  817.  
  818. mciSendCommand(SoundDevice.usSoundDeviceID,  /* Device to play the waves.     */
  819.                MCI_PLAY,                     /* MCI message.                  */
  820.                0,                            /* Flags for the MCI message.    */
  821.                (PVOID) &mciOpenParameters,   /* Parameters for the message.   */
  822.                0);                           /* Parameter for notify message. */
  823.  
  824. Notice that I neglected to send any flags for MCI_PLAY.  It is advisable to 
  825. send an MCI_WAIT if you want your sound to play to its entirety, but if 
  826. MCI_WAIT is not sent, it is possible to receive another MCI_PLAY message while 
  827. a prevoius sound is still playing. The resulting effect is that the prevoius 
  828. sound is abruptly cut short and the new sound is played.  Both have their place 
  829. depending on how you want sounds to react to certain events. 
  830.  
  831. Select this to go to the next section 
  832.  
  833.  
  834. ΓòÉΓòÉΓòÉ 2.1.10. Closing the Sound Device ΓòÉΓòÉΓòÉ
  835.  
  836. This is just to make sure we tie up all the loose ends and return the memory 
  837. buffers to the free memory list. 
  838.  
  839. for( usCounter=0; usCounter<NUMBER_OF_WAVES; usCounter++ )
  840.         free( (VOID *) SoundFiles[usCounter].Address);
  841.  
  842. /* close sound device */
  843. mciSendCommand(usSoundDeviceID,              /* Device to play the chimes.    */
  844.                MCI_CLOSE,                    /* MCI message.                  */
  845.                MCI_WAIT,                     /* Flags for the MCI message.    */
  846.                (ULONG) NULL,                 /* Parameters for the message.   */
  847.                (ULONG) NULL );               /* Parameter for notify message. */
  848.  
  849. Select this to go to the next section 
  850.  
  851.  
  852. ΓòÉΓòÉΓòÉ 2.1.11. Conclusion ΓòÉΓòÉΓòÉ
  853.  
  854. You should now have a basic understanding of how the MCI Command Interface 
  855. works and hopefully be able to extend the supplied source code in the zipfile 
  856. to fit your needs.  Let's get some OS/2 apps with some dazzling sound effects 
  857. out there!! 
  858.  
  859.  
  860. ΓòÉΓòÉΓòÉ 2.2. Making Noise with MMPM/2 - Part 1 ΓòÉΓòÉΓòÉ
  861.  
  862. Written by Marc van Woerkom 
  863.  
  864. Introduction 
  865.  
  866. Begining with version 2.1, IBM has delivered two new disks with OS/2 containing 
  867. the previously separately sold Multimedia Presentation Manager/2 1.1 (MMPM/2). 
  868. Having once visited a colleague who used a Mac and being enamoured with the 
  869. characteristic <WHOOOSH!> emminating from the system speaker after he dropped 
  870. some superfluous files on a toilet icon, I installed it to try out the system 
  871. sounds on my SoundBlaster-equipped machine. Although it is quite satisfying in 
  872. its own right that our beloved operating system is now able to cope with the 
  873. Mac in this particular area, one should take the little extra time to have a 
  874. closer look on the MMPM/2 for it has more to offer. 
  875.  
  876. This is the first of two articles which do so.  It gives an overview of MMPM/2 
  877. and introduces programming for multimedia, using the REXX language which is 
  878. sufficient for many cases.  The second article will deal with MMPM/2 
  879. programming in C and C++.  Both articles focus on the audio capabilites of 
  880. MMPM/2. 
  881.  
  882. Select this to go to the next section 
  883.  
  884.  
  885. ΓòÉΓòÉΓòÉ 2.2.1. Introduction ΓòÉΓòÉΓòÉ
  886.  
  887. Written by Marc van Woerkom 
  888.  
  889. Begining with version 2.1, IBM has delivered two new disks with OS/2 containing 
  890. the previously separately sold Multimedia Presentation Manager/2 1.1 (MMPM/2). 
  891. Having once visited a colleague who used a Mac and being enamoured with the 
  892. characteristic <WHOOOSH!> emminating from the system speaker after he dropped 
  893. some superfluous files on a toilet icon, I installed it to try out the system 
  894. sounds on my SoundBlaster-equipped machine. Although it is quite satisfying in 
  895. its own right that our beloved operating system is now able to cope with the 
  896. Mac in this particular area, one should take the little extra time to have a 
  897. closer look on the MMPM/2 for it has more to offer. 
  898.  
  899. This is the first of two articles which do so.  It gives an overview of MMPM/2 
  900. and introduces programming for multimedia, using the REXX language which is 
  901. sufficient for many cases.  The second article will deal with MMPM/2 
  902. programming in C and C++.  Both articles focus on the audio capabilites of 
  903. MMPM/2. 
  904.  
  905. Select this to go to the next section 
  906.  
  907.  
  908. ΓòÉΓòÉΓòÉ 2.2.2. MMPM/2 Overview ΓòÉΓòÉΓòÉ
  909.  
  910. Like its name suggests, the Multimedia Presentation Manager adds multimedia 
  911. capabilities to IBM's OS/2 2.1 operating system, but what does this actually 
  912. mean?  For me, the term multimedia has acquired a slightly negative connotation 
  913. in recent months because it is used so extensivly by computer hardware vendors 
  914. to hawk their wares.  They take a PC, put a soundboard and a CD-ROM drive into 
  915. it, drape it with two tiny speakers and voila(!)  it's perfect multimedia!  The 
  916. hardware is sold but the purpose is still rather vague. 
  917.  
  918. In the publication "OS/2 2.1 Technical Update" (GG24-3948-00) IBM writes: 
  919.  
  920. The key benefit of multimedia is an enhanced quality of information and 
  921. communication.  When audio, image, and video are combined with text and 
  922. graphics, customers can access richer forms of information, and communication 
  923. becomes more effective. 
  924.  
  925. This is not a bad definition.  The variety of the types of data computers are 
  926. asked to crunch has become richer over the years, starting with numbers and 
  927. text, followed by graphics, and now also images (be it photos or live motion 
  928. video) and audio data.  The need for the capability to combine these different 
  929. kinds of data is also crucial. 
  930.  
  931. IBM writes further: 
  932.  
  933. MMPM/2 enables this increase in productivity by providing device control, 
  934. streaming, synchronization, and multimedia object I/O support. 
  935.  
  936. That is a brief summary of MMPM/2.  The package consists of: 
  937.  
  938. Physical Device Drivers (PDD) A collection of drivers for multimedia hardware 
  939.                     like soundcards, CD-ROM drives, video digitizers, video 
  940.                     disc players, MIDI devices, etc. 
  941.  
  942. Media Control Interface (MCI) The MCI is a programming interface which allows 
  943.                     the easy use of the media devices, handling them all in a 
  944.                     common way via command strings like open, close, play, 
  945.                     record, stop, seek, etc. 
  946.  
  947. Synchronization & Streaming Programming Interface (SPI) The SPI multimedia 
  948.                     subsystem is responsible for the smooth execution of 
  949.                     multimedia applications under OS/2.  It ensures that those 
  950.                     tasks get the needed processor time and synchronizes 
  951.                     different streams of multimedia data. 
  952.  
  953. Multimedia I/O Services The MMIO subsystem extends the OS/2 file services. It 
  954.                     provides a standard mechanism for accessing and 
  955.                     manipulating multimedia data files (media elements). 
  956.                     Supported are buffered I/O, RIFF file I/O, memory file I/O, 
  957.                     compound file I/O; it is also possible to add additional 
  958.                     I/O procedures to the MMIO subsystem. 
  959.  
  960. PM Extensions       MMPM/2 provides some new Presentation Manager window 
  961.                     classes tailored for the control of multimedia applications 
  962.                     (graphical button, circular slider, etc.) 
  963.  
  964. Also several multimedia utilities are delivered with MMPM/2, like a CD audio 
  965. player, a waveaudio player and editor, software video players and more. 
  966.  
  967. This first article only deals with the MCI because it is programmable from REXX 
  968. in a very easy way and gives a nice impression of the MMPM/2 architecture.  The 
  969. other features will be treated in the second article. 
  970.  
  971. Select this to go to the next section 
  972.  
  973.  
  974. ΓòÉΓòÉΓòÉ 2.2.3. The Media Control Interface ΓòÉΓòÉΓòÉ
  975.  
  976. If you have installed MMPM/2 you'll find on your harddisk a online manual about 
  977. using the MCI from a REXX programmer's point of view. 
  978.  
  979. Note:  this manual has many gaps!  A more complete description can be found in 
  980. the MMPM/2 Programming Reference of the OS/2 Technical Library (available in 
  981. online form on the bookware CD-ROM). 
  982.  
  983. To allow interaction with the multimedia hardware MMPM/2 defines a set of media 
  984. devices on top of the PDDs, which can be controled via the textual string 
  985. commands of the Media Control Interface (MCI), defined by IBM and Microsoft in 
  986. 1991. 
  987.  
  988. The standard media devices are: 
  989.  
  990. Device name         Description 
  991. CDaudio             A CD-ROM device which supports standard audio compact disc 
  992.                     playback 
  993. Digitalvideo        A device which supports audio/video files, either 
  994.                     hardware-assisted or software motion video-only (e.g. it 
  995.                     plays Intel Indeo and IBM Ultimotion *.AVI files) 
  996. Sequencer           A device which supports MIDI files (*.MID) 
  997. Videodisc           A videodisc player 
  998. Videotape           A videotape player or recorder 
  999. Waveaudio           A device which supports digital audio files (*.WAV) 
  1000.  
  1001. The MMPM/2 headers also define these devices: 
  1002.  
  1003. Device name         Description 
  1004. Ampmix              An amplifier/mixer device 
  1005. Animation           A device for playing Autodesk Animator (*.FLC, *.FLI) and 
  1006.                     MacroMind Director files (*.MMM) 
  1007. Audiotape           An analog audio tape deck 
  1008. CDXA                A CD-ROM device which supports the extended architecture 
  1009.                     (XA) audio compact disc standard (up to 8h stereo, 15h 
  1010.                     music, 30h voice quality playback) 
  1011. DAT                 A digital audio tape deck 
  1012. Headphone           Headphones 
  1013. Microphone          A microphone 
  1014. Monitor             A monitor output device 
  1015. Other               An undefined MCI device 
  1016. Overlay             A video overlay device - video output in a window 
  1017. Scanner             An image scanner 
  1018. Speaker             A speaker 
  1019.  
  1020. These media devices are modeled closely after their real-world pendants and 
  1021. their programming via the MCI command strings mirrors the actions one usually 
  1022. performs on a common audio or video component.  Also the controls of the 
  1023. utilities delivered with the MMPM/2 have the same typical look and feel of 
  1024. their real-world counterparts. 
  1025.  
  1026. All devices are either of simple or compound type.  Compound devices use data 
  1027. files (in MCI-speak:  can load a device element in the device context), simple 
  1028. devices don't.  CDaudio is a simple device, Waveaudio a compound one. 
  1029.  
  1030. Let's start with a simple example which should play a certain waveform audio 
  1031. file.  The MCI command strings one needs for this are: 
  1032.  
  1033. open Waveaudio alias wave shareable wait
  1034. load wave c:\mmos2\sounds\boing.wav wait
  1035. play wave wait
  1036. close wave wait
  1037.  
  1038. First the Waveaudio device is opened in shareable mode (in MCI-speak:  a device 
  1039. context is created) and it gets the alias wave assigned to it.  Then using the 
  1040. alias to address the media device, the BOING.WAV file is loaded into memory.  A 
  1041. play command is issued on wave and finally the device is closed.  This also 
  1042. frees the memory assigned to the media device when loading the *.WAV file.  The 
  1043. wait keyword tells MMPM/2 to wait until the command has finished before 
  1044. continuing. 
  1045.  
  1046. Now let's play the end from "U got the look" (track 1, 3.40) to the beginning 
  1047. of "if I was your girlfriend" (track 2, 0.20) from disc 2 of Prince's "Sign o' 
  1048. the Times" album: 
  1049.  
  1050. open CDaudio alias cd wait
  1051. set cd time format tmsf wait
  1052. play cd from 1:3:40 to 2:0:20 wait
  1053. close cd wait
  1054.  
  1055. The CDaudio device is opened and the alias cd is assigned to it.  Next the 
  1056. media device is told to count in tracks, minutes, seconds, and frames. 
  1057. Finally, both tracks are played from the given positions and the device is 
  1058. closed. 
  1059.  
  1060. Note that the CDaudio device is directly manipulated; it is not necessary to 
  1061. load a data file. 
  1062.  
  1063. The third examples plays a software video slowed down to 25% speed.  Note that 
  1064. in this mode audio is switched off. 
  1065.  
  1066. open digitalvideo alias video wait
  1067. info video product wait
  1068. load video some.avi wait
  1069. set video speed format percentage wait
  1070. play video speed 25 wait
  1071. close video wait
  1072.  
  1073. The digitalvideo device gets opened and video is the assigned alias.  Then the 
  1074. device is queried for the information product; this will create a return string 
  1075. with some describing text.  The file SOME.AVI gets loaded and the format for 
  1076. speed settings is set to percentages.  The video is then played with 25% speed 
  1077. and the device is closed. 
  1078.  
  1079. I hope the basic recipe of a MCI script was conveyed from these examples. They 
  1080. illustrate that the different media devices are handled in similar manners. 
  1081. Armed with a MCI manual you should be able to write your own scripts. 
  1082.  
  1083. To send those command strings to the interpreting Media Device Manager (MDM) 
  1084. one can use either REXX, C/C++ or the String Test tool provided with the 
  1085. Multimedia Toolkit. 
  1086.  
  1087. Select this to go to the next section 
  1088.  
  1089.  
  1090. ΓòÉΓòÉΓòÉ 2.2.4. Using the MCI from REXX ΓòÉΓòÉΓòÉ
  1091.  
  1092. Now let's look how to realize those examples in REXX. 
  1093.  
  1094. Example 1: 
  1095.  
  1096. /************************************************************
  1097.     boing.cmd
  1098.  
  1099.     REXX procedure to play boing.wav via MMPM
  1100.  
  1101.  
  1102.     Marc E.E. van Woerkom, 1/94
  1103.  ************************************************************/
  1104.  
  1105.  
  1106. rc = call RxFuncAdd('mciRxInit', 'MCIAPI',,         /* register MCI REXX API */
  1107.                     'mciRxInit')
  1108. rc = call mciRxInit()                               /* and initialize it */
  1109.  
  1110.  
  1111. rc = mciRxSendString('open waveaudio',              /* open digital audio */
  1112.                      'alias wave shareable wait',,
  1113.                      'RetStr', '0', '0')
  1114.  
  1115. boing = 'c:\mmos2\sounds\boing.wav'
  1116.  
  1117. rc = mciRxSendString('load wave' boing,,            /* load wav */
  1118.                      'RetStr', '0', '0')
  1119.  
  1120. rc = mciRxSendString('play wave wait',,             /* play file */
  1121.                      'RetStr', '0', '0')
  1122.  
  1123. rc = mciRxSendString('close wave wait',,            /* close device "wave" */
  1124.                      'RetStr', '0', '0')
  1125.  
  1126. call mciRxExit
  1127.  
  1128. exit rc                                             /* exit with rc code */
  1129.  
  1130. The first step in BOING.CMD is to register the MCI REXX application interface 
  1131. using the call to RxFuncAdd() and then by initializing it via a call to 
  1132. mciRxInit(). 
  1133.  
  1134. Statements of the form mciRxSendString('some_MCI_command',RetStr,'0','0') send 
  1135. the MCI command string to the MDM and may receive a return string in the RetStr 
  1136. variable. 
  1137.  
  1138. Business finishes with a call to mciRxExit(). 
  1139.  
  1140. Example 2: 
  1141.  
  1142. /************************************************************
  1143.     prince.cmd
  1144.  
  1145.     REXX procedure to play some audio CD tracks via MMPM
  1146.  
  1147.  
  1148.     Marc E.E. van Woerkom, 1/94
  1149.  ************************************************************/
  1150.  
  1151.  
  1152. rc = call RxFuncAdd('mciRxInit', 'MCIAPI',,         /* register MCI REXX API */
  1153.                     'mciRxInit')
  1154. rc = call mciRxInit()                               /* and initialize it */
  1155.  
  1156.  
  1157. rc = mciRxSendString('open CDaudio',                /* open CDaudio */
  1158.                      'alias cd wait',,
  1159.                      'RetStr', '0', '0')
  1160.  
  1161. rc = mciRxSendString('set cd time format',          /* count in tracks */
  1162.                      'tmsf wait ',,                 /* (tt:mm:ss:ff)   */
  1163.                      'RetStr', '0', '0')
  1164. say rc
  1165.  
  1166. rc = mciRxSendString('play cd from 1:3:40 to 2:0:20 wait',,   /* play file */
  1167.                      'RetStr', '0', '0')
  1168. if rc <> 0 then do
  1169.     rc2 = mciRxGetErrorString(rc, 'ErrStVar')
  1170.     say 'ERROR! rc =' rc ', ErrStVar =' ErrStVar
  1171.     exit rc2
  1172. end
  1173.  
  1174. rc = mciRxSendString('close cd wait',,              /* close device "cd" */
  1175.                      'RetStr', '0', '0')
  1176. say rc
  1177.  
  1178. call mciRxExit
  1179.  
  1180. exit rc                                             /* exit with rc code */
  1181.  
  1182. This example also uses the function mciRxGetErrorString() to receive the error 
  1183. message belonging to the return value RC. 
  1184.  
  1185. Example 3: 
  1186.  
  1187. /************************************************************
  1188.     playvid.cmd
  1189.  
  1190.     REXX procedure to play a software video at 25% speed
  1191.     usage: pmrexx playvid name.avi
  1192.  
  1193.     Marc E.E. van Woerkom, 1/94
  1194.  ************************************************************/
  1195.  
  1196. parse arg avi                                      /* fetch first argument */
  1197.  
  1198. if avi = '' then do
  1199.     say 'usage: pmrexx playvid name.avi'
  1200.     exit 0
  1201. end
  1202.  
  1203. say '(Playing a Software Video at 25% speed ...'
  1204.  
  1205. rc = call RxFuncAdd('mciRxInit', 'MCIAPI',,        /* register MCI REXX API */
  1206.                     'mciRxInit')
  1207. rc = call mciRxInit()                              /* and initialize it */
  1208.  
  1209. rc = mciRxSendString('open digitalvideo alias',    /* open player */
  1210.                      'video wait',,
  1211.                      'RetStr', '0', '0')
  1212.  
  1213. rc = mciRxSendString('info video',                 /* ask for product info */
  1214.                      'product wait',,
  1215.                      'videoinfo', '0', '0')
  1216.  
  1217. say '    Digital Video Device is' videoinfo
  1218.  
  1219. rc2 = mciRxSendString('load video' avi,,           /* play *.avi */
  1220.                      'RetStr', '0', '0')
  1221.  
  1222. rc = mciRxSendString('set video speed',            /* set speed in % */
  1223.                      'format percentage wait',,
  1224.                      'RetStr', '0', '0')
  1225.  
  1226. rc = mciRxSendString('play video speed 25 wait',,  /* play file */
  1227.                      'RetStr', '0', '0')
  1228.  
  1229. rc = mciRxSendString('close video wait',,          /* close video device */
  1230.                      'RetStr', '0', '0')
  1231. if rc2 <> 0 then do
  1232.     rc = mciRxGetErrorString(rc2, 'ErrStVar')
  1233.     say '    ERROR! rc =' rc2 ', ErrStVar =' ErrStVar
  1234.     say ' ... failed!)'
  1235.     exit rc
  1236. end
  1237. else say ' ... done.)'
  1238.  
  1239. call mciRxExit
  1240.  
  1241. exit rc                                            /* exit with rc code */
  1242.  
  1243. This one starts with a parsing of the command line arguments.  Later the 
  1244. example queries the digitalvideo device for a product description string which 
  1245. is delivered into the videoinfo variable. 
  1246.  
  1247. Note that the digitalvideo device needs the Presentation Manager, so if this 
  1248. REXX procedure is to be used from the command line, one has to call it through 
  1249. PMREXX. 
  1250.  
  1251. Example 4: 
  1252.  
  1253. /************************************************************
  1254.     cdlock.cmd
  1255.  
  1256.     REXX procedure to lock the CD device
  1257.     usage: cdlock
  1258.  
  1259.  
  1260.     Marc E.E. van Woerkom, 11/93
  1261.  ************************************************************/
  1262.  
  1263.  
  1264. say '(Locking the CD drive''s door ...'
  1265.  
  1266. rc = call RxFuncAdd('mciRxInit', 'MCIAPI',,        /* register MCI REXX */
  1267.                     'mciRxInit')
  1268. rc = call mciRxInit()                              /* and initialize it */
  1269.  
  1270. rc = mciRxSendString('open CDaudio alias cd',      /* open CDaudio */
  1271.                      'shareable wait',,
  1272.                      'RetStr', '0', '0')
  1273.  
  1274. rc2 = mciRxSendString('set cd door locked wait',,  /* lock CD door */
  1275.                       'RetStr', '0', '0')
  1276.  
  1277. rc = mciRxSendString('close cd wait',,             /* close device "cd" */
  1278.                      'RetStr', '0', '0')
  1279.  
  1280. if rc2 <> 0 then
  1281.     say ' ... failed!)'
  1282. else
  1283.     say ' ... done.)'
  1284.  
  1285. call mciRxExit
  1286.  
  1287. exit rc2                                           /* exit with rc2 code */
  1288.  
  1289. Did you notice already the menu item lock disk in the context menu of the 
  1290. CD-ROM drive icon?  As does that menu item, this procedure locks the door of 
  1291. the CD-ROM drive so that it can't be opened with a push on the external 
  1292. open/close button. 
  1293.  
  1294. Example 5: 
  1295.  
  1296. /************************************************************
  1297.     cdunlock.cmd
  1298.  
  1299.     REXX procedure to unlock the CD device
  1300.     usage: cdunlock
  1301.  
  1302.  
  1303.     Marc E.E. van Woerkom, 11/93
  1304.  ************************************************************/
  1305.  
  1306.  
  1307. say '(Unlocking the CD drive''s door ...'
  1308.  
  1309. rc = call RxFuncAdd('mciRxInit', 'MCIAPI',,          /* register MCI API */
  1310.                     'mciRxInit')
  1311. rc = call mciRxInit()                                /* and initialize it */
  1312.  
  1313. rc = mciRxSendString('open CDaudio alias cd',        /* open CDaudio */
  1314.                      'shareable wait',,
  1315.                      'RetStr', '0', '0')
  1316.  
  1317. rc2 = mciRxSendString('set cd door unlocked wait',,  /* unlock CD door */
  1318.                       'RetStr', '0', '0')
  1319.  
  1320. rc = mciRxSendString('close cd wait',,               /* close device "cd" */
  1321.                      'RetStr', '0', '0')
  1322.  
  1323. if rc2 <> 0 then
  1324.     say ' ... failed!)'
  1325. else
  1326.     say ' ... done.)'
  1327.  
  1328. call mciRxExit
  1329.  
  1330. exit rc2                                             /* exit with rc2 code */
  1331.  
  1332. This procedure unlocks the CD-ROM drive, reenabling the external open/close 
  1333. button. 
  1334.  
  1335. Example 6: 
  1336.  
  1337. /************************************************************
  1338.     cdopen.cmd
  1339.  
  1340.     REXX procedure to open the CD door
  1341.     usage: cdopen
  1342.  
  1343.  
  1344.     Marc E.E. van Woerkom, 11/93
  1345.  ************************************************************/
  1346.  
  1347.  
  1348. say '(Opening the CD drive''s door ...'
  1349.  
  1350. rc = call RxFuncAdd('mciRxInit', 'MCIAPI',,       /* register MCI REXX API */
  1351.                     'mciRxInit')
  1352. rc = call mciRxInit()                             /* and initialize it */
  1353.  
  1354. rc = mciRxSendString('open CDaudio alias cd',     /* open CDaudio */
  1355.                      'shareable wait',,
  1356.                      'RetStr', '0', '0')
  1357.  
  1358. rc2 = mciRxSendString('set cd door open wait',,   /* open CD door */
  1359.                       'RetStr', '0', '0')
  1360.  
  1361. rc = mciRxSendString('close cd wait',,            /* close device "cd" */
  1362.                      'RetStr', '0', '0')
  1363.  
  1364. if rc2 <> 0 then
  1365.     say ' ... failed!)'
  1366. else
  1367.     say ' ... done.)'
  1368.  
  1369. call mciRxExit
  1370.  
  1371. exit rc2                                          /* exit with rc2 code */
  1372.  
  1373. This procedure opens the door of your CD-ROM drive and ejects the tray. I 
  1374. admit, it is a bit drole (somehow it reminds me of little R2-D2 from Star Wars) 
  1375. but I'd be grateful if someone points out a serious application of this to me. 
  1376.  
  1377. Example 7: 
  1378.  
  1379. /************************************************************
  1380.     playall.cmd
  1381.  
  1382.     REXX procedure to play all *.wav *.mid
  1383.     usage: playall directory [wav] [midi] [rec]
  1384.  
  1385.  
  1386.     Marc E.E. van Woerkom, 1/94
  1387.  ************************************************************/
  1388.  
  1389.  
  1390. parse arg dir arg1 arg2 arg3 /* copy argv[1] and remove */
  1391.  
  1392. say
  1393. if dir = '' then do
  1394.     say 'PlayAll seeks and plays all soundfiles'
  1395.     say
  1396.     say 'usage: playall directory [wav] [midi] [rec]'
  1397.     exit 0
  1398. end
  1399.  
  1400. if dir = '.' then dir = directory()                /* current dir */
  1401.  
  1402.  
  1403. dowav  = 0
  1404. domidi = 0
  1405. dorec  = 0
  1406.  
  1407.  
  1408. parse upper arg dummy arg1 arg2 arg3  /* copy argv[1..3] */
  1409.  
  1410. if arg1 = 'WAV'  then dowav  = 1
  1411.  
  1412. if arg1 = 'MIDI' then domidi = 1
  1413. if arg2 = 'MIDI' then domidi = 1
  1414.  
  1415. if arg1 = 'REC'  then dorec  = 1
  1416. if arg2 = 'REC'  then dorec  = 1
  1417. if arg3 = 'REC'  then dorec  = 1
  1418.  
  1419.  
  1420. if (\dowav) & (\domidi) then do
  1421.    dowav  = 1
  1422.    domidi = 1
  1423. end
  1424.  
  1425.  
  1426. files = ''                                         /* message */
  1427. if dowav  then files = ' *.wav'
  1428. if domidi then files = files' *.mid'
  1429.  
  1430.  
  1431. opt = 'O'                                          /* dir tree options       */
  1432. if dorec then do                                   /* F: files only          */
  1433.     opt = opt'S'                                   /* O: only qualified name */
  1434.     recstr = 'and beneath '                        /* S: recurse             */
  1435. end
  1436. else recstr = ''
  1437.  
  1438.  
  1439. say '(Playing all'files 'files from' dir recstr'...'
  1440. say
  1441.  
  1442.  
  1443. rc = call RxFuncAdd('SysLoadFuncs', 'RexxUtil',,   /* register REXX Utils */
  1444.                     'SysLoadFuncs')
  1445. rc = call SysLoadFuncs()                           /* and initialize them */
  1446.  
  1447.  
  1448. rc = call RxFuncAdd('mciRxInit', 'MCIAPI',,        /* register MCI REXX API */
  1449.                     'mciRxInit')
  1450. rc = call mciRxInit()                              /* and initialize it */
  1451.  
  1452.  
  1453. if dowav then do
  1454.     rc = mciRxSendString('open waveaudio',         /* open digital audio */
  1455.                          'alias wave shareable wait',,
  1456.                          'RetStr', '0', '0')
  1457.     if rc <> 0 then do
  1458.         rc2 = mciRxGetErrorString(rc, 'ErrStVar')
  1459.         say 'ERROR! rc =' rc ', ErrStVar =' ErrStVar
  1460.         exit rc2
  1461.     end
  1462.  
  1463.     rc = mciRxSendString('info wave',              /* ask for product info */
  1464.                          'product wait',,
  1465.                          'waveinfo', '0', '0')
  1466.  
  1467.     rc = mciRxSendString('close wave wait',,       /* close device "wave" */
  1468.                          'RetStr', '0', '0')
  1469.  
  1470.     say '    Waveaudio device is' waveinfo
  1471. end
  1472.  
  1473.  
  1474. if domidi then do
  1475.     rc = mciRxSendString('open sequencer',         /* open MIDI device */
  1476.                          'alias midi2 shareable wait',,
  1477.                          'RetStr', '0', '0')
  1478.  
  1479.     if rc <> 0 then do
  1480.         rc2 = mciRxGetErrorString(rc, 'ErrStVar')
  1481.         say 'ERROR! rc =' rc ', ErrStVar =' ErrStVar
  1482.         exit rc2
  1483.     end
  1484.  
  1485.     rc = mciRxSendString('info midi2',             /* ask for product info */
  1486.                          'product wait',,
  1487.                          'midi2info', '0', '0')
  1488.  
  1489.     rc = mciRxSendString('close midi2 wait',,      /* close device "midi" */
  1490.                          'RetStr', '0', '0')
  1491.  
  1492.     say '    MIDI device is' midi2info
  1493. end
  1494.  
  1495. if Right(dir, 1) = '\' then
  1496.     mask = dir'*.*'
  1497. else
  1498.     mask = dir'\*.*'
  1499.  
  1500. rc = call SysFileTree(mask, 'file', opt)           /* read dir tree */
  1501.  
  1502. if file.0 = 0 then do
  1503.     say '    No files found!'
  1504.     rc = 0
  1505. end
  1506.  
  1507. do i=1 to file.0
  1508.     ext = Translate(Right(file.i, 4))
  1509.  
  1510.     if dowav & (ext = '.WAV') then do
  1511.         say
  1512.         say '    File' file.i':'
  1513.  
  1514.         rc = mciRxSendString('open waveaudio',     /* open digital audio */
  1515.                              'alias wave shareable wait',,
  1516.                              'RetStr', '0', '0')
  1517.  
  1518.         rc = mciRxSendString('set wave time',      /* set time format to ms */
  1519.                              'format ms wait',,
  1520.                              'RetStr', '0', '0')
  1521.  
  1522.         rc = mciRxSendString('load wave' file.i 'wait',,   /* play *.wav */
  1523.                              'RetStr', '0', '0')
  1524.  
  1525.         rc = mciRxSendString('status wave length wait',,   /* query length */
  1526.                              'RetStr', '0', '0')
  1527.  
  1528.         if rc = 0 then do
  1529.             time = RetStr / 1000.0
  1530.             say '    Playing time is' time 's'
  1531.         end
  1532.  
  1533.         rc = mciRxSendString('play wave wait',,    /* play file */
  1534.                              'RetStr', '0', '0')
  1535.  
  1536.         rc = mciRxSendString('close wave wait',,   /* close device "wave" */
  1537.                              'RetStr', '0', '0')
  1538.     end
  1539.  
  1540.     if domidi & (ext = '.MID') then do
  1541.         say
  1542.         say '    File' file.i':'
  1543.  
  1544.         rc = mciRxSendString('open sequencer',     /* open MIDI device */
  1545.                              'alias midi2 shareable wait',,
  1546.                              'RetStr', '0', '0')
  1547.  
  1548.         rc = mciRxSendString('set midi2 time',     /* set time format to ms */
  1549.                              'format ms wait',,
  1550.                              'RetStr', '0', '0')
  1551.  
  1552.         rc = mciRxSendString('load midi2' file.i 'wait',,   /* play *.mid */
  1553.                              'RetStr', '0', '0')
  1554.  
  1555.         rc = mciRxSendString('status midi2 length wait',,   /* query length */
  1556.                              'RetStr', '0', '0')
  1557.         if rc = 0 then do
  1558.             time = RetStr / 1000.0
  1559.             say '    Playing time is' time 's'
  1560.         end
  1561.  
  1562.         rc = mciRxSendString('play midi2 wait',,   /* play file */
  1563.                              'RetStr', '0', '0')
  1564.  
  1565.         rc = mciRxSendString('close midi2 wait',,  /* close device "midi" */
  1566.                              'RetStr', '0', '0')
  1567.     end
  1568. end
  1569.  
  1570. say
  1571. if rc = 0 then
  1572.     say '... done.)'
  1573. else
  1574.     say '... playing aborted!)'
  1575.  
  1576. call mciRxExit
  1577.  
  1578. exit rc                                            /* exit with rc code */
  1579.  
  1580. I wrote this procedure to try out a whole directory of audio files at once.  To 
  1581. play all *.WAV files from the current directory and all directories under it, 
  1582. type playall . wav rec on the command line. 
  1583.  
  1584. Select this to go to the next section 
  1585.  
  1586.  
  1587. ΓòÉΓòÉΓòÉ 2.2.5. Literature on MMPM/2 ΓòÉΓòÉΓòÉ
  1588.  
  1589. Dick Conklin (Ed.):
  1590. OS/2 2.x Notebook - The Best of OS/2 Developer Magazine.
  1591. Van Nostrand Reinhold, New York, 1993.
  1592.  
  1593. IBM Doc. S41G-2919:
  1594. MMPM/2 Programming Guide.
  1595. "Describes application and subsystem programming interfaces to help you
  1596. select and implement functions for OS/2 multimedia applications"
  1597.  
  1598. IBM Doc. S41G-2920:
  1599. MMPM/2 Programming Reference.
  1600. "Provides detailed information on multimedia functions, messages, and data
  1601. structures to enable you to write code for multimedia applications"
  1602.  
  1603. IBM Doc. S41G-2921:
  1604. MMPM/2 Sample Programs Workbook.
  1605. "Gives code examples from MMPM/2 sample application programms and
  1606. templates for subsystem components"
  1607.  
  1608. IBM Doc. S41G-2922:
  1609. CUA Guide to Multimedia User Interface Design.
  1610. "Describes design concepts to consider when designing a CUA multimedia
  1611. interface"
  1612.  
  1613. IBM Doc. S41G-2923:
  1614. OS/2 Multimedia Advantages.
  1615. "Describes advantages offered by OS/2 and MMPM/2 multimedia platform"
  1616.  
  1617. IBM Doc. S41G-3321:
  1618. Complete MMPM/2 Technical Library.
  1619.  
  1620. John Musser:
  1621. A Multimedia Class Library for Windows.
  1622. Dr. Dobb's Journal, 7:84-90, 1993.
  1623.  
  1624. Select this to go to the next section 
  1625.  
  1626.  
  1627. ΓòÉΓòÉΓòÉ 2.3. Utilizing Hooks for Added Capabilities ΓòÉΓòÉΓòÉ
  1628.  
  1629. Written by Larry Salomon, Jr. 
  1630.  
  1631. Introduction 
  1632.  
  1633. Beginning with the MVS operating system, user exits have existed in order to 
  1634. give those with a bit of programming skill the opportunity to change the 
  1635. default behavior for a particular event.  Since then, these have evolved into 
  1636. system hooks, which provide the same ability; unfortunately, whether known as 
  1637. user exits or system hooks, there has been a lack of documentation on how to 
  1638. utilize these provisions from with OS/2 applications. 
  1639.  
  1640. This article will discuss what hooks are, what the different types of hooks 
  1641. are, and the uses of some of the various types of hooks.  Finally, we will look 
  1642. at a (new) version of Life Saver, which uses one of the hooks to implement a 
  1643. screen saver. 
  1644.  
  1645. Select this to go to the next section 
  1646.  
  1647.  
  1648. ΓòÉΓòÉΓòÉ 2.3.1. Introduction ΓòÉΓòÉΓòÉ
  1649.  
  1650. Written by Larry Salomon, Jr. 
  1651.  
  1652. Beginning with the MVS operating system, user exits have existed in order to 
  1653. give those with a bit of programming skill the opportunity to change the 
  1654. default behavior for a particular event.  Since then, these have evolved into 
  1655. system hooks, which provide the same ability; unfortunately, whether known as 
  1656. user exits or system hooks, there has been a lack of documentation on how to 
  1657. utilize these provisions from with OS/2 applications. 
  1658.  
  1659. This article will discuss what hooks are, what the different types of hooks 
  1660. are, and the uses of some of the various types of hooks.  Finally, we will look 
  1661. at a (new) version of Life Saver, which uses one of the hooks to implement a 
  1662. screen saver. 
  1663.  
  1664. Select this to go to the next section 
  1665.  
  1666.  
  1667. ΓòÉΓòÉΓòÉ 2.3.2. Going Fishing? ΓòÉΓòÉΓòÉ
  1668.  
  1669. What is a hook?  In the sport of fishing, a hook attaches the fishing line) to 
  1670. the fish so that the line follows the movement of the fish.  In programming, 
  1671. the concept is similar - you use a hook to attach your application to an event 
  1672. so that your application knows whenever the event occurs.  Why is this useful? 
  1673. Consider an application that needs to know when the system is rebooting, or 
  1674. needs to know whenever the PrintScreen key was pressed (regardless of who has 
  1675. the focus).  These types of things can only be done with hooks. 
  1676.  
  1677. OS/2 defines 17 types of hooks that can be set. 
  1678.  
  1679. Send message event  (HK_SENDMSG)  The hook is invoked whenever WinSendMsg() is 
  1680.                     called. 
  1681.  
  1682. Input event         (HK_INPUT)  The hook is invoked whenever WinPostMsg() is 
  1683.                     called, including - and especially - for all mouse and 
  1684.                     keyboard input. 
  1685.  
  1686. Message filter      (HK_MSGFILTER)  The hook is invoked during any Win function 
  1687.                     which enters a message loop, e.g. WinDlgBox(), 
  1688.                     WinMessageBox(), etc. 
  1689.  
  1690. Journal record      (HK_JOURNALRECORD)  The hook is invoked to record (journal) 
  1691.                     a message. 
  1692.  
  1693. Journal playback    (HK_JOURNALPLAYBACK)  The hook is invoked to play back a 
  1694.                     journaled message. 
  1695.  
  1696. Help event          (HK_HELP)  The hook is invoked whenever help is requested 
  1697.                     by the user or an application. 
  1698.  
  1699. Library and procedure event (HK_LOADER)  The hook is invoked whenever 
  1700.                     WinLoadLibrary() or WinLoadProcedure() is called. 
  1701.  
  1702. User message registration event (HK_REGISTERUSERMSG)  The hook is invoked 
  1703.                     whenever WinRegisterUserMsg() is called. 
  1704.  
  1705. Message control event (HK_MSGCONTROL)  The hook is invoked whenever any of the 
  1706.                     following four functions are called: 
  1707.                     WinSetClassMsgInterest(), WinSetMsgInterest(), 
  1708.                     WinSetMsgMode(), and WinSetSynchroMode(). 
  1709.  
  1710. Find word event     (HK_FINDWORD)  The hook is invoked whenever a word to be 
  1711.                     drawn by the WinDrawText() function is too long to fit 
  1712.                     within the bounding rectangle. 
  1713.  
  1714. Code page change event (HK_CODEPAGECHANGED)  The hook is invoked whenever a 
  1715.                     message queue's code page changes. 
  1716.  
  1717. Device context association event (HK_WINDOWDC)  The hook is invoked whenever a 
  1718.                     device context is associated with a window. 
  1719.  
  1720. Window destroy event (HK_DESTROYWINDOW)  The hook is invoked whenever a window 
  1721.                     is destroyed. 
  1722.  
  1723. Message filter event (HK_CHECKMSGFILTER)  The hook is invoked whenever 
  1724.                     WinGetMsg() or WinPeekMsg() is called. 
  1725.  
  1726. Input insertion     (HK_MSGINPUT)  The hook is used to insert mouse or keyboard 
  1727.                     messages similar to the journal playback hook. 
  1728.  
  1729. System lockup event (HK_LOCKUP)  The hook is invoked whenever WinLockupSystem() 
  1730.                     is called. 
  1731.  
  1732. System shutdown event (HK_FLUSHBUF) The hook is invoked whenever the 
  1733.                     Ctrl-Alt-Del key sequence is pressed. 
  1734.  
  1735. The user message registration event is generated by a function that is no 
  1736. longer documented.  The WinRegisterUserMessage() function existed in the 1.3 
  1737. Toolkit documentation but does no longer. 
  1738.  
  1739. Two other hooks - HK_PLIST_ENTRY and HK_PLIST_EXIT - are also defined in 
  1740. pmwin.h but they do not exist within the system (as far as I know). 
  1741.  
  1742. Hook Contexts 
  1743.  
  1744. Some hooks can be defined as application-wide or system-wide; the difference is 
  1745. that an application-wide hook will be called whenever the event occurs within 
  1746. the application, while a system-wide hook will be called whenever the event 
  1747. occurs anywhere within the system.  Because of this property of system-wide 
  1748. hooks, the corresponding hook procedure must reside within a DLL.  This has 
  1749. other interesting connotations which we will see later. 
  1750.  
  1751. Some hooks must be application-wide, while others must be system-wide. Such 
  1752. hooks do not have a choice. 
  1753.  
  1754. Select this to go to the next section 
  1755.  
  1756.  
  1757. ΓòÉΓòÉΓòÉ 2.3.3. Chaining Hooks ΓòÉΓòÉΓòÉ
  1758.  
  1759. In reality, a hook is nothing more than a function that accepts one or more 
  1760. arguments.  The function is called by the system, meaning that the function 
  1761. must be exportable and use the _System calling convention. Additionally, the 
  1762. system must be made aware of the hook's existance; it is registered via the 
  1763. function WinSetHook().  Similarly, when the hook is to be removed, the function 
  1764. WinReleaseHook() is called. 
  1765.  
  1766. Figure 1)  Before and after calling WinSetHook(). 
  1767.  
  1768. BOOL WinSetHook(HAB habAnchor,
  1769.                 HMQ hmqQueue,
  1770.                 LONG lHookType,
  1771.                 PFN pfnHook,
  1772.                 HMODULE hmModule);
  1773. BOOL WinReleaseHook(HAB habAnchor,
  1774.                     HMQ hmqQueue,
  1775.                     LONG lHookType,
  1776.                     PFN pfnHook,
  1777.                     HMODULE hmModule);
  1778.  
  1779. Both function take the following parameters: 
  1780.  
  1781. habAnchor           Anchor block of the calling thread. 
  1782. hmqQueue            Handle of the message queue to install the hook on.  This 
  1783.                     can also be HMQ_CURRENT to specify the message queue of the 
  1784.                     calling thread, or NULLHANDLE if this is to be a 
  1785.                     system-wide queue. 
  1786. lHookType           One of the HK_* constants (see the first section). 
  1787. pfnHook             Pointer to the hook procedure. 
  1788. hmModule            Handle to the DLL containing the hook procedure.  This must 
  1789.                     be specified if hmqQueue is NULLHANDLE. 
  1790.  
  1791. Functions for the various hook types are chained together, which allows for 
  1792. multiple hook functions to be installed for a particular hook type.  The 
  1793. WinSetHook() call installs the hook function at the head of the chain. 
  1794.  
  1795. Figure 2)  Multiple hooks of the same type.  The hook on top was installed last 
  1796. using WinSetHook(). 
  1797.  
  1798. This is important because some Win functions install hooks themselves, such as 
  1799. the WinCreateHelpInstance() function.  Since the help hook installed by this 
  1800. function indicates to the system that no other hooks after it should be called, 
  1801. any help hooks you install before calling this function will never see any help 
  1802. events. 
  1803.  
  1804. Select this to go to the next section 
  1805.  
  1806.  
  1807. ΓòÉΓòÉΓòÉ 2.3.4. Two Examples ΓòÉΓòÉΓòÉ
  1808.  
  1809. A Small Example 
  1810.  
  1811. Consider the application provided as hooks.zip.  It is nothing more than a 
  1812. dialog box with two hooks attached - the associate window HDC hook, and the 
  1813. destroy window hook.  Whenever either hook is invoked, they beep if the window 
  1814. provided is a frame window. 
  1815.  
  1816. BOOL EXPENTRY assocWindowHook(HAB habAnchor,
  1817.                               HDC hdcContext,
  1818.                               HWND hwndAssoc,
  1819.                               BOOL bAssoc)
  1820. //-------------------------------------------------------------------------
  1821. // This hook is invoked whenever an HDC is associated or disassociated with
  1822. // a window.
  1823. //
  1824. // Input:  habAnchor - anchor block of the thread in whose context the
  1825. //                     event occurred.
  1826. //         hdcContext - handle of the device context.
  1827. //         hwndAssoc - handle of the window associated/disassociated.
  1828. //         bAssoc - TRUE if hwndAssoc is associated with hdcContext.  FALSE
  1829. //                  otherwise.
  1830. // Returns:  TRUE if successful processing, FALSE otherwise.
  1831. //-------------------------------------------------------------------------
  1832. {
  1833.    CHAR achClass[256];
  1834.  
  1835.    WinQueryClassName(hwndAssoc,sizeof(achClass),achClass);
  1836.  
  1837.    if ((WinFindAtom(WinQuerySystemAtomTable(),
  1838.                     achClass)==LOUSHORT(WC_FRAME)) && bAssoc) {
  1839.       WinAlarm(HWND_DESKTOP,WA_NOTE);
  1840.    } /* endif */
  1841.  
  1842.    return TRUE;
  1843. }
  1844.  
  1845. BOOL EXPENTRY destroyWindowHook(HAB habAnchor,
  1846.                                 HWND hwndDestroy,
  1847.                                 ULONG ulReserved)
  1848. //-------------------------------------------------------------------------
  1849. // This hook is invoked whenever a window is destroyed.
  1850. //
  1851. // Input:  habAnchor - anchor block of the thread in whose context the
  1852. //                     event occurred.
  1853. //         hwndDestroy - handle of the window being destroyed.
  1854. //         ulReserved - reserved.
  1855. // Returns:  TRUE if successful processing, FALSE otherwise.
  1856. //-------------------------------------------------------------------------
  1857. {
  1858.    CHAR achClass[256];
  1859.  
  1860.    WinQueryClassName(hwndDestroy,sizeof(achClass),achClass);
  1861.  
  1862.    if (WinFindAtom(WinQuerySystemAtomTable(),achClass)==LOUSHORT(WC_FRAME)) {
  1863.       WinAlarm(HWND_DESKTOP,WA_ERROR);
  1864.    } /* endif */
  1865.  
  1866.    return TRUE;
  1867. }
  1868.  
  1869. Figure 3)  Two simple hook procedures. 
  1870.  
  1871. The hooks are installed, as we noted, using the WinSetHook() call and are 
  1872. released using the WinReleaseHook() call. 
  1873.  
  1874. WinSetHook(habAnchor,
  1875.            HMQ_CURRENT,
  1876.            HK_WINDOWDC,
  1877.            (PFN)assocWindowHook,
  1878.            NULLHANDLE);
  1879. WinSetHook(habAnchor,
  1880.            HMQ_CURRENT,
  1881.            HK_DESTROYWINDOW,
  1882.            (PFN)destroyWindowHook,
  1883.            NULLHANDLE);
  1884.   :
  1885.   :
  1886. WinReleaseHook(habAnchor,
  1887.                HMQ_CURRENT,
  1888.                HK_WINDOWDC,
  1889.                (PFN)assocWindowHook,
  1890.                NULLHANDLE);
  1891. WinReleaseHook(habAnchor,
  1892.                HMQ_CURRENT,
  1893.                HK_DESTROYWINDOW,
  1894.                (PFN)destroyWindowHook,
  1895.                NULLHANDLE);
  1896.  
  1897. Figure 4)  WinSetHook() and WinReleaseHook() calls. 
  1898.  
  1899. Note that the hooks are local to the message queue for the thread.  Also, note 
  1900. how the hook procedures are cast to the generic type PFN to avoid 
  1901. warnings/errors by the compiler. 
  1902.  
  1903. A Large Example 
  1904.  
  1905. Now, look at the application provided in life.zip.  This is a screen saver 
  1906. which utilizes the input hook to determine when a period of inactivity elapses; 
  1907. actually, the word should be "expires" since the input hook is invoked when 
  1908. there is no inactivity.  The strategy for the application is to start a timer 
  1909. using WinStartTimer() to count the number of seconds of inactivity.  Whenever 
  1910. the input hook is invoked, it tells the application to reset this counter. 
  1911. Should the threshold be reached, the screen saver goes into effect. 
  1912.  
  1913. There are a couple of interesting points to be made: 
  1914.  
  1915. o The hook is a system-wide hook.  Since this means it can be invoked from 
  1916.   within the context of any process, it must reside in a DLL. 
  1917.  
  1918. o Since the hook and the application need to share data but the DLL needs to 
  1919.   access it from within any process' context, the data must be declared in the 
  1920.   DLL and a pointer to it is kept by the application.  Additionally, the DLL 
  1921.   has the attributes DATA SHARED SINGLE specified in the .DEF file to indicate 
  1922.   that all processes share the same copy of the DLL's data segment. 
  1923.  
  1924. Select this to go to the next section 
  1925.  
  1926.  
  1927. ΓòÉΓòÉΓòÉ 2.3.5. Summary ΓòÉΓòÉΓòÉ
  1928.  
  1929. Hooks provide a way to code certain types of logic into your application, which 
  1930. could not be done (easily) in other ways.  While they are very useful, care 
  1931. must be taken because, in the case of system-wide hooks, what you do affects 
  1932. the entire system.  For example, in a send message hook, you wouldn't use 
  1933. WinSendMsg() to notify your application, or else you would have an endless 
  1934. loop.  For all development involving hooks, one must be sure that the design is 
  1935. completely worked out, or you could spend fruitless hours debugging your 
  1936. hook...and these are not easy beasts to debug, either. 
  1937.  
  1938. Probably the worst detriment during development is the lack of good 
  1939. documentation.  Many of the hooks are scantily documented in pmwin.h at best, 
  1940. while others have incorrect parameters listed, making things even more 
  1941. difficult.  If you have access to CompuServe, the best help you can get is from 
  1942. the IBMers that reside there, since they have been using many of these hooks 
  1943. since their first appearance. 
  1944.  
  1945. Select this to go to the next section 
  1946.  
  1947.  
  1948. ΓòÉΓòÉΓòÉ 3. Columns ΓòÉΓòÉΓòÉ
  1949.  
  1950. The following columns can be found in this issue: 
  1951.  
  1952. o C++ Corner 
  1953. o Introduction to PM Programming 
  1954. o Scratch Patch 
  1955.  
  1956.  
  1957. ΓòÉΓòÉΓòÉ 3.1. C++ Corner ΓòÉΓòÉΓòÉ
  1958.  
  1959. Written by Gordon Zeglinski 
  1960.  
  1961. Introduction 
  1962.  
  1963. Welcome to the latest EDM/2 feature, The C++ Corner.  As the title suggests, 
  1964. this column is dedicated to exploring various aspects of using C++ to make OS/2 
  1965. programming easier.  I have taken the liberty of assuming that the reader has a 
  1966. good working knowledge of C++ concepts and has C++ coding experience. 
  1967.  
  1968. So What's On The Menu For This Issue? 
  1969.  
  1970. We'll start the column off by looking at some of the basics of the User 
  1971. Interface Class Library (UICL for short) provided by IBM and included with the 
  1972. C-Set++ compiler product.  Although, this library is C-Set++ specific, I will 
  1973. try to make this interesting for everyone by touching on some of the design 
  1974. aspects behind the library and discussing generic C++ considerations. 
  1975. Additionally, since the best way of learning is by example, we will look at 
  1976. extending the library by adding a generic dialog class. 
  1977.  
  1978. Select this to go to the next section 
  1979.  
  1980.  
  1981. ΓòÉΓòÉΓòÉ 3.1.1. Introduction ΓòÉΓòÉΓòÉ
  1982.  
  1983. Written by Gordon Zeglinski 
  1984.  
  1985. Welcome to the latest EDM/2 feature, The C++ Corner.  As the title suggests, 
  1986. this column is dedicated to exploring various aspects of using C++ to make OS/2 
  1987. programming easier.  I have taken the liberty of assuming that the reader has a 
  1988. good working knowledge of C++ concepts and has C++ coding experience. 
  1989.  
  1990. So What's On The Menu For This Issue? 
  1991.  
  1992. We'll start the column off by looking at some of the basics of the User 
  1993. Interface Class Library (UICL for short) provided by IBM and included with the 
  1994. C-Set++ compiler product.  Although, this library is C-Set++ specific, I will 
  1995. try to make this interesting for everyone by touching on some of the design 
  1996. aspects behind the library and discussing generic C++ considerations. 
  1997. Additionally, since the best way of learning is by example, we will look at 
  1998. extending the library by adding a generic dialog class. 
  1999.  
  2000. Select this to go to the next section 
  2001.  
  2002.  
  2003. ΓòÉΓòÉΓòÉ 3.1.2. The Basics of Event Handling ΓòÉΓòÉΓòÉ
  2004.  
  2005. What Are Events? 
  2006.  
  2007. The UICL uses the concept of events to encapsulate messages sent by the various 
  2008. PM controls.  The base object in the event hierarchy is called the IEvent 
  2009. class.  When looking at the UICL, one should always keep in mind the basics of 
  2010. the underlying PM API; even though the encapsulation of the PM message 
  2011. structure is done to a level which could allow the UICL to be ported to 
  2012. different platforms, it is still firmly rooted in PM. 
  2013.  
  2014. PM messages are composed of three 32-bit numbers:  the first is the message 
  2015. identifier, while the remaining two are used as the message data. Depending 
  2016. upon the message identifier, the message data can have many different 
  2017. interpretations.  Part of the event encapsulation relates the message 
  2018. identifier to the specific meaning of the other two numbers. 
  2019.  
  2020. Events are grouped into classes based on semantic relationships (i.e. 
  2021. menu-related, notification, etc.) or originator (i.e. container) and each event 
  2022. class is processed by one or more member functions denoted event handlers.  The 
  2023. base handler class is called IHandler and it contains the virtual member 
  2024. function dispatchHandlerEvent().  When a window has one or more handlers 
  2025. associated with it, the primary dispatch function calls dispatchHandlerEvent() 
  2026. for each registered handler. (See my article C++ Encapsulation of PM in volume 
  2027. 1 issue 4, for the basic design goals, and a definition of the primary dispatch 
  2028. function). If true is returned, the event is considered processed; otherwise, 
  2029. the next handler is invoked, and so on until true is returned from 
  2030. dispatchHandlerEvent(). 
  2031.  
  2032. Planes, Trains, Events and Handlers 
  2033.  
  2034. Well planes and trains don't really fit with the UICL, but the title sounded 
  2035. good :).  To understand handlers, let's use the ICommandHandler class as an 
  2036. example.  Following is a stripped down version of the class definition given in 
  2037. icmdhdr.hpp. 
  2038.  
  2039. class ICommandHandler : public IHandler {
  2040. typedef IHandler
  2041.    Inherited;
  2042.  
  2043. public:
  2044.    ICommandHandler();
  2045.    virtual ~ICommandHandler();
  2046.    Boolean dispatchHandlerEvent( IEvent& event );
  2047.  
  2048. protected:
  2049.    virtual Boolean command( ICommandEvent& event );
  2050.    virtual Boolean systemCommand( ICommandEvent& event );
  2051. };
  2052.  
  2053. When called, the function dispatchHandlerEvent() acts like a filter.  It checks 
  2054. the message ID of the event and calls the appropriate virtual function.  If the 
  2055. message is WM_COMMAND, it returns the value returned by the virtual function 
  2056. command(); if the message is WM_SYSCOMMAND, it returns the value returned by 
  2057. the virtual function systemCommand().  Otherwise, it returns the value false 
  2058. indicating that the next registered handler should be invoked. 
  2059.  
  2060.                                                                    WM_COMMAND
  2061.                                                                   ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
  2062.                                                                ΓöîΓöÇ Γöécommand()Γöé
  2063. ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ  virtual   ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ  virtualΓöé  ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
  2064. ΓöéPrimary DispatchΓöé ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ  ΓöédispatchHandlerEvent()Γöé ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
  2065. Γöé  Function      Γöé   call     ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ   call  Γöé  ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
  2066. ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ                                             ΓööΓöÇ ΓöésystemCommand()Γöé
  2067.                                                                   ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
  2068.                                                                     WM_SYSCOMMAND
  2069.  
  2070. Figure 1) Event Dispatch Function Chain 
  2071.  
  2072. What's This ICommandEvent Object? 
  2073.  
  2074. A quick peek at the PM reference, yields the following information about the 
  2075. WM_COMMAND and WM_SYSCOMMAND messages: 
  2076.  
  2077. param1
  2078.    USHORT usCmd         Command value.
  2079.  
  2080. param2
  2081.    USHORT usSource      Source type.
  2082.    USHORT usPointer     Pointing-device indicator.
  2083.  
  2084. returns
  2085.    ULONG  flReply       Reserved.
  2086.  
  2087. The ICommandEvent allows access to the value in param1 through the member 
  2088. function commandId(). Similarly, the member function source() allows access to 
  2089. usSource although the value returned is also abstracted in order to be 
  2090. independent of the PM definitions for the CMDSRC_* constants. So not only does 
  2091. the dispatchHandlerEvent() function act as a filter, it also transforms generic 
  2092. IEvent's into specific event objects, as in the ICommandEvent example. 
  2093.  
  2094. Notice the flReply variable above.  It is set using the IEvent member function 
  2095. setResult().  This is different than the way a window procedure in C returns 
  2096. flReply to PM. 
  2097.  
  2098. Select this to go to the next section 
  2099.  
  2100.  
  2101. ΓòÉΓòÉΓòÉ 3.1.3. Moving On Up ΓòÉΓòÉΓòÉ
  2102.  
  2103. One thing "missing" from the UICL is a dialog window object. It isn't really 
  2104. missing, it's just not packaged as a dialog class. Dialog windows pose 
  2105. interesting encapsulation problems for the class designers - do they allow the 
  2106. class users to use a PM-like method of communicating to the controls?  Or do 
  2107. they force you to create instances of C++ objects that mirror the controls? 
  2108. The UICL uses the second approach.  Being somewhat of a rebel, I decided that I 
  2109. didn't like that approach I created the IDialogWindow class.  The IDialogWindow 
  2110. class is specifically designed to break the predefined rules of encapsulation. 
  2111. The price you pay is that the application becomes tightly bound to the PM 
  2112. messaging structure.  Following is the class definition for IDialogWindow. 
  2113.  
  2114. Note:  _exp_ is used to toggle the header file between "DLL" mode and "EXE" 
  2115. mode. 
  2116.  
  2117. class IDialogWindow:  public IFrameWindow, public IFrameHandler {
  2118.  
  2119. public:
  2120.    _exp_ IDialogWindow(unsigned long ID);
  2121.    _exp_ IDialogWindow(unsigned long ID, IWindow *Parent, IWindow *Owner);
  2122.    _exp_ ~IDialogWindow();
  2123.  
  2124.    IDialogWindow& _exp_ setItemText(unsigned long ID, char *Text);
  2125.    IDialogWindow& _exp_ sendItemMessage(unsigned long ID,
  2126.                                         unsigned long iMess,
  2127.                                         unsigned long mp1=0,
  2128.                                         unsigned long mp2=0);
  2129.    IDialogWindow& _exp_ sendItemMessage(unsigned long ID,
  2130.                                         unsigned long iMess,
  2131.                                         unsigned short int ch1,
  2132.                                         unsigned short int ch2,
  2133.                                         unsigned long mp2=0);
  2134.    IDialogWindow& _exp_ sendItemMessage(unsigned long ID,
  2135.                                         unsigned long iMess,
  2136.                                         void* mp1,
  2137.                                         void* mp2=0);
  2138.  
  2139.    IDialogWindow& _exp_ queryItemText(unsigned long ID,
  2140.                                       unsigned long maxLen,
  2141.                                       char *retBuf);
  2142.    unsigned long _exp_ queryItemTextLength(unsigned long ID);
  2143.  
  2144. protected:
  2145.    Boolean _exp_ dispatchHandlerEvent(IEvent& evt);
  2146. };
  2147.  
  2148. The member functions mimic the WinSetDlgItemText(), WinQueryDlgItemText(), 
  2149. WinSendDlgItemMsg(), etc.. functions.  The IDialogWindow class is derived from 
  2150. both the IFrameWindow and the IFrameHandler object.  If you are already 
  2151. familiar with the UICL, you know that the IFrameWindow class can create dialog 
  2152. windows if the appropriate constructor is called. Thus, it makes sense that we 
  2153. would subclass it.  But why is the IFrameHandler class used as a parent?  The 
  2154. answer is easy - the IDialogWindow has the added feature of hiding controls 
  2155. that may obscure the icon when the window is minimized.  In order to be 
  2156. notified whenever we are minimized, we need to override the 
  2157. IFrameHandler::dispatchHandlerEvent() member function. An alternative method, 
  2158. would have been to create another handler class that would take care of this 
  2159. case. 
  2160.  
  2161. Peeking at the Constructors 
  2162.  
  2163. The constructors for IDialogWindow merely call the correct IFrameWindow 
  2164. constructor, and register the IDialogWindow instance as a handler. 
  2165.  
  2166. IDialogWindow::IDialogWindow(unsigned long ID) :
  2167.    IFrameWindow(IResourceId(ID))
  2168. {
  2169.    handleEventsFor(this);
  2170. }
  2171.  
  2172. IDialogWindow::IDialogWindow(unsigned long ID,
  2173.                              IWindow *Parent,
  2174.                              IWindow *Owner) :
  2175.    IFrameWindow(IResourceId(ID),Parent,Owner)
  2176. {
  2177.    handleEventsFor(this);
  2178. }
  2179.  
  2180. Breaking the Rules 
  2181.  
  2182. As I said in the beginning of this section, the IDialogWindow is specifically 
  2183. designed to break some of the original design choices.  In keeping with this 
  2184. rebellious spirit, we break yet another design decision. Way back, when we 
  2185. looked at handlers and events, we seen that one of the tasks a handler 
  2186. performed was to repackage the event and then pass it on to a specific virtual 
  2187. function that would do the actual processing of the event. The PM message we 
  2188. are after here is WM_MINMAXFRAME; this message is sent every time the window is 
  2189. minimized, restored, or maximized.  If we were to follow with the original 
  2190. design, we should have created a new event object to encapsulate this PM 
  2191. message.  But because we only want to process this message in the IDialogWindow 
  2192. object, we take a short cut. Following is the modified dispatchHandlerEvent() 
  2193. function: 
  2194.  
  2195. Boolean IDialogWindow::dispatchHandlerEvent(IEvent& evt)
  2196. {
  2197.    HWND hwnd;
  2198.    HENUM henum;
  2199.    PSWP pswp;
  2200.    Boolean ShowState;
  2201.    SWP winPos,Pos;
  2202.    int cx,cy;
  2203.  
  2204.    // check the message
  2205.    if (evt.eventId()==WM_MINMAXFRAME) {
  2206.       // start the child window enumeration
  2207.       henum=WinBeginEnumWindows(handle());
  2208.  
  2209.       //extract the PSWP paramter from the event
  2210.       pswp=(PSWP)evt.parameter1().asUnsignedLong();
  2211.  
  2212.       //check if the window is being minimized or not
  2213.       if ((pswp->fl) & SWP_MINIMIZE)
  2214.          ShowState=false;
  2215.       else
  2216.          ShowState=true;
  2217.  
  2218.       // Just for fun, query the dialog window's position
  2219.       WinQueryWindowPos(handle(),&Pos);
  2220.  
  2221.       // loop through all child windows
  2222.       while (hwnd=WinGetNextWindow(henum)) {
  2223.          // query the child windows position
  2224.          WinQueryWindowPos(hwnd,&winPos);
  2225.  
  2226.          // check if the child window overlaps the icon and hide/restore
  2227.          // if necessary
  2228.          if (((winPos.x)<=pswp->cx) &&
  2229.              ((winPos.y)<=pswp->cy))
  2230.             WinShowWindow(hwnd,ShowState);
  2231.       }
  2232.  
  2233.       // all children are done, end the enumeration
  2234.       WinEndEnumWindows(henum);
  2235.    }
  2236.  
  2237.    // be sure to pass the event to the proper handler!
  2238.    return IFrameHandler::dispatchHandlerEvent(evt);
  2239. }
  2240.  
  2241. OK.  OK.  We didn't break the rules, we just bent them a bit.  The call to 
  2242. IFrameHandler::dispatchHandlerEvent() insures that the events get to the other 
  2243. IFrameHandler instances that the user may have registered. 
  2244.  
  2245. Getting WM_CONTROL Messages 
  2246.  
  2247. In some cases, we may want to get the WM_CONTROL messages in their PM form.  To 
  2248. do this, we need to create a new handler class that will pass unmodified 
  2249. WM_CONTROL messages to our handlers.  The following handler class performs this 
  2250. task, and is modelled after the ICommandHandler class. 
  2251.  
  2252. class ControlHandler: public IHandler {
  2253.  
  2254. public:
  2255.    Boolean _exp_ dispatchHandlerEvent(IEvent& evt);
  2256.  
  2257. protected:
  2258.    virtual Boolean _exp_ control(const IControlEvent &control);
  2259. };
  2260.  
  2261.    :
  2262.    :
  2263.  
  2264. Boolean ControlHandler::dispatchHandlerEvent(IEvent& evt) {
  2265.    // verify message ID
  2266.    if (evt.eventId()==WM_CONTROL) {
  2267.       IControlEvent controlEvnt(evt);
  2268.  
  2269.       // call control member function
  2270.       Boolean rc=control(controlEvnt);
  2271.  
  2272.       evt.setResult(controlEvnt.result());
  2273.       return rc;
  2274.    }
  2275.    return false;
  2276. }
  2277.  
  2278. // Default handler returns false to indicate the message (event) has not
  2279. // been processed.
  2280. Boolean ControlHandler::control(const IControlEvent &control) {
  2281.    return false;
  2282. }
  2283.  
  2284. Keeping with the original design of the UICL, to create an instance of the 
  2285. handler, one does not override the dispatchHandlerEvent() function, but rather 
  2286. some other go-between function.  In this case, the member function control() is 
  2287. overridden. 
  2288.  
  2289. Select this to go to the next section 
  2290.  
  2291.  
  2292. ΓòÉΓòÉΓòÉ 3.1.4. Putting It All Together ΓòÉΓòÉΓòÉ
  2293.  
  2294. So far we've examined some of the underlying design decisions in the UICL, and 
  2295. found ways to work around them.  A new dialog window class has been developed 
  2296. to allow one work around, and to enhance the basic dialog window support 
  2297. already present in the UICL.  The file dlgdem.zip contains a sample application 
  2298. that uses this class. 
  2299.  
  2300. Fun For The Reader 
  2301.  
  2302. For those interested in testing their understanding, modify the sample 
  2303. application to process WM_CONTROL messages.  In particular, watch for the 
  2304. BN_DBLCLICKED notification message coming from the exit button.  When this 
  2305. message is received (ie. a ControlEvent has occurred), change the text in the 
  2306. button. 
  2307.  
  2308. Hint: 
  2309.  
  2310. You will need to disable the processing of the exit button in the command() 
  2311. member function, and subclass the ControlHandler object. 
  2312.  
  2313. Source Files 
  2314.  
  2315. The file idialog.zip contains the files: 
  2316.  
  2317. o idialog.cpp 
  2318. o idialog.def 
  2319. o IDIALOG.DEP 
  2320. o idialog.hpp 
  2321. o IDIALOG.MAK 
  2322.  
  2323. The idialog.mak file should be passed through NMAKE before making the demo 
  2324. program. The file dlgdem.zip contains the files: 
  2325.  
  2326. o DlgDem.def 
  2327. o DlgDem.dep 
  2328. o dlgdem.ICO 
  2329. o DlgDem.mak 
  2330. o DlgDem.RC 
  2331. o DlgDemMain.cpp 
  2332. o DlgDemMain.h 
  2333. o DlgDemRC.H 
  2334. o main.cpp 
  2335.  
  2336. No executables are included, because you must have the C-Set++ compiler to have 
  2337. the UICL run time DLL files. 
  2338.  
  2339. Select this to go to the next section 
  2340.  
  2341.  
  2342. ΓòÉΓòÉΓòÉ 3.1.5. Wrapping Things Up ΓòÉΓòÉΓòÉ
  2343.  
  2344. Here we are again, at the end of another C++ adventure.  You should now 
  2345. understand how event and handler objects encapsulate the PM message structure, 
  2346. how to create new window classes, and how to pull a few tricks to make the UICL 
  2347. fit your style. 
  2348.  
  2349. What's up for the Next Issue? 
  2350.  
  2351. I'm not too sure what we'll look at next time.  If my copy of C-Set++ 2.1 
  2352. finally arrives, an in-depth review of it will be presented.  If it doesn't, 
  2353. we'll look at either more UICL topics, or encapsulating extended attributes. 
  2354.  
  2355. Select this to go to the next section 
  2356.  
  2357.  
  2358. ΓòÉΓòÉΓòÉ 3.2. Introduction to PM Programming ΓòÉΓòÉΓòÉ
  2359.  
  2360. Written by Larry Salomon, Jr. 
  2361.  
  2362. Introduction 
  2363.  
  2364. The purpose of this column is to provide to the readers out there who are not 
  2365. familiar with PM application development the information necessary to satisfy 
  2366. their curiousity, educate themselves, and give them an advantage over the 
  2367. documentation supplied by IBM.  Of course, much of this stuff could probably be 
  2368. found in one of the many books out there, but the problem with books in general 
  2369. is that they don't answer the questions you have after you read the book the 
  2370. first time through. 
  2371.  
  2372. I will gladly entertain feedback from the readers about what was "glossed over" 
  2373. or what was detailed well, what tangential topics need to be covered and what 
  2374. superfluous crap should have been removed.  This feedback is essential in 
  2375. guaranteeing that you get what you pay for.  :) 
  2376.  
  2377. It should be said that you must not depend solely on this column to teach you 
  2378. how to develop PM applications; instead, this should be viewed as a supplement 
  2379. to your other information storehouses (books, the network conferences, etc.). 
  2380. Because this column must take a general approach, there will be some topics 
  2381. that would like to see discussed that really do not belong here.  Specific 
  2382. questions can be directed to the Scratch Patch, where an attempt to answer them 
  2383. will be made. 
  2384.  
  2385. Select this to go to the next section 
  2386.  
  2387.  
  2388. ΓòÉΓòÉΓòÉ 3.2.1. Introduction ΓòÉΓòÉΓòÉ
  2389.  
  2390. Written by Larry Salomon, Jr. 
  2391.  
  2392. The purpose of this column is to provide to the readers out there who are not 
  2393. familiar with PM application development the information necessary to satisfy 
  2394. their curiousity, educate themselves, and give them an advantage over the 
  2395. documentation supplied by IBM.  Of course, much of this stuff could probably be 
  2396. found in one of the many books out there, but the problem with books in general 
  2397. is that they don't answer the questions you have after you read the book the 
  2398. first time through. 
  2399.  
  2400. I will gladly entertain feedback from the readers about what was "glossed over" 
  2401. or what was detailed well, what tangential topics need to be covered and what 
  2402. superfluous crap should have been removed.  This feedback is essential in 
  2403. guaranteeing that you get what you pay for.  :) 
  2404.  
  2405. It should be said that you must not depend solely on this column to teach you 
  2406. how to develop PM applications; instead, this should be viewed as a supplement 
  2407. to your other information storehouses (books, the network conferences, etc.). 
  2408. Because this column must take a general approach, there will be some topics 
  2409. that would like to see discussed that really do not belong here.  Specific 
  2410. questions can be directed to the Scratch Patch, where an attempt to answer them 
  2411. will be made. 
  2412.  
  2413. Select this to go to the next section 
  2414.  
  2415.  
  2416. ΓòÉΓòÉΓòÉ 3.2.2. Looking Back and Looking Forward ΓòÉΓòÉΓòÉ
  2417.  
  2418. Last issue, we discussed some of the fundamental concepts of PM such as windows 
  2419. and window classes, events, resources, and handles.  This issue we will take 
  2420. these basic concepts and discuss the components of our first PM application 
  2421. entitled (what else?) "Hello World" (provided in intro.zip). 
  2422.  
  2423. After reading this installment, you should... 
  2424.  
  2425. o ...be able to explain the purpose of each line in the main() function of a 
  2426.   typical PM application. 
  2427. o ...understand the hierarchical organization of windows. 
  2428. o ...know what some window styles are. 
  2429. o ...be able to describe the purpose of a frame creation flag. 
  2430. o ...be able to define the terms top-level window and well-behaved application. 
  2431.  
  2432. ...as well as a few other things. 
  2433.  
  2434. Select this to go to the next section 
  2435.  
  2436.  
  2437. ΓòÉΓòÉΓòÉ 3.2.3. My main() Squeeze ΓòÉΓòÉΓòÉ
  2438.  
  2439. Back in the dark ages when all of OS/2 and PM was 16-bit, there were ring 2 
  2440. stack (which I will not define) requirements imposed by PM applications that 
  2441. exceeded the default size provided when OS/2 started a new process. Because of 
  2442. this and other reasons, PM required that you call the function WinInitialize() 
  2443. at the begining of your application, which in turn called DosR2StackRealloc() 
  2444. to increase the size of the ring 2 stack and performed some additional 
  2445. housekeeping to enable your to successfully call other PM functions. 
  2446.  
  2447. While the need to increase the ring 2 stack may or may not have disappeared, 
  2448. the housekeeping initialization didn't.  So, the first call in a PM application 
  2449. should always be WinInitialize() (and have the corresponding call to 
  2450. WinTerminate() before each exit point of your application).  This function 
  2451. returns something called an anchor block, which is used as a parameter to other 
  2452. functions; for the curious, the value of the anchor block is the process 
  2453. identifier in the high word and a one in the low word (actually, the low word 
  2454. contains the thread identifier, which will always be one for quite a while in 
  2455. this column).  This might change in the future, though, so do not depend on 
  2456. this always being true. 
  2457.  
  2458. Also, many PM functions indirectly cause messages (referred to last time in the 
  2459. section Events) to be sent to various windows, and messages require a message 
  2460. queue so the call the typically follows WinInitialize() is WinCreateMsgQueue() 
  2461. (and the corresponding call to WinDestroyMsgQueue() prior to the call to 
  2462. WinTerminate()).  The reason why sending messages requires a message queue is 
  2463. beyond the scope of this column. 
  2464.  
  2465. Instead of questioning why you have to bother with these things, just accept 
  2466. them as givens, and you will save yourself much worrying. 
  2467.  
  2468. After the first two calls, there might be some application initialization, but 
  2469. inevitably there is one or more calls to WinRegisterClass() to register the 
  2470. window classes used by the application with PM.  If you'll remember, this 
  2471. associates a window class name with a window class procedure. 
  2472.  
  2473. This is then followed by a call to WinCreateStdWindow(); this function creates 
  2474. a frame window and other standard window components. 
  2475.  
  2476. Figure 1)  Some components of a "standard window". 
  2477.  
  2478. The WinCreateStdWindow() function takes many parameters and is described below: 
  2479.  
  2480. HWND WinCreateStdWindow(HWND hwndParent,
  2481.                         ULONG ulFrameStyle,
  2482.                         PULONG pulFlags,
  2483.                         PSZ pszClientClass,
  2484.                         PSZ pszTitle,
  2485.                         ULONG ulClientStyle,
  2486.                         HMODULE hmResources,
  2487.                         ULONG ulIdResources,
  2488.                         PHWND phwndClient);
  2489.  
  2490. hwndParent          Specifies the parent of the standard window.  If 
  2491.                     HWND_DESKTOP is specified, a top level window is created. 
  2492. ulFrameStyle        Specifies a set of WS_* (Window Style) constants for the 
  2493.                     frame window. 
  2494. pulFlags            Specifies a set of FCF_* (Frame Creation Flag) constants 
  2495.                     that indicate the desired components (see Figure 1 and the 
  2496.                     description below) and properties of the standard window: 
  2497. pszClientClass      " Points" to the name of the class of the window to be used 
  2498.                     as the client area in Figure 1.  "Points" is used in quotes 
  2499.                     because this can also be a WC_* (Window Class) constant 
  2500.                     indicating one of the predefined window classes (known as 
  2501.                     public window classes). 
  2502. pszTitle            Points to the window text of the frame window.  This text 
  2503.                     is also displayed on the titlebar.  This cannot be NULL. 
  2504.                     To specify nothing, use an empty string (""). 
  2505. ulClientStyle       Specifies a set of WS_* (Window Style) constants for the 
  2506.                     client window.  This is typically specified as zero, but 
  2507.                     could be used if a public window class is used as the 
  2508.                     client. 
  2509. hmResources         Specifies the module handle where any required resourced 
  2510.                     are located.  If NULLHANDLE is used, the resources are 
  2511.                     assumed to be appended to the .EXE file. 
  2512. ulIdResources       The identifier of any required resources. 
  2513. phwndClient         Points to the variable to receive the window handle of the 
  2514.                     client. 
  2515.  
  2516. (See the next sections for detailed explanations of the umpteen million new 
  2517. things mentioned above) 
  2518.  
  2519. After the standard window is created, the message loop is typically entered. 
  2520. The message loop is comprised of a call to WinGetMsg() followed by a call to 
  2521. WinDispatchMsg(). WinGetMsg() consistently returns TRUE until a WM_QUIT message 
  2522. is received which causes the loop to end. 
  2523.  
  2524. Following the loop is a call to WinDestroyWindow() which destroys the frame 
  2525. window, and thus all of its children. 
  2526.  
  2527. ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ 
  2528.  
  2529. I intentionally wanted this to sound mechanical, because it is.  The main() 
  2530. function of all PM applications are very similar, if not identical. 
  2531.  
  2532. INT main(VOID)
  2533. {
  2534.    HAB habAnchor;
  2535.    HMQ hmqQueue;
  2536.    ULONG ulCreate;
  2537.    HWND hwndFrame;
  2538.    HWND hwndClient;
  2539.    BOOL bLoop;
  2540.    QMSG qmMsg;
  2541.  
  2542.    habAnchor=WinInitialize(0);
  2543.    hmqQueue=WinCreateMsgQueue(habAnchor,0);
  2544.  
  2545.    WinRegisterClass(habAnchor,CLS_CLIENT,windowProc,CS_SIZEREDRAW,0);
  2546.  
  2547.    ulCreate=FCF_SYSMENU | FCF_TITLEBAR | FCF_MINMAX | FCF_SIZEBORDER |
  2548.                FCF_SHELLPOSITION | FCF_TASKLIST | FCF_ICON;
  2549.  
  2550.    hwndFrame=WinCreateStdWindow(HWND_DESKTOP,
  2551.                                 WS_VISIBLE,
  2552.                                 &ulCreate,
  2553.                                 CLS_CLIENT,
  2554.                                 "Hello World Sample",
  2555.                                 0,
  2556.                                 NULLHANDLE,
  2557.                                 RES_CLIENT,
  2558.                                 &hwndClient);
  2559.    if (hwndFrame!=NULLHANDLE) {
  2560.       bLoop=WinGetMsg(habAnchor,&qmMsg,NULLHANDLE,0,0);
  2561.       while (bLoop) {
  2562.          WinDispatchMsg(habAnchor,&qmMsg);
  2563.          bLoop=WinGetMsg(habAnchor,&qmMsg,NULLHANDLE,0,0);
  2564.       } /* endwhile */
  2565.  
  2566.       WinDestroyWindow(hwndFrame);
  2567.    } /* endif */
  2568.  
  2569.    WinDestroyMsgQueue(hmqQueue);
  2570.    WinTerminate(habAnchor);
  2571.    return 0;
  2572. }
  2573.  
  2574. Figure 2)  Sample main() function for a PM application coded in C 
  2575.  
  2576. If you thought that was a lot of typing, here's an incentive to learn C++ using 
  2577. IBM's User Interface class library:  the entire main() function can be coded in 
  2578. 8 lines, and 3 of those are required by the language! 
  2579.  
  2580. INT main(VOID)
  2581. {
  2582.    new IFrameWindow("Hello World Sample",
  2583.                     RES_CLIENT,
  2584.                     IFrameWindow::defaultStyle() |
  2585.                        IFrameWindow::minimizedIcon);
  2586.    (IApplication::current()).run();
  2587.    return 0;
  2588. }
  2589.  
  2590. Figure 3)  Sample main() function for a PM application coded in C++ 
  2591.  
  2592. Select this to go to the next section 
  2593.  
  2594.  
  2595. ΓòÉΓòÉΓòÉ 3.2.4. New Concepts ΓòÉΓòÉΓòÉ
  2596.  
  2597. There are many things mentioned in the last section, which are described in 
  2598. more detail below. 
  2599.  
  2600. Frame Windows 
  2601.  
  2602. Frame windows are windows that serve as the focal point for a standard window 
  2603. construct.  It usually receives messages from other windows, of which it sends 
  2604. the important ones to the client.  Physically, it encompases all of the 
  2605. standard window components. 
  2606.  
  2607. Top Level Windows 
  2608.  
  2609. Since it is possible to have a standard window within a standard window, the 
  2610. standard window that is the parent of the other standard windows is known as 
  2611. the top level window (actually, a top level window is defined as one whose 
  2612. parent is the desktop).  The top level frame has special significance because 
  2613. enumerations of the windows present only iterate through the top level windows. 
  2614.  
  2615. When a top level window contains other standard windows, the application is 
  2616. termed an MDI - or multiple document interface - application. 
  2617.  
  2618. Window Text 
  2619.  
  2620. All windows have a common set of characteristics, which was alluded to last 
  2621. issue when class styles versus window styles was discussed.  Besides window 
  2622. styles, many other characteristics are found in all windows; window text is one 
  2623. of them.  Actually, text is a misnomer because it is not required that this be 
  2624. text, only that this points to accessable memory.  Another useful 
  2625. characteristic queryable by all windows is the window identifer. 
  2626.  
  2627. There are many different WinQuery*() functions to retrieve these common 
  2628. characteristics. 
  2629.  
  2630. Resources 
  2631.  
  2632. This is something which we will describe in more detail next issue. Suffice it 
  2633. to say for now that a resource is a definable item that would be too much 
  2634. trouble to write code to construct, so a resource language and corresponding 
  2635. compiler was written to allow you to describe things like menus, string tables 
  2636. (for easy translation), accelerator tables, etc. 
  2637.  
  2638. Window Hierarchies 
  2639.  
  2640. You've already seen the terms parent, children, siblings, and owner used.  What 
  2641. exactly do they mean? The first three have meaning analagous to "Real Life" 
  2642. (tm).  A parent window can have 0 or more child windows each of whom are 
  2643. siblings of each other. 
  2644.  
  2645. Figure 4)  Parent-child relationships 
  2646.  
  2647. This parent-child relationship is exploited in various ways, most notably in 
  2648. clipping.  If a child window lies completely or partially outside the 
  2649. rectangular area defined by the parent window, those portions are clipped, i.e. 
  2650. not shown. 
  2651.  
  2652. Figure 5)  Children clipped to their parent 
  2653.  
  2654. Other ways that this relationship is used will be discussed in later issues. 
  2655.  
  2656. Unlike parent-child one-to-many relationships, owner-ownee relationships are 
  2657. one-to-one, but the same window can own many other windows.  The distinction is 
  2658. that there is no concept of a sibling in this regard.  An owner window is 
  2659. notified by the owned window whenever something happens that the owner might be 
  2660. interested in.  Notification occurs via a message named WM_CONTROL.  We will 
  2661. also see more of this message in later issues. 
  2662.  
  2663. Figure 6)  Owner-ownee relationship 
  2664.  
  2665. Select this to go to the next section 
  2666.  
  2667.  
  2668. ΓòÉΓòÉΓòÉ 3.2.5. New Constants ΓòÉΓòÉΓòÉ
  2669.  
  2670. Window Style Constants 
  2671.  
  2672. There are many window style constants which are common to all windows and even 
  2673. more which are specific to a particular window class.  Listed below are the 
  2674. common window styles and their meanings. 
  2675.  
  2676. WS_VISIBLE          Specifies that the window is to be visible. 
  2677. WS_DISABLED         Specifies that the window is to be disabled. 
  2678. WS_CLIPCHILDREN     Specifies that, when painting, children should be prevented 
  2679.                     from being painted over. 
  2680. WS_CLIPSIBLINGS     Specifies that, when painting, siblings should be prevented 
  2681.                     from being painted over. 
  2682. WS_PARENTCLIP       Specifies that, when painting, the parent should be 
  2683.                     prevented from being painted over. 
  2684. WS_SAVEBITS         Specifies that a bitmap area of any windows covered by this 
  2685.                     window should be saved, and restored as the window is 
  2686.                     moved, sized, hidden, or destroyed. 
  2687. WS_SYNCPAINT        Specifies that paint notification should be synchronous, 
  2688.                     i.e. the window should be notified immediately following 
  2689.                     any invalidation. 
  2690. WS_MINIMIZED        Specifies that the window is minimized.  This flag is 
  2691.                     ignored in the WinCreateStdWindow() function. 
  2692. WS_MAXIMIZED        Specifies that the window is maximized.  This flag is 
  2693.                     ignored in the WinCreateStdWindow() function. 
  2694. WS_ANIMATE          Specifies that the creation and destruction of the window 
  2695.                     should be accompanied by zoom-out/zoom-in animation. 
  2696. WS_GROUP, WS_TABSTOP, WS_MULTISELECT Used for dialogs only and will not be 
  2697.                     discussed here. 
  2698.  
  2699. Frame Creation Flag Constants 
  2700.  
  2701. In addition to window styles, standard windows also have many flags which 
  2702. determine the behavior of the WinCreateStdWindow() call.  These flags, and 
  2703. their meanings are described below. 
  2704.  
  2705. FCF_TITLEBAR        Specifies that a titlebar should be included. 
  2706. FCF_SYSMENU         Specifies that the system menu should be included. 
  2707. FCF_MENU            Specifies that an action bar (not shown) should be 
  2708.                     included. 
  2709. FCF_SIZEBORDER      Specifies that a sizing border should be used as the window 
  2710.                     border. 
  2711. FCF_MINBUTTON       Specifies that a min(imize) button should be included. 
  2712. FCF_MAXBUTTON       Specifies that a max(imize) button should be included. 
  2713. FCF_MINMAX          Specifies that both min and max buttons should be included. 
  2714. FCF_VERTSCROLL      Specifies that a vertical scrollbar (not shown) should be 
  2715.                     included. 
  2716. FCF_HORZSCROLL      Specifies that a horizontal scrollbar (not shown) should be 
  2717.                     included. 
  2718. FCF_DLGBORDER       Specifies that a dialog border (not shown) should be used 
  2719.                     as the window border. 
  2720. FCF_BORDER          Specifies that a normal border (not shown) should be used 
  2721.                     as the window border. 
  2722. FCF_SHELLPOSITION   Specifies that the Workplace Shell should automatically 
  2723.                     move, size, and then show the window after it is created. 
  2724. FCF_TASKLIST        Specifies that the Workplace Shell should automatically add 
  2725.                     an entry in the Window List for the standard window. 
  2726. FCF_NOBYTEALIGN     Specifies that the window position should not snap to an 
  2727.                     8-pel grid when it is moved.  This can result in less than 
  2728.                     optimal performance. 
  2729. FCF_NOMOVEWITHOWNER Specifies that the window position relative to its owner 
  2730.                     should not be adjusted when the owner is moved. 
  2731. FCF_ICON            Specifies that an icon with the identifier specified in 
  2732.                     ulIdResources should be associated with the standard 
  2733.                     window. 
  2734. FCF_ACCELTABLE      Specifies that an accelerator with the identifier specified 
  2735.                     in ulIdResources should be associated with the standard 
  2736.                     window. 
  2737. FCF_SYSMODAL        Specifies that the window should be system modal. 
  2738. FCF_SCREENALIGN, FCF_MOUSEALIGN Used for dialogs only and will not be discussed 
  2739.                     here. 
  2740. FCF_HIDEBUTTON      Specifies that a hide button should be included. 
  2741. FCF_HIDEMAX         Specifies that both hide button and max buttons should be 
  2742.                     included. 
  2743. FCF_DBE_APPSTAT     This flag is undocumented. 
  2744. FCF_AUTOICON        Specifies that, when minimized, the system should redraw 
  2745.                     the icon when necessary instead of notifying the window. 
  2746.  
  2747. Note the following sets of mutually exclusive flags.  If more than one of each 
  2748. group is specified, the first is the default. 
  2749.  
  2750. o FCF_MINBUTTON and FCF_HIDEBUTTON 
  2751. o FCF_SCREENALIGN and FCF_MOUSEALIGN 
  2752. o FCF_SIZEBORDER, FCF_DLGBORDER, and FCF_BORDER 
  2753.  
  2754. If all of this seems to be too much, don't fret; this function is one of the 
  2755. most complicated in terms of the myriad of flags that can be used (not 
  2756. surprisingly, another one is the WinCreateWindow() function). 
  2757.  
  2758. Select this to go to the next section 
  2759.  
  2760.  
  2761. ΓòÉΓòÉΓòÉ 3.2.6. More About the Message Loop ΓòÉΓòÉΓòÉ
  2762.  
  2763. Let's take a closer look at the message loop. 
  2764.  
  2765. bLoop=WinGetMsg(habAnchor,&qmMsg,NULLHANDLE,0,0);
  2766. while (bLoop) {
  2767.    WinDispatchMsg(habAnchor,&qmMsg);
  2768.    bLoop=WinGetMsg(habAnchor,&qmMsg,NULLHANDLE,0,0);
  2769. } /* endwhile */
  2770.  
  2771. Figure 7)  Message loop 
  2772.  
  2773. In PM, the only way you receive messages is via the WinGetMsg() call.  The 
  2774. message construct (QMSG) is then translated to a call to the window class 
  2775. procedure (introduced last time) via the WinDispatchMsg() call. 
  2776.  
  2777. When PM was designed, someone decided that the concept of type-ahead was 
  2778. important, so they decided to synchronize all input from the mouse and keyboard 
  2779. via a single system-wide input message queue, which gets checked whenever the 
  2780. application with the focus calls WinGetMsg().  This is important because the 
  2781. only way to switch between applications is via the keyboard or mouse; however, 
  2782. if the application with the focus is taking a long time to process the current 
  2783. message, it will never allow PM to change the input focus to another 
  2784. application. 
  2785.  
  2786. To avoid what will appear to the user to be a system lockup, IBM defined the 
  2787. well-behaved application rule which states that a PM application should take no 
  2788. longer than 1/10th of a second to process a message.  Of course, no one is 
  2789. going to time each message processing section to insure this, but it does 
  2790. indicate to the PM programmer that you should do what you have to quickly and 
  2791. "get back to the loop."  Longer tasks, like loading a file, must be done in 
  2792. another thread.  Multithreading from within PM applications was discussed in 
  2793. the article Threads in PM Applications in volume 1, issue 6 of EDM/2. 
  2794.  
  2795. Select this to go to the next section 
  2796.  
  2797.  
  2798. ΓòÉΓòÉΓòÉ 3.2.7. The Window Class Procedure ΓòÉΓòÉΓòÉ
  2799.  
  2800. As discussed last time, the window class procedure (often abbreviated to window 
  2801. procedure or even window proc) is not much more than a giant switch statement 
  2802. which tests for particular message identifiers (abbeviated to just messages) 
  2803. and processing those only.  The default case calls and returns the value from 
  2804. the function WinDefWindowProc(). 
  2805.  
  2806. MRESULT EXPENTRY windowProc(HWND hwndWnd,
  2807.                             ULONG ulMsg,
  2808.                             MPARAM mpParm1,
  2809.                             MPARAM mpParm2)
  2810. {
  2811.    switch (ulMsg) {
  2812.    case WM_PAINT:
  2813.       {
  2814.          HPS hpsPaint;
  2815.          RECTL rclPaint;
  2816.  
  2817.          hpsPaint=WinBeginPaint(hwndWnd,NULLHANDLE,&rclPaint);
  2818.  
  2819.          WinFillRect(hpsPaint,&rclPaint,SYSCLR_WINDOW);
  2820.  
  2821.          WinQueryWindowRect(hwndWnd,&rclPaint);
  2822.  
  2823.          WinDrawText(hpsPaint,
  2824.                      -1,
  2825.                      "Hello world.",
  2826.                      &rclPaint,
  2827.                      CLR_BLACK,
  2828.                      0,
  2829.                      DT_CENTER|DT_VCENTER);
  2830.  
  2831.          WinEndPaint(hpsPaint);
  2832.       }
  2833.       break;
  2834.    default:
  2835.       return WinDefWindowProc(hwndWnd,ulMsg,mpParm1,mpParm2);
  2836.    } /* endswitch */
  2837.  
  2838.    return MRFROMSHORT(FALSE);
  2839. }
  2840.  
  2841. Figure 8)  Simple window procedure 
  2842.  
  2843. The sample simple window procedure shown above does just what we described. A 
  2844. question often asked is why the choice of FALSE on the return statement.  My 
  2845. answer is "I don't know"; you must return something to avoid warnings by the 
  2846. compiler and FALSE is the more frequent return value from specific messages, so 
  2847. I used it instead of TRUE. 
  2848.  
  2849. There are a multitude of messages that the system sends to an application; so 
  2850. many, in fact, that detailing each of them would fill a book (hey, that's an 
  2851. idea...).  Instead, you will find that you use a few much more than the rest: 
  2852.  
  2853. WM_CREATE           Sent whenever the window is being created to allow it to 
  2854.                     initialize itself; by returning MRFROMSHORT(TRUE) you tell 
  2855.                     the system that initialization failed, causing the function 
  2856.                     resulting in this message to also fail. 
  2857. WM_DESTROY          Sent whenever the window is about to be destroyed.  There 
  2858.                     is no way to halt the destruction once this message is 
  2859.                     received. 
  2860. WM_CLOSE            Sent whenever the user requested that the window close 
  2861.                     itself.  This allows you to confirm the action, if needed, 
  2862.                     and then call the appropriate function to destroy yourself 
  2863.                     if confirmed. 
  2864. WM_SIZE             Sent after the window has been resized. 
  2865. WM_CONTROL          Sent whenever an owned window has something to tell you. 
  2866. WM_COMMAND          Sent whenever the user selected a menuitem or a pushbutton, 
  2867.                     which indicates that a command is to be performed. 
  2868. WM_MOUSEMOVE        Sent whenever the mouse moves over your window. 
  2869. WM_BUTTONxDOWN      Sent whenever button x is depressed. 
  2870. WM_BUTTONxUP        Sent whenever button x is released. 
  2871. WM_BUTTONxCLICK     Sent whenever button x is clicked. 
  2872. WM_BUTTONxDBLCLK    Sent whenever button x is double-clicked. 
  2873. WM_CHAR             Sent whenever a key is pressed. 
  2874. WM_PAINT            Sent whenever a portion of the window needs to be 
  2875.                     repainted. 
  2876. WM_TIMER            Sent whenever a timer started by the application expires. 
  2877. WM_BEGINDRAG        Sent whenever the user attempts to initiate a direct 
  2878.                     manipulation operation. 
  2879. WM_CONTEXTMENU      Sent whenever the user attempts to display a popup 
  2880.                     context-sensitive menu. 
  2881.  
  2882. Now, what do you do when you receive any of these (or other) messages? That is 
  2883. up to you, but you will find a function to do just about anything you need; all 
  2884. you have to do is put them together in the right order. 
  2885.  
  2886. Select this to go to the next section 
  2887.  
  2888.  
  2889. ΓòÉΓòÉΓòÉ 3.2.8. Summary ΓòÉΓòÉΓòÉ
  2890.  
  2891. This month we learned quite a lot, and the begining can definately be called 
  2892. the hardest part of adjusting to PM programming.  Next month, we will look at 
  2893. the processing for WM_PAINT in our "Hello World" sample application and discuss 
  2894. presentation spaces.  We will also be introduced to some of the messages listed 
  2895. in the last section. 
  2896.  
  2897. Select this to go to the next section 
  2898.  
  2899.  
  2900. ΓòÉΓòÉΓòÉ 3.3. Scratch Patch ΓòÉΓòÉΓòÉ
  2901.  
  2902. Written by Larry Salomon, Jr. 
  2903.  
  2904. Welcome to this month's "Scratch Patch"!  Each month, I collect various items 
  2905. that fit into this column sent to me via email. The ones that I feel contribute 
  2906. the most to developers, whether in terms of information or as a nifty trick to 
  2907. tuck into your cap, get published in this column. 
  2908.  
  2909. To submit an item, send it via email to my address - os2man@panix.com - and be 
  2910. sure to grant permission to publish it (those that forget will not be 
  2911. considered for publication).  This month, we have the following: 
  2912.  
  2913. o Questions and Answers 
  2914. o Snippet(s) of the Month 
  2915. o Documentation Chop Shop 
  2916. o Want Ads 
  2917.  
  2918. Select this to go to the next section 
  2919.  
  2920.  
  2921. ΓòÉΓòÉΓòÉ 3.3.1. Questions and Answers ΓòÉΓòÉΓòÉ
  2922.  
  2923. Keith Murray (murrayk@prism.cs.orst.edu) writes: Your book has given me a lot 
  2924. of help with a lot of things.  It does a good job of talking about ownerdrawn 
  2925. listboxes.  There is one omission however.  How do you deal with horizontal 
  2926. scrolling?  I've got to the point of being able to draw my graphic and measure 
  2927. everything correctly.  Vertical scrolling works just great and the horizontal 
  2928. scroll bar seems scaled correctly.  But I don't know what to do from there. 
  2929. How do I figure out where to draw my text relative to the scroll bar? 
  2930.  
  2931. Since you seem to know how to draw the text, what you are really asking is how 
  2932. do you query the position of the horizontal scrollbar.  In order to do this, 
  2933. you need to send the scrollbar an SBM_QUERYPOS message.  But you don't have the 
  2934. handle of the scrollbar! 
  2935.  
  2936. If you would press ENTER to go to the next section, you will find the code for 
  2937. the queryLbScroll() function, which will return what you want. 
  2938.  
  2939. Select this to go to the next section 
  2940.  
  2941.  
  2942. ΓòÉΓòÉΓòÉ 3.3.2. Snippet(s) of the Month ΓòÉΓòÉΓòÉ
  2943.  
  2944. Even worse than last month, I received no submissions for this section of the 
  2945. column.  Fortunately, the question of the month requires a bit of code, which I 
  2946. will place here.  The code, and a test application, can be found in 
  2947. scratch.zip. 
  2948.  
  2949. HWND queryLbScroll(HWND hwndLb,BOOL bWantVert)
  2950. //-------------------------------------------------------------------------
  2951. // This function returns the specified scrollbar for a listbox.
  2952. //
  2953. // Input:  hwndLb - handle of the listbox window to query
  2954. //         bWantVert - TRUE if the vertical scrollbar is desired, FALSE if
  2955. //                     horizontal
  2956. // Returns:  handle of the scrollbar window if successful, NULLHANDLE
  2957. //           otherwise
  2958. //-------------------------------------------------------------------------
  2959. {
  2960.    ULONG ulWantStyle;
  2961.    HENUM heEnum;
  2962.    HWND hwndScroll;
  2963.    CHAR achClass[256];
  2964.    ATOM aClass;
  2965.    ULONG ulStyle;
  2966.  
  2967.    ulWantStyle=bWantVert?SBS_VERT:0;
  2968.  
  2969.    //----------------------------------------------------------------------
  2970.    // Enumerate all of the children of the scrollbar
  2971.    //----------------------------------------------------------------------
  2972.    heEnum=WinBeginEnumWindows(WinWindowFromID(hwndWnd,DLG_LB_TEST));
  2973.  
  2974.    hwndScroll=WinGetNextWindow(heEnum);
  2975.    while (hwndScroll!=NULLHANDLE) {
  2976.       //-------------------------------------------------------------------
  2977.       // Query the class name.  Public window classes have class names
  2978.       // which although they are in the for "#dddd" are really atoms.
  2979.       // Thus, we need to check the system atom table and compare the
  2980.       // result against the low word of the WC_* constant.
  2981.       //
  2982.       // Oh.  Why, you ask?  Because the numerical form of an integer
  2983.       // atom contains the number in the low word and 0xFFFF in the high
  2984.       // word.  Since all of the WC_* constants have 0xFFFF in the high
  2985.       // word, they are all integer atoms.
  2986.       //-------------------------------------------------------------------
  2987.       WinQueryClassName(hwndScroll,sizeof(achClass),achClass);
  2988.  
  2989.       aClass=WinFindAtom(WinQuerySystemAtomTable(),achClass);
  2990.       if (aClass==LOUSHORT(WC_SCROLLBAR)) {
  2991.          //----------------------------------------------------------------
  2992.          // We found a scrollbar; now check to see if it is the right
  2993.          // type.
  2994.          //----------------------------------------------------------------
  2995.          ulStyle=WinQueryWindowULong(hwndScroll,QWL_STYLE);
  2996.          if ((ulStyle & SBS_VERT)==ulWantStyle) {
  2997.             break;
  2998.          } /* endif */
  2999.       } /* endif */
  3000.  
  3001.       hwndScroll=WinGetNextWindow(heEnum);
  3002.    } /* endwhile */
  3003.  
  3004.    WinEndEnumWindows(heEnum);
  3005.    return hwndScroll;
  3006. }
  3007.  
  3008. Select this to go to the next section 
  3009.  
  3010.  
  3011. ΓòÉΓòÉΓòÉ 3.3.3. Documentation Chop Shop ΓòÉΓòÉΓòÉ
  3012.  
  3013. Thanks to Gordon Zeglinski (zeglins@cc.umanitoba.ca) for this submission.  He 
  3014. writes: 
  3015.  
  3016. The documentation states that the WinSwitchToProgram() has no effect if the 
  3017. calling program does not have the focus.  This apparently is not true, as my 
  3018. app will switch an app to the foreground regardless of whether it has the focus 
  3019. or not. 
  3020.  
  3021. Gordon, this is not entirely correct; the documentation states that this 
  3022. function does nothing unless the process calling it is the foreground process 
  3023. and there is an important distinction here, if I understand the implied things 
  3024. in your note. 
  3025.  
  3026. According to one of the original PM developers, who will remain nameless in 
  3027. case I get this wrong, code within a window procedure is only executing when it 
  3028. is the foreground process.  This is related, I believe, to the synchronized 
  3029. system input queue, and a host of other design-of-PM related issues which I 
  3030. will not go into (mainly because I'm not entirely confident of the accuracy of 
  3031. my knowledge).  Thus, if you are calling WinSwitchToProgram() from within your 
  3032. window procedure or any function (in)directly called by your window procedure, 
  3033. you must be the foreground process. 
  3034.  
  3035. I am not sure what would happen if an application started a second thread which 
  3036. creates a message queue, does some processing, and calls this function without 
  3037. utilizing window procedures in any way, but I imagine the behavior described in 
  3038. the documentation would be observed. 
  3039.  
  3040. Select this to go to the next section 
  3041.  
  3042.  
  3043. ΓòÉΓòÉΓòÉ 3.3.4. Want Ads ΓòÉΓòÉΓòÉ
  3044.  
  3045. Below are the hot topics as of this issue's writing.  Feel free to write on any 
  3046. of these. 
  3047.  
  3048. Workplace Shell Programming (hot) - lately, I have noticed two things: 1) lots 
  3049. of people want to learn how to write new Workplace Shell classes and 2) no one 
  3050. who knows anything about it is telling the rest of us how to do it.  I'll even 
  3051. stoop down to accepting an article in ASCII format on this topic! :) 
  3052.  
  3053. Client/Server (hot) - using either named pipes (with or without a network) or 
  3054. sockets, client/server programming is all-the-rage these days.  On a related 
  3055. note, some people have also expressed an interest in learning about interfacing 
  3056. with the various protocol drivers (e.g.  NDIS, IPX/SPX, etc.).  Any articles in 
  3057. this area are most welcome. 
  3058.  
  3059. Anything on Rexx/2 (hot) - many people have requested more articles on Rexx/2. 
  3060. This issue sees the first article on this topic, but we can always use more. 
  3061. Writing "Enhanced Editor" macros in Rexx/2 and interfacing with the Workplace 
  3062. Shell from Rexx/2 are still open topics. 
  3063.  
  3064. Animation (warm) - a few readers expressed an interest in the various animation 
  3065. techniques that can be applied to PM applications.  The ultimate article, in my 
  3066. opinion, would be one that develops a sprite library a la the Commodore 64's 
  3067. (and Amiga's?) built-in routines, since this is probably the hardest component 
  3068. of any good animation sequence.  Call this a "personal request"... 
  3069.  
  3070. Select this to go to the next section 
  3071.  
  3072.  
  3073. ΓòÉΓòÉΓòÉ 4. How do I get EDM/2? ΓòÉΓòÉΓòÉ
  3074.  
  3075. EDM/2 can be obtained in any of the following ways: 
  3076.  
  3077. On the Internet 
  3078.  
  3079. o All back issues are available via anonymous FTP from ftp.cdrom.com in the 
  3080.   /pub/os2/2_x/program/newsltr directory. 
  3081. o The EDM/2 mailing list.  Send an empty message to edm2-info@knex.via.mind.org 
  3082.   to receive a file containing (among other things) instructions for 
  3083.   subscribing to EDM/2. 
  3084. o IBM's external gopher server, which I have not the address of, in Almaden. 
  3085.  
  3086. On Compuserve 
  3087.  
  3088. All back issues are available in the OS/2 Developers Forum 2. 
  3089.  
  3090. IBM Internal 
  3091.  
  3092. o IBM's internal gopher server, which I have not the address of, in Almaden. 
  3093. o IBM's REQUEST command on all internal VM systems.  Enter the VM command 
  3094.   REQUEST LIST FROM ASSELIN AT RALVM12 and a list of the requestable packages 
  3095.   will be sent to you; in this list are the names of the packages containing 
  3096.   the EDM/2 issues. 
  3097.  
  3098. Select this to go to the next section 
  3099.  
  3100.  
  3101. ΓòÉΓòÉΓòÉ 5. Contributors to this Issue ΓòÉΓòÉΓòÉ
  3102.  
  3103. Are You a Potential Author? 
  3104.  
  3105. As always, we are always looking for (new) authors.  If you have a topic about 
  3106. which you would like to write, send a brief description of the topic 
  3107. electronically to any of the editors, whose addresses are listed below, by the 
  3108. 15th of the month in which your article will appear.  This alerts us that you 
  3109. will be sending an article so that we can plan the issue layout accordingly. 
  3110. After you have done this, get the latest copy of the Article Submission 
  3111. Guidelines from ftp.cdrom.com in the /pub/os2/2_x/program/newsltr directory. 
  3112. (the file is artsub.zip)  The completed text of your article should be sent to 
  3113. us no later than the last day of the month; any articles received after that 
  3114. time may be pushed to the next issue. 
  3115.  
  3116. The editors can be reached at the following email addresses: 
  3117.  
  3118. o Larry Salomon - os2man@panix.com (Internet). 
  3119.  
  3120. The following people contributed to this issue in one form or another (in 
  3121. alphabetical order): 
  3122.  
  3123. o Semir Patel 
  3124. o Larry Salomon, Jr. 
  3125. o Marc van Woerkom 
  3126. o Gordon Zeglinski 
  3127. o Network distributors 
  3128.  
  3129.  
  3130. ΓòÉΓòÉΓòÉ 5.1. Semir Patel ΓòÉΓòÉΓòÉ
  3131.  
  3132. I am a junior in the undergraduate CS program at the University of Texas at 
  3133. Austin; currently, I'm working on an addictive multithreaded, MMPM aware game 
  3134. for OS/2 called Bonkers!  I hope to find co-op oppourtunity in the Austin area 
  3135. in the near future. 
  3136.  
  3137. I can be reached in the following manners: 
  3138.  
  3139. o spatel@cs.utexas.edu (Internet email) 
  3140. o Darkgoob (Internet Relay Chat) 
  3141.  
  3142. Semir Patel 
  3143.  
  3144.  
  3145. ΓòÉΓòÉΓòÉ 5.2. Larry Salomon, Jr. ΓòÉΓòÉΓòÉ
  3146.  
  3147. Larry Salomon wrote his first Presentation Manager application for OS/2 version 
  3148. 1.1 in 1989.  Since that time, he has written numerous VIO and PM applications, 
  3149. including the Scramble applet included with OS/2 and the I-Brow/Magnify/Screen 
  3150. Capture trio included with the IBM Professional Developers Kit CD-ROM currently 
  3151. being distributed by IBM.  Currently, he works for International Masters 
  3152. Publishers in Stamford, Connecticut and resides in Bellerose, New York with his 
  3153. wife Lisa. 
  3154.  
  3155. Larry can be reached electronically via the Internet at os2man@panix.com. 
  3156.  
  3157.  
  3158. ΓòÉΓòÉΓòÉ 5.3. Marc van Woerkom ΓòÉΓòÉΓòÉ
  3159.  
  3160. Marc van Woerkom is a physics student at the Rheinisch-Westfaelische Technische 
  3161. Hochschule Aachen in Germany.  Currently, he works there at the Aachen Center 
  3162. for Solidification in Space, ACCESS, on his Diplomarbeit. 
  3163.  
  3164. When he isn't wrestling with FORTRAN 77 code for his diploma, he enjoys the 
  3165. progress made in the last 15 years i.e. programming in C++ on his private OS/2 
  3166. machine.  Next to his computer addiction, he likes to cross the nearby Dutch 
  3167. border to see a good movie, read science-fiction, play Badminton on occasion 
  3168. and also watch a lot of cable television. 
  3169.  
  3170. Marc can be reached at either of the following addresses: 
  3171.  
  3172. o marc_van-woerkom@ac3.maus.de (home) 
  3173. o marc@pxcl1.gi.rwth-aachen.de (work) 
  3174.  
  3175.  
  3176. ΓòÉΓòÉΓòÉ 5.4. Gordon Zeglinski ΓòÉΓòÉΓòÉ
  3177.  
  3178. Gordon Zeglinski is a freelance programmer/consultant who received his Master's 
  3179. degree in Mechanical Engineering with a thesis on C++ sparse matrix objects. 
  3180. He has been programming in C++ for 6 years and also has a strong background in 
  3181. FORTRAN.  He started developing OS/2 applications with version 2.0 . 
  3182.  
  3183. His current projects include a client/server communications program that 
  3184. utilitizes OS/2's features which has entered beta testing.  Additionally, he is 
  3185. involved in the development of a "real-time" automated vehicle based on OS/2 
  3186. and using C++ in which he does device driver development and designs the 
  3187. applications that comprise the control logic and user interface. 
  3188.  
  3189. He can be reached via the Internet at zeglins@cc.umanitoba.ca. 
  3190.  
  3191.  
  3192. ΓòÉΓòÉΓòÉ 5.5. Network distributors ΓòÉΓòÉΓòÉ
  3193.  
  3194. These people are part of our distribution system to provide EDM/2 on networks 
  3195. other than the Internet.  Their help to provide access to this magazine for 
  3196. others is voluntary and we appreciate them a lot! 
  3197.  
  3198. o Paul Hethman (hethman@cs.utk.edu) - Compuserve 
  3199. o Gess Shankar (gess@knex.via.mind.org) - Internet 
  3200. o David Singer (singer@almaden.ibm.com) - IBM Internal 
  3201. o Andre Asselin (ASSELIN AT RALVM12) - IBM Internal 
  3202.  
  3203. If you would like to become a "network distributor", be sure to contact the 
  3204. editors so that we can give you the credit you deserve!