home *** CD-ROM | disk | FTP | other *** search
-
- ΓòÉΓòÉΓòÉ 1. Title Page ΓòÉΓòÉΓòÉ
-
- Welcome to EDM/2 - The Electronic OS/2 Developers Magazine!
- Portions copyright (c) by Larry Salomon Jr.
- Volume 2, issue 1
-
- Copyright Notice and Other Stuff
-
- The editor of this electronic magazine is Larry Salomon, Jr.
-
- Portions of EDM/2 are copyrighted by the editors. This publication may be
- freely distributed in electronic form provided that all parts are present in
- their original unmodified form. A reasonable fee may be charged for the
- physical act of distribution; no fee may be charged for the publication itself.
-
- All articles are copyrighted by their authors. No part of any article may be
- reproduced without permission from the original author.
-
- Neither this publication nor the editors are affiliated with International
- Business Machines Corporation.
-
- OS/2 is a registered trademark of International Business Machines Corporation.
- Other trademarks are property of their respective owners. Any mention of a
- product in this publication does not constitute an endorsement or affiliation
- unless specifically stated in the text.
-
- Administrivia
-
- Eesh. After a one-week vacation, I returned home with a SoundBlaster 16 and a
- pair of Koss speakers; delighted, I decided to install them as soon as I
- returned, but only after I finished hooking up my Super Nintendo system that I
- also received from my younger brother (isn't family great? :) . It's only when
- you receive a new hardware card more than a year after buying your computer
- that you really appreciate the value of the Microchannel architecture,
- especially when you have an ISA machine.
-
- "Let's see, was that DMA channel 5 or 6 used by the Adaptec card, and address
- 330h is used by my modem, correct?" After two hours, I finally got the machine
- to boot and was able to use the rest of the hardware in my system, but still
- had trouble with my SoundBlaster. Some email suggested that I change the IRQ
- number from 7 to something else, because OS/2 reserves that for the printer.
- You have to love computer conferencing; thanks to everyone who responded.
-
- Will 1994 see an explosion in MMPM/2 programming? I certainly hope so. Having
- been a member at the initial requirements meeting in Atlanta while I was an
- IBMer, I would like to learn how to develop applications to use this extension
- to OS/2 that has been hyped by those who have used it. While we are waiting
- for the myriad of application, check out the two head starts in this issue.
- Not that I'm complaining, but can we see a few on video, too?
-
- Well, considering all that has happened in 1993, I must say the greatest thing
- was that EDM/2 had close to 1000 subscriptions total at the end of December!
- What a number! While I keep receiving email saying how I'm doing such a great
- job with this, and blah blah blah, I must turn it around and say thank you to
- all of the authors who have kept this magazine alive (and special thanks for
- making this issue possible!) and also to all of the readers who have given the
- authors a reason to write. Considering that everything is done voluntarily, I
- must hand it to everyone - we all did a fantastic job for a pilot year...
-
- And now that we are in volume 2 (did you notice that?), I get the extreme
- pleasure of announcing the winners from the Reader's Choice Awards. By the end
- of December, I had received a total of 14 sets of votes, which I consider a
- pathetic number given the number of readers, but I was consoled by email where
- I was told that Analog Magazine (a scientific publication) which has been in
- existance for 30+ years has always had an extremely low votes/reader ratio. I
- shall digress, however. The winning articles, their authors, and the prize
- each author shall receive is listed below.
-
- 1st Place
-
- 1st place goes to Andre Asselin for the OS/2 Installable File Systems series.
- For this, he will receive a copy of HockWare, Inc.'s VisPro/REXX Bronze version
- 2.0 (when it becomes available in mid-January) and a 1 year subscription to the
- OS/2 Developer magazine.
-
- 2nd Place
-
- 2nd place goes to Gordon Zeglinski for the Rexx-ercising Your Applications
- series. For this, he will receive a copy of HockWare Inc.'s VisPro/REXX Bronze
- version 2.0.
-
- 3rd Place
-
- 3rd place was a tie between Gavin Baker's Intro to PM column and my Threads in
- PM applications article. While I realize that this looks fishy, I - in the
- spirit of random distribution - flipped a coin for it. (Boy, was I glad it
- wasn't a three-way tie! :) Fortunately, Gavin won, so he will receive a 1 year
- subscription to the OS/2 Developer magazine...as soon as I can figure out how
- to reach him! Mail to his address gets bounced. If I do not hear from him by
- next issue, I will award the prize to someone else.
-
- A hearty congratulations to all of the winners from us here at EDM/2! We would
- also like to thank Dick Conklin, the editor of OS/2 Developer, and Dave Hock,
- the president of HockWare, Inc., for their generous contributions.
-
- If you have a topic to write about, this should be enough incentive; see the
- section Are You a Potential Author? for information on submitting articles for
- publication.
-
- This just in from the network - IBM will be presenting on February 3 a seminar
- on its C-Set++ product. This will be in New York City at the Madison Avenue
- location (590 Madison Avenue, between 56th and 57th streets), in room 610
- (auditorium). The agenda currently looks like so:
-
- 6:15-7:15 C-Set++ Class Library: Design and Use
-
- Rich Hickey, a C++ a C SIG Technical Leader and teacher of
- Advanced C++ at the NYU School of Continuing Education will
- be the speaker.
-
- 7:45-9:00 C-Set++ for OS/2 Discussion and Demonstration
-
- IBMers from the C-Set++ development group in Toronto,
- Canada will be the speakers.
-
- Post partum Dutch treat (i.e. you pay for yourself) dinner at a nearby
- restaurant for good food and great conversation.
-
- Admission is free, but a voluntary contribution of $3 (US) to defray the cost
- of the meetings, etc. is asked. All are welcome to attend. I will try to be
- there so if you ever wanted to meet the editor of EDM/2, you know where to find
- me (this, unfortunately, is subject to change without notice. :( ).
-
- EDM/2 officially welcomes its newest columnist with whom we should all be
- familiar. Gordon Zeglinski takes up his post as resident C++ columnist
- beginning this issue. See C++ Corner for a first taste.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ <hidden> Votes ΓòÉΓòÉΓòÉ
-
- The breakdown of the votes is as follows:
-
- o OS/2 Installable File Systems - 6 votes
- o Rexx-ercising Your Applications - 5 votes
- o Threads in PM applications - 4 votes
- o Introduction to PM Programming (Gavin Baker) - 4 votes
- o Getting started with EMX/GCC - 3 votes
- o Programming the Container Control - 3 votes
- o A Review of C++ Compliers - 2 votes
- o Introduction to PM Programming (Larry Salomon) - 2 votes
- o Writing a Direct Manipulation Spy - 1 vote
- o C++ Encapsulation of PM - 1 vote
- o The Unofficial Guide to the Palette Manager - 1 vote
- o Advanced GPI: Retained Segments and Transformations - 1 vote
- o The Making of MineSweeper - 1 vote
- o Development of a New Window Class - 1 vote
- o Customizing the Enhanced Editor - 1 vote
- o The Help Manager & Online Documentation - 1 vote
-
-
- ΓòÉΓòÉΓòÉ <hidden> VisPro/REXX Bronze version 2.0 ΓòÉΓòÉΓòÉ
-
- The following excerpts are taken from an advance copy of the press release soon
- to be made available to trade publications, etc.
-
- VisPro/REXX Bronze is an easy-to-use visual programming tool that provides a
- quick way to build OS/2 2.x GUI applications. Some of the key features
- include:
-
- o Drag and drop programming which provides the easiest and quickest way to
- develop programs. It automatically creates REXX code that can be used as-is
- or modified.
- o Interactive debugger with break points, animation, error reporting, tracing
- and graphical event browsing for quicker debugging.
- o Support for REXX APIs including DB2/2, APPC, and EHLLAPI.
- o Multiple views such as Settings, Layout, Event Tree and List for efficient
- development.
- o Ability to create multiple-thread applications that allow some program tasks
- to run while one task is still available to the user.
- o More CUA '91 objects than similar products in order to provide flexible user
- interface design.
- o New and improved: macro support, SOM Toolkit, Q+E Database Library support,
- improved debugger, OS/2-style help, information line tips, support for adding
- pop-up menus to programs, enhanced print support, accelerator key support and
- more sample programs.
-
- Suggested retail pricing for VisPro/REXX Bronze version 2.0 is $99 US. Special
- upgrade pricing is available until March 31, 1994...$39 US ($59 after 3/31).
-
- The VisPro/REXX products are available directly from HockWare or from software
- resellers worldwide. [It] is available from IBM Australia, Limited for the
- following countries: Australia, Hong Kong, Indonesia, Korea, Malaysia, New
- Zealand, Philippines, Sri Lanka, Republic of China, Taiwan and Thailand.
-
- HockWare, Incorporated, based in Cary, North Carolina, can be reached at by
- voice at (919) 380-0616 and by fax at (919) 380-0757.
-
-
- ΓòÉΓòÉΓòÉ <hidden> OS/2 Developer ΓòÉΓòÉΓòÉ
-
- The following was composed by me, so any inaccuracies are my fault; however,
- OS/2 Developer was asked to return some form of text describing what they are
- about, of which I have received none.
-
- OS/2 Developer was established by IBM's Personal Systems Line of Business
- division in the late 1980's. Since then, they have grown to a very large
- reader base in many areas of OS/2 development, from MIS to the hard-core
- programmers and all areas in between. The magazine is currently published
- bi-monthly by Miller Freeman, Incorporated and costs $39.95 US for 6 issues (1
- year). Canada, Mexico, and international surface mail, add $16 US per year for
- postage. (Canadian GST number is 124513185.) For international air mail, add
- $30 US for postage. Foreign orders must be accompanied by payments in US
- funds.
-
- OS/2 Developer can be ordered in the US by calling (800) WANT OS2
- (800-926-8672) and outside of the US by calling (708) 647-5960. Questions may
- also be directed to those phone numbers or via the Internet at
- os2mag@vnet.ibm.com.
-
-
- ΓòÉΓòÉΓòÉ 2. This Issue's Features ΓòÉΓòÉΓòÉ
-
- The following articles constitute this issue's features:
-
- o Adding Sound to Your OS/2 Application
- o Making Noise with MMPM/2 - Part 1
- o Utilizing Hooks for Added Capabilities
-
-
- ΓòÉΓòÉΓòÉ 2.1. Adding Sound to Your OS/2 Application ΓòÉΓòÉΓòÉ
-
- Written by Semir Patel
-
- Introduction
-
- As PC based systems become cheaper and cheaper, soundcards are fastly becoming
- a standard peripheral just like your hard disk or keyboard. Todays PC's, are
- for the most part, multimedia capable complete with 16bit sound cards and a
- cdrom drive. What better way to take advantage of that 16 bit (OK...so some of
- you have 8 bit) sound card than to jazz up your OS/2 application with some
- stunning sound effects. They will definitely give your application a certain
- edge over your competitor's.
-
- First and foremost, this article is not meant to be an introduction to MMPM/2
- programming. This article is aimed at those of you who are asking yourself the
- question, "How can I add sound to my program in the shortest amount of time
- possible without trudging through the enormous and very complex MMPM/2
- subsystem?" Hopefully, this article will succeed in guiding you from point A
- to point B without wandering too far off track. It may be a little lean on
- concept and instructional in style, but it explains only what is necessary to
- get the job done. In the end, you will have a fully functional thead that you
- can plug into your already existing application.
-
- This article assumes previous knowledge of C, Presentation Manager, and basic
- sound terminology. The following hardware and software prerequisites are also
- needed:
-
- o OS/2 2.x w/ MMPM/2 support installed
- o OS/2 compiler
- o MMPM/2 Toolkit
- o Sound Card supported by MMPM/2
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.1.1. Introduction ΓòÉΓòÉΓòÉ
-
- Written by Semir Patel
-
- As PC based systems become cheaper and cheaper, soundcards are fastly becoming
- a standard peripheral just like your hard disk or keyboard. Todays PC's, are
- for the most part, multimedia capable complete with 16bit sound cards and a
- cdrom drive. What better way to take advantage of that 16 bit (OK...so some of
- you have 8 bit) sound card than to jazz up your OS/2 application with some
- stunning sound effects. They will definitely give your application a certain
- edge over your competitor's.
-
- First and foremost, this article is not meant to be an introduction to MMPM/2
- programming. This article is aimed at those of you who are asking yourself the
- question, "How can I add sound to my program in the shortest amount of time
- possible without trudging through the enormous and very complex MMPM/2
- subsystem?" Hopefully, this article will succeed in guiding you from point A
- to point B without wandering too far off track. It may be a little lean on
- concept and instructional in style, but it explains only what is necessary to
- get the job done. In the end, you will have a fully functional thead that you
- can plug into your already existing application.
-
- This article assumes previous knowledge of C, Presentation Manager, and basic
- sound terminology. The following hardware and software prerequisites are also
- needed:
-
- o OS/2 2.x w/ MMPM/2 support installed
- o OS/2 compiler
- o MMPM/2 Toolkit
- o Sound Card supported by MMPM/2
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.1.2. The MCI String and Command Interfaces ΓòÉΓòÉΓòÉ
-
- The MMPM/2 subsystem serves as the layer of abstraction between you and your
- multimedia peripherals. Its sole purpose is to allow you to effectively
- communicate with and use your multimedia peripherals to their fullest extent.
- It offers a rich and complex set of standardized APIs with which you can
- comminuicate with media devices ranging from CD/LaserDisc players to sound
- cards.
-
- There are two approaches one can take from this point forward. The first is by
- using the Media Control Interface (MCI) String Interface. This approach is
- delightfully simple, far from complex, requires the least amount of time to
- learn, and can even be accessed directly from IBM's powerful REXX language.
- The string interface's claim to fame is its amazing simplicity. Playing a
- sound file is as easy as issuing the folloing sequence of character strings via
- the mciSendString(). function.
-
- open waveaudio alias soundcard // open/acquire sound device and give it the alias "soundcard"
- load soundcard sample.wav // load sound file onto device (read into memory)
- play soundcard // play whatever is loaded onto device
- close soundcard // close/release sound device
-
- All other multimedia devices can be accessed in the same general way. It has
- one shortcoming with respect to waveaudio devices, though - only one sound file
- per device can be kept in memory at any one point in time. This approach is
- clearly not suitable for applications that require a few sound files or
- situations where sounds are expected to be played synchronously in conjunction
- with events. Having to constantly shuffle sounds in and out from the hard disk
- can also dramatically increase CPU usage.
-
- As opposed to the MCI string interface, the MCI command interface centers
- around sending individual commands to a particluar media device. It is in many
- aspects very similar to the message based scheme used in window procedures.
- The big advantage and our main reason for using the MCI command interface is
- its low CPU usage and its ability to play one or more sounds that reside in
- memory rather than on the hard disk. Granted, the command interface is somewhat
- more complicated and harder to grasp, but it's more than worth it for the
- efficiency and quick response.
-
- The following list outlines what needs to be done using the MCI command
- interface to accomplish what the string interface does in four lines. All
- points will be discussed in detail throughout the article.
-
- 1. Open sound file
- 2. Read into memory buffers
- 3. Get information about sound file
- 4. Close sound file
- 5. Create memory playlist
- 6. Insert sound file into memory playlist
- 7. Open sound device
- 8. Mount memory play list onto sound device
- 9. Adjust sound device to match characteristics of sound file
- 10. Issue command to play memory playlist
- 11. Free memory sound file memory buffers
- 12. Close sound device
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.1.3. The MCI Command Interface ΓòÉΓòÉΓòÉ
-
- You can communicate with any media device by sending it specific pre-defined
- commands. Just like PM window messages have the WM_* prefix, MCI commands and
- flags have a suffix of MCI_*. You also might have noticed the family of MCI
- functions is prefixed by mci. These commands coupled with flags of various
- sorts give you a very high degree of control over a device. For example, with
- a sound device we can manipulate it - PLAY, STOP, REWIND, etc.) or set certain
- qualities like samples per second, volume, and bits per sample. The
- mciSendCommand() function's sole purpose is to deliver your commands to a
- specific media device and to return a message once the command is completed if
- so specified.
-
- mciSendCommand(usDeviceID, /* device ID of media device (sound card, CD, .... ) */
- usMessage, /* command to be performed (MCI_PLAY, MCI_SEEK, MCI_STOP, .... ) */
- ulParam1, /* flags for this command (MCI_WAIT, MCI_NOTIFY, MCI_, .... ) */
- uParam2, /* ptr to info structure (MCI_OPEN_PARMS, MCI_SEEK_PARMS, ...) */
- usUserParm); /* param returned if MCI_NOTIFY (user defined message WM_USER + x ) */
- /* is a flag in ulParam1 */
-
- The following are MCI commands with some of their associated flags and
- information structures that we will be using in our example program:
-
- ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
- ΓöéCommand ΓöéDescription ΓöéValid Flags ΓöéFlag Desc. ΓöéInfo Structure Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- ΓöéMCI_CLOSE ΓöéClose the sound device ΓöéNone Γöé ΓöéNone Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- ΓöéMCI_SET ΓöéUse to set device ΓöéMCI_WAVE_SET_SAMPLESPERSEC ΓöéSet samples per second ΓöéMCI_WAVE_SET_PARMS Γöé
- Γöé Γöéinformation Γöé Γöé Γöé Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- Γöé(MCI_SET) Γöé ΓöéMCI_WAVE_SET_BITSPERSAMPLE ΓöéSet bits per sample (8 orΓöéMCI_WAVE_SET_PARMS Γöé
- Γöé Γöé Γöé Γöé16) Γöé Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- ΓöéMCI_STOP ΓöéStop the audio playback ΓöéNone Γöé ΓöéNone Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- ΓöéMCI_SEEK ΓöéChanges position in wave ΓöéMCI_TO ΓöéSeek to specified postionΓöéMCI_SEEK_PARMS Γöé
- Γöé Γöéfile Γöé Γöé Γöé Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- Γöé(MCI_SEEK) Γöé ΓöéMCI_TO_START ΓöéSeek to beginning ΓöéMCI_SEEK_PARMS Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- Γöé(MCI_SEEK) Γöé ΓöéMCI_TO_END ΓöéSeek to end ΓöéMCI_SEEK_PARMS Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- ΓöéMCI_PLAY ΓöéSignals the sound device ΓöéMCI_FROM ΓöéPlay starting from this ΓöéMCI_PLAY_PARMS Γöé
- Γöé Γöéto start playing Γöé Γöéposition Γöé Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- Γöé(MCI_PLAY) Γöé ΓöéMCI_TO ΓöéPlay to this postition ΓöéMCI_PLAY_PARMS Γöé
- ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
-
- Flags commmon to all Commands:
-
- MCI_WAIT Wait for the operation to finish succesfully instead of
- executing it asynchronously and returning from the function
- call
- MCI_NOTIFY Send usUserParam back to calling window (via
- mciOpenParameters.hwndCallBack more on this below) once the
- operation has finished.
-
- It is also possible to logically OR flags together to complete two operations
- in one function call. Of course, all the OR'ed flags must be valid for the
- particular command you are about to issue.
-
- So before calling mciSendCommand() you should:
-
- o Decide on the command
- o OR together the appropriate flags for your desired command
- o Fill in the appropriate info structure with only the relevant information
-
- Let's say we want to change the bits per sample and the samples per second of
- our sound device and also specify that the function not return before
- completing the command. The follow code should produce the desired effect:
-
- MCI_WAVE_SET_PARMS mwspWaveFormParameters; /* info struc for MCI_SET command*/
-
- mwspWaveFormParameters.ulSamplesPerSec = 11025;
- mwspWaveFormParameters.usBitsPerSample = 8;
-
- mciSendCommand (SoundDevice.usSoundDeviceID, /* sound device id. more details on this later */
- MCI_SET, /* command to set device */
- MCI_WAIT | /* OR'ed flags */
- MCI_WAVE_SET_SAMPLESPERSEC |
- MCI_WAVE_SET_BITSPERSAMPLE ,
- (PVOID)&mwspWaveFormParameters, /* info structure */
- 0); /* no notification message*/
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.1.4. Wave Audio Structures ΓòÉΓòÉΓòÉ
-
- As you might already know, wave files are not all the same. First of all, they
- can either be 8 or 16 bit. Furthermore, they may vary in Khz from 11 to 44.
- It is imperative that the characteristics of the sound device match those of
- the wave file. For example, if your wave file is recorded at 11025Hz and your
- sound device is set at 22050Hz, your output will obviously be distorted. The
- information we need to know about the wave file is stored in its header.
- Calling the function mmioGetHeader() along with the following structure and a
- file handle obtained throught mmioOpen() will extract that information for us.
- Comments are beside the fields that are of interest to us.
-
- mmioGetHeader(hmmio, /* file handle returned by mmioOpen */
- pHeader, /* pointer to header structure */
- usHeaderLength, /* size of header stucture */
- plBytesRead, /* returns # bytes read into header */
- ulReserved, /* reserved */
- ulFlags); /* reserved */
-
- ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
- Γöé MMAUDIOHEADER Γöé
- Γöé ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ Γöé
- Γöé Γöé MMXWAV_HEADER Γöé Γöé
- Γöé Γöé ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ Γöé Γöé
- Γöé Γöé Γöé WAVE_HEADER Γöé Γöé Γöé
- Γöé Γöé ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ Γöé Γöé
- Γöé Γöé ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ Γöé Γöé
- Γöé Γöé Γöé XWAV_HEADERINFO Γöé Γöé Γöé
- Γöé Γöé ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ Γöé Γöé
- Γöé ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ Γöé
- ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
-
- typedef struct _MMAUDIOHEADER {
- ULONG ulHeaderLength;
- ULONG ulContentType;
- ULONG ulMediaType;
- MMXWAV_HEADER mmXWAVHeader;
- } MMAUDIOHEADER;
-
- typedef struct _MMXWAV_HEADER {
- WAVE_HEADER WAVEHeader;
- XWAV_HEADERINFO XWAVHeaderInfo;
- } MMXWAV_HEADER;
-
- typedef struct _WAVE_HEADER {
- USHORT usFormatTag;
- USHORT usChannels; /* mono,stereo or quad*/
- ULONG ulSamplesPerSec; /* samples per second */
- ULONG ulAvgBytesPerSec;
- USHORT usBlockAlign;
- USHORT usBitsPerSample; /* bits per sample */
- } WAVE_HEADER;
-
- typedef struct _XWAV_HEADERINFO {
- ULONG ulAudioLengthInMS;
- ULONG ulAudioLengthInBytes; /* length of raw data */
- ULONG ulReserved;
- } XWAV_HEADERINFO;
-
- Once the header has been obtained, the next step would be to copy the commented
- fields into an MCI_WAVE_SET_PARMS structure and issue a MCI_SET as in the
- previous section.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.1.5. Memory Playlists ΓòÉΓòÉΓòÉ
-
- A memory playlist is the glue that holds the whole thing together. Without it,
- all you would have is an open sound device and wave files sitting in memory but
- no way to play them. Memory playlists are simply a set of instructions. Each
- entry of a memory playlist consists of one command (the instruction) and up to
- 3 operands.
-
- /* playlist structure */
- typedef struct pls
- {
- ULONG ulCommand;
- ULONG ulOperandOne;
- ULONG ulOperandTwo;
- ULONG ulOperandThree;
- } PLAY_LIST_STRUCTURE;
-
- Memory playlists are very flexible and offer a variety of instructions. Here
- are a few of interest with along with descriptions of the three operands. Pay
- special attention to the DATA_OPERATION.
-
- BRANCH_OPERATION
-
- Transfers control to another instruction in the playlist.
-
- Operand 1 Ignored.
- Operand 2 The absolute instruction number in the playlist to which
- control is being transferred. Because the playlist is
- defined as an array of structures (instruction, operation,
- and operand values) its first instruction is referenced as
- array element, index 0. Therefore, the first instruction in
- the list is 0, the second instruction is 1, and so on.
- Operand 3 Ignored.
-
- DATA_OPERATION
-
- Specifies a data buffer to be played from or recorded into.
-
- Operand 1 Long pointer to a buffer in the application.
- Operand 2 Length of the buffer pointed to by Operand 1.
- Operand 3 Current position in the buffer. This operand is updated by
- the system during a recording or playback operation. For a
- playback operation, it is the number of bytes that have
- been sent to the output device handler. For a recording
- operation, it is the number of bytes that have been placed
- into a user buffer.
-
- The buffer indicated by the DATA instruction must only contain the raw data
- bytes from the device and cannot include any header information. For example, a
- buffer for a sequencer device can contain only MIDI multibyte messages, as
- defined by the International MIDI Association. Therefore, the precise meaning
- or format of the data is dependent on the current settings of the media device.
- For example, a wave audio data element is assumed to have the format PCM or
- ADPCM, number of bits per sample, and so on, that is indicated by the settings
- of the audio device.
-
- The address range of a DATA statement cannot overlap the address range of any
- another DATA statement. However, the same DATA statement can be repeated.
-
- EXIT_OPERATION
-
- Indicates the end of the playlist.
-
- Operand 1 Ignored.
- Operand 2 Ignored.
- Operand 3 Ignored.
-
- Using playlist instructions, you can play audio objects in succession from one
- or more memory buffers. Instructions include branching to and returning from
- subroutines within the playlist. In addition, the playlist can be modified
- dynamically by the application while it is being played.
-
- The MCI_OPEN_PLAYLIST flag is specified for the MCI_OPEN or MCI_LOAD command
- message to indicate that the pszElementName field in the MCI_OPEN_PARMS or
- MCI_LOAD_PARMS data structure is a pointer to a memory playlist. This has the
- effect of mounting the playlist onto or linking the playlist to the sound
- device.
-
- For example, if we wanted to play the contents of a memory buffer the command
- would be DATA_OPERATION with the first and second operands being a pointer to a
- memory buffer and the length of the memory buffer respectively. When the
- MMPM/2 subsystem receives a command to play a memory playlist, it simply goes
- down the list and sequentially executes the commands. Changing the first and
- second operands dynamically enables you to play any wave file loaded into a
- memory buffer.
-
- /* example declaration for a memory playlist with 2 commands and */
- /* nulled out operands */
- PLAY_LIST_STRUCTURE PlayList [NUMBER_OF_COMMANDS] =
- {
- DATA_OPERATION, 0, 0, 0, /* play command */
- EXIT_OPERATION, 0, 0, 0 /* terminate playlist */
- };
-
- PlayList[0].ulOperandOne = (ULONG)&MemoryBufferWhereRawWaveDataIsStored;
- PlayList[0].ulOperandTwo = (ULONG)SizeofMemoryBuffer;
- Now an MCI_PLAY would play the playlist.
-
- mciSendCommand (SoundDevice.usSoundDeviceID,
- MCI_PLAY,
- MCI_WAIT,
- (PVOID)&mciOpenParameters,
- 0);
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.1.6. Putting the pieces together ΓòÉΓòÉΓòÉ
-
- Now that the groundwork has been laid out, let's work on building a fully
- functional thread thread that can easily be plugged into your already existing
- program and play waves when requested to do so. We will need to:
-
- 1. Spawn a thread to act as a server for all sound operations
- 2. Read the wave files in from disk to memory
- 3. Open the sound device and mount playlist onto it
- 4. Accept requests to play wave files until the server is instructed to
- terminate
- 5. Close the sound device and free memory buffers
- 6. End thread
-
- We'll skip the explanation for part one here since threads are a whole
- different topic. The code for this is of course in the accompanying zip file.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.1.7. Reading in the wave files from disk to memory ΓòÉΓòÉΓòÉ
-
- We will use an array of the following structure to store information about each
- individual sound file.
-
- struct SOUNDINFO{
- char szFileName[255]; /* filename..already initialized */
- LONG *Address; /* pointer to this wav's memory buffer */
- ULONG ulSize; /* size of the wave file */
- ULONG ulSamplesPerSec; /* samples per second */
- USHORT usBitsPerSample; /* bits per sample */
- };
-
- struct SOUNDINFO SoundFiles[NUMBER_OF_WAVES];
-
- We loop through the array of structures and do the following on each iteration:
-
- First we want to attach a file handle to the wave file so we can operate on it.
-
- hmmioFileHandle = mmioOpen (acFileName, /* file name */
- (PMMIOINFO)NULL, /* extra params not needed */
- MMIO_READ); /* opens file for reading only */
-
- Next we obtain the header information by calling mmioGetHeader() which fills in
- the mmAudioHeader structure.
-
- mmioGetHeader (hmmioFileHandle, /* file handle */
- (PVOID)&mmAudioHeader, /* info copied into this struc */
- sizeof (MMAUDIOHEADER), /* size of header structure */
- (PLONG) &ulBytesRead, /* # bytes read into header */
- (ULONG) NULL, /* reserved */
- (ULONG) NULL); /* reserved */
-
- Now we copy the information from the header structure into our own array of
- soundfile information and then malloc and copy the raw data into memory.
-
- SoundFiles[usSoundFileID].ulSize = mmAudioHeader.mmXWAVHeader.XWAVHeaderInfo.ulAudioLengthInBytes;
- SoundFiles[usSoundFileID].ulSamplesPerSec = mmAudioHeader.mmXWAVHeader.WAVEHeader.ulSamplesPerSec;
- SoundFiles[usSoundFileID].usBitsPerSample = mmAudioHeader.mmXWAVHeader.WAVEHeader.usBitsPerSample;
- SoundFiles[usSoundFileID].Address = (LONG *) malloc(SoundFiles[usSoundFileID].ulSize);
-
- mmioRead(hmmioFileHandle, /* handle of source */
- (PSZ)SoundFiles[usSoundFileID].Address, /* ptr to destination */
- SoundFiles[usSoundFileID].ulSize); /* amount to copy */
-
- Finally we release the file by calling mmioClose().
-
- mmioClose(hmmioFileHandle,0 );
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.1.8. Opening the Sound Device ΓòÉΓòÉΓòÉ
-
- Let's go through the 3 steps before calling mciSendCommand().
-
- 1. Pick command
-
- In order to open a sound device, an MCI_OPEN must be issued
-
- 2. OR flags together
-
- ULONG ulOpenFlags = MCI_WAIT | /* wait for function to return */
- MCI_OPEN_PLAYLIST | /* this flag says that you are mounting playlist onto the sound device */
- /* the address of the playlist resides in the info structure */
- MCI_OPEN_TYPE_ID | /* you are specifying a device type in the info structure */
- MCI_OPEN_SHAREABLE; /* if another process requests the sound device, you are willing to give it up */
-
- 3. Pick appropriate info structure and fill in relevant info. In our case,
- MCI_OPEN_PARMS is the info structure
-
- typedef struct _MCI_OPEN_PARMS {
- DWORD hwndCallback /* Window handle */
- WORD usDeviceID; /* Device ID */
- WORD usReserved0; /* Reserved */
- PSZ pszDeviceType; /* Device type */
- PSZ pszElementName; /* Element name */
- PSZ pszAlias; /* Device alias */
- } MCI_OPEN_PARMS;
-
- MCI_OPEN_PARMS mciOpenParameters;
-
- mciOpenParameters.pszDeviceType = (PSZ) MAKEULONG ( MCI_DEVTYPE_WAVEFORM_AUDIO, 1 ); /* specifies that we want a waveform audio device */
- mciOpenParameters.hwndCallback = (HWND) NULL; /* no callback window */
- mciOpenParameters.pszAlias = (CHAR) NULL; /* no alias */
-
- Now this is the part that links the playlist and sound device together. By
- making the pszElementName of the MCI_OPEN_PARMS structure point to the base
- address of the playlist, any operations you perform on that device (like
- MCI_PLAY or MCI_STOP) are performed on the playlist. The mciSendCommand()
- essentially opens the sound device and moints the playlist onto it. Note that
- the device id is hardcoded in as a zero. The actual device id will be returned
- in mciOpenParameters.usDeviceID.
-
- mciOpenParameters.pszElementName = (PSZ)PlayList[0];
-
- mciSendCommand(0, /* We don't know the device yet. */
- MCI_OPEN, /* MCI message. */
- ulOpenFlags, /* Flags for the MCI message. */
- (PVOID) &mciOpenParameters, /* Parameters for the message. */
- 0); /* Parameter for notify message. */
-
- We're going to include one more structure to keep track of the current
- characteristics of the sound device. This way, if a wave files samples/sec or
- bits/sample do not match those of the sound device, we know to adjust the sound
- device.
-
- struct SOUNDDEVICE {
- USHORT usSoundDeviceID; /* device id */
- ULONG ulSamplesPerSec; /* samps/sec */
- USHORT usBitsPerSample; /* bits/samp */
- };
-
- struct SOUNDDEVICE SoundDevice;
-
- Let's copy the the device id into SoundDevice so we can use it on future calls
- to mciSendCommand().
-
- SoundDevice.usSoundDeviceID = mciOpenParameters.usDeviceID;
-
- Now the sound device is open and ready to accept commands.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.1.9. Playing Wave Files ΓòÉΓòÉΓòÉ
-
- This part of the MCI command interface works exactly like you would work your
- VCR or tape player. There are commands to PLAY, STOP, PAUSE, REWIND, FAST
- FORWARD, etc.
-
- Given an index, usSoundFileID, into an array of SOUND_INFO structures, the
- first step is to modify the playlist so that it points to that waves memory
- buffer. This is easily done by copying the address and memory buffer size to
- operands one and two respectively.
-
- PlayList[0].ulOperandOne = (ULONG)SoundFiles[usSoundFileID].Address;
- PlayList[0].ulOperandTwo = SoundFiles[usSoundFileID].Size;
-
- Next is the check to make sure that the number of bits per sample and the
- number of samples per second match for the wave file and the sound device. If
- they are not identical, then the sound device is changed accordingly.
-
- MCI_WAVE_SET_PARMS mwspWaveFormParameters;
-
- if ((SoundFiles[usSoundFileID].ulSamplesPerSec != SoundDevice.ulSamplesPerSec) ||
- (SoundFiles[usSoundFileID].usBitsPerSample != SoundDevice.usBitsPerSample))
- {
- /* null out structure */
- memset( &mwspWaveFormParameters, 0, sizeof(mwspWaveFormParameters));
-
- /* update sound device info */
- SoundDevice.ulSamplesPerSec = SoundFiles[usSoundFileID].ulSamplesPerSec;
- SoundDevice.usBitsPerSample = SoundFiles[usSoundFileID].usBitsPerSample;
-
- /* setup the info structire for an MCI_SET */
- mwspWaveFormParameters.ulSamplesPerSec = SoundDevice.ulSamplesPerSec;
- mwspWaveFormParameters.usBitsPerSample = SoundDevice.usBitsPerSample;
-
- mciSendCommand(SoundDevice.usSoundDeviceID, /* Device to play the waves. */
- MCI_SET, /* MCI message. */
- MCI_WAIT |
- MCI_WAVE_SET_SAMPLESPERSEC | /* Flags for the MCI message. */
- MCI_WAVE_SET_BITSPERSAMPLE ,
- (PVOID) &mwspWaveFormParameters, /* Parameters for the message. */
- 0); /* Parameter for notify message. */
- }
-
- Stop and rewind the playlist just in case a previous wave file is still
- playing.
-
- mciSendCommand(SoundDevice.usSoundDeviceID, /* Device to play the waves. */
- MCI_STOP, /* MCI message. */
- MCI_WAIT, /* Flags for the MCI message. */
- (PVOID) &mciOpenParameters, /* Parameters for the message. */
- 0); /* Parameter for notify message. */
-
- mciSendCommand(SoundDevice.usSoundDeviceID, /* Device to play the waves. */
- MCI_SEEK, /* MCI message. */
- MCI_WAIT | MCI_TO_START, /* Flags for the MCI message. */
- (PVOID) &mciOpenParameters, /* Parameters for the message. */
- 0); /* Parameter for notify message. */
-
- Now we're ready to issue an MCI_PLAY command
-
- mciSendCommand(SoundDevice.usSoundDeviceID, /* Device to play the waves. */
- MCI_PLAY, /* MCI message. */
- 0, /* Flags for the MCI message. */
- (PVOID) &mciOpenParameters, /* Parameters for the message. */
- 0); /* Parameter for notify message. */
-
- Notice that I neglected to send any flags for MCI_PLAY. It is advisable to
- send an MCI_WAIT if you want your sound to play to its entirety, but if
- MCI_WAIT is not sent, it is possible to receive another MCI_PLAY message while
- a prevoius sound is still playing. The resulting effect is that the prevoius
- sound is abruptly cut short and the new sound is played. Both have their place
- depending on how you want sounds to react to certain events.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.1.10. Closing the Sound Device ΓòÉΓòÉΓòÉ
-
- This is just to make sure we tie up all the loose ends and return the memory
- buffers to the free memory list.
-
- for( usCounter=0; usCounter<NUMBER_OF_WAVES; usCounter++ )
- free( (VOID *) SoundFiles[usCounter].Address);
-
- /* close sound device */
- mciSendCommand(usSoundDeviceID, /* Device to play the chimes. */
- MCI_CLOSE, /* MCI message. */
- MCI_WAIT, /* Flags for the MCI message. */
- (ULONG) NULL, /* Parameters for the message. */
- (ULONG) NULL ); /* Parameter for notify message. */
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.1.11. Conclusion ΓòÉΓòÉΓòÉ
-
- You should now have a basic understanding of how the MCI Command Interface
- works and hopefully be able to extend the supplied source code in the zipfile
- to fit your needs. Let's get some OS/2 apps with some dazzling sound effects
- out there!!
-
-
- ΓòÉΓòÉΓòÉ 2.2. Making Noise with MMPM/2 - Part 1 ΓòÉΓòÉΓòÉ
-
- Written by Marc van Woerkom
-
- Introduction
-
- Begining with version 2.1, IBM has delivered two new disks with OS/2 containing
- the previously separately sold Multimedia Presentation Manager/2 1.1 (MMPM/2).
- Having once visited a colleague who used a Mac and being enamoured with the
- characteristic <WHOOOSH!> emminating from the system speaker after he dropped
- some superfluous files on a toilet icon, I installed it to try out the system
- sounds on my SoundBlaster-equipped machine. Although it is quite satisfying in
- its own right that our beloved operating system is now able to cope with the
- Mac in this particular area, one should take the little extra time to have a
- closer look on the MMPM/2 for it has more to offer.
-
- This is the first of two articles which do so. It gives an overview of MMPM/2
- and introduces programming for multimedia, using the REXX language which is
- sufficient for many cases. The second article will deal with MMPM/2
- programming in C and C++. Both articles focus on the audio capabilites of
- MMPM/2.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.2.1. Introduction ΓòÉΓòÉΓòÉ
-
- Written by Marc van Woerkom
-
- Begining with version 2.1, IBM has delivered two new disks with OS/2 containing
- the previously separately sold Multimedia Presentation Manager/2 1.1 (MMPM/2).
- Having once visited a colleague who used a Mac and being enamoured with the
- characteristic <WHOOOSH!> emminating from the system speaker after he dropped
- some superfluous files on a toilet icon, I installed it to try out the system
- sounds on my SoundBlaster-equipped machine. Although it is quite satisfying in
- its own right that our beloved operating system is now able to cope with the
- Mac in this particular area, one should take the little extra time to have a
- closer look on the MMPM/2 for it has more to offer.
-
- This is the first of two articles which do so. It gives an overview of MMPM/2
- and introduces programming for multimedia, using the REXX language which is
- sufficient for many cases. The second article will deal with MMPM/2
- programming in C and C++. Both articles focus on the audio capabilites of
- MMPM/2.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.2.2. MMPM/2 Overview ΓòÉΓòÉΓòÉ
-
- Like its name suggests, the Multimedia Presentation Manager adds multimedia
- capabilities to IBM's OS/2 2.1 operating system, but what does this actually
- mean? For me, the term multimedia has acquired a slightly negative connotation
- in recent months because it is used so extensivly by computer hardware vendors
- to hawk their wares. They take a PC, put a soundboard and a CD-ROM drive into
- it, drape it with two tiny speakers and voila(!) it's perfect multimedia! The
- hardware is sold but the purpose is still rather vague.
-
- In the publication "OS/2 2.1 Technical Update" (GG24-3948-00) IBM writes:
-
- The key benefit of multimedia is an enhanced quality of information and
- communication. When audio, image, and video are combined with text and
- graphics, customers can access richer forms of information, and communication
- becomes more effective.
-
- This is not a bad definition. The variety of the types of data computers are
- asked to crunch has become richer over the years, starting with numbers and
- text, followed by graphics, and now also images (be it photos or live motion
- video) and audio data. The need for the capability to combine these different
- kinds of data is also crucial.
-
- IBM writes further:
-
- MMPM/2 enables this increase in productivity by providing device control,
- streaming, synchronization, and multimedia object I/O support.
-
- That is a brief summary of MMPM/2. The package consists of:
-
- Physical Device Drivers (PDD) A collection of drivers for multimedia hardware
- like soundcards, CD-ROM drives, video digitizers, video
- disc players, MIDI devices, etc.
-
- Media Control Interface (MCI) The MCI is a programming interface which allows
- the easy use of the media devices, handling them all in a
- common way via command strings like open, close, play,
- record, stop, seek, etc.
-
- Synchronization & Streaming Programming Interface (SPI) The SPI multimedia
- subsystem is responsible for the smooth execution of
- multimedia applications under OS/2. It ensures that those
- tasks get the needed processor time and synchronizes
- different streams of multimedia data.
-
- Multimedia I/O Services The MMIO subsystem extends the OS/2 file services. It
- provides a standard mechanism for accessing and
- manipulating multimedia data files (media elements).
- Supported are buffered I/O, RIFF file I/O, memory file I/O,
- compound file I/O; it is also possible to add additional
- I/O procedures to the MMIO subsystem.
-
- PM Extensions MMPM/2 provides some new Presentation Manager window
- classes tailored for the control of multimedia applications
- (graphical button, circular slider, etc.)
-
- Also several multimedia utilities are delivered with MMPM/2, like a CD audio
- player, a waveaudio player and editor, software video players and more.
-
- This first article only deals with the MCI because it is programmable from REXX
- in a very easy way and gives a nice impression of the MMPM/2 architecture. The
- other features will be treated in the second article.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.2.3. The Media Control Interface ΓòÉΓòÉΓòÉ
-
- If you have installed MMPM/2 you'll find on your harddisk a online manual about
- using the MCI from a REXX programmer's point of view.
-
- Note: this manual has many gaps! A more complete description can be found in
- the MMPM/2 Programming Reference of the OS/2 Technical Library (available in
- online form on the bookware CD-ROM).
-
- To allow interaction with the multimedia hardware MMPM/2 defines a set of media
- devices on top of the PDDs, which can be controled via the textual string
- commands of the Media Control Interface (MCI), defined by IBM and Microsoft in
- 1991.
-
- The standard media devices are:
-
- Device name Description
- CDaudio A CD-ROM device which supports standard audio compact disc
- playback
- Digitalvideo A device which supports audio/video files, either
- hardware-assisted or software motion video-only (e.g. it
- plays Intel Indeo and IBM Ultimotion *.AVI files)
- Sequencer A device which supports MIDI files (*.MID)
- Videodisc A videodisc player
- Videotape A videotape player or recorder
- Waveaudio A device which supports digital audio files (*.WAV)
-
- The MMPM/2 headers also define these devices:
-
- Device name Description
- Ampmix An amplifier/mixer device
- Animation A device for playing Autodesk Animator (*.FLC, *.FLI) and
- MacroMind Director files (*.MMM)
- Audiotape An analog audio tape deck
- CDXA A CD-ROM device which supports the extended architecture
- (XA) audio compact disc standard (up to 8h stereo, 15h
- music, 30h voice quality playback)
- DAT A digital audio tape deck
- Headphone Headphones
- Microphone A microphone
- Monitor A monitor output device
- Other An undefined MCI device
- Overlay A video overlay device - video output in a window
- Scanner An image scanner
- Speaker A speaker
-
- These media devices are modeled closely after their real-world pendants and
- their programming via the MCI command strings mirrors the actions one usually
- performs on a common audio or video component. Also the controls of the
- utilities delivered with the MMPM/2 have the same typical look and feel of
- their real-world counterparts.
-
- All devices are either of simple or compound type. Compound devices use data
- files (in MCI-speak: can load a device element in the device context), simple
- devices don't. CDaudio is a simple device, Waveaudio a compound one.
-
- Let's start with a simple example which should play a certain waveform audio
- file. The MCI command strings one needs for this are:
-
- open Waveaudio alias wave shareable wait
- load wave c:\mmos2\sounds\boing.wav wait
- play wave wait
- close wave wait
-
- First the Waveaudio device is opened in shareable mode (in MCI-speak: a device
- context is created) and it gets the alias wave assigned to it. Then using the
- alias to address the media device, the BOING.WAV file is loaded into memory. A
- play command is issued on wave and finally the device is closed. This also
- frees the memory assigned to the media device when loading the *.WAV file. The
- wait keyword tells MMPM/2 to wait until the command has finished before
- continuing.
-
- Now let's play the end from "U got the look" (track 1, 3.40) to the beginning
- of "if I was your girlfriend" (track 2, 0.20) from disc 2 of Prince's "Sign o'
- the Times" album:
-
- open CDaudio alias cd wait
- set cd time format tmsf wait
- play cd from 1:3:40 to 2:0:20 wait
- close cd wait
-
- The CDaudio device is opened and the alias cd is assigned to it. Next the
- media device is told to count in tracks, minutes, seconds, and frames.
- Finally, both tracks are played from the given positions and the device is
- closed.
-
- Note that the CDaudio device is directly manipulated; it is not necessary to
- load a data file.
-
- The third examples plays a software video slowed down to 25% speed. Note that
- in this mode audio is switched off.
-
- open digitalvideo alias video wait
- info video product wait
- load video some.avi wait
- set video speed format percentage wait
- play video speed 25 wait
- close video wait
-
- The digitalvideo device gets opened and video is the assigned alias. Then the
- device is queried for the information product; this will create a return string
- with some describing text. The file SOME.AVI gets loaded and the format for
- speed settings is set to percentages. The video is then played with 25% speed
- and the device is closed.
-
- I hope the basic recipe of a MCI script was conveyed from these examples. They
- illustrate that the different media devices are handled in similar manners.
- Armed with a MCI manual you should be able to write your own scripts.
-
- To send those command strings to the interpreting Media Device Manager (MDM)
- one can use either REXX, C/C++ or the String Test tool provided with the
- Multimedia Toolkit.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.2.4. Using the MCI from REXX ΓòÉΓòÉΓòÉ
-
- Now let's look how to realize those examples in REXX.
-
- Example 1:
-
- /************************************************************
- boing.cmd
-
- REXX procedure to play boing.wav via MMPM
-
-
- Marc E.E. van Woerkom, 1/94
- ************************************************************/
-
-
- rc = call RxFuncAdd('mciRxInit', 'MCIAPI',, /* register MCI REXX API */
- 'mciRxInit')
- rc = call mciRxInit() /* and initialize it */
-
-
- rc = mciRxSendString('open waveaudio', /* open digital audio */
- 'alias wave shareable wait',,
- 'RetStr', '0', '0')
-
- boing = 'c:\mmos2\sounds\boing.wav'
-
- rc = mciRxSendString('load wave' boing,, /* load wav */
- 'RetStr', '0', '0')
-
- rc = mciRxSendString('play wave wait',, /* play file */
- 'RetStr', '0', '0')
-
- rc = mciRxSendString('close wave wait',, /* close device "wave" */
- 'RetStr', '0', '0')
-
- call mciRxExit
-
- exit rc /* exit with rc code */
-
- The first step in BOING.CMD is to register the MCI REXX application interface
- using the call to RxFuncAdd() and then by initializing it via a call to
- mciRxInit().
-
- Statements of the form mciRxSendString('some_MCI_command',RetStr,'0','0') send
- the MCI command string to the MDM and may receive a return string in the RetStr
- variable.
-
- Business finishes with a call to mciRxExit().
-
- Example 2:
-
- /************************************************************
- prince.cmd
-
- REXX procedure to play some audio CD tracks via MMPM
-
-
- Marc E.E. van Woerkom, 1/94
- ************************************************************/
-
-
- rc = call RxFuncAdd('mciRxInit', 'MCIAPI',, /* register MCI REXX API */
- 'mciRxInit')
- rc = call mciRxInit() /* and initialize it */
-
-
- rc = mciRxSendString('open CDaudio', /* open CDaudio */
- 'alias cd wait',,
- 'RetStr', '0', '0')
-
- rc = mciRxSendString('set cd time format', /* count in tracks */
- 'tmsf wait ',, /* (tt:mm:ss:ff) */
- 'RetStr', '0', '0')
- say rc
-
- rc = mciRxSendString('play cd from 1:3:40 to 2:0:20 wait',, /* play file */
- 'RetStr', '0', '0')
- if rc <> 0 then do
- rc2 = mciRxGetErrorString(rc, 'ErrStVar')
- say 'ERROR! rc =' rc ', ErrStVar =' ErrStVar
- exit rc2
- end
-
- rc = mciRxSendString('close cd wait',, /* close device "cd" */
- 'RetStr', '0', '0')
- say rc
-
- call mciRxExit
-
- exit rc /* exit with rc code */
-
- This example also uses the function mciRxGetErrorString() to receive the error
- message belonging to the return value RC.
-
- Example 3:
-
- /************************************************************
- playvid.cmd
-
- REXX procedure to play a software video at 25% speed
- usage: pmrexx playvid name.avi
-
- Marc E.E. van Woerkom, 1/94
- ************************************************************/
-
- parse arg avi /* fetch first argument */
-
- if avi = '' then do
- say 'usage: pmrexx playvid name.avi'
- exit 0
- end
-
- say '(Playing a Software Video at 25% speed ...'
-
- rc = call RxFuncAdd('mciRxInit', 'MCIAPI',, /* register MCI REXX API */
- 'mciRxInit')
- rc = call mciRxInit() /* and initialize it */
-
- rc = mciRxSendString('open digitalvideo alias', /* open player */
- 'video wait',,
- 'RetStr', '0', '0')
-
- rc = mciRxSendString('info video', /* ask for product info */
- 'product wait',,
- 'videoinfo', '0', '0')
-
- say ' Digital Video Device is' videoinfo
-
- rc2 = mciRxSendString('load video' avi,, /* play *.avi */
- 'RetStr', '0', '0')
-
- rc = mciRxSendString('set video speed', /* set speed in % */
- 'format percentage wait',,
- 'RetStr', '0', '0')
-
- rc = mciRxSendString('play video speed 25 wait',, /* play file */
- 'RetStr', '0', '0')
-
- rc = mciRxSendString('close video wait',, /* close video device */
- 'RetStr', '0', '0')
- if rc2 <> 0 then do
- rc = mciRxGetErrorString(rc2, 'ErrStVar')
- say ' ERROR! rc =' rc2 ', ErrStVar =' ErrStVar
- say ' ... failed!)'
- exit rc
- end
- else say ' ... done.)'
-
- call mciRxExit
-
- exit rc /* exit with rc code */
-
- This one starts with a parsing of the command line arguments. Later the
- example queries the digitalvideo device for a product description string which
- is delivered into the videoinfo variable.
-
- Note that the digitalvideo device needs the Presentation Manager, so if this
- REXX procedure is to be used from the command line, one has to call it through
- PMREXX.
-
- Example 4:
-
- /************************************************************
- cdlock.cmd
-
- REXX procedure to lock the CD device
- usage: cdlock
-
-
- Marc E.E. van Woerkom, 11/93
- ************************************************************/
-
-
- say '(Locking the CD drive''s door ...'
-
- rc = call RxFuncAdd('mciRxInit', 'MCIAPI',, /* register MCI REXX */
- 'mciRxInit')
- rc = call mciRxInit() /* and initialize it */
-
- rc = mciRxSendString('open CDaudio alias cd', /* open CDaudio */
- 'shareable wait',,
- 'RetStr', '0', '0')
-
- rc2 = mciRxSendString('set cd door locked wait',, /* lock CD door */
- 'RetStr', '0', '0')
-
- rc = mciRxSendString('close cd wait',, /* close device "cd" */
- 'RetStr', '0', '0')
-
- if rc2 <> 0 then
- say ' ... failed!)'
- else
- say ' ... done.)'
-
- call mciRxExit
-
- exit rc2 /* exit with rc2 code */
-
- Did you notice already the menu item lock disk in the context menu of the
- CD-ROM drive icon? As does that menu item, this procedure locks the door of
- the CD-ROM drive so that it can't be opened with a push on the external
- open/close button.
-
- Example 5:
-
- /************************************************************
- cdunlock.cmd
-
- REXX procedure to unlock the CD device
- usage: cdunlock
-
-
- Marc E.E. van Woerkom, 11/93
- ************************************************************/
-
-
- say '(Unlocking the CD drive''s door ...'
-
- rc = call RxFuncAdd('mciRxInit', 'MCIAPI',, /* register MCI API */
- 'mciRxInit')
- rc = call mciRxInit() /* and initialize it */
-
- rc = mciRxSendString('open CDaudio alias cd', /* open CDaudio */
- 'shareable wait',,
- 'RetStr', '0', '0')
-
- rc2 = mciRxSendString('set cd door unlocked wait',, /* unlock CD door */
- 'RetStr', '0', '0')
-
- rc = mciRxSendString('close cd wait',, /* close device "cd" */
- 'RetStr', '0', '0')
-
- if rc2 <> 0 then
- say ' ... failed!)'
- else
- say ' ... done.)'
-
- call mciRxExit
-
- exit rc2 /* exit with rc2 code */
-
- This procedure unlocks the CD-ROM drive, reenabling the external open/close
- button.
-
- Example 6:
-
- /************************************************************
- cdopen.cmd
-
- REXX procedure to open the CD door
- usage: cdopen
-
-
- Marc E.E. van Woerkom, 11/93
- ************************************************************/
-
-
- say '(Opening the CD drive''s door ...'
-
- rc = call RxFuncAdd('mciRxInit', 'MCIAPI',, /* register MCI REXX API */
- 'mciRxInit')
- rc = call mciRxInit() /* and initialize it */
-
- rc = mciRxSendString('open CDaudio alias cd', /* open CDaudio */
- 'shareable wait',,
- 'RetStr', '0', '0')
-
- rc2 = mciRxSendString('set cd door open wait',, /* open CD door */
- 'RetStr', '0', '0')
-
- rc = mciRxSendString('close cd wait',, /* close device "cd" */
- 'RetStr', '0', '0')
-
- if rc2 <> 0 then
- say ' ... failed!)'
- else
- say ' ... done.)'
-
- call mciRxExit
-
- exit rc2 /* exit with rc2 code */
-
- This procedure opens the door of your CD-ROM drive and ejects the tray. I
- admit, it is a bit drole (somehow it reminds me of little R2-D2 from Star Wars)
- but I'd be grateful if someone points out a serious application of this to me.
-
- Example 7:
-
- /************************************************************
- playall.cmd
-
- REXX procedure to play all *.wav *.mid
- usage: playall directory [wav] [midi] [rec]
-
-
- Marc E.E. van Woerkom, 1/94
- ************************************************************/
-
-
- parse arg dir arg1 arg2 arg3 /* copy argv[1] and remove */
-
- say
- if dir = '' then do
- say 'PlayAll seeks and plays all soundfiles'
- say
- say 'usage: playall directory [wav] [midi] [rec]'
- exit 0
- end
-
- if dir = '.' then dir = directory() /* current dir */
-
-
- dowav = 0
- domidi = 0
- dorec = 0
-
-
- parse upper arg dummy arg1 arg2 arg3 /* copy argv[1..3] */
-
- if arg1 = 'WAV' then dowav = 1
-
- if arg1 = 'MIDI' then domidi = 1
- if arg2 = 'MIDI' then domidi = 1
-
- if arg1 = 'REC' then dorec = 1
- if arg2 = 'REC' then dorec = 1
- if arg3 = 'REC' then dorec = 1
-
-
- if (\dowav) & (\domidi) then do
- dowav = 1
- domidi = 1
- end
-
-
- files = '' /* message */
- if dowav then files = ' *.wav'
- if domidi then files = files' *.mid'
-
-
- opt = 'O' /* dir tree options */
- if dorec then do /* F: files only */
- opt = opt'S' /* O: only qualified name */
- recstr = 'and beneath ' /* S: recurse */
- end
- else recstr = ''
-
-
- say '(Playing all'files 'files from' dir recstr'...'
- say
-
-
- rc = call RxFuncAdd('SysLoadFuncs', 'RexxUtil',, /* register REXX Utils */
- 'SysLoadFuncs')
- rc = call SysLoadFuncs() /* and initialize them */
-
-
- rc = call RxFuncAdd('mciRxInit', 'MCIAPI',, /* register MCI REXX API */
- 'mciRxInit')
- rc = call mciRxInit() /* and initialize it */
-
-
- if dowav then do
- rc = mciRxSendString('open waveaudio', /* open digital audio */
- 'alias wave shareable wait',,
- 'RetStr', '0', '0')
- if rc <> 0 then do
- rc2 = mciRxGetErrorString(rc, 'ErrStVar')
- say 'ERROR! rc =' rc ', ErrStVar =' ErrStVar
- exit rc2
- end
-
- rc = mciRxSendString('info wave', /* ask for product info */
- 'product wait',,
- 'waveinfo', '0', '0')
-
- rc = mciRxSendString('close wave wait',, /* close device "wave" */
- 'RetStr', '0', '0')
-
- say ' Waveaudio device is' waveinfo
- end
-
-
- if domidi then do
- rc = mciRxSendString('open sequencer', /* open MIDI device */
- 'alias midi2 shareable wait',,
- 'RetStr', '0', '0')
-
- if rc <> 0 then do
- rc2 = mciRxGetErrorString(rc, 'ErrStVar')
- say 'ERROR! rc =' rc ', ErrStVar =' ErrStVar
- exit rc2
- end
-
- rc = mciRxSendString('info midi2', /* ask for product info */
- 'product wait',,
- 'midi2info', '0', '0')
-
- rc = mciRxSendString('close midi2 wait',, /* close device "midi" */
- 'RetStr', '0', '0')
-
- say ' MIDI device is' midi2info
- end
-
- if Right(dir, 1) = '\' then
- mask = dir'*.*'
- else
- mask = dir'\*.*'
-
- rc = call SysFileTree(mask, 'file', opt) /* read dir tree */
-
- if file.0 = 0 then do
- say ' No files found!'
- rc = 0
- end
-
- do i=1 to file.0
- ext = Translate(Right(file.i, 4))
-
- if dowav & (ext = '.WAV') then do
- say
- say ' File' file.i':'
-
- rc = mciRxSendString('open waveaudio', /* open digital audio */
- 'alias wave shareable wait',,
- 'RetStr', '0', '0')
-
- rc = mciRxSendString('set wave time', /* set time format to ms */
- 'format ms wait',,
- 'RetStr', '0', '0')
-
- rc = mciRxSendString('load wave' file.i 'wait',, /* play *.wav */
- 'RetStr', '0', '0')
-
- rc = mciRxSendString('status wave length wait',, /* query length */
- 'RetStr', '0', '0')
-
- if rc = 0 then do
- time = RetStr / 1000.0
- say ' Playing time is' time 's'
- end
-
- rc = mciRxSendString('play wave wait',, /* play file */
- 'RetStr', '0', '0')
-
- rc = mciRxSendString('close wave wait',, /* close device "wave" */
- 'RetStr', '0', '0')
- end
-
- if domidi & (ext = '.MID') then do
- say
- say ' File' file.i':'
-
- rc = mciRxSendString('open sequencer', /* open MIDI device */
- 'alias midi2 shareable wait',,
- 'RetStr', '0', '0')
-
- rc = mciRxSendString('set midi2 time', /* set time format to ms */
- 'format ms wait',,
- 'RetStr', '0', '0')
-
- rc = mciRxSendString('load midi2' file.i 'wait',, /* play *.mid */
- 'RetStr', '0', '0')
-
- rc = mciRxSendString('status midi2 length wait',, /* query length */
- 'RetStr', '0', '0')
- if rc = 0 then do
- time = RetStr / 1000.0
- say ' Playing time is' time 's'
- end
-
- rc = mciRxSendString('play midi2 wait',, /* play file */
- 'RetStr', '0', '0')
-
- rc = mciRxSendString('close midi2 wait',, /* close device "midi" */
- 'RetStr', '0', '0')
- end
- end
-
- say
- if rc = 0 then
- say '... done.)'
- else
- say '... playing aborted!)'
-
- call mciRxExit
-
- exit rc /* exit with rc code */
-
- I wrote this procedure to try out a whole directory of audio files at once. To
- play all *.WAV files from the current directory and all directories under it,
- type playall . wav rec on the command line.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.2.5. Literature on MMPM/2 ΓòÉΓòÉΓòÉ
-
- Dick Conklin (Ed.):
- OS/2 2.x Notebook - The Best of OS/2 Developer Magazine.
- Van Nostrand Reinhold, New York, 1993.
-
- IBM Doc. S41G-2919:
- MMPM/2 Programming Guide.
- "Describes application and subsystem programming interfaces to help you
- select and implement functions for OS/2 multimedia applications"
-
- IBM Doc. S41G-2920:
- MMPM/2 Programming Reference.
- "Provides detailed information on multimedia functions, messages, and data
- structures to enable you to write code for multimedia applications"
-
- IBM Doc. S41G-2921:
- MMPM/2 Sample Programs Workbook.
- "Gives code examples from MMPM/2 sample application programms and
- templates for subsystem components"
-
- IBM Doc. S41G-2922:
- CUA Guide to Multimedia User Interface Design.
- "Describes design concepts to consider when designing a CUA multimedia
- interface"
-
- IBM Doc. S41G-2923:
- OS/2 Multimedia Advantages.
- "Describes advantages offered by OS/2 and MMPM/2 multimedia platform"
-
- IBM Doc. S41G-3321:
- Complete MMPM/2 Technical Library.
-
- John Musser:
- A Multimedia Class Library for Windows.
- Dr. Dobb's Journal, 7:84-90, 1993.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.3. Utilizing Hooks for Added Capabilities ΓòÉΓòÉΓòÉ
-
- Written by Larry Salomon, Jr.
-
- Introduction
-
- Beginning with the MVS operating system, user exits have existed in order to
- give those with a bit of programming skill the opportunity to change the
- default behavior for a particular event. Since then, these have evolved into
- system hooks, which provide the same ability; unfortunately, whether known as
- user exits or system hooks, there has been a lack of documentation on how to
- utilize these provisions from with OS/2 applications.
-
- This article will discuss what hooks are, what the different types of hooks
- are, and the uses of some of the various types of hooks. Finally, we will look
- at a (new) version of Life Saver, which uses one of the hooks to implement a
- screen saver.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.3.1. Introduction ΓòÉΓòÉΓòÉ
-
- Written by Larry Salomon, Jr.
-
- Beginning with the MVS operating system, user exits have existed in order to
- give those with a bit of programming skill the opportunity to change the
- default behavior for a particular event. Since then, these have evolved into
- system hooks, which provide the same ability; unfortunately, whether known as
- user exits or system hooks, there has been a lack of documentation on how to
- utilize these provisions from with OS/2 applications.
-
- This article will discuss what hooks are, what the different types of hooks
- are, and the uses of some of the various types of hooks. Finally, we will look
- at a (new) version of Life Saver, which uses one of the hooks to implement a
- screen saver.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.3.2. Going Fishing? ΓòÉΓòÉΓòÉ
-
- What is a hook? In the sport of fishing, a hook attaches the fishing line) to
- the fish so that the line follows the movement of the fish. In programming,
- the concept is similar - you use a hook to attach your application to an event
- so that your application knows whenever the event occurs. Why is this useful?
- Consider an application that needs to know when the system is rebooting, or
- needs to know whenever the PrintScreen key was pressed (regardless of who has
- the focus). These types of things can only be done with hooks.
-
- OS/2 defines 17 types of hooks that can be set.
-
- Send message event (HK_SENDMSG) The hook is invoked whenever WinSendMsg() is
- called.
-
- Input event (HK_INPUT) The hook is invoked whenever WinPostMsg() is
- called, including - and especially - for all mouse and
- keyboard input.
-
- Message filter (HK_MSGFILTER) The hook is invoked during any Win function
- which enters a message loop, e.g. WinDlgBox(),
- WinMessageBox(), etc.
-
- Journal record (HK_JOURNALRECORD) The hook is invoked to record (journal)
- a message.
-
- Journal playback (HK_JOURNALPLAYBACK) The hook is invoked to play back a
- journaled message.
-
- Help event (HK_HELP) The hook is invoked whenever help is requested
- by the user or an application.
-
- Library and procedure event (HK_LOADER) The hook is invoked whenever
- WinLoadLibrary() or WinLoadProcedure() is called.
-
- User message registration event (HK_REGISTERUSERMSG) The hook is invoked
- whenever WinRegisterUserMsg() is called.
-
- Message control event (HK_MSGCONTROL) The hook is invoked whenever any of the
- following four functions are called:
- WinSetClassMsgInterest(), WinSetMsgInterest(),
- WinSetMsgMode(), and WinSetSynchroMode().
-
- Find word event (HK_FINDWORD) The hook is invoked whenever a word to be
- drawn by the WinDrawText() function is too long to fit
- within the bounding rectangle.
-
- Code page change event (HK_CODEPAGECHANGED) The hook is invoked whenever a
- message queue's code page changes.
-
- Device context association event (HK_WINDOWDC) The hook is invoked whenever a
- device context is associated with a window.
-
- Window destroy event (HK_DESTROYWINDOW) The hook is invoked whenever a window
- is destroyed.
-
- Message filter event (HK_CHECKMSGFILTER) The hook is invoked whenever
- WinGetMsg() or WinPeekMsg() is called.
-
- Input insertion (HK_MSGINPUT) The hook is used to insert mouse or keyboard
- messages similar to the journal playback hook.
-
- System lockup event (HK_LOCKUP) The hook is invoked whenever WinLockupSystem()
- is called.
-
- System shutdown event (HK_FLUSHBUF) The hook is invoked whenever the
- Ctrl-Alt-Del key sequence is pressed.
-
- The user message registration event is generated by a function that is no
- longer documented. The WinRegisterUserMessage() function existed in the 1.3
- Toolkit documentation but does no longer.
-
- Two other hooks - HK_PLIST_ENTRY and HK_PLIST_EXIT - are also defined in
- pmwin.h but they do not exist within the system (as far as I know).
-
- Hook Contexts
-
- Some hooks can be defined as application-wide or system-wide; the difference is
- that an application-wide hook will be called whenever the event occurs within
- the application, while a system-wide hook will be called whenever the event
- occurs anywhere within the system. Because of this property of system-wide
- hooks, the corresponding hook procedure must reside within a DLL. This has
- other interesting connotations which we will see later.
-
- Some hooks must be application-wide, while others must be system-wide. Such
- hooks do not have a choice.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.3.3. Chaining Hooks ΓòÉΓòÉΓòÉ
-
- In reality, a hook is nothing more than a function that accepts one or more
- arguments. The function is called by the system, meaning that the function
- must be exportable and use the _System calling convention. Additionally, the
- system must be made aware of the hook's existance; it is registered via the
- function WinSetHook(). Similarly, when the hook is to be removed, the function
- WinReleaseHook() is called.
-
- Figure 1) Before and after calling WinSetHook().
-
- BOOL WinSetHook(HAB habAnchor,
- HMQ hmqQueue,
- LONG lHookType,
- PFN pfnHook,
- HMODULE hmModule);
- BOOL WinReleaseHook(HAB habAnchor,
- HMQ hmqQueue,
- LONG lHookType,
- PFN pfnHook,
- HMODULE hmModule);
-
- Both function take the following parameters:
-
- habAnchor Anchor block of the calling thread.
- hmqQueue Handle of the message queue to install the hook on. This
- can also be HMQ_CURRENT to specify the message queue of the
- calling thread, or NULLHANDLE if this is to be a
- system-wide queue.
- lHookType One of the HK_* constants (see the first section).
- pfnHook Pointer to the hook procedure.
- hmModule Handle to the DLL containing the hook procedure. This must
- be specified if hmqQueue is NULLHANDLE.
-
- Functions for the various hook types are chained together, which allows for
- multiple hook functions to be installed for a particular hook type. The
- WinSetHook() call installs the hook function at the head of the chain.
-
- Figure 2) Multiple hooks of the same type. The hook on top was installed last
- using WinSetHook().
-
- This is important because some Win functions install hooks themselves, such as
- the WinCreateHelpInstance() function. Since the help hook installed by this
- function indicates to the system that no other hooks after it should be called,
- any help hooks you install before calling this function will never see any help
- events.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.3.4. Two Examples ΓòÉΓòÉΓòÉ
-
- A Small Example
-
- Consider the application provided as hooks.zip. It is nothing more than a
- dialog box with two hooks attached - the associate window HDC hook, and the
- destroy window hook. Whenever either hook is invoked, they beep if the window
- provided is a frame window.
-
- BOOL EXPENTRY assocWindowHook(HAB habAnchor,
- HDC hdcContext,
- HWND hwndAssoc,
- BOOL bAssoc)
- //-------------------------------------------------------------------------
- // This hook is invoked whenever an HDC is associated or disassociated with
- // a window.
- //
- // Input: habAnchor - anchor block of the thread in whose context the
- // event occurred.
- // hdcContext - handle of the device context.
- // hwndAssoc - handle of the window associated/disassociated.
- // bAssoc - TRUE if hwndAssoc is associated with hdcContext. FALSE
- // otherwise.
- // Returns: TRUE if successful processing, FALSE otherwise.
- //-------------------------------------------------------------------------
- {
- CHAR achClass[256];
-
- WinQueryClassName(hwndAssoc,sizeof(achClass),achClass);
-
- if ((WinFindAtom(WinQuerySystemAtomTable(),
- achClass)==LOUSHORT(WC_FRAME)) && bAssoc) {
- WinAlarm(HWND_DESKTOP,WA_NOTE);
- } /* endif */
-
- return TRUE;
- }
-
- BOOL EXPENTRY destroyWindowHook(HAB habAnchor,
- HWND hwndDestroy,
- ULONG ulReserved)
- //-------------------------------------------------------------------------
- // This hook is invoked whenever a window is destroyed.
- //
- // Input: habAnchor - anchor block of the thread in whose context the
- // event occurred.
- // hwndDestroy - handle of the window being destroyed.
- // ulReserved - reserved.
- // Returns: TRUE if successful processing, FALSE otherwise.
- //-------------------------------------------------------------------------
- {
- CHAR achClass[256];
-
- WinQueryClassName(hwndDestroy,sizeof(achClass),achClass);
-
- if (WinFindAtom(WinQuerySystemAtomTable(),achClass)==LOUSHORT(WC_FRAME)) {
- WinAlarm(HWND_DESKTOP,WA_ERROR);
- } /* endif */
-
- return TRUE;
- }
-
- Figure 3) Two simple hook procedures.
-
- The hooks are installed, as we noted, using the WinSetHook() call and are
- released using the WinReleaseHook() call.
-
- WinSetHook(habAnchor,
- HMQ_CURRENT,
- HK_WINDOWDC,
- (PFN)assocWindowHook,
- NULLHANDLE);
- WinSetHook(habAnchor,
- HMQ_CURRENT,
- HK_DESTROYWINDOW,
- (PFN)destroyWindowHook,
- NULLHANDLE);
- :
- :
- WinReleaseHook(habAnchor,
- HMQ_CURRENT,
- HK_WINDOWDC,
- (PFN)assocWindowHook,
- NULLHANDLE);
- WinReleaseHook(habAnchor,
- HMQ_CURRENT,
- HK_DESTROYWINDOW,
- (PFN)destroyWindowHook,
- NULLHANDLE);
-
- Figure 4) WinSetHook() and WinReleaseHook() calls.
-
- Note that the hooks are local to the message queue for the thread. Also, note
- how the hook procedures are cast to the generic type PFN to avoid
- warnings/errors by the compiler.
-
- A Large Example
-
- Now, look at the application provided in life.zip. This is a screen saver
- which utilizes the input hook to determine when a period of inactivity elapses;
- actually, the word should be "expires" since the input hook is invoked when
- there is no inactivity. The strategy for the application is to start a timer
- using WinStartTimer() to count the number of seconds of inactivity. Whenever
- the input hook is invoked, it tells the application to reset this counter.
- Should the threshold be reached, the screen saver goes into effect.
-
- There are a couple of interesting points to be made:
-
- o The hook is a system-wide hook. Since this means it can be invoked from
- within the context of any process, it must reside in a DLL.
-
- o Since the hook and the application need to share data but the DLL needs to
- access it from within any process' context, the data must be declared in the
- DLL and a pointer to it is kept by the application. Additionally, the DLL
- has the attributes DATA SHARED SINGLE specified in the .DEF file to indicate
- that all processes share the same copy of the DLL's data segment.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.3.5. Summary ΓòÉΓòÉΓòÉ
-
- Hooks provide a way to code certain types of logic into your application, which
- could not be done (easily) in other ways. While they are very useful, care
- must be taken because, in the case of system-wide hooks, what you do affects
- the entire system. For example, in a send message hook, you wouldn't use
- WinSendMsg() to notify your application, or else you would have an endless
- loop. For all development involving hooks, one must be sure that the design is
- completely worked out, or you could spend fruitless hours debugging your
- hook...and these are not easy beasts to debug, either.
-
- Probably the worst detriment during development is the lack of good
- documentation. Many of the hooks are scantily documented in pmwin.h at best,
- while others have incorrect parameters listed, making things even more
- difficult. If you have access to CompuServe, the best help you can get is from
- the IBMers that reside there, since they have been using many of these hooks
- since their first appearance.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 3. Columns ΓòÉΓòÉΓòÉ
-
- The following columns can be found in this issue:
-
- o C++ Corner
- o Introduction to PM Programming
- o Scratch Patch
-
-
- ΓòÉΓòÉΓòÉ 3.1. C++ Corner ΓòÉΓòÉΓòÉ
-
- Written by Gordon Zeglinski
-
- Introduction
-
- Welcome to the latest EDM/2 feature, The C++ Corner. As the title suggests,
- this column is dedicated to exploring various aspects of using C++ to make OS/2
- programming easier. I have taken the liberty of assuming that the reader has a
- good working knowledge of C++ concepts and has C++ coding experience.
-
- So What's On The Menu For This Issue?
-
- We'll start the column off by looking at some of the basics of the User
- Interface Class Library (UICL for short) provided by IBM and included with the
- C-Set++ compiler product. Although, this library is C-Set++ specific, I will
- try to make this interesting for everyone by touching on some of the design
- aspects behind the library and discussing generic C++ considerations.
- Additionally, since the best way of learning is by example, we will look at
- extending the library by adding a generic dialog class.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 3.1.1. Introduction ΓòÉΓòÉΓòÉ
-
- Written by Gordon Zeglinski
-
- Welcome to the latest EDM/2 feature, The C++ Corner. As the title suggests,
- this column is dedicated to exploring various aspects of using C++ to make OS/2
- programming easier. I have taken the liberty of assuming that the reader has a
- good working knowledge of C++ concepts and has C++ coding experience.
-
- So What's On The Menu For This Issue?
-
- We'll start the column off by looking at some of the basics of the User
- Interface Class Library (UICL for short) provided by IBM and included with the
- C-Set++ compiler product. Although, this library is C-Set++ specific, I will
- try to make this interesting for everyone by touching on some of the design
- aspects behind the library and discussing generic C++ considerations.
- Additionally, since the best way of learning is by example, we will look at
- extending the library by adding a generic dialog class.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 3.1.2. The Basics of Event Handling ΓòÉΓòÉΓòÉ
-
- What Are Events?
-
- The UICL uses the concept of events to encapsulate messages sent by the various
- PM controls. The base object in the event hierarchy is called the IEvent
- class. When looking at the UICL, one should always keep in mind the basics of
- the underlying PM API; even though the encapsulation of the PM message
- structure is done to a level which could allow the UICL to be ported to
- different platforms, it is still firmly rooted in PM.
-
- PM messages are composed of three 32-bit numbers: the first is the message
- identifier, while the remaining two are used as the message data. Depending
- upon the message identifier, the message data can have many different
- interpretations. Part of the event encapsulation relates the message
- identifier to the specific meaning of the other two numbers.
-
- Events are grouped into classes based on semantic relationships (i.e.
- menu-related, notification, etc.) or originator (i.e. container) and each event
- class is processed by one or more member functions denoted event handlers. The
- base handler class is called IHandler and it contains the virtual member
- function dispatchHandlerEvent(). When a window has one or more handlers
- associated with it, the primary dispatch function calls dispatchHandlerEvent()
- for each registered handler. (See my article C++ Encapsulation of PM in volume
- 1 issue 4, for the basic design goals, and a definition of the primary dispatch
- function). If true is returned, the event is considered processed; otherwise,
- the next handler is invoked, and so on until true is returned from
- dispatchHandlerEvent().
-
- Planes, Trains, Events and Handlers
-
- Well planes and trains don't really fit with the UICL, but the title sounded
- good :). To understand handlers, let's use the ICommandHandler class as an
- example. Following is a stripped down version of the class definition given in
- icmdhdr.hpp.
-
- class ICommandHandler : public IHandler {
- typedef IHandler
- Inherited;
-
- public:
- ICommandHandler();
- virtual ~ICommandHandler();
- Boolean dispatchHandlerEvent( IEvent& event );
-
- protected:
- virtual Boolean command( ICommandEvent& event );
- virtual Boolean systemCommand( ICommandEvent& event );
- };
-
- When called, the function dispatchHandlerEvent() acts like a filter. It checks
- the message ID of the event and calls the appropriate virtual function. If the
- message is WM_COMMAND, it returns the value returned by the virtual function
- command(); if the message is WM_SYSCOMMAND, it returns the value returned by
- the virtual function systemCommand(). Otherwise, it returns the value false
- indicating that the next registered handler should be invoked.
-
- WM_COMMAND
- ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
- ΓöîΓöÇ Γöécommand()Γöé
- ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ virtual ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ virtualΓöé ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
- ΓöéPrimary DispatchΓöé ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ ΓöédispatchHandlerEvent()Γöé ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- Γöé Function Γöé call ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ call Γöé ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
- ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ ΓööΓöÇ ΓöésystemCommand()Γöé
- ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
- WM_SYSCOMMAND
-
- Figure 1) Event Dispatch Function Chain
-
- What's This ICommandEvent Object?
-
- A quick peek at the PM reference, yields the following information about the
- WM_COMMAND and WM_SYSCOMMAND messages:
-
- param1
- USHORT usCmd Command value.
-
- param2
- USHORT usSource Source type.
- USHORT usPointer Pointing-device indicator.
-
- returns
- ULONG flReply Reserved.
-
- The ICommandEvent allows access to the value in param1 through the member
- function commandId(). Similarly, the member function source() allows access to
- usSource although the value returned is also abstracted in order to be
- independent of the PM definitions for the CMDSRC_* constants. So not only does
- the dispatchHandlerEvent() function act as a filter, it also transforms generic
- IEvent's into specific event objects, as in the ICommandEvent example.
-
- Notice the flReply variable above. It is set using the IEvent member function
- setResult(). This is different than the way a window procedure in C returns
- flReply to PM.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 3.1.3. Moving On Up ΓòÉΓòÉΓòÉ
-
- One thing "missing" from the UICL is a dialog window object. It isn't really
- missing, it's just not packaged as a dialog class. Dialog windows pose
- interesting encapsulation problems for the class designers - do they allow the
- class users to use a PM-like method of communicating to the controls? Or do
- they force you to create instances of C++ objects that mirror the controls?
- The UICL uses the second approach. Being somewhat of a rebel, I decided that I
- didn't like that approach I created the IDialogWindow class. The IDialogWindow
- class is specifically designed to break the predefined rules of encapsulation.
- The price you pay is that the application becomes tightly bound to the PM
- messaging structure. Following is the class definition for IDialogWindow.
-
- Note: _exp_ is used to toggle the header file between "DLL" mode and "EXE"
- mode.
-
- class IDialogWindow: public IFrameWindow, public IFrameHandler {
-
- public:
- _exp_ IDialogWindow(unsigned long ID);
- _exp_ IDialogWindow(unsigned long ID, IWindow *Parent, IWindow *Owner);
- _exp_ ~IDialogWindow();
-
- IDialogWindow& _exp_ setItemText(unsigned long ID, char *Text);
- IDialogWindow& _exp_ sendItemMessage(unsigned long ID,
- unsigned long iMess,
- unsigned long mp1=0,
- unsigned long mp2=0);
- IDialogWindow& _exp_ sendItemMessage(unsigned long ID,
- unsigned long iMess,
- unsigned short int ch1,
- unsigned short int ch2,
- unsigned long mp2=0);
- IDialogWindow& _exp_ sendItemMessage(unsigned long ID,
- unsigned long iMess,
- void* mp1,
- void* mp2=0);
-
- IDialogWindow& _exp_ queryItemText(unsigned long ID,
- unsigned long maxLen,
- char *retBuf);
- unsigned long _exp_ queryItemTextLength(unsigned long ID);
-
- protected:
- Boolean _exp_ dispatchHandlerEvent(IEvent& evt);
- };
-
- The member functions mimic the WinSetDlgItemText(), WinQueryDlgItemText(),
- WinSendDlgItemMsg(), etc.. functions. The IDialogWindow class is derived from
- both the IFrameWindow and the IFrameHandler object. If you are already
- familiar with the UICL, you know that the IFrameWindow class can create dialog
- windows if the appropriate constructor is called. Thus, it makes sense that we
- would subclass it. But why is the IFrameHandler class used as a parent? The
- answer is easy - the IDialogWindow has the added feature of hiding controls
- that may obscure the icon when the window is minimized. In order to be
- notified whenever we are minimized, we need to override the
- IFrameHandler::dispatchHandlerEvent() member function. An alternative method,
- would have been to create another handler class that would take care of this
- case.
-
- Peeking at the Constructors
-
- The constructors for IDialogWindow merely call the correct IFrameWindow
- constructor, and register the IDialogWindow instance as a handler.
-
- IDialogWindow::IDialogWindow(unsigned long ID) :
- IFrameWindow(IResourceId(ID))
- {
- handleEventsFor(this);
- }
-
- IDialogWindow::IDialogWindow(unsigned long ID,
- IWindow *Parent,
- IWindow *Owner) :
- IFrameWindow(IResourceId(ID),Parent,Owner)
- {
- handleEventsFor(this);
- }
-
- Breaking the Rules
-
- As I said in the beginning of this section, the IDialogWindow is specifically
- designed to break some of the original design choices. In keeping with this
- rebellious spirit, we break yet another design decision. Way back, when we
- looked at handlers and events, we seen that one of the tasks a handler
- performed was to repackage the event and then pass it on to a specific virtual
- function that would do the actual processing of the event. The PM message we
- are after here is WM_MINMAXFRAME; this message is sent every time the window is
- minimized, restored, or maximized. If we were to follow with the original
- design, we should have created a new event object to encapsulate this PM
- message. But because we only want to process this message in the IDialogWindow
- object, we take a short cut. Following is the modified dispatchHandlerEvent()
- function:
-
- Boolean IDialogWindow::dispatchHandlerEvent(IEvent& evt)
- {
- HWND hwnd;
- HENUM henum;
- PSWP pswp;
- Boolean ShowState;
- SWP winPos,Pos;
- int cx,cy;
-
- // check the message
- if (evt.eventId()==WM_MINMAXFRAME) {
- // start the child window enumeration
- henum=WinBeginEnumWindows(handle());
-
- //extract the PSWP paramter from the event
- pswp=(PSWP)evt.parameter1().asUnsignedLong();
-
- //check if the window is being minimized or not
- if ((pswp->fl) & SWP_MINIMIZE)
- ShowState=false;
- else
- ShowState=true;
-
- // Just for fun, query the dialog window's position
- WinQueryWindowPos(handle(),&Pos);
-
- // loop through all child windows
- while (hwnd=WinGetNextWindow(henum)) {
- // query the child windows position
- WinQueryWindowPos(hwnd,&winPos);
-
- // check if the child window overlaps the icon and hide/restore
- // if necessary
- if (((winPos.x)<=pswp->cx) &&
- ((winPos.y)<=pswp->cy))
- WinShowWindow(hwnd,ShowState);
- }
-
- // all children are done, end the enumeration
- WinEndEnumWindows(henum);
- }
-
- // be sure to pass the event to the proper handler!
- return IFrameHandler::dispatchHandlerEvent(evt);
- }
-
- OK. OK. We didn't break the rules, we just bent them a bit. The call to
- IFrameHandler::dispatchHandlerEvent() insures that the events get to the other
- IFrameHandler instances that the user may have registered.
-
- Getting WM_CONTROL Messages
-
- In some cases, we may want to get the WM_CONTROL messages in their PM form. To
- do this, we need to create a new handler class that will pass unmodified
- WM_CONTROL messages to our handlers. The following handler class performs this
- task, and is modelled after the ICommandHandler class.
-
- class ControlHandler: public IHandler {
-
- public:
- Boolean _exp_ dispatchHandlerEvent(IEvent& evt);
-
- protected:
- virtual Boolean _exp_ control(const IControlEvent &control);
- };
-
- :
- :
-
- Boolean ControlHandler::dispatchHandlerEvent(IEvent& evt) {
- // verify message ID
- if (evt.eventId()==WM_CONTROL) {
- IControlEvent controlEvnt(evt);
-
- // call control member function
- Boolean rc=control(controlEvnt);
-
- evt.setResult(controlEvnt.result());
- return rc;
- }
- return false;
- }
-
- // Default handler returns false to indicate the message (event) has not
- // been processed.
- Boolean ControlHandler::control(const IControlEvent &control) {
- return false;
- }
-
- Keeping with the original design of the UICL, to create an instance of the
- handler, one does not override the dispatchHandlerEvent() function, but rather
- some other go-between function. In this case, the member function control() is
- overridden.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 3.1.4. Putting It All Together ΓòÉΓòÉΓòÉ
-
- So far we've examined some of the underlying design decisions in the UICL, and
- found ways to work around them. A new dialog window class has been developed
- to allow one work around, and to enhance the basic dialog window support
- already present in the UICL. The file dlgdem.zip contains a sample application
- that uses this class.
-
- Fun For The Reader
-
- For those interested in testing their understanding, modify the sample
- application to process WM_CONTROL messages. In particular, watch for the
- BN_DBLCLICKED notification message coming from the exit button. When this
- message is received (ie. a ControlEvent has occurred), change the text in the
- button.
-
- Hint:
-
- You will need to disable the processing of the exit button in the command()
- member function, and subclass the ControlHandler object.
-
- Source Files
-
- The file idialog.zip contains the files:
-
- o idialog.cpp
- o idialog.def
- o IDIALOG.DEP
- o idialog.hpp
- o IDIALOG.MAK
-
- The idialog.mak file should be passed through NMAKE before making the demo
- program. The file dlgdem.zip contains the files:
-
- o DlgDem.def
- o DlgDem.dep
- o dlgdem.ICO
- o DlgDem.mak
- o DlgDem.RC
- o DlgDemMain.cpp
- o DlgDemMain.h
- o DlgDemRC.H
- o main.cpp
-
- No executables are included, because you must have the C-Set++ compiler to have
- the UICL run time DLL files.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 3.1.5. Wrapping Things Up ΓòÉΓòÉΓòÉ
-
- Here we are again, at the end of another C++ adventure. You should now
- understand how event and handler objects encapsulate the PM message structure,
- how to create new window classes, and how to pull a few tricks to make the UICL
- fit your style.
-
- What's up for the Next Issue?
-
- I'm not too sure what we'll look at next time. If my copy of C-Set++ 2.1
- finally arrives, an in-depth review of it will be presented. If it doesn't,
- we'll look at either more UICL topics, or encapsulating extended attributes.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 3.2. Introduction to PM Programming ΓòÉΓòÉΓòÉ
-
- Written by Larry Salomon, Jr.
-
- Introduction
-
- The purpose of this column is to provide to the readers out there who are not
- familiar with PM application development the information necessary to satisfy
- their curiousity, educate themselves, and give them an advantage over the
- documentation supplied by IBM. Of course, much of this stuff could probably be
- found in one of the many books out there, but the problem with books in general
- is that they don't answer the questions you have after you read the book the
- first time through.
-
- I will gladly entertain feedback from the readers about what was "glossed over"
- or what was detailed well, what tangential topics need to be covered and what
- superfluous crap should have been removed. This feedback is essential in
- guaranteeing that you get what you pay for. :)
-
- It should be said that you must not depend solely on this column to teach you
- how to develop PM applications; instead, this should be viewed as a supplement
- to your other information storehouses (books, the network conferences, etc.).
- Because this column must take a general approach, there will be some topics
- that would like to see discussed that really do not belong here. Specific
- questions can be directed to the Scratch Patch, where an attempt to answer them
- will be made.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 3.2.1. Introduction ΓòÉΓòÉΓòÉ
-
- Written by Larry Salomon, Jr.
-
- The purpose of this column is to provide to the readers out there who are not
- familiar with PM application development the information necessary to satisfy
- their curiousity, educate themselves, and give them an advantage over the
- documentation supplied by IBM. Of course, much of this stuff could probably be
- found in one of the many books out there, but the problem with books in general
- is that they don't answer the questions you have after you read the book the
- first time through.
-
- I will gladly entertain feedback from the readers about what was "glossed over"
- or what was detailed well, what tangential topics need to be covered and what
- superfluous crap should have been removed. This feedback is essential in
- guaranteeing that you get what you pay for. :)
-
- It should be said that you must not depend solely on this column to teach you
- how to develop PM applications; instead, this should be viewed as a supplement
- to your other information storehouses (books, the network conferences, etc.).
- Because this column must take a general approach, there will be some topics
- that would like to see discussed that really do not belong here. Specific
- questions can be directed to the Scratch Patch, where an attempt to answer them
- will be made.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 3.2.2. Looking Back and Looking Forward ΓòÉΓòÉΓòÉ
-
- Last issue, we discussed some of the fundamental concepts of PM such as windows
- and window classes, events, resources, and handles. This issue we will take
- these basic concepts and discuss the components of our first PM application
- entitled (what else?) "Hello World" (provided in intro.zip).
-
- After reading this installment, you should...
-
- o ...be able to explain the purpose of each line in the main() function of a
- typical PM application.
- o ...understand the hierarchical organization of windows.
- o ...know what some window styles are.
- o ...be able to describe the purpose of a frame creation flag.
- o ...be able to define the terms top-level window and well-behaved application.
-
- ...as well as a few other things.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 3.2.3. My main() Squeeze ΓòÉΓòÉΓòÉ
-
- Back in the dark ages when all of OS/2 and PM was 16-bit, there were ring 2
- stack (which I will not define) requirements imposed by PM applications that
- exceeded the default size provided when OS/2 started a new process. Because of
- this and other reasons, PM required that you call the function WinInitialize()
- at the begining of your application, which in turn called DosR2StackRealloc()
- to increase the size of the ring 2 stack and performed some additional
- housekeeping to enable your to successfully call other PM functions.
-
- While the need to increase the ring 2 stack may or may not have disappeared,
- the housekeeping initialization didn't. So, the first call in a PM application
- should always be WinInitialize() (and have the corresponding call to
- WinTerminate() before each exit point of your application). This function
- returns something called an anchor block, which is used as a parameter to other
- functions; for the curious, the value of the anchor block is the process
- identifier in the high word and a one in the low word (actually, the low word
- contains the thread identifier, which will always be one for quite a while in
- this column). This might change in the future, though, so do not depend on
- this always being true.
-
- Also, many PM functions indirectly cause messages (referred to last time in the
- section Events) to be sent to various windows, and messages require a message
- queue so the call the typically follows WinInitialize() is WinCreateMsgQueue()
- (and the corresponding call to WinDestroyMsgQueue() prior to the call to
- WinTerminate()). The reason why sending messages requires a message queue is
- beyond the scope of this column.
-
- Instead of questioning why you have to bother with these things, just accept
- them as givens, and you will save yourself much worrying.
-
- After the first two calls, there might be some application initialization, but
- inevitably there is one or more calls to WinRegisterClass() to register the
- window classes used by the application with PM. If you'll remember, this
- associates a window class name with a window class procedure.
-
- This is then followed by a call to WinCreateStdWindow(); this function creates
- a frame window and other standard window components.
-
- Figure 1) Some components of a "standard window".
-
- The WinCreateStdWindow() function takes many parameters and is described below:
-
- HWND WinCreateStdWindow(HWND hwndParent,
- ULONG ulFrameStyle,
- PULONG pulFlags,
- PSZ pszClientClass,
- PSZ pszTitle,
- ULONG ulClientStyle,
- HMODULE hmResources,
- ULONG ulIdResources,
- PHWND phwndClient);
-
- hwndParent Specifies the parent of the standard window. If
- HWND_DESKTOP is specified, a top level window is created.
- ulFrameStyle Specifies a set of WS_* (Window Style) constants for the
- frame window.
- pulFlags Specifies a set of FCF_* (Frame Creation Flag) constants
- that indicate the desired components (see Figure 1 and the
- description below) and properties of the standard window:
- pszClientClass " Points" to the name of the class of the window to be used
- as the client area in Figure 1. "Points" is used in quotes
- because this can also be a WC_* (Window Class) constant
- indicating one of the predefined window classes (known as
- public window classes).
- pszTitle Points to the window text of the frame window. This text
- is also displayed on the titlebar. This cannot be NULL.
- To specify nothing, use an empty string ("").
- ulClientStyle Specifies a set of WS_* (Window Style) constants for the
- client window. This is typically specified as zero, but
- could be used if a public window class is used as the
- client.
- hmResources Specifies the module handle where any required resourced
- are located. If NULLHANDLE is used, the resources are
- assumed to be appended to the .EXE file.
- ulIdResources The identifier of any required resources.
- phwndClient Points to the variable to receive the window handle of the
- client.
-
- (See the next sections for detailed explanations of the umpteen million new
- things mentioned above)
-
- After the standard window is created, the message loop is typically entered.
- The message loop is comprised of a call to WinGetMsg() followed by a call to
- WinDispatchMsg(). WinGetMsg() consistently returns TRUE until a WM_QUIT message
- is received which causes the loop to end.
-
- Following the loop is a call to WinDestroyWindow() which destroys the frame
- window, and thus all of its children.
-
- ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
-
- I intentionally wanted this to sound mechanical, because it is. The main()
- function of all PM applications are very similar, if not identical.
-
- INT main(VOID)
- {
- HAB habAnchor;
- HMQ hmqQueue;
- ULONG ulCreate;
- HWND hwndFrame;
- HWND hwndClient;
- BOOL bLoop;
- QMSG qmMsg;
-
- habAnchor=WinInitialize(0);
- hmqQueue=WinCreateMsgQueue(habAnchor,0);
-
- WinRegisterClass(habAnchor,CLS_CLIENT,windowProc,CS_SIZEREDRAW,0);
-
- ulCreate=FCF_SYSMENU | FCF_TITLEBAR | FCF_MINMAX | FCF_SIZEBORDER |
- FCF_SHELLPOSITION | FCF_TASKLIST | FCF_ICON;
-
- hwndFrame=WinCreateStdWindow(HWND_DESKTOP,
- WS_VISIBLE,
- &ulCreate,
- CLS_CLIENT,
- "Hello World Sample",
- 0,
- NULLHANDLE,
- RES_CLIENT,
- &hwndClient);
- if (hwndFrame!=NULLHANDLE) {
- bLoop=WinGetMsg(habAnchor,&qmMsg,NULLHANDLE,0,0);
- while (bLoop) {
- WinDispatchMsg(habAnchor,&qmMsg);
- bLoop=WinGetMsg(habAnchor,&qmMsg,NULLHANDLE,0,0);
- } /* endwhile */
-
- WinDestroyWindow(hwndFrame);
- } /* endif */
-
- WinDestroyMsgQueue(hmqQueue);
- WinTerminate(habAnchor);
- return 0;
- }
-
- Figure 2) Sample main() function for a PM application coded in C
-
- If you thought that was a lot of typing, here's an incentive to learn C++ using
- IBM's User Interface class library: the entire main() function can be coded in
- 8 lines, and 3 of those are required by the language!
-
- INT main(VOID)
- {
- new IFrameWindow("Hello World Sample",
- RES_CLIENT,
- IFrameWindow::defaultStyle() |
- IFrameWindow::minimizedIcon);
- (IApplication::current()).run();
- return 0;
- }
-
- Figure 3) Sample main() function for a PM application coded in C++
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 3.2.4. New Concepts ΓòÉΓòÉΓòÉ
-
- There are many things mentioned in the last section, which are described in
- more detail below.
-
- Frame Windows
-
- Frame windows are windows that serve as the focal point for a standard window
- construct. It usually receives messages from other windows, of which it sends
- the important ones to the client. Physically, it encompases all of the
- standard window components.
-
- Top Level Windows
-
- Since it is possible to have a standard window within a standard window, the
- standard window that is the parent of the other standard windows is known as
- the top level window (actually, a top level window is defined as one whose
- parent is the desktop). The top level frame has special significance because
- enumerations of the windows present only iterate through the top level windows.
-
- When a top level window contains other standard windows, the application is
- termed an MDI - or multiple document interface - application.
-
- Window Text
-
- All windows have a common set of characteristics, which was alluded to last
- issue when class styles versus window styles was discussed. Besides window
- styles, many other characteristics are found in all windows; window text is one
- of them. Actually, text is a misnomer because it is not required that this be
- text, only that this points to accessable memory. Another useful
- characteristic queryable by all windows is the window identifer.
-
- There are many different WinQuery*() functions to retrieve these common
- characteristics.
-
- Resources
-
- This is something which we will describe in more detail next issue. Suffice it
- to say for now that a resource is a definable item that would be too much
- trouble to write code to construct, so a resource language and corresponding
- compiler was written to allow you to describe things like menus, string tables
- (for easy translation), accelerator tables, etc.
-
- Window Hierarchies
-
- You've already seen the terms parent, children, siblings, and owner used. What
- exactly do they mean? The first three have meaning analagous to "Real Life"
- (tm). A parent window can have 0 or more child windows each of whom are
- siblings of each other.
-
- Figure 4) Parent-child relationships
-
- This parent-child relationship is exploited in various ways, most notably in
- clipping. If a child window lies completely or partially outside the
- rectangular area defined by the parent window, those portions are clipped, i.e.
- not shown.
-
- Figure 5) Children clipped to their parent
-
- Other ways that this relationship is used will be discussed in later issues.
-
- Unlike parent-child one-to-many relationships, owner-ownee relationships are
- one-to-one, but the same window can own many other windows. The distinction is
- that there is no concept of a sibling in this regard. An owner window is
- notified by the owned window whenever something happens that the owner might be
- interested in. Notification occurs via a message named WM_CONTROL. We will
- also see more of this message in later issues.
-
- Figure 6) Owner-ownee relationship
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 3.2.5. New Constants ΓòÉΓòÉΓòÉ
-
- Window Style Constants
-
- There are many window style constants which are common to all windows and even
- more which are specific to a particular window class. Listed below are the
- common window styles and their meanings.
-
- WS_VISIBLE Specifies that the window is to be visible.
- WS_DISABLED Specifies that the window is to be disabled.
- WS_CLIPCHILDREN Specifies that, when painting, children should be prevented
- from being painted over.
- WS_CLIPSIBLINGS Specifies that, when painting, siblings should be prevented
- from being painted over.
- WS_PARENTCLIP Specifies that, when painting, the parent should be
- prevented from being painted over.
- WS_SAVEBITS Specifies that a bitmap area of any windows covered by this
- window should be saved, and restored as the window is
- moved, sized, hidden, or destroyed.
- WS_SYNCPAINT Specifies that paint notification should be synchronous,
- i.e. the window should be notified immediately following
- any invalidation.
- WS_MINIMIZED Specifies that the window is minimized. This flag is
- ignored in the WinCreateStdWindow() function.
- WS_MAXIMIZED Specifies that the window is maximized. This flag is
- ignored in the WinCreateStdWindow() function.
- WS_ANIMATE Specifies that the creation and destruction of the window
- should be accompanied by zoom-out/zoom-in animation.
- WS_GROUP, WS_TABSTOP, WS_MULTISELECT Used for dialogs only and will not be
- discussed here.
-
- Frame Creation Flag Constants
-
- In addition to window styles, standard windows also have many flags which
- determine the behavior of the WinCreateStdWindow() call. These flags, and
- their meanings are described below.
-
- FCF_TITLEBAR Specifies that a titlebar should be included.
- FCF_SYSMENU Specifies that the system menu should be included.
- FCF_MENU Specifies that an action bar (not shown) should be
- included.
- FCF_SIZEBORDER Specifies that a sizing border should be used as the window
- border.
- FCF_MINBUTTON Specifies that a min(imize) button should be included.
- FCF_MAXBUTTON Specifies that a max(imize) button should be included.
- FCF_MINMAX Specifies that both min and max buttons should be included.
- FCF_VERTSCROLL Specifies that a vertical scrollbar (not shown) should be
- included.
- FCF_HORZSCROLL Specifies that a horizontal scrollbar (not shown) should be
- included.
- FCF_DLGBORDER Specifies that a dialog border (not shown) should be used
- as the window border.
- FCF_BORDER Specifies that a normal border (not shown) should be used
- as the window border.
- FCF_SHELLPOSITION Specifies that the Workplace Shell should automatically
- move, size, and then show the window after it is created.
- FCF_TASKLIST Specifies that the Workplace Shell should automatically add
- an entry in the Window List for the standard window.
- FCF_NOBYTEALIGN Specifies that the window position should not snap to an
- 8-pel grid when it is moved. This can result in less than
- optimal performance.
- FCF_NOMOVEWITHOWNER Specifies that the window position relative to its owner
- should not be adjusted when the owner is moved.
- FCF_ICON Specifies that an icon with the identifier specified in
- ulIdResources should be associated with the standard
- window.
- FCF_ACCELTABLE Specifies that an accelerator with the identifier specified
- in ulIdResources should be associated with the standard
- window.
- FCF_SYSMODAL Specifies that the window should be system modal.
- FCF_SCREENALIGN, FCF_MOUSEALIGN Used for dialogs only and will not be discussed
- here.
- FCF_HIDEBUTTON Specifies that a hide button should be included.
- FCF_HIDEMAX Specifies that both hide button and max buttons should be
- included.
- FCF_DBE_APPSTAT This flag is undocumented.
- FCF_AUTOICON Specifies that, when minimized, the system should redraw
- the icon when necessary instead of notifying the window.
-
- Note the following sets of mutually exclusive flags. If more than one of each
- group is specified, the first is the default.
-
- o FCF_MINBUTTON and FCF_HIDEBUTTON
- o FCF_SCREENALIGN and FCF_MOUSEALIGN
- o FCF_SIZEBORDER, FCF_DLGBORDER, and FCF_BORDER
-
- If all of this seems to be too much, don't fret; this function is one of the
- most complicated in terms of the myriad of flags that can be used (not
- surprisingly, another one is the WinCreateWindow() function).
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 3.2.6. More About the Message Loop ΓòÉΓòÉΓòÉ
-
- Let's take a closer look at the message loop.
-
- bLoop=WinGetMsg(habAnchor,&qmMsg,NULLHANDLE,0,0);
- while (bLoop) {
- WinDispatchMsg(habAnchor,&qmMsg);
- bLoop=WinGetMsg(habAnchor,&qmMsg,NULLHANDLE,0,0);
- } /* endwhile */
-
- Figure 7) Message loop
-
- In PM, the only way you receive messages is via the WinGetMsg() call. The
- message construct (QMSG) is then translated to a call to the window class
- procedure (introduced last time) via the WinDispatchMsg() call.
-
- When PM was designed, someone decided that the concept of type-ahead was
- important, so they decided to synchronize all input from the mouse and keyboard
- via a single system-wide input message queue, which gets checked whenever the
- application with the focus calls WinGetMsg(). This is important because the
- only way to switch between applications is via the keyboard or mouse; however,
- if the application with the focus is taking a long time to process the current
- message, it will never allow PM to change the input focus to another
- application.
-
- To avoid what will appear to the user to be a system lockup, IBM defined the
- well-behaved application rule which states that a PM application should take no
- longer than 1/10th of a second to process a message. Of course, no one is
- going to time each message processing section to insure this, but it does
- indicate to the PM programmer that you should do what you have to quickly and
- "get back to the loop." Longer tasks, like loading a file, must be done in
- another thread. Multithreading from within PM applications was discussed in
- the article Threads in PM Applications in volume 1, issue 6 of EDM/2.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 3.2.7. The Window Class Procedure ΓòÉΓòÉΓòÉ
-
- As discussed last time, the window class procedure (often abbreviated to window
- procedure or even window proc) is not much more than a giant switch statement
- which tests for particular message identifiers (abbeviated to just messages)
- and processing those only. The default case calls and returns the value from
- the function WinDefWindowProc().
-
- MRESULT EXPENTRY windowProc(HWND hwndWnd,
- ULONG ulMsg,
- MPARAM mpParm1,
- MPARAM mpParm2)
- {
- switch (ulMsg) {
- case WM_PAINT:
- {
- HPS hpsPaint;
- RECTL rclPaint;
-
- hpsPaint=WinBeginPaint(hwndWnd,NULLHANDLE,&rclPaint);
-
- WinFillRect(hpsPaint,&rclPaint,SYSCLR_WINDOW);
-
- WinQueryWindowRect(hwndWnd,&rclPaint);
-
- WinDrawText(hpsPaint,
- -1,
- "Hello world.",
- &rclPaint,
- CLR_BLACK,
- 0,
- DT_CENTER|DT_VCENTER);
-
- WinEndPaint(hpsPaint);
- }
- break;
- default:
- return WinDefWindowProc(hwndWnd,ulMsg,mpParm1,mpParm2);
- } /* endswitch */
-
- return MRFROMSHORT(FALSE);
- }
-
- Figure 8) Simple window procedure
-
- The sample simple window procedure shown above does just what we described. A
- question often asked is why the choice of FALSE on the return statement. My
- answer is "I don't know"; you must return something to avoid warnings by the
- compiler and FALSE is the more frequent return value from specific messages, so
- I used it instead of TRUE.
-
- There are a multitude of messages that the system sends to an application; so
- many, in fact, that detailing each of them would fill a book (hey, that's an
- idea...). Instead, you will find that you use a few much more than the rest:
-
- WM_CREATE Sent whenever the window is being created to allow it to
- initialize itself; by returning MRFROMSHORT(TRUE) you tell
- the system that initialization failed, causing the function
- resulting in this message to also fail.
- WM_DESTROY Sent whenever the window is about to be destroyed. There
- is no way to halt the destruction once this message is
- received.
- WM_CLOSE Sent whenever the user requested that the window close
- itself. This allows you to confirm the action, if needed,
- and then call the appropriate function to destroy yourself
- if confirmed.
- WM_SIZE Sent after the window has been resized.
- WM_CONTROL Sent whenever an owned window has something to tell you.
- WM_COMMAND Sent whenever the user selected a menuitem or a pushbutton,
- which indicates that a command is to be performed.
- WM_MOUSEMOVE Sent whenever the mouse moves over your window.
- WM_BUTTONxDOWN Sent whenever button x is depressed.
- WM_BUTTONxUP Sent whenever button x is released.
- WM_BUTTONxCLICK Sent whenever button x is clicked.
- WM_BUTTONxDBLCLK Sent whenever button x is double-clicked.
- WM_CHAR Sent whenever a key is pressed.
- WM_PAINT Sent whenever a portion of the window needs to be
- repainted.
- WM_TIMER Sent whenever a timer started by the application expires.
- WM_BEGINDRAG Sent whenever the user attempts to initiate a direct
- manipulation operation.
- WM_CONTEXTMENU Sent whenever the user attempts to display a popup
- context-sensitive menu.
-
- Now, what do you do when you receive any of these (or other) messages? That is
- up to you, but you will find a function to do just about anything you need; all
- you have to do is put them together in the right order.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 3.2.8. Summary ΓòÉΓòÉΓòÉ
-
- This month we learned quite a lot, and the begining can definately be called
- the hardest part of adjusting to PM programming. Next month, we will look at
- the processing for WM_PAINT in our "Hello World" sample application and discuss
- presentation spaces. We will also be introduced to some of the messages listed
- in the last section.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 3.3. Scratch Patch ΓòÉΓòÉΓòÉ
-
- Written by Larry Salomon, Jr.
-
- Welcome to this month's "Scratch Patch"! Each month, I collect various items
- that fit into this column sent to me via email. The ones that I feel contribute
- the most to developers, whether in terms of information or as a nifty trick to
- tuck into your cap, get published in this column.
-
- To submit an item, send it via email to my address - os2man@panix.com - and be
- sure to grant permission to publish it (those that forget will not be
- considered for publication). This month, we have the following:
-
- o Questions and Answers
- o Snippet(s) of the Month
- o Documentation Chop Shop
- o Want Ads
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 3.3.1. Questions and Answers ΓòÉΓòÉΓòÉ
-
- Keith Murray (murrayk@prism.cs.orst.edu) writes: Your book has given me a lot
- of help with a lot of things. It does a good job of talking about ownerdrawn
- listboxes. There is one omission however. How do you deal with horizontal
- scrolling? I've got to the point of being able to draw my graphic and measure
- everything correctly. Vertical scrolling works just great and the horizontal
- scroll bar seems scaled correctly. But I don't know what to do from there.
- How do I figure out where to draw my text relative to the scroll bar?
-
- Since you seem to know how to draw the text, what you are really asking is how
- do you query the position of the horizontal scrollbar. In order to do this,
- you need to send the scrollbar an SBM_QUERYPOS message. But you don't have the
- handle of the scrollbar!
-
- If you would press ENTER to go to the next section, you will find the code for
- the queryLbScroll() function, which will return what you want.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 3.3.2. Snippet(s) of the Month ΓòÉΓòÉΓòÉ
-
- Even worse than last month, I received no submissions for this section of the
- column. Fortunately, the question of the month requires a bit of code, which I
- will place here. The code, and a test application, can be found in
- scratch.zip.
-
- HWND queryLbScroll(HWND hwndLb,BOOL bWantVert)
- //-------------------------------------------------------------------------
- // This function returns the specified scrollbar for a listbox.
- //
- // Input: hwndLb - handle of the listbox window to query
- // bWantVert - TRUE if the vertical scrollbar is desired, FALSE if
- // horizontal
- // Returns: handle of the scrollbar window if successful, NULLHANDLE
- // otherwise
- //-------------------------------------------------------------------------
- {
- ULONG ulWantStyle;
- HENUM heEnum;
- HWND hwndScroll;
- CHAR achClass[256];
- ATOM aClass;
- ULONG ulStyle;
-
- ulWantStyle=bWantVert?SBS_VERT:0;
-
- //----------------------------------------------------------------------
- // Enumerate all of the children of the scrollbar
- //----------------------------------------------------------------------
- heEnum=WinBeginEnumWindows(WinWindowFromID(hwndWnd,DLG_LB_TEST));
-
- hwndScroll=WinGetNextWindow(heEnum);
- while (hwndScroll!=NULLHANDLE) {
- //-------------------------------------------------------------------
- // Query the class name. Public window classes have class names
- // which although they are in the for "#dddd" are really atoms.
- // Thus, we need to check the system atom table and compare the
- // result against the low word of the WC_* constant.
- //
- // Oh. Why, you ask? Because the numerical form of an integer
- // atom contains the number in the low word and 0xFFFF in the high
- // word. Since all of the WC_* constants have 0xFFFF in the high
- // word, they are all integer atoms.
- //-------------------------------------------------------------------
- WinQueryClassName(hwndScroll,sizeof(achClass),achClass);
-
- aClass=WinFindAtom(WinQuerySystemAtomTable(),achClass);
- if (aClass==LOUSHORT(WC_SCROLLBAR)) {
- //----------------------------------------------------------------
- // We found a scrollbar; now check to see if it is the right
- // type.
- //----------------------------------------------------------------
- ulStyle=WinQueryWindowULong(hwndScroll,QWL_STYLE);
- if ((ulStyle & SBS_VERT)==ulWantStyle) {
- break;
- } /* endif */
- } /* endif */
-
- hwndScroll=WinGetNextWindow(heEnum);
- } /* endwhile */
-
- WinEndEnumWindows(heEnum);
- return hwndScroll;
- }
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 3.3.3. Documentation Chop Shop ΓòÉΓòÉΓòÉ
-
- Thanks to Gordon Zeglinski (zeglins@cc.umanitoba.ca) for this submission. He
- writes:
-
- The documentation states that the WinSwitchToProgram() has no effect if the
- calling program does not have the focus. This apparently is not true, as my
- app will switch an app to the foreground regardless of whether it has the focus
- or not.
-
- Gordon, this is not entirely correct; the documentation states that this
- function does nothing unless the process calling it is the foreground process
- and there is an important distinction here, if I understand the implied things
- in your note.
-
- According to one of the original PM developers, who will remain nameless in
- case I get this wrong, code within a window procedure is only executing when it
- is the foreground process. This is related, I believe, to the synchronized
- system input queue, and a host of other design-of-PM related issues which I
- will not go into (mainly because I'm not entirely confident of the accuracy of
- my knowledge). Thus, if you are calling WinSwitchToProgram() from within your
- window procedure or any function (in)directly called by your window procedure,
- you must be the foreground process.
-
- I am not sure what would happen if an application started a second thread which
- creates a message queue, does some processing, and calls this function without
- utilizing window procedures in any way, but I imagine the behavior described in
- the documentation would be observed.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 3.3.4. Want Ads ΓòÉΓòÉΓòÉ
-
- Below are the hot topics as of this issue's writing. Feel free to write on any
- of these.
-
- Workplace Shell Programming (hot) - lately, I have noticed two things: 1) lots
- of people want to learn how to write new Workplace Shell classes and 2) no one
- who knows anything about it is telling the rest of us how to do it. I'll even
- stoop down to accepting an article in ASCII format on this topic! :)
-
- Client/Server (hot) - using either named pipes (with or without a network) or
- sockets, client/server programming is all-the-rage these days. On a related
- note, some people have also expressed an interest in learning about interfacing
- with the various protocol drivers (e.g. NDIS, IPX/SPX, etc.). Any articles in
- this area are most welcome.
-
- Anything on Rexx/2 (hot) - many people have requested more articles on Rexx/2.
- This issue sees the first article on this topic, but we can always use more.
- Writing "Enhanced Editor" macros in Rexx/2 and interfacing with the Workplace
- Shell from Rexx/2 are still open topics.
-
- Animation (warm) - a few readers expressed an interest in the various animation
- techniques that can be applied to PM applications. The ultimate article, in my
- opinion, would be one that develops a sprite library a la the Commodore 64's
- (and Amiga's?) built-in routines, since this is probably the hardest component
- of any good animation sequence. Call this a "personal request"...
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 4. How do I get EDM/2? ΓòÉΓòÉΓòÉ
-
- EDM/2 can be obtained in any of the following ways:
-
- On the Internet
-
- o All back issues are available via anonymous FTP from ftp.cdrom.com in the
- /pub/os2/2_x/program/newsltr directory.
- o The EDM/2 mailing list. Send an empty message to edm2-info@knex.via.mind.org
- to receive a file containing (among other things) instructions for
- subscribing to EDM/2.
- o IBM's external gopher server, which I have not the address of, in Almaden.
-
- On Compuserve
-
- All back issues are available in the OS/2 Developers Forum 2.
-
- IBM Internal
-
- o IBM's internal gopher server, which I have not the address of, in Almaden.
- o IBM's REQUEST command on all internal VM systems. Enter the VM command
- REQUEST LIST FROM ASSELIN AT RALVM12 and a list of the requestable packages
- will be sent to you; in this list are the names of the packages containing
- the EDM/2 issues.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 5. Contributors to this Issue ΓòÉΓòÉΓòÉ
-
- Are You a Potential Author?
-
- As always, we are always looking for (new) authors. If you have a topic about
- which you would like to write, send a brief description of the topic
- electronically to any of the editors, whose addresses are listed below, by the
- 15th of the month in which your article will appear. This alerts us that you
- will be sending an article so that we can plan the issue layout accordingly.
- After you have done this, get the latest copy of the Article Submission
- Guidelines from ftp.cdrom.com in the /pub/os2/2_x/program/newsltr directory.
- (the file is artsub.zip) The completed text of your article should be sent to
- us no later than the last day of the month; any articles received after that
- time may be pushed to the next issue.
-
- The editors can be reached at the following email addresses:
-
- o Larry Salomon - os2man@panix.com (Internet).
-
- The following people contributed to this issue in one form or another (in
- alphabetical order):
-
- o Semir Patel
- o Larry Salomon, Jr.
- o Marc van Woerkom
- o Gordon Zeglinski
- o Network distributors
-
-
- ΓòÉΓòÉΓòÉ 5.1. Semir Patel ΓòÉΓòÉΓòÉ
-
- I am a junior in the undergraduate CS program at the University of Texas at
- Austin; currently, I'm working on an addictive multithreaded, MMPM aware game
- for OS/2 called Bonkers! I hope to find co-op oppourtunity in the Austin area
- in the near future.
-
- I can be reached in the following manners:
-
- o spatel@cs.utexas.edu (Internet email)
- o Darkgoob (Internet Relay Chat)
-
- Semir Patel
-
-
- ΓòÉΓòÉΓòÉ 5.2. Larry Salomon, Jr. ΓòÉΓòÉΓòÉ
-
- Larry Salomon wrote his first Presentation Manager application for OS/2 version
- 1.1 in 1989. Since that time, he has written numerous VIO and PM applications,
- including the Scramble applet included with OS/2 and the I-Brow/Magnify/Screen
- Capture trio included with the IBM Professional Developers Kit CD-ROM currently
- being distributed by IBM. Currently, he works for International Masters
- Publishers in Stamford, Connecticut and resides in Bellerose, New York with his
- wife Lisa.
-
- Larry can be reached electronically via the Internet at os2man@panix.com.
-
-
- ΓòÉΓòÉΓòÉ 5.3. Marc van Woerkom ΓòÉΓòÉΓòÉ
-
- Marc van Woerkom is a physics student at the Rheinisch-Westfaelische Technische
- Hochschule Aachen in Germany. Currently, he works there at the Aachen Center
- for Solidification in Space, ACCESS, on his Diplomarbeit.
-
- When he isn't wrestling with FORTRAN 77 code for his diploma, he enjoys the
- progress made in the last 15 years i.e. programming in C++ on his private OS/2
- machine. Next to his computer addiction, he likes to cross the nearby Dutch
- border to see a good movie, read science-fiction, play Badminton on occasion
- and also watch a lot of cable television.
-
- Marc can be reached at either of the following addresses:
-
- o marc_van-woerkom@ac3.maus.de (home)
- o marc@pxcl1.gi.rwth-aachen.de (work)
-
-
- ΓòÉΓòÉΓòÉ 5.4. Gordon Zeglinski ΓòÉΓòÉΓòÉ
-
- Gordon Zeglinski is a freelance programmer/consultant who received his Master's
- degree in Mechanical Engineering with a thesis on C++ sparse matrix objects.
- He has been programming in C++ for 6 years and also has a strong background in
- FORTRAN. He started developing OS/2 applications with version 2.0 .
-
- His current projects include a client/server communications program that
- utilitizes OS/2's features which has entered beta testing. Additionally, he is
- involved in the development of a "real-time" automated vehicle based on OS/2
- and using C++ in which he does device driver development and designs the
- applications that comprise the control logic and user interface.
-
- He can be reached via the Internet at zeglins@cc.umanitoba.ca.
-
-
- ΓòÉΓòÉΓòÉ 5.5. Network distributors ΓòÉΓòÉΓòÉ
-
- These people are part of our distribution system to provide EDM/2 on networks
- other than the Internet. Their help to provide access to this magazine for
- others is voluntary and we appreciate them a lot!
-
- o Paul Hethman (hethman@cs.utk.edu) - Compuserve
- o Gess Shankar (gess@knex.via.mind.org) - Internet
- o David Singer (singer@almaden.ibm.com) - IBM Internal
- o Andre Asselin (ASSELIN AT RALVM12) - IBM Internal
-
- If you would like to become a "network distributor", be sure to contact the
- editors so that we can give you the credit you deserve!