home *** CD-ROM | disk | FTP | other *** search
-
- ΓòÉΓòÉΓòÉ 1. March 1994's Title Page ΓòÉΓòÉΓòÉ
-
- Welcome to EDM/2 - The Electronic OS/2 Developer's Magazine!
- Portions copyright (c) by Larry Salomon Jr.
- Volume 2, issue 3
-
- 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
-
- First of all, I would like to say Happy Anniversary!!! to EDM/2. Yes, it has
- indeed been one year since Steve Luzynski published the first issue. We have
- come a long way since then, but we can go a lot further, as long as we always
- have your continued support. Our success has not been only due to the hard work
- of the many authors, but to the readers who have given us all feedback on the
- good and bad points of the magazine. A hearty thanks to you all(!) and many
- more years for EDM/2 to come!
-
- Prizes, Prizes, Who has the Prizes?
-
- I finally received the copies of Vis-Pro/REXX Bronze Edition from HockWare,
- Inc. (thanks again), which were mailed out. Since Gavin Baker never contacted
- me after my mail to him bounced, I decided to award the 3rd Place prize to the
- next person down the list who wasn't me and didn't already receive a prize.
- Thus, the winner is...
-
- (drum roll please)
-
- Raja Thiagaraian, with The Unofficial Guide to the Palette Manager, published
- in volume 1, issue 1. I have sent him email about this and he has accepted the
- prize. Many congratulations to you, Raja!
-
- New York City C++ SIG
-
- This went very well; it was nice to meet a few of the readers, even if they
- didn't figure out that it was me who plugged the magazine in the announcements
- (who did you think it was, anyway? :) . I think the plug picked up a few more
- subscribers and/or ftp-ers.
-
- My next "stop" will hopefully be at the IBM Technical Interchange in San
- Fransisco, CA at the end of March. In case you can be there, too, a bunch of
- people (including me) are planning a food fight (just kidding) at a local
- restaurant or something like that. Let me know if you want to join us and have
- some fun.
-
- Official Announcement
-
- I posted this on comp.os.os2.programmer.misc, but it should go here also. This
- month sees the first installment of our new column - Book Review (yes, the
- title is lame, but we can't think of anything better). The author is Carsten
- Whimster (that is indeed his real name. Ask him for a complete history that
- goes back to the Vikings, if you're interested :) and he has a pretty ambitious
- job - review a new development or very technical book each month; of course,
- the books will be OS/2 related. I think you will like this one.
-
- Guess the Graphic
-
- Since I only received one email guess about last month's graphic for the
- communications category (what do I have to do to get a decent response rate?
- "If you order now, we'll send you this free thingymajig that slices, dices,
- pares, and even writes Workplace Shell objects!" :), the answers were a socket,
- named pipe (named Larry, no less), and a token ring. Ba-doom tssh!
-
- Workplace Shell
-
- Last month I said that I would try to have an introductory WPS article for this
- issue. Unfortunately, immediately following the issue's release, things got
- really hairy at work, so I never could finish my class development.
-
- The good news is that someone else decided to write such an article, which is
- in this issue. Enjoy, and thanks to BjФrn for his submission.
-
- One More Time
-
- And speaking of BjФrn, in his article submission which he sent through the mail
- was an .INF file. I usually look at these to get an idea of how much rework
- was necessary. "Hmm...He did the 'duplicated introduction' thing that I had so
- much trouble with. But let me try Alt+F anyways." Much to my surprise, it did
- not display the introduction twice!
-
- The net result is that, if you put no text after the heading for the article,
- the compiler issues a warning message, but when you display the article
- heading, it automatically jumps to the next heading with text. I quickly
- adopted this trick in this issue.
-
-
- ΓòÉΓòÉΓòÉ 2. Features for March 1994 ΓòÉΓòÉΓòÉ
-
- The following articles constitute this issue's features:
-
- o Making Noise with MMPM/2 - Part 2
- o Porting STEP02 to ICLUI
- o Workplace Shell Development 101
-
-
- ΓòÉΓòÉΓòÉ 2.1. Making Noise with MMPM/2 - Part 2 ΓòÉΓòÉΓòÉ
-
-
- ΓòÉΓòÉΓòÉ 2.1.1. Introduction ΓòÉΓòÉΓòÉ
-
- Written by Marc van Woerkom
-
- Introduction
-
- This is the second article on MMPM/2 programming. It was originally planned to
- be the final one, but two reasons forced me to extend this series:
-
- 1. Semir Patel's fine article in volume 2, issue 1 of EDM/2 already touched
- most topics I intended to present here, blasting my concept. (Watch out
- for my upcoming DOOM-style game, featuring a certain editor :-)
-
- 2. There is so much to cover about MMPM/2 that two parts are simply not
- enough.
-
-
- ΓòÉΓòÉΓòÉ 2.1.2. Limitations of Using REXX ΓòÉΓòÉΓòÉ
-
- Limitations of Using REXX
-
- When I delved further into MMPM/2 programming it became clear to me that the
- features accessible from the REXX Media Control Interface (MCI) are very
- powerful but that there is still more to see, much like the part of an iceberg
- lying under water. After all, let's face the music (pun intended); MMPM/2 is
- written in C meaning all of the APIs will be accessible from C. The only APIs
- provided for REXX are:
-
- o mciSendString (mciRxSendString)
-
- o mciGetErrorString (mciRxGetErrorString)
-
- o mciGetDeviceID (mciRxGetDeviceID)
-
- Compare this with the near 200 MMPM/2 API calls accessible through C/C++.
-
- To be fair, it should be possible to close the gaps since VX-REXX 2.0 from
- Watcom seems to have multimedia support. However, one must stick to C/C++ if
- one wants...
-
- o to get complex feedback (be it a whole structure of information, or
- synchronization messages)
- o to use the basic Multimedia I/O subsystem (MMIO API)
- o to control streaming and synchronization through the SPI subsystem
- o to use the PM extensions provided by MMPM/2 (i.e. new graphical controls and
- secondary window support)
-
- This doesn't even include esoteric business like driver development.
- ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
-
- After all of this bashing on poor REXX, let's close this section with the
- biggest advantage of REXX:
-
- o It's pretty easy to use and provides a great introduction! :-)
-
- Please read the first part of this series for more information on this topic.
-
-
- ΓòÉΓòÉΓòÉ 2.1.3. Preliminary Thoughts ΓòÉΓòÉΓòÉ
-
- Preliminary Thoughts
-
- Before you start MMPM/2 programming, you need a C or C++ compiler and MMPM/2
- bindings. I recommend using either:
-
- o one of the IBM compilers (C Set++, Firststep) together with the Developer's
- Toolkit and the MMPM/2 Toolkit (which has been included in the Developer's
- Toolkit since the 2.1 release)
- o or, the EMX port of the GNU GCC C/C++/Objective C compiler (revisions 0.8h
- and 2.5.8 or later) together with the MM4EMX package (revision 1.0).
-
- Although this should suffice to recompile the examples given, and you'll get a
- lot of information from here, you still should acquire some of the literature
- mentioned at the end of the first part of this series and at the end of this
- part.
-
- Another great source of information are the computer networks. Often you can
- contact someone on the Internet news groups or CompuServe fora who can help you
- or share thoughts with you.
-
- For those with Usenet access I recommend comp.os.os2.multimedia, or (if you
- somehow feel an urge to polish your German a little bit :-) maus.os2.prog.
-
- So much for the basic preparations. (Hmm...did I already tell you to install
- MMPM/2? :-)
-
-
- ΓòÉΓòÉΓòÉ 2.1.4. Multimedia Programming Using EMX ΓòÉΓòÉΓòÉ
-
- Multimedia Programming Using EMX
-
- To state it shortly - it's no problem to do any MMPM/2 programming with EMX.
-
- As an EMX user you have the choice between using the OS/2 and MMPM/2 headers
- and libraries from the IBM Toolkits or using those of the EMX and MM4EMX
- packages. Both have their strengths and weaknesses. However, this article
- series should feature nothing which is specific to one of the above
- combinations, or even the EMX system.
-
- The MM4EMX Package
-
- The Multimedia for EMX package (MM4EMX) is a freeware package for the EMX
- development environment. It contains all necessary 32-bit header files and
- import libraries in the BSD .A and Intel OMF .LIB formats together with source
- code.
-
- The samples presented and explained in this part of the article series are the
- ones from MM4EMX (release 1.0). As a side note, please contact the author if
- you should note incompatibilities.
-
-
- ΓòÉΓòÉΓòÉ 2.1.5. The Textual Media Control Interface ΓòÉΓòÉΓòÉ
-
- The Textual Media Control Interface
-
- The easiest way to use the MCI is via its textual interface, using the
- mciSendString() API call. Below is an example of this.
-
- #
- # bach.mci -- play the Bach MIDI file
- #
- open Sequencer alias midi2 shareable wait
- info midi2 product wait
- set midi2 time format mmtime wait
- load midi2 c:\mmos2\sounds\bach.mid wait
- status midi2 length wait
- play midi2 wait
- close midi2
-
- It opens the Sequencer (MIDI) multimedia device, giving it the alias "midi2".
- The product info is queried and mmtime is chosen as timeformat. Then a MIDI
- file from Bach is loaded into the context of the device, its length is queried
- and it gets played. Finally the device is closed.
-
- The following example will take a file of MCI command strings and sends them
- line by line for execution to the MDM via mciSendString(), thus interpreting
- the file.
-
- //////////////////////////////////////////
- // mci -- an interpreter for MCI scripts
- //
- // using emx 0.8h, mm4emx 1.0
- //
- // based on a C program by
- // John J. McDonough
- //
- // Marc E.E. van Woerkom, 2/94
- //
-
-
- #include <iostream.h>
- #include <fstream.h>
-
- #include <os2.h>
-
- One has to define INCL_OS2MM to include the proper definitions and
- declarations, i.e. the 32-bit version of the MMPM/2 API with naming conventions
- conforming to OS/2.
-
- A defined INCL_MCIOS2 will include the MCI support.
-
- #define INCL_OS2MM
- #define INCL_MCIOS2
- #include <os2me.h>
-
-
- //
- // this sample demonstrates the following MMPM/2 API's:
- //
- // mciSendString() send a MCI command string, receive an answer
- // mciGetErrorString() look up the error string for the error code
- //
-
- int main(int argc, char* argv[])
- {
- if ( argc != 2 ) {
- cerr << "usage: mci <filename>\n";
- return 1;
- }
-
- ifstream infile(argv[1]);
- if (!infile) {
- cerr << "error: can't open input file " << argv[1] << '\n';
- return 2;
- }
-
- int line = 0;
-
- const bsize = 128;
- char buff[bsize];
-
- while (!infile.eof()) {
- char c = infile.peek(); // peek one char forward
-
- if (infile.good()) {
-
- infile.get(buff, bsize, '\n');
-
- cout.width(3);
- cout << ++line << ": [" << buff << "]\n";
-
- if (c != '#') {
-
- Now we finally got a non-comment line into the variable buff.
-
- The next thing to do is to provide a return buffer for mciSendString(), which
- has to be empty.
-
- const rsize = 128;
- char rbuff[rsize];
- for (int i=0; i<rsize; i++) rbuff[i] = 0;
-
- ULONG rc = mciSendString(buff, // buffer with MCI string
- rbuff, // return buffer
- rsize, // rbuff size
- 0, // no callback window handle
- 0); // no user parameter
-
- if (rc == MCIERR_SUCCESS) {
- if (rbuff[0])
- cout << " -> " << rbuff << "\n\n";
- }
- else {
-
- The return code wasn't MCIERR_SUCCESS, so something strange happened. Because
- an error code is not too enlightening, we use mciGetErrorString() to get a nice
- string.
-
- ULONG rc2 = mciGetErrorString(rc, // error code
- rbuff, // return buffer
- rsize); // rbuff size
-
- In case the rc is out of range we give up and display the number.
-
- if (rc2 == MCIERR_SUCCESS)
- cerr << " -> MCI error: " << rbuff << "\n\n";
- else
- cerr << " -> error #" << rc << " has occured!\n\n";
- }
- }
- }
-
- infile.get(c); // eat the \r after the \n
- }
-
- return 0;
- }
-
- All of this is pretty straight forward. Now we have a tool to try out MCI
- command strings quick and easy. Below are some other MCI scripts:
-
- #
- # boing.mci -- play boing.wav
- #
- open waveaudio alias wave shareable wait
- load wave c:\mmos2\sounds\boing.wav wait
- play wave wait
- close wave wait
-
- This example shows how the audio CD player applet recognizes CDs, it checks for
- the 8 byte long ID:
-
- #
- # playcd.mci -- play an audio CD
- #
- open cdaudio01 alias cdda shareable wait
- status cdda media present wait
- status cdda mode wait
- set cdda time format tmsf wait
- status cdda volume wait
- status cdda number of tracks wait
- status cdda length wait
- status cdda type track 1 wait
- # check unique ID (8 bytes)
- info cdda ID wait
- # check unique UPC (bcd number)
- info cdda UPC wait
- seek cdda to start wait
- # this provokes an error, for there is no window to notify
- play cdda notify
- play cdda wait
- close cdda wait
-
-
- ΓòÉΓòÉΓòÉ 2.1.6. The Procedural Media Control Interface ΓòÉΓòÉΓòÉ
-
- The Procedural Media Control Interface
-
- While the ease of the textual MCI is unbeatable, it has some weaknesses in
- comparison to the procedural MCI.
-
- o It has to be translated (parsed) internally which needs time.
- o Anything that goes beyond a text string can't be returned as a result.
-
- The following example uses the procedural interface to query an audio CD for
- it's table of contents.
-
- ////////////////////////////////
- // cdtoc - audio CD toc sample
- //
- // using emx 0.8h, mm4emx 1.0
- //
- //
- // Marc E.E. van Woerkom, 2/94
- //
-
-
- #include <os2.h>
-
- #define INCL_OS2MM
- #define INCL_MCIOS2
- #include <os2me.h>
-
- #include <iostream.h>
- #include <iomanip.h>
-
-
- //
- // mci_err: translate the MCI return code into an error string
- //
-
- void mci_err(ULONG rc)
- {
- const rsize = 128;
- char rbuff[rsize];
-
- ULONG rc2 = mciGetErrorString(rc, // error code
- rbuff, // return buffer
- rsize); // rbuff size
-
- if (rc2 == MCIERR_SUCCESS)
- cerr << "MCI error: " << rbuff << "\n\n";
- else
- cerr << "error #" << rc << " has occured!\n\n";
- }
-
- This function prints out a time given in MMTIME format. It employs the helper
- macros ULONG_LWLB, ULONG_LWHB and ULONG_HWLB to get the information out of the
- ULONG.
-
-
- //
- // print_mmtime: print time given in MMTIME as hh.mm.ss
- //
-
- void print_mmtime(ULONG mmtime)
- {
- ULONG hms = HMSFROMMM(mmtime);
-
- // hms packing is: |--|ss|mm|hh|
-
- int hour = int(ULONG_LWLB(hms));
- int min = int(ULONG_LWHB(hms));
- int sec = int(ULONG_HWLB(hms));
-
- if (hour)
- cout << setw(4) << setfill('0')
- << hour << '.'
- << setfill('0');
- else
- cout << setfill(' '); // I believe this shouldn't be neccessary
-
- cout << setw(2) << min << '.';
-
- cout << setw(2) << setfill('0')
- << sec
- << setfill(' '); // this neither
- }
-
-
- //
- // main
- //
-
- int main()
- {
- cout << "cdtoc -- Audio CD Table of Contents\n\n";
-
- // open the audio CD device
-
- Each MCI command has its specific parameter structure. All unused fields
- should be set to 0.
-
- The device to work with (CDaudio) is identified via its name.
-
- MCI_OPEN_PARMS mop;
-
- mop.hwndCallback = 0;
- mop.usDeviceID = 0;
- mop.pszDeviceType = MCI_DEVTYPE_CD_AUDIO_NAME;
- mop.pszElementName = 0;
-
- The first parameter of mciSendCommand() is the ID of the device. In this
- special case it's not necessary - the ID is returned to a field of the
- parameter structure.
-
- Next is a message specifying the command, MCI_OPEN in this case.
-
- The third parameter contains some so-called message flags. Here they tell the
- MDM to wait until the open action is completed and to open it in shared mode.
-
- Then a pointer to the parameter structure is given.
-
- The last parameter is usually 0.
-
- ULONG rc = mciSendCommand(0,
- MCI_OPEN, // open message
- MCI_WAIT | MCI_OPEN_SHAREABLE, // message flags
- &mop, // parameters
- 0);
-
- if (rc != MCIERR_SUCCESS) {
- mci_err(rc);
- return 1;
- }
-
- Now we issue a GETTOC command. It's not accessible from the textual MCI,
- because it returns a bunch of information that doesn't fit into a simple line
- of text. (Let's hope there is no CD with more than 99 tracks.)
-
- // ask for the table of contents
-
- const MAXTOCRECS = 99;
- MCI_TOC_REC mtr[MAXTOCRECS];
-
- MCI_TOC_PARMS mtp;
-
- mtp.hwndCallback = 0;
- mtp.pBuf = mtr;
- mtp.ulBufSize = sizeof(mtr);
-
- Note that this time (like in most cases) the device to work with is specified
- via the ID obtained from MCI_OPEN
-
- rc = mciSendCommand(mop.usDeviceID, // device ID
- MCI_GETTOC, // get toc message
- MCI_WAIT, // message flags
- &mtp, // parameters
- 0);
-
- if (rc != MCIERR_SUCCESS) mci_err(rc);
-
- // close the device
-
- MCI_CLOSE doesn't have any special parameters, so the parameter structure is of
- the type MCI_GENERIC_PARMS:
-
- MCI_GENERIC_PARMS mgp;
- mgp.hwndCallback = 0;
-
- ULONG rc2 = mciSendCommand(mop.usDeviceID, MCI_CLOSE, MCI_WAIT, &mgp, 0);
-
- if (rc2 != MCIERR_SUCCESS) mci_err(rc2);
-
-
- // now show the TOC, if been successful
-
- if (rc == MCIERR_SUCCESS) {
-
- OK, MCI_GETTOC was successful, so print out the obtained toc entries:
-
- int i = 0;
-
- while (mtr[i].TrackNum) {
- cout << "Track" << setw(3);
- cout << int(mtr[i].TrackNum)
- << ": Length ";
- print_mmtime(mtr[i].ulEndAddr - mtr[i].ulStartAddr);
- cout << " [";
- print_mmtime(mtr[i].ulStartAddr);
- cout << " to ";
- print_mmtime(mtr[i].ulEndAddr);
- cout << "] Control " << int(mtr[i].Control)
- << ", Country " << mtr[i].usCountry
- << ", Owner " << mtr[i].ulOwner
- << ", #" << mtr[i].ulSerialNum << "\n";
- i++;
- }
- }
-
-
- // that's all folks!
-
- return 0;
- }
-
- Now let's try CDTOC.EXE on a certain audio CD with "Gorgeous Gals",
- "Transsylvanian Parties" etc. :-)
-
- cdtoc -- Audio CD Table of Contents
-
- Track 1: Length 4.32 [ 0.02 to 4.34] Control 1, Country 0, Owner 0, #0
- Track 2: Length 2.46 [ 4.34 to 7.21] Control 1, Country 0, Owner 0, #0
- Track 3: Length 2.45 [ 7.21 to 10.06] Control 1, Country 0, Owner 0, #0
- Track 4: Length 3.19 [10.06 to 13.26] Control 1, Country 0, Owner 0, #0
- Track 5: Length 3.24 [13.26 to 16.50] Control 1, Country 0, Owner 0, #0
- Track 6: Length 2.12 [16.50 to 19.02] Control 1, Country 0, Owner 0, #0
- Track 7: Length 3.04 [19.02 to 22.07] Control 1, Country 0, Owner 0, #0
- Track 8: Length 1.48 [22.07 to 23.55] Control 1, Country 0, Owner 0, #0
- Track 9: Length 2.31 [23.55 to 26.27] Control 1, Country 0, Owner 0, #0
- Track 10: Length 2.46 [26.27 to 29.14] Control 1, Country 0, Owner 0, #0
- Track 11: Length 8.19 [29.14 to 37.33] Control 1, Country 0, Owner 0, #0
- Track 12: Length 2.54 [37.33 to 40.28] Control 1, Country 0, Owner 0, #0
- Track 13: Length 3.04 [40.28 to 43.32] Control 1, Country 0, Owner 0, #0
- Track 14: Length 1.31 [43.32 to 45.04] Control 1, Country 0, Owner 0, #0
-
- Yup, this is the table of contents of the Rocky Horror Picture Show soundtrack!
- I chose this CD because track 11 has 3 subtracks (2.46, 3.34, 1.53). However
- they don't show up here, so I have to guess further what the Control field is
- good for ("track control field").
-
-
- ΓòÉΓòÉΓòÉ 2.1.7. Querying MMPM/2 System Values ΓòÉΓòÉΓòÉ
-
- Querying MMPM/2 System Values
-
- Remember PM's WinQuerySysValue() API call? MMPM/2 has a counterpart named
- mciQuerySysValue(). But in contrast to PM there are only less than a dozen
- MMPM/2 system values defined, which the following example displays.
-
- ///////////////////////////////////////////
- // mmpmvals - MMPM/2 system values sample
- //
- // using emx 0.8h, mm4emx 1.0
- //
- //
- // Marc E.E. van Woerkom, 2/94
- //
-
-
- #include <os2.h>
-
- #define INCL_OS2MM
- #define INCL_MCIOS2
- #include <os2me.h>
-
- #include <iostream.h>
-
-
- //
- // main
- //
-
- int main()
- {
- cout << "mmpmvals -- MMPM/2 System Values\n\n";
-
- BOOL ClosedCaption;
- mciQuerySysValue(MSV_CLOSEDCAPTION, &ClosedCaption);
- cout << "MSV_CLOSEDCAPTION : " << int(ClosedCaption) << "\n";
-
- ULONG MasterVolume;
- mciQuerySysValue(MSV_MASTERVOLUME, &MasterVolume);
- cout << "MSV_MASTERVOLUME : " << int(MasterVolume) << "\n";
-
- ULONG Headphones;
- mciQuerySysValue(MSV_HEADPHONES, &Headphones);
- cout << "MSV_HEADPHONES : " << int(Headphones) << "\n";
-
- ULONG Speakers;
- mciQuerySysValue(MSV_SPEAKERS, &Speakers);
- cout << "MSV_SPEAKERS : " << int(Speakers) << "\n";
-
- CHAR WorkPath[CCHMAXPATH];
- mciQuerySysValue(MSV_WORKPATH, WorkPath);
- cout << "MSV_WORKPATH : " << WorkPath << "\n";
-
- ULONG SysqOsValue;
- mciQuerySysValue(MSV_SYSQOSVALUE, &SysqOsValue);
- cout << "MSV_SYSQOSVALUE : " << int(SysqOsValue) << "\n";
-
- ULONG SysqOsErrorFlag;
- mciQuerySysValue(MSV_SYSQOSERRORFLAG, &SysqOsErrorFlag);
- cout << "MSV_SYSQOSERRORFLAG: " << int(SysqOsErrorFlag) << "\n";
-
-
- // that's all folks!
-
- return 0;
- }
-
- Running MMPMVALS.EXE on my system yields:
-
- mmpmvals -- MMPM/2 System Values
-
- MSV_CLOSEDCAPTION : 1
- MSV_MASTERVOLUME : 100
- MSV_HEADPHONES : 1
- MSV_SPEAKERS : 1
- MSV_WORKPATH : C:\MMOS2
- MSV_SYSQOSVALUE : 65537
- MSV_SYSQOSERRORFLAG: 2
-
-
- ΓòÉΓòÉΓòÉ 2.1.8. A First Rendezvous with the MMIO Subsystem ΓòÉΓòÉΓòÉ
-
- A First Rendezvous with the MMIO Subsystem
-
- The MMIO subsystem is responsible for I/O on multimedia files. It comes with
- several I/O procedures installed, which can handle specific kinds of data. The
- example below will show the installed MMIO procedures.
-
- //////////////////////////////////////////
- // mmiofmts - MMPM/2 mmio formats sample
- //
- // using emx 0.8h, mm4emx 1.0
- //
- //
- // Marc E.E. van Woerkom, 2/94
- //
-
-
- #include <os2.h>
-
- #define INCL_OS2MM
- #include <os2me.h>
-
- #include <iostream.h>
- #include <iomanip.h>
-
- Wrap MMFORMATINFO into a C++ class.
-
- //
- // mmformatinfo
- //
-
- class mmformatinfo {
- MMFORMATINFO mmfi;
- public:
- mmformatinfo(FOURCC IOProc=0);
- MMFORMATINFO* get_addr() { return &mmfi; }
- LONG get_NameLength() { return mmfi.lNameLength; }
- PSZ get_DefaultFormatExt() { return mmfi.szDefaultFormatExt; }
- FOURCC get_IOProc() { return mmfi.fccIOProc; }
- };
-
- This constructor clears the MMFORMATINFO structure and initializes the
- fccIOProc field with the proper four character code specific to the MMIO
- procedure and the format it handles. Use 0 as default argument.
-
- mmformatinfo::mmformatinfo(FOURCC IOProc=0)
- {
- char* p = (char*) &mmfi;
- for (int i=0; i<sizeof(mmfi); i++) p[i] = 0;
-
- mmfi.fccIOProc = IOProc;
- }
-
-
- //
- // mmio_err: translate MMIO error code into a string
- //
-
- void mmio_err(ULONG rc)
- {
- cerr << "MMIO error: ";
-
- char* s;
-
- switch (rc) {
- case MMIO_SUCCESS:
- s = "SUCCESS (huh?)";
- break;
- case MMIOERR_UNBUFFERED:
- s = "UNBUFFERD";
- break;
- case MMIOERR_INVALID_HANDLE:
- s = "INVALID HANDLE";
- break;
- case MMIOERR_INVALID_PARAMETER:
- s = "INVALID PARAMETER";
- break;
- case MMIOERR_READ_ONLY_FILE:
- s = "READ ONLY FILE";
- break;
- case MMIOERR_WRITE_ONLY_FILE:
- s = "WRITE ONLY FILE";
- break;
- case MMIOERR_WRITE_FAILED:
- s = "WRITE FAILED";
- break;
- case MMIOERR_READ_FAILED:
- s = "READ FAILED";
- break;
- case MMIOERR_SEEK_FAILED:
- s = "SEEK FAILED";
- break;
- case MMIOERR_NO_FLUSH_NEEDED:
- s = "NO FLUSH NEEDED";
- break;
- case MMIOERR_OUTOFMEMORY:
- s = "OUT OF MEMORY";
- break;
- case MMIOERR_CANNOTEXPAND:
- s = "CANNOT EXPAND";
- break;
- case MMIOERR_FREE_FAILED:
- s = "FREE FAILED";
- break;
- case MMIOERR_CHUNKNOTFOUND:
- s = "CHUNK NOT FOUND";
- break;
- case MMIO_ERROR:
- s = "ERROR";
- break;
- case MMIO_WARNING:
- s = "WARNING";
- break;
- case MMIO_CF_FAILURE:
- s = "CF FAILURE";
- break;
- default:
- cerr << rc;
- s = " (hmm...)";
- }
-
- cerr << s << "\n";
- }
-
-
- //
- // main procedure
- //
- //
- // WARNING: The MMPM/2 stops working on my system
- // if I use a string as argument for the FOURCC mask
- // which is none of the registered ones! (e.g. wuff)
- //
- // Looks like a MMPM/2 bug to me.
- // (A major confusion of mmio.dll?)
- //
-
- int main(int argc, char* argv[])
- {
- cout << "mmiofmts -- MMPM/2 MMIO Formats\n\n";
-
-
- // parse args
-
- FOURCC mask = 0;
-
- if (argc>1) {
-
- The mmioStringToFOURCC() API call translates a string into a four character
- code.
-
- mask = mmioStringToFOURCC(argv[1], MMIO_TOUPPER);
-
- A FOURCC is a 32-bit variable, containing 4 characters.
-
- cout << "mask in use is [";
- char* p = (char*) &mask;
- for (int i=0; i<4; i++) cout << p[i];
- cout << "]\n\n";
- }
-
-
- // query # of IOProcedures
-
- mmformatinfo mmfi_spec(mask);
-
- ULONG NumFormats = 0;
-
- mmioQueryFormatCount() returns the number of installed MMIO procedures.
-
- ULONG rc = mmioQueryFormatCount(mmfi_spec.get_addr(),
- &NumFormats,
- 0,
- 0);
-
- if (rc != MMIO_SUCCESS) {
- mmio_err(rc);
- return 1;
- }
-
- cout << "formats supported: " << NumFormats;
-
- if (!NumFormats) return 0;
-
-
- // get formats
-
- Get all format information via mmioGetFormats().
-
- mmformatinfo* mmfip = new mmformatinfo[NumFormats];
-
- ULONG FormatsRead = 0;
-
- rc = mmioGetFormats(mmfi_spec.get_addr(),
- NumFormats,
- mmfip,
- &FormatsRead,
- 0,
- 0);
-
- if (rc != MMIO_SUCCESS) {
- mmio_err(rc);
- return 2;
- }
-
- cout << " (" << FormatsRead << " formats read)\n\n";
-
-
- // print information
-
- cout << "no. 4-cc name leng extn\n\n";
-
- for (int i=0; i<NumFormats; i++) {
- cout.setf(ios::right, ios::adjustfield);
- cout << setw(2) << i+1 << ": [";
-
- FOURCC IOProc = mmfip[i].get_IOProc();
- char* p = (char*) &IOProc;
-
- for (int j=0; j<4; j++) cout << p[j];
- cout << "] ";
-
- LONG NameLength = mmfip[i].get_NameLength();
-
- if (NameLength) {
- PSZ name = new CHAR[NameLength+1];
- LONG BytesRead = 0;
-
- Extract the name of the format via mmioGetFormatName().
-
- rc = mmioGetFormatName(mmfip[i].get_addr(),
- name,
- &BytesRead,
- 0,
- 0);
-
- name[NameLength] = 0;
-
- if (rc != MMIO_SUCCESS) {
- mmio_err(rc);
- cout << " ";
- }
- else {
- cout.setf(ios::left, ios::adjustfield);
- cout << setw(40) << name << " ("
- << setw(2) << BytesRead;
- }
-
- delete[] name;
- }
- else
- cout << "-" << setw(43) << "( 0";
-
- cout.setf(ios::left, ios::adjustfield);
- cout << ") ."
- << setw(3) << mmfip[i].get_DefaultFormatExt() << "\n";
- }
-
- delete[] mmfip;
-
-
- // that's all folks!
-
- return 0;
- }
-
- So let's run mmiofmts.exe to display the currently installed MMIO procedures:
-
- mmiofmts -- MMPM/2 MMIO Formats
-
- formats supported: 15 (15 formats read)
-
- no. 4-cc name leng extn
-
- 1: [RDIB] RIFF DIB Image (14) .RDI
- 2: [AVCI] IBM AVC Still Video Image (25) ._IM
- 3: [MMOT] IBM MMotion Still Video Image (29) .VID
- 4: [AVCA] IBM AVC ADPCM Digital Audio (27) ._AU
- 5: [VOC ] Creative Labs Voice File (24) .VOC
- 6: [WI30] MS Windows DIB Image (20) .DIB
- 7: [MIDI] Midi File Format I/O Procedure (30) .MID
- 8: [OS13] IBM OS/2 1.3 PM Bitmap Image (28) .BMP
- 9: [OS20] IBM OS/2 2.0 BMP (16) .BMP
- 10: [AVI ] AVI IO Procedure (16) .AVI
- 11: [WAVE] RIFF WAVE Digital Audio (23) .WAV
- 12: [CF ] - ( 0) .
- 13: [BND ] - ( 0) .BND
- 14: [MEM ] - ( 0) .MEM
- 15: [DOS ] - ( 0) .DOS
-
- Save this utility for the next part of this article series when we go to
- install our own MMIO procedure!
-
-
- ΓòÉΓòÉΓòÉ 2.1.9. Memory Playlists ΓòÉΓòÉΓòÉ
-
- Memory Playlists
-
- Using an MCI script, playing several soundfiles is not smooth, for the
- waveaudio device must load each soundfile (element) into its context, delaying
- the play. A possible work-around is to load the elements into memory before
- playing starts. This can be achieved using the MMIO waveaudio I/O procedure
- and the MMPM/2 waveaudio playlist processor.
-
- Loading the waveaudio file via the MMIO subsystem into memory has the advantage
- that one can extract the necessary data without knowing much about the maybe
- complicated internal structure of such a file. The .WAV format is a special
- kind of the more general RIFF (Resource Interchange File Format) multimedia
- format. For example, the M1.WAV file is 26504 bytes long, but contains only
- 26460 bytes of pure sound data. Using MMIO we don't have to worry where it is
- located in the file.
-
- The usual encoding scheme for sound data is pulse code modulation (PCM). This
- means at a fixed rate per second (the sampling frequency) the amplitude of the
- signal of each channel is converted by an audio to digital converter into a
- number. The M1.WAV file was sampled in mono with 11kHz and 8 bit resolution,
- so it contains 26460 bytes/(11000 bytes/s) = 2.4s worth of audio data.
-
- Note: once loaded into memory, one can easily work with this data in ways such
- as applying a compression scheme to it, mixing in a second waveaudio file (take
- the mean of both amplitude values) if you want to play more than one sound at
- once, adding effects such as echoing (rescale the amplitudes and mix this data
- with a short delay over the original data), etc.
-
- A playlist is an array of playlist instructions, which is processed by the
- playlist processor. Among those instructions is not only the command to play a
- waveaudio file residing in memory, but also flow control instructions which
- allow jumps, loops and subroutines.
-
- Look at this example, which employs a playlist to play a certain rhythm :-)
-
- ///////////////////////////////////////
- // rhythm - waveaudio playlist sample
- //
- // using emx 0.8h, mm4emx 1.0
- //
- //
- // Marc E.E. van Woerkom, 2/94
- //
-
-
- #include <os2.h>
-
- #define INCL_OS2MM
- #include <os2me.h>
-
- #include <iostream.h>
- #include <iomanip.h>
-
-
- // prototypes
-
- void mci_err(ULONG);
- void mmio_err(ULONG);
-
- The MMAUDIOHEADER structure will get the header information of the waveaudio
- file from the MMIO.
-
- //
- // mmaudioheader
- //
-
- class mmaudioheader {
- MMAUDIOHEADER mmah;
- public:
- mmaudioheader();
- MMAUDIOHEADER* get_addr() { return &mmah; }
- LONG get_size() { return sizeof(mmah); }
- };
-
-
- mmaudioheader::mmaudioheader()
- {
- char* p = (char*) &mmah;
- for (int i=0; i<sizeof(mmah); i++) p[i] = 0;
- }
-
- The constructor of the mem_wav class will load a waveaudio file (given by its
- filename) into memory via the MMIO subsystem.
-
- //
- // mem_wav: a waveaudio file loaded into memory
- //
-
- class mem_wav {
- HMMIO hmmio;
- PSZ bptr;
- ULONG bsize;
- ULONG SamplesPerSec;
- USHORT BitsPerSample;
- public:
- mem_wav(char*);
- ~mem_wav();
- PSZ get_bptr() { return bptr; }
- ULONG get_bsize() { return bsize; }
- ULONG get_SamplesPerSec() { return SamplesPerSec; }
- USHORT get_BitsPerSample() { return BitsPerSample; }
- };
-
-
- mem_wav::mem_wav(char* name)
- {
-
- The MMIO subsystem looks at the .WAV extension and calls the proper I/O
- procedure to open it, delivering a handle as the result.
-
- // open the file
-
- hmmio = mmioOpen(name, 0, MMIO_READ);
-
- Now get the header information of the waveaudio file.
-
- // get header
-
- mmaudioheader mmah;
-
- ULONG BytesRead = 0;
-
- ULONG rc = mmioGetHeader(hmmio, mmah.get_addr(), mmah.get_size(),
- &BytesRead, 0, 0);
-
- if (rc != MMIO_SUCCESS) mmio_err(rc);
-
- The header contains the length in bytes (needed to allocate the buffer memory),
- the sampling frequency and the sampling resolution (these settings are needed
- for a proper reproduction).
-
- // get some infos about the waveaudio file
-
- SamplesPerSec = mmah.get_addr()->mmXWAVHeader.WAVEHeader.ulSamplesPerSec;
- BitsPerSample = mmah.get_addr()->mmXWAVHeader.WAVEHeader.usBitsPerSample;
-
- bsize = mmah.get_addr()->mmXWAVHeader.XWAVHeaderInfo.ulAudioLengthInBytes;
- bptr = new CHAR[bsize];
-
- The buffer is allocated, now read all information into it.
-
- // read file
-
- rc = mmioRead(hmmio, bptr, bsize);
-
- if (rc == MMIO_ERROR) mmio_err(rc);
-
- cout << "[file " << name
- << ": read" << setw(7) << rc
- << " bytes of" << setw(7) << bsize << "]\n";
-
- Finally close the file. That's all! (And we didn't need to know anything
- about RIFF chunks, etc.)
-
- // close file
-
- rc = mmioClose(hmmio, 0);
- if (rc != MMIO_SUCCESS) mmio_err(rc);
- }
-
- The destructor of the class gets rid of the allocated memory resources.
-
- mem_wav::~mem_wav()
- {
- delete[] bptr;
- }
-
- Now comes the playlist. A playlist entry is composed of four ULONG variables.
- The first represents the instruction and the others are its possible arguments.
-
- //
- // ple: a playlist entry
- //
-
- struct ple {
- ULONG operation;
- ULONG operand1;
- ULONG operand2;
- ULONG operand3;
- };
-
-
- This class represents a playlist. It should be stated that it is tailored for
- this special example. (For it reads exactly 7 different samples and the
- playlist is hardcoded into the setup() member function. A general class of
- this kind should be able to deal with a variable amount of samples and should
- read the playlist from a data file or a resource block.)
-
-
- //
- // playlist: a waveaudio playlist
- //
-
- class playlist {
- ple* pl;
- int size, used;
- mem_wav& m1, m2, m3, m4, m5, s1, p1;
- void setup();
- public:
- playlist(mem_wav&, mem_wav&, mem_wav&, mem_wav&,
- mem_wav&, mem_wav&, mem_wav&, int);
- ~playlist();
- ple* get_addr() { return pl; }
- int add(ULONG =0, ULONG =0, ULONG =0, ULONG =0);
- int add_branch(ULONG);
- int add_call(ULONG);
- int add_data(mem_wav&);
- int add_exit();
- int add_return();
- };
-
- Allocate the playlist entries.
-
- playlist::playlist(mem_wav& M1, mem_wav& M2, mem_wav& M3,
- mem_wav& M4, mem_wav& M5, mem_wav& S1, mem_wav& P1,
- int Size)
- : m1(M1), m2(M2), m3(M3), m4(M4), m5(M5), s1(S1), p1(P1),
- size(Size)
- {
- if (size < 1) cerr << "error: wrong playlist size!\n";
-
- pl = new ple[size];
- used = 0;
-
- setup();
- }
-
-
- playlist::~playlist()
- {
- delete[] pl;
- }
-
- This member function will fill a playlist entry with the proper values. Note
- the default arguments. And it returns the number of the current entry which
- will come in handy when employed in setup().
-
- int playlist::add(ULONG op=0, ULONG opd1=0, ULONG opd2=0, ULONG opd3=0)
- {
- if (used >= size) {
- cerr << "error: playlist is too small!\n";
- return -1;
- }
-
- pl[used].operation = op;
- pl[used].operand1 = opd1;
- pl[used].operand2 = opd2;
- pl[used].operand3 = opd3;
-
- return used++;
- }
-
- A branch operation (jump to a specific entry).
-
- int playlist::add_branch(ULONG addr)
- {
- return add(BRANCH_OPERATION, 0, addr);
- }
-
- A call operation (call a playlist subroutine).
-
- int playlist::add_call(ULONG addr)
- {
- return add(CALL_OPERATION, 0, addr);
- }
-
- A data operation (play a waveaudio file from a buffer).
-
- int playlist::add_data(mem_wav& mw)
- {
- return add(DATA_OPERATION, ULONG(mw.get_bptr()), mw.get_bsize());
- }
-
- An exit operation (end the playlist processing).
-
- int playlist::add_exit()
- {
- return add(EXIT_OPERATION);
- }
-
- A return operation (return from a playlist subroutine).
-
- int playlist::add_return()
- {
- return add(RETURN_OPERATION);
- }
-
- This is a hardwired playlist. Note that is ordered in a way that only one
- forward reference (70) is needed.
-
- void playlist::setup()
- {
-
- Jump to the 70th playlist entry.
-
- add_branch(70);
-
- This is one of several subroutines. It plays the buffer containing the audio
- data of M1.WAV thrice.
-
- ULONG Intro = add_data(m1);
- add_data(m1);
- add_data(m1);
- add_return();
-
- ULONG A10a = add_data(m2);
- add_data(s1);
- add_data(p1);
- add_data(m3);
- add_data(m4);
- add_data(m5);
- add_data(m4);
- add_data(m5);
- add_data(m4);
- add_data(m5);
- add_return();
-
- ULONG A10 = add_data(m2);
- add_data(s1);
- add_data(p1);
- add_data(m5);
- add_data(m4);
- add_data(m5);
- add_data(m4);
- add_data(m5);
- add_data(m4);
- add_data(m5);
- add_return();
-
- ULONG B10 = add_data(m2);
- add_data(s1);
- add_data(p1);
- add_data(m1);
- add_data(m1);
- add_data(m1);
- add_data(m1);
- add_data(m1);
- add_data(m1);
- add_data(m1);
- add_return();
-
- ULONG C10 = add_data(m2);
- add_data(s1);
- add_data(p1);
- add_data(m2);
- add_data(m2);
- add_data(m2);
- add_data(m2);
- add_data(m2);
- add_data(m2);
- add_data(m2);
- add_return();
-
- ULONG A6 = add_data(m2);
- add_data(s1);
- add_data(p1);
- add_data(m5);
- add_data(m4);
- add_data(m5);
- add_return();
-
- ULONG B6 = add_data(m2);
- add_data(s1);
- add_data(p1);
- add_data(m1);
- add_data(m1);
- add_data(m1);
- add_return();
-
- ULONG C6 = add_data(m2);
- add_data(s1);
- add_data(p1);
- add_data(m2);
- add_data(m2);
- add_data(m2);
- add_return();
-
- Well, I didn't count until here. I simply printed out the return code of the
- next call in a prior version of this source and noted it.
-
- // #70
- add_call(Intro);
- add_call(A10a);
- add_call(B10);
- add_call(A10);
- add_call(B10);
- add_call(A10);
- add_call(C10);
- add_call(A6);
- add_call(B6);
- add_call(A6);
- add_call(C10);
- add_call(A10);
- add_call(C10);
- add_call(A10);
- add_call(A10);
- add_call(A10);
- add_call(C10);
- add_call(A6);
- add_call(C6);
- add_call(A6);
- add_call(B10);
- add_data(s1);
- add_exit();
- }
-
- This class represents the waveaudio device together with an associated
- playlist. The characteristics of the mem_wav given to the constructor are used
- for the processing of the whole playlist.
-
- //
- // waveaudio: a waveaudio device
- //
-
- class waveaudio {
- MCI_OPEN_PARMS mop;
- public:
- waveaudio(playlist&, mem_wav&);
- ~waveaudio();
- void play();
- };
-
-
- waveaudio::waveaudio(playlist& pl, mem_wav& mw)
- {
-
- Open the waveaudio device via an MCI command message in a way that it will use
- a playlist as data.
-
- // open device
-
- mop.hwndCallback = 0;
- mop.usDeviceID = 0;
- mop.pszDeviceType = MCI_DEVTYPE_WAVEFORM_AUDIO_NAME;
- mop.pszElementName = PSZ(pl.get_addr());
-
- ULONG rc = mciSendCommand(0,
- MCI_OPEN, // open message
- MCI_WAIT | MCI_OPEN_SHAREABLE | // message flags
- MCI_OPEN_PLAYLIST,
- &mop, // parameters
- 0);
-
- if (rc != MCIERR_SUCCESS) mci_err(rc);
-
- If these values aren't set via MCI_SET, the waveaudio data will be played with
- improper speed or even be garbled.
-
- // set device parameters
-
- MCI_WAVE_SET_PARMS wsp;
-
- wsp.hwndCallback = 0;
- wsp.ulSamplesPerSec = mw.get_SamplesPerSec();
- wsp.usBitsPerSample = mw.get_BitsPerSample();
-
- rc = mciSendCommand(mop.usDeviceID,
- MCI_SET,
- MCI_WAIT |
- MCI_WAVE_SET_SAMPLESPERSEC |
- MCI_WAVE_SET_BITSPERSAMPLE,
- &wsp,
- 0);
-
- if (rc != MCIERR_SUCCESS) mci_err(rc);
- }
-
- Close the waveaudio device.
-
- waveaudio::~waveaudio()
- {
- // close device
-
- MCI_GENERIC_PARMS mgp;
-
- mgp.hwndCallback = 0;
-
- ULONG rc = mciSendCommand(mop.usDeviceID,
- MCI_CLOSE,
- MCI_WAIT,
- &mgp,
- 0);
-
- if (rc != MCIERR_SUCCESS) mci_err(rc);
- }
-
- Set the waveaudio device on 'play'.
-
- void waveaudio::play()
- {
- // play the playlist
-
- MCI_PLAY_PARMS mpp;
-
- mpp.hwndCallback = 0;
-
- ULONG rc = mciSendCommand(mop.usDeviceID,
- MCI_PLAY,
- MCI_WAIT,
- &mpp,
- 0);
-
- if (rc != MCIERR_SUCCESS) mci_err(rc);
- }
-
-
- Routines to print pretty error messages.
-
- //
- // mci_err: translate the MCI return code into an error string
- //
-
- void mci_err(ULONG rc)
- {
- const rsize = 128;
- char rbuff[rsize];
-
- ULONG rc2 = mciGetErrorString(rc, // error code
- rbuff, // return buffer
- rsize); // rbuff size
-
- if (rc2 == MCIERR_SUCCESS)
- cerr << "MCI error: " << rbuff << "\n\n";
- else
- cerr << "error #" << rc << " has occured!\n\n";
- }
-
-
- //
- // mmio_err: translate MMIO error code into a string
- //
-
- void mmio_err(ULONG rc)
- {
- cerr << "MMIO error: ";
-
- char* s;
-
- switch (rc) {
- case MMIO_SUCCESS:
- s = "SUCCESS (huh?)";
- break;
- case MMIOERR_UNBUFFERED:
- s = "UNBUFFERD";
- break;
- case MMIOERR_INVALID_HANDLE:
- s = "INVALID HANDLE";
- break;
- case MMIOERR_INVALID_PARAMETER:
- s = "INVALID PARAMETER";
- break;
- case MMIOERR_READ_ONLY_FILE:
- s = "READ ONLY FILE";
- break;
- case MMIOERR_WRITE_ONLY_FILE:
- s = "WRITE ONLY FILE";
- break;
- case MMIOERR_WRITE_FAILED:
- s = "WRITE FAILED";
- break;
- case MMIOERR_READ_FAILED:
- s = "READ FAILED";
- break;
- case MMIOERR_SEEK_FAILED:
- s = "SEEK FAILED";
- break;
- case MMIOERR_NO_FLUSH_NEEDED:
- s = "NO FLUSH NEEDED";
- break;
- case MMIOERR_OUTOFMEMORY:
- s = "OUT OF MEMORY";
- break;
- case MMIOERR_CANNOTEXPAND:
- s = "CANNOT EXPAND";
- break;
- case MMIOERR_FREE_FAILED:
- s = "FREE FAILED";
- break;
- case MMIOERR_CHUNKNOTFOUND:
- s = "CHUNK NOT FOUND";
- break;
- case MMIO_ERROR:
- s = "ERROR";
- break;
- case MMIO_WARNING:
- s = "WARNING";
- break;
- case MMIO_CF_FAILURE:
- s = "CF FAILURE";
- break;
- default:
- cerr << rc;
- s = " (hmm...)";
- }
-
- cerr << s << "\n";
- }
-
- Main function. Perhaps you should start reading this example from here.
-
- //
- // main
- //
-
- int main()
- {
- cout << "rhythm -- a Rhythm Generator\n\n";
-
-
- // load waveaudio files into memory
-
- cout << "loading waveaudio files into memory ...\n\n";
-
- mem_wav m1("m1.wav");
- mem_wav m2("m2.wav");
- mem_wav m3("m3.wav");
- mem_wav m4("m4.wav");
- mem_wav m5("m5.wav");
- mem_wav s1("s1.wav");
- mem_wav p1("p1.wav");
-
-
- // set up playlist
-
- cout << "\nsetting up playlist ...\n\n";
-
- playlist pl(m1, m2, m3, m4, m5, s1, p1, 100);
-
-
- // play playlist
-
- cout << "and ... go! (you should pump up the volume :-)\n\n";
-
- waveaudio wav(pl, m1);
- wav.play();
-
-
- // that's all folks!
-
- cout << "... done. yeah!\n";
-
- return 0;
- }
-
- Try it! :-)
-
- Note that this example is not too far away from the .MOD file playing
- mechanism. The hardest thing for an extension in this direction is probably
- getting the proper .MOD file definition. Since there was an unconfirmed report
- on Usenet that IBM may deliver a .MOD MMIO procedure in the next MMPM/2
- release, I personally won't put time into something like that.
-
-
- ΓòÉΓòÉΓòÉ 2.1.10. What's Next? ΓòÉΓòÉΓòÉ
-
- What's Next?
-
- At least two important topics are still on my list:
-
- o Expect to see more on the MMIO subsystem. Procedures for handling compressed
- waveaudiofiles are under development.
-
- o The PM extensions (graphical buttons, circular sliders and the secondary
- windows support) cry for some nice examples.
-
-
- ΓòÉΓòÉΓòÉ 2.1.11. More Literature on MMPM/2 ΓòÉΓòÉΓòÉ
-
- More Literature on MMPM/2
-
- IBM Doc. S53G-2166:
- OS/2 Online Book Collection CD-ROM.
-
- This CD-ROM contains 144 different OS/2 manuals in *.boo format and
- readers for OS/2 and DOS. (Highly recommended!)
-
-
- ΓòÉΓòÉΓòÉ 2.2. Porting STEP02 to ICLUI ΓòÉΓòÉΓòÉ
-
-
- ΓòÉΓòÉΓòÉ 2.2.1. Introduction ΓòÉΓòÉΓòÉ
-
- Written by Kenton W. Shaver
-
- Introduction
-
- This article describes the translation of Gavin Baker's STEP02.C to the IBM's
- C++ libraries for PM (ICLUI). Hopefully it will assist the programmer who is
- now learning these libraries or is attempting such ports of their own.
-
- The result - CLOCK.EXE - requires the C-Set++ DLLs to run, as it is linked
- dynamically. A slightly modified copy of the original program that will
- compile under C-Set++ is also included so that the reader can conduct their own
- performance analysis and browsing sessions with the original program and the
- resulting port, if desired.
-
- Scope
-
- I'm assuming that you have read Gavin Baker's article in volume 1, issue 2,
- that you basically understand Presentation Manager programming, and that you
- have perused the recent articles here appearing about ICLUI [by Gordon
- Zeglinski, in volume 2, issue 1 - editor] detailing the IEvent, IWindow, and
- the IHandler clases, etc. Some important issues will be mentioned again for
- easier retention.
-
-
- ΓòÉΓòÉΓòÉ 2.2.2. Overall Structure ΓòÉΓòÉΓòÉ
-
- Overall Structure
-
- In the ported program, a main window is created which in turn creates a client
- window, a menu window, and an object window for its own use; with the exception
- of the object window, this is what the original program does also. The object
- window launches a thread in charge of keeping time and updating the main
- window's client window at one second intervals. The messages exchanged between
- the frame window and its object window don't figure for much, though,
- especially since no easy method to thread the message processing loop of the
- class myObjectWindow is provided; this device is left in place for
- illustration.
-
- The two other classes introduced here modify IFrameHandler so that it processes
- WM_SYSCOMMAND events and the creation of another IHandler class capable of
- processing WM_QUERYTRACKINFO events.
-
- The program itself does basically the same thing as STEP02.C did, with the
- exceptions that it uses DosSleep() instead of WinTimer() to provide the timer
- support and it subclasses the frame window so that it cannot be resized below
- certain dimensions.
-
-
- ΓòÉΓòÉΓòÉ 2.2.3. Subclassing ΓòÉΓòÉΓòÉ
-
- Subclassing
-
- What do we mean when we say that our program uses subclassing? Do we mean
- subclass in the C++ sense, or in the PM sense? In the C++ sense, we might be
- indicating that we have used inheritance in our program, while in the PM sense,
- we are saying that we have replaced an existing window procedure, thus
- modifying the behavior of the object in question.
-
- Our program does the latter, as illustrated by the following snippet:
-
- Boolean mainWindow::trackAction(queryTrackEvent &qte)
- {
- IWindow::defaultProcedure(qte);
- grabMinTrackSize(qte).x *= 3;
- grabMinTrackSize(qte).y *=5;
- return true; // don't call defaultProcedure again!! thanks
- }
-
- A member function of an IHandler derived class returns true when no further
- processing of an event is needed. This subclassing method returns true each
- time it is called because it has already called IWindow::defaultProcedure(),
- IBM's C++ equivalent of WinDefWindowProc().
-
- Because of this method of subclassing, WinSubclassWindow() isn't needed -
- that's all there is to it.
-
-
- ΓòÉΓòÉΓòÉ 2.2.4. Window Procedures in ICLUI ΓòÉΓòÉΓòÉ
-
- Window Procedures in ICLUI
-
- When looking at the definition of the myObjectWindow class, we see that it only
- needs a dispatchHandlerEvent() procedure to handle events. This member can make
- no assumptions about what type of events it will be getting.
-
- In contrast, command() can assume that it will only be sent WM_COMMAND events,
- systemCommand() can be sure it will only get WM_SYSCOMMAND events, and
- trackAction() will only be called on to handle WM_QUERYTRACKINFO events. These
- methods don't need to look at the event ID, but in the case of
- myObjectWindow::dispatchHandlerEvent(), we have to actually look at the event
- ID and use a switch block.
-
-
- ΓòÉΓòÉΓòÉ 2.2.5. The About Box ΓòÉΓòÉΓòÉ
-
- The About Box Figure 1) CLOCK.CPP about box
-
- Modifying the original about box presents little trouble. First, STEP02.RES is
- loaded into the Dialog Editor. Be sure to specify MSGDEFINES.H as included in
- your resource file. We will rename STEP02.RC to CLOCK.RC to distinguish the old
- from the new.
-
- Now, since we are not going to have any children of aboutBoxClass - the class
- we'll represent the about box with in our program, it would certainly be okay
- to inherit from IHandler and then define a new dispatchHandlerEvent() method to
- process the few messages that an about box is concerned with; since all of them
- will be WM_COMMAND messages anyway, we'll inherit handler functionality from
- the ICommandHandler class.
-
- In either case, IHandler::handleEventsFor() initiates event processing.
-
-
- ΓòÉΓòÉΓòÉ 2.2.6. General Porting Cautions ΓòÉΓòÉΓòÉ
-
- General Porting Cautions
-
- If you undertake a port of your own from C to C++ and the UI libraries, it
- might save time to note several items.
-
- o C++ has keywords that C doesn't, such as class. Be sure you leave the
- #include <os2.h> statement outside of extern "C" blocks that you create and
- that you have the \TOOLKT21\CPLUS\OS2H path specified in the environment
- variable INCLUDE. If you use the C includes in your program, you might
- encounter compile-time errors caused by leaving these additional reserved
- words in.
-
- o Be sure to include IHandler or an IHandler descendant along with such
- IWindow-derived classes as IObjectWindow and IFrameWindow, whether it be by
- ancestry or by membership.
-
- o If you register both IFrameHandler and ICommandHandler they will both call
- your command() method, and WM_COMMAND event processing will happen twice in
- instances where a false is returned indicating that the event wasn't
- processed. For example, if you just mark the occurrence of "Open file..."
- menu selections with squawk(), each selection of this menu item will be noted
- twice if both ICommandHandler and IFrameHandler are active.
-
- o It is a good idea to copy the #define statements showing the hexadecimal
- numbers of the messages that you are using in your program. "grep" or another
- such tool will easily extract definitions of the window messages such as
- WM_CLOSE from your \TOOLKT21\CPLUS\OS2H directory if need be.
-
- o The definition of the TRACKINFO is copied from the headers because it isn't
- included with what PM header information we use [this is included if you
- #define INCL_WINTRACKRECT before #include-ing <os2.h>. Copying from the
- header files should be avoided whenever possible to minimize the amount of
- program maintenance when the definitions change - editor].
-
- o Any programs that use the IThreadMemberFn<> template must be compiled and
- linked to handle templates. If you use this template class and tell C-Set++
- to ignore template information, your program will compile and run as normal
- but it won't create the desired additional threads!
-
-
- ΓòÉΓòÉΓòÉ 2.3. Workplace Shell Development 101 ΓòÉΓòÉΓòÉ
-
-
- ΓòÉΓòÉΓòÉ 2.3.1. Introduction ΓòÉΓòÉΓòÉ
-
- Written by BjФrn Fahller
-
- Introduction
-
- Where to begin?
-
- While flipping through the System Object Model, Guide and Reference, I thought,
- "There's a lot in this thin book, although it doesn't seem impossible." Later
- while flipping through the list of WPS classes and methods in the PM
- Programming Reference, I thought, "What are all these classes? What do all
- these methods do?" Procrastination. Maybe I'll just write a classical program
- for now, and take up WPS programming in the next project?
-
- Do you recognise this line of thinking? I have done it for a long time now,
- but decided I have done it for too long, and it was high time to actually do
- something.
-
- To avoid taking on too large a task, and yet do something useful, I decided to
- try and write an extended program class. There is a serious limitation to the
- normal program class; only one object can be dropped on it. If several objects
- are dropped, the associated program will start once for every object. What I
- want, is a class that can send all the files dropped on the program object to
- the associated program as a series of parameters. Can this be done? Yes, it
- can. Turn to the next page to see how.
-
-
- ΓòÉΓòÉΓòÉ 2.3.2. What Do We Do Now That We Know What To Do? ΓòÉΓòÉΓòÉ
-
- What Do We Do Now That We Know What To Do?
-
- Creating a class description file
-
- The first thing to do is to create a class description file. Classes are
- described with the language neutral SOM Object Interface Definition language,
- or OIDL for short. This is a file with a .CSC extension. I called mine
- MAPROG.CSC for "Multiple Argument Program."
-
- Inheritance
-
- In SOM programming, unlike object oriented programming with, for example, C++,
- all classes must inherit from some class. In this case, the class to inherit
- from is obvious. We want to make a class with the functionality of the program
- class, although enhanced. So, let us begin with inheriting from the WPProgram
- class. First, this is done by including the definition of WPProgram to the .CSC
- file.
-
- include <wppgm.sc> // We need the definition of WPProgram, so it can be
- // inherited from.
-
- Once this is done, the basics of the class can be declared, as well as
- declaring the parent class.
-
- class: MAProg,
- external stem = maprog,
- local,
- external prefix = maprog_,
- classprefix = maprogM_,
- major version = 1,
- minor version = 1;
-
- parent: WPProgram;
-
- The details of interest in the above is the name of the class, MAProg, and the
- parent class. The other fields are unimportant for now.
-
- What functionality to change?
-
- The default behaviour of WPProgram objects, when objects are dropped on them,
- is to start the associated program once for every object. We want to start the
- associated program once, and send all the objects as parameters. It seems as
- if wpDrop is a good candidate for a change. This is declared in MAPROG.CSC as:
-
- methods:
-
- override wpDrop;
-
- If other methods needed to be overridden, they would be listed below wpDrop.
- The same would go for adding new methods.
-
- For the minimalist, this is all that is needed. Running the SOM compiler would
- create a C source file, where the functionality of wpDrop can be changed. Let
- us not be minimalists, though.
-
-
- ΓòÉΓòÉΓòÉ 2.3.3. Nifty Additions ΓòÉΓòÉΓòÉ
-
- Nifty Additions
-
- Class methods
-
- If the class was implemented as currently described, and the result registered,
- a template would turn up in the templates folder. The template would have the
- name "Program" and the icon of it would be the same as the one for the program
- class. Of course, we want at least the name to be different, and a different
- icon would be nice too. To achieve this, the two class methods
- wpclsQueryIconData and wpclsQueryTitle should be overridden. The following
- lines in MAPROG.CSC take care of that.
-
- override wpclsQueryTitle, class;
-
- override wpclsQueryIconData, class;
-
- A class method operates on the class itself, while other methods operate on
- instances of the class, the objects.
-
- All classes that need a template should at least override wpclsQueryTitle, so
- the template will have a unique name.
-
- Impatience
-
- So now what? We have declared what, but what about how? Patience. The
- answers will come.
-
- Compiling MAPROG.CSC with the SOM Compiler will result in, among others, a
- MAPROG.C file. Now, let us deal with how.
-
-
- ΓòÉΓòÉΓòÉ 2.3.4. Implementation ΓòÉΓòÉΓòÉ
-
- Implementation
-
- The drop
-
- How to deal with things being dropped? In maprog.c, the following lines
- implement the default drop handling:
-
- SOM_Scope MRESULT SOMLINK maprog_wpDrop(MAProg *somSelf,
- HWND hwndCnr,
- PDRAGINFO pdrgInfo,
- PDRAGITEM pdrgItem)
- {
- /* MAProgData *somThis = MAProgGetData(somSelf); */
- MAProgMethodDebug("MAProg","maprog_wpDrop");
-
- return parent_wpDrop(somSelf, hwndCnr, pdrgInfo, pdrgItem);
- }
-
- A call to this method can be handled just as a DM_DROP message being sent to a
- window procedure, except for one little detail. The method is called once for
- every object, with pdrgItem pointing to the item currently handled. That is
- exactly what we do not want, but there does not seem to be any way to avoid it.
- Instead a way around it is needed.
-
- It is fortunate that all the DRAGITEM structures can be retrieved from the
- DRAGINFO structure, and some experimentation has shown that the first time the
- method is called, pdrgItem points to the first DRAGITEM in an array. One of
- many ways around the problem, is this:
-
- if (!DrgAccessDraginfo(pdrgInfo)||
- DrgQueryDragitemPtr(pdrgInfo, 0) != pdrgItem) // Yucky way to make
- // sure we only deal
- // with this method
- // once/drop
-
- return 0;
-
- Who's holding the queue?
-
- wpDrop is called as a direct result of a DM_DROP message being received by the
- WPS, thus we are holding the message queue while processing this method call.
- We all know how annoying it is with programs that occupy the message queue.
- WPS objects occupying the message queue is many times worse, so let us avoid
- that.
-
- Getting the information from the DRAGITEM structures, and starting the
- application with WinStartApp() is simple. To do it without unnecessarily
- occupying the message queue, collect the names of the objects being dropped as
- quickly as possible, and start a parameter parsing and application starting
- thread, and return from the method call. The queue is free, and the
- application can be started.
-
- for (i=0; i < arguments; i++) {
- char *s = NULL;
- PDRAGITEM p = DrgQueryDragitemPtr(pdrgInfo, i);
- int lenContainer = DrgQueryStrNameLen(p->hstrContainerName);
- int lenSource = DrgQueryStrNameLen(p->hstrSourceName);
-
- argument[i] = (char *)_wpAllocMem(somSelf,
- lenContainer + lenSource +1,
- FALSE);
- size+= lenContainer + lenSource;
-
- if (argument[i]) {
- DrgQueryStrName(p->hstrContainerName,
- lenContainer+1,
- argument[i]);
- DrgQueryStrName(p->hstrSourceName,
- lenSource+1,
- argument[i]+lenContainer);
- } /* endif */
- } /* endfor */
- pi->somSelf = somSelf;
- pi->pDragInfo = pdrgInfo;
- pi->arguments = arguments;
- pi->argument = argument;
- _beginthread(startThread, NULL, 4096, (PVOID)pi);
- DrgDeleteDraginfoStrHandles(pdrgInfo);
- DrgFreeDraginfo(pdrgInfo);
- return 0;
-
- A problem with the above is that dynamic memory management is necessary since
- the amount of memory needed for the strings cannot be determined at compile
- time. This can lead to...
-
- Memory leaks?
-
- The normal way to allocate dynamic memory when needed, is to malloc() the
- storage. If, for some reason, you forget to free a pointer given by malloc(),
- the storage will be deallocated when the process terminates; therein lies the
- problem with malloc and WPS programming. Every byte you allocate with malloc
- belongs to the same process, the WPS itself. Every pointer you forget to free
- will make the whole system perform worse and worse.
-
- Fortunately, there is half a salvation, an object-bound malloc-like call.
- Memory allocated with _wpAllocMem will be deallocated when the object that
- allocated it, is no longer in use. Deallocate the memory with _wpFreeMem.
-
- Although malloc() works, avoid it whenever possible, and if you for whatever
- reason must use malloc, be very careful. The last thing you want to hear your
- customers say is that their whole system became sluggish and unstable after
- installing your program.
-
-
- ΓòÉΓòÉΓòÉ 2.3.5. Conclusion ΓòÉΓòÉΓòÉ
-
- Conclusion
-
- If someone with less than one week's experience can write a WPS class, anyone
- can. A week before writing this, I knew nothing about WPS programming. Now,
- after having completed this mini-project, I'm working on a real WPS
- application, so let's get some WPS apps out there!
-
-
- ΓòÉΓòÉΓòÉ 3. Columns ΓòÉΓòÉΓòÉ
-
- The following columns can be found in this issue:
-
- o Book Review - Real World Programming for OS/2 2.1
- o C++ Corner
- o Introduction to PM Programming
- o Scratch Patch
-
-
- ΓòÉΓòÉΓòÉ 3.1. Book Review - Real World Programming for OS/2 2.1 ΓòÉΓòÉΓòÉ
-
-
- ΓòÉΓòÉΓòÉ 3.1.1. Introduction ΓòÉΓòÉΓòÉ
-
- Written by Carsten Whimster
-
- Introduction
-
- The Book Review column is a new monthly column which will focus on development
- oriented books and material. This will mostly consist of programming books,
- such as this month's Real World Programming for OS/2 2.1, but could also cover
- books like The Design of OS/2. The column will be from a beginning PM
- programmer's eyes (because that's what I am), and hopefully that is most useful
- to the community in general. As I get on with the column, however, I expect to
- get better, and so within a year or so, I will (presumably :) qualify as an
- intermediate programmer, or perhaps even somewhat of an expert. Hopefully you,
- the readers, will move with me in that respect. When reading this column, try
- to pick up whichever book strikes your fancy, and join the group of people
- following our introductory PM programming columns. I will try to review books
- aimed at beginners to start with, and then move on from there.
-
- The column will largely be demand-oriented (after I get my own books out of the
- way early on), so if you have a book you would like reviewed, send me e-mail
- and tell me about it, or even better, send me the book (sorry, no returns).
- Finally, please send me your comments and thoughts so that I can make this
- column as effective as possible. After all, this is our magazine.
-
- This month's selection was chosen because it is the one of my books I like best
- :). It was recommended to me on CompuServe, and has a good reputation as a
- beginner's PM programming book.
-
-
- ΓòÉΓòÉΓòÉ 3.1.2. Review ΓòÉΓòÉΓòÉ
-
- Review
-
- Real World Programming for OS/2 2.1 is a fairly hefty book at no less than 868
- pages! It is aimed at (it says here, on the back) intermediate to advanced
- (users or programmers?), and is meant as a PM programming how-to book. It
- comes with a 3.5" diskette with all the source code from the book. It even has
- its own installation program, and a very nice one at that. The authors
- encourage you to use the source code from the book in your own programs,
- although they do insist that enough modifications should have been made to make
- the code basically your own work.
-
- After installing the sample code, it is recommended that you set or check the
- COMPILER, PATH, INCLUDE, and LIBPATH environment variables. I also found it
- necessary to set the LIB environment variable. Makefiles are included for IBM
- CSet, Borland C++,Watcom C/C++, Zortech C++, and Microsoft's C compiler. Once
- the environment variables are set properly, each of the sample apps can be
- recompiled in their own sub-directory by a file called GO.CMD, in case you
- should want to try your own modifications to the sample code (which is
- recommended by the authors.)
-
- To familiarize you a little with the book, I have included the titles of the
- chapters here:
-
- 1. Programming for OS/2
- 2. OS/2 Application Window Fundamentals
- 3. Window Management
- 4. 2.1 Common Dialogs
- 5. Menus
- 6. Presentation Spaces and Drawing
- 7. Creating and Manipulating PM Fonts and Text
- 8. Profile Management
- 9. Memory Management
- 10. Dynamic Link Libraries
- 11. Printing
- 12. Threads and Semaphores
- 13. Calling 16-Bit Code
- 14. Communication Basics
- 15. Miscellaneous Topics
-
- I haven't read every chapter of course, but I have used material from a
- surprising number of them. The authors recommend that you read chapters one
- through four, and then go on to whatever chapters interest you, but I only made
- it through the first three before curiousity and need took me elsewhere. All
- the chapters have an introduction explaining the importance of the material
- before launching into the code example. Each chapter has a sample program to
- demonstrate the use of the various APIs involved, as well as showing a possible
- application for these calls. Code from these sample programs is introduced in
- the text of each chapter, and at or near the end of each chapter the full
- source code is presented.
-
- Although this book is large, it is not a complete coverage of the API, nor is
- it meant as such. It is intended to get the reader started into various
- important intermediate areas, and to expose what the authors consider the more
- useful function calls. The book is sometimes a little sparse in its coverage
- of some of the relevant data structures, but this is usually easily rectified
- by cross-referencing with the IBM toolkit reference material.
-
- Due to the vast nature of the complete OS/2 API, there are (and must be) areas
- of interest which the authors for whatever reason, have decided to treat
- lightly, or skip altogether. Unfortunately, the application I was working on
- uses several features of the API which aren't covered well in the book. This
- necessitated a lot of foot-work which might have been avoided otherwise. For
- example, the TRACKINFO structure used to control frame windows is mentioned
- only once, and briefly at that. I also found it difficult to extract the
- correct information about pop-up menus from the complicated examples to build
- my own fairly simple pop-up, but eventually I did succeed. Frequently, I found
- that the sample code had a large number of intricately woven features which
- made it quite tricky to pull out just the necessary bits. In spite of having a
- good reputation for beginners, it probably is better off targeted at developers
- who have used the PM API for a few months already, and who consequently have
- the necessary understanding of the way things work to extract the needed parts,
- all the needed parts, and nothing but the needed parts from the sample code.
-
- Another small, but noticeable flaw is the index at the back. It has most, if
- not all of the actual API names and data structures, but when you don't know
- the name of what you are looking for (which presumably will happen frequently,
- otherwise use the reference material instead of a programming book,) it can be
- a tedious job trying to imagine what the name of a certain call or data
- structure might be. Unfortunately this is quite common among books, and
- programming books in particular, and is not just a fault with this book.
-
- Despite these minor concerns, the book remains a favorite of mine, and I use it
- frequently. The lucid writing, the good organization, and the depth of the
- material make it enjoyable most of the time. I recommend this book to anyone
- willing to dig a little, and who wants something a little more than an
- introductory book, with useful sample code, and a great breadth of coverage.
-
-
- ΓòÉΓòÉΓòÉ 3.1.3. Summary and Ratings ΓòÉΓòÉΓòÉ
-
- Summary and Ratings
-
- This book is a good buy overall, although it is a little lacking in some areas.
- It was recommended to me as a beginning PM programmer's book, but I see it as
- being a little too complex for that, unless you have experience in programming
- some other GUI. The skeleton program is good, but separating out the code you
- need from the more advanced examples can be tedious and difficult. Had the
- pace of chapters two and three been more leisurely, I would have picked up a
- lot more, and felt a lot more at ease with it. As it was, I found myself
- constantly picking it up, putting it down, looking elsewhere for simpler
- examples, and then picking it up again to give it another try. If I had a bit
- of PM, X, Mac or Windows programming background, this book would have been
- perfect.
-
- The example programs in the chapters are very good, though, and as the authors
- like to say, much more than just the normal do-nothing programs that many books
- have. The INITOR application actually made it into my \BIN directory with my
- other utilities, and most of the other programs were interesting at the very
- least. Intermediate or experienced PM programmers should love it.
-
- ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
- ΓöéBOOK ΓöéAUDIENCE ΓöéMARKΓöéCOMMENTS Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- ΓöéReal World Programming for OS/2 ΓöéIntermediateΓöéB+ ΓöéLots of good code examples, but Γöé
- Γöé2.1, Blain, Delimon, and Γöéto Advanced Γöé Γöésometimes it is too complex for Γöé
- ΓöéEnglish, SAMS Publishing. ISBN ΓöéPM C Γöé Γöénovices. Accurate. Well organized.Γöé
- Γöé0-672-30300-0. US $40, CAN $50.Γöéprogrammers Γöé ΓöéThe index needs a little beefing Γöé
- Γöé Γöé Γöé Γöéup. Good, but not entirely Γöé
- Γöé Γöé Γöé Γöécomplete how-to reference. Good Γöé
- Γöé Γöé Γöé Γöépurchase. Γöé
- ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
-
- This table will contain all books I have reviewed, so that books can be
- compared against each other. I will be very careful to rate books fairly
- relative to each other. I will rate conservatively until I get a better feel
- for what's out there. If I feel a need to adjust ratings, I will adjust all of
- them at the same time, and write a note explaining why I felt this necessary,
- for example, a new book came out which was so good that there wasn't enough
- room at the top of the scale to properly convey this. :)
-
- BOOK: The name of the book, author(s), publishing company, ISBN, and price
- (approximate.)
-
- AUDIENCE: This is a description of the audience I think the book targets best.
- This is not intended as gospel, just a guideline for people not familiar with
- the book.
-
- MARK: My opinion of the success of the book's presentation, and how well it
- targets its audience. Technical content, technical accuracy, organization,
- readability, and quality of index all weigh heavily here. I may revise,
- expand, or add categories in the future. I don't expect to see any book score
- less than C, but the scale is there if necessary.
-
- A+ Ground-breaking, all-around outstanding book.
- A Excellent book. This is what I want to see happen a lot.
- A- Excellent book with minor flaws or omissions.
- B+ Very good book with minor flaws or omissions.
- B Good book with some flaws or omissions.
- B- Good book, but in need of improvement.
- C Mediocre book with some potential, but in need of repairing.
- D Don't buy this book unless you need it, and nothing else exists.
- F Don't buy this book. Period.
-
- COMMENTS: To some extent, this is a summary of the review proper, but also
- explains why the book may have gotten a better/worse mark than otherwise
- expected. For example, if a book is very popular but didn't fare too well,
- this discrepancy would be explained here.
-
-
- ΓòÉΓòÉΓòÉ 3.1.4. Coming Up ΓòÉΓòÉΓòÉ
-
- Coming Up
-
- Next month I will be looking at Learning to Program OS/2 2.0 Presentation
- Manager by Example, Knight. Other books I intend to review are:
-
- o The Art of OS/2 C Programming, Panov and Salomon
- o OS/2 Presentation Manager Programming, Petzold - 1994
- o OS/2 Presentation Manager GPI, Winn
- o The Design of OS/2, 2nd Edititon, Kogan and Deitel - 1994
-
- This list is not set in stone, but they are books I am interested in. I am
- considering reviewing OS/2 Unleashed, but it is not strictly speaking a
- development book, so I'm going to wait until the list of real development books
- has diminished a bit. By the way, does anyone know why the special edition of
- OS/2 Unleashed has completely different authors?
-
- If anyone has a book they would like to have reviewed, I will be happy to
- oblige, so long as I can afford it, ie. no $500 books. :) Of course, the
- request would be fulfilled a lot quicker if accompanied by a book. :)
-
-
- ΓòÉΓòÉΓòÉ 3.2. C++ Corner ΓòÉΓòÉΓòÉ
-
-
- ΓòÉΓòÉΓòÉ 3.2.1. Introduction ΓòÉΓòÉΓòÉ
-
- Written by Gordon Zeglinski
-
- Introduction
-
- In this issue, we will get back to encapsulating bits and pieces of the OS/2
- API. Instead of just dumping out the final version, I will take an iterative
- approach. In this issue, the first version of a queue encapsulation hierarchy,
- will be developed. In subsequent issues, a refined version will be presented.
- The focus here, will be on the API calls, and encapsulation concepts.
-
- Loose Ends
-
- In the last issue, the makefile distributed to build the Watcom version of POV
- 2.1 was actually a NMAKE file not a WMAKE file.
-
- I recently added 4 more megs of RAM to the system here. This has greatly
- improved the stability of the WorkFrame. I suspect its instability was somehow
- related to swapping. Hopefully the OS/2 2.1 CSD will be out soon, and help
- improve its stability even more.
-
-
- ΓòÉΓòÉΓòÉ 3.2.2. Queue Basics ΓòÉΓòÉΓòÉ
-
- Queue Basics
-
- What are Queues?
-
- A queue (as implemented by the OS/2 kernel) is a linked list of elements
- allowing inter-process communication. Each record in the list contains a
- pointer to the data, the length of the data, and a ULONG parameter. The queue
- can be a FIFO (first-in, first-out) or LIFO (last-in, first out) and can have
- up to 16 priority levels. The queue is a one way communication method from the
- client to the server.
-
-
- ΓòöΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòù
- ΓòæULONG ParameterΓòæ - Application Defined
- ΓòƒΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓòó (similar to message IDs in PM)
- ΓòæData Pointer Γòæ
- ΓòƒΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓòó
- ΓòæData Length Γòæ
- ΓòÜΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓòÉΓò¥
-
- Visualization of a queue element
-
- You should have noted that queues use pointers to the application defined data.
- This means that when the client and server are in different processes, shared
- memory must be used. Using shared memory efficiently will be a refinement
- examined in the next revision of this class.
-
- The Queue API
-
- OS/2 provides the following functions to manipulate queues. Our goal is to
- encapsulate these functions for both the client and server processes.
-
- o DosCloseQueue()
- o DosCreateQueue()
- o DosOpenQueue()
- o DosPeekQueue()
- o DosPurgeQueue()
- o DosQueryQueue()
- o DosReadQueue()
- o DosWriteQueue()
-
- We will examine some of these functions in this column. The reader is referred
- to the Control Program Reference and Guide for an explanation of those
- functions not covered and for more details on those covered. As with all
- kernel functions, a return code of 0 indicates that no error occurred.
-
- DosCreateQueue()
-
- This function is used by the server process to create the queue. Its syntax
- follows:
-
- APIRET DosCreateQueue(PHQUEUE phqHandle,
- ULONG ulFlags,
- PSZ pszName);
-
- phqHandle The variable pointed to by this parameter will contain the
- queue handle on return.
- ulFlags Used to set the queue type, one of the QUE_FIFO, QUE_LIFO,
- or QUE_PRIORITY flags can be or'd with one of
- QUE_NOCONVERT_ADDRESS or QUE_CONVERT_ADDRESS.
- pszName The name of the queue. It must have the prefix "\QUEUES\" .
-
- DosOpenQueue()
-
- This function is used by the client process to obtain access to the queue. Its
- syntax follows:
-
- APIRET DosOpenQueue(PPID ppidOwner,
- PHQUEUE phqHandle,
- PSZ pszName);
-
- The parameters of this function are the same as for the DosCreateQueue()
- function, except for the ppidOwner parameter. This parameter is used to return
- the process ID of the queue's creator to the process calling this function,
- which can later be used to grant access to the shared memory object.
-
- DosWriteQueue()
-
- This function is used by the client to place data into the queue. Its syntax
- follows:
-
- APIRET DosWriteQueue(HQUEUE hqHandle,
- ULONG ulParam,
- ULONG ulSzBuf,
- PVOID pvBuffer,
- ULONG ulPriority);
-
- hqHandle The handle of the queue to be written to.
- ulParam The parameter in the queue record discussed previously.
- ulSzBuf Lenght of the data buffer.
- pvBuffer Pointer to the data buffer.
- ulPriority If the queue was created as a priority based queue, then
- this contains the elements priority. Otherwise, it is
- ignored.
-
- DosReadQueue()
-
- This function is used by the server to read data from the queue. Its syntax
- follows:
-
- APIRET DosReadQueue(HQUEUE hqHandle,
- PREQUESTDATA prdRequest,
- PULONG pulSzData,
- PPVOID ppvData,
- ULONG ulCode,
- BOOL32 bNoWait,
- PBYTE pbPriority,
- HEV hevSemaphore);
-
- hqHandle The handle of the queue to be read from.
- prdRequest Pointer to a REQUESTDATA structure.
- pulSzData Pointer to the variable which will receive the length of
- the data item.
- ppvData Pointer to the variable which will receive the address of
- the data item.
- ulCode A value of 0 is used to read the first element from the
- queue. A value returned by DosPeekQueue() can be used to
- read the element previously peeked at.
- bNoWait If 0, the thread will block until a data element is in the
- queue if one is not already present. If 1, the thread
- returns immediately.
- pbPriority Pointer to the variable which will hold the elements
- priority.
- hevSemaphore Handle of an event semaphore which will be posted when a
- element is placed into the queue and the bNoWait is 1.
-
- The REQUESTDATA structure is defined as follows:
-
- typedef struct _REQUESTDATA
- {
- PID pid; // PID of the process which placed the element in the queue
- ULONG ulData; // The Param parameter of DosWriteQueue
- } REQUESTDATA;
-
- Note: the first time this function is called with a bNoWait of 1, the handle
- of the event semaphore is stored by the system; this handle must be used in
- subsequent reads from the queue. The event semaphore must also be created
- using the DC_SEM_SHARED flag.
-
-
- ΓòÉΓòÉΓòÉ 3.2.3. Encapsulating the Queue API ΓòÉΓòÉΓòÉ
-
- Encapsulating the Queue API
-
- We start the encapsulation process by listing the properties of queues that we
- wish to encapsulate:
-
- o Reading of queue elements
- o Writing of queue elements
- o Creating the queue
- o Opening the queue
- o Manipulating the shared memory
-
- We also note that the queue has two separate sides, a client and a server.
- These two sides have several properties in common which will lead us to this
- first approximation to the queue hierarchy:
-
- ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
- ΓöéQueueObjΓöé
- ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
- Γöé Γöé
- ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
- ΓöéClientQueueObjΓöé ΓöéServerQueueObjΓöé
- ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
-
- Queue Hierarchy
-
- We will now look at the design of these three classes.
-
- QueueObj
-
- As the base class for this hierarchy, QueueObj provides the data members that
- are common to both the client and the server, as well as various support
- functions. These data members include the names of the queue and shared memory
- object, the handle of the queue, the address of the shared memory, and the size
- of the shared memory. For the first pass, the shared memory will be maintained
- by member functions of the hierarchy.
-
- The class definition follows:
-
- class QueueObj
- {
- public:
- enum _Action { None, Create, Open };
-
- _exp_ QueueObj(char *ShMemNm,
- unsigned long MemSize,
- char *QueueNm,
- unsigned long flag=QUE_PRIORITY,
- _Action Mem=None,
- _Action Queue=None);
- virtual _exp_ ~QueueObj();
-
- unsigned long GetError() { return Error; }
-
- void _exp_ SetMemName(char *N);
- void _exp_ OpenMem(unsigned long MemSize);
- void _exp_ CreateMem(unsigned long MemSize);
-
- void _exp_ SetQueueName(char *N);
- void _exp_ OpenQueue();
- void _exp_ CreateQueue(unsigned long flag=QUE_PRIORITY);
-
- PID GetOwner() { return pidQueueOwner; }
-
- protected:
- typedef unsigned long HQUEUE;
-
- HQUEUE hqueue;
-
- char *ShareMemName;
- char *QueueName;
- char *MemBase;
- unsigned long ShMemSize;
-
- PID pidQueueOwner;
-
- unsigned long Error;
- };
-
- This class provides support methods for both the client and server classes to
- use. These methods allow both the shared memory and the queue to be named,
- created, or opened.
-
- ClientQueueObj
-
- class ClientQueueObj : public QueueObj
- {
- public:
- _exp_ ClientQueueObj(char *ShMemNm,
- unsigned long MemSize,
- char *QueueNm,
- unsigned long flag=QUE_PRIORITY,
- int Reserv=0,
- _Action Mem=Open,
- _Action Queue=Open);
-
- int _exp_ Write(ULONG Req,ULONG Priority=15);
- int _exp_ Write(ULONG Req,char *Buff,ULONG BuffSize,ULONG Priority=15);
-
- void * _exp_ RequestMemBlock(unsigned long BlSize);
- void SetReserveLen(int Len) { ReservedLen=Len; }
-
- protected:
- char *CurrLoc;
- int CurrLenght;
- int ReservedLen;
- };
-
- By default, the constructor for the client object opens the queue and shared
- memory. This object is responsible for maintaining the position of the free
- space in the shared memory object. The variable ReservedLen is used to reserve
- a section of shared memory from the start. The member function Write() uses
- the variables CurrLoc and CurrLength as parameters in the call to
- DosWriteQueue().
-
- To write to the queue, the RequestMemBlock() member function is used to request
- a block of shared memory. This block is then initialized by the application,
- and either version of the member function Write() is called. The second form
- of the member function requires that the location and length of the shared
- memory block be specific as parameters while the first assumes the last request
- block of shared memory is the data buffer source.
-
- ServerQueueObj
-
- class ServerQueueObj : public QueueObj
- {
- public:
- _exp_ ServerQueueObj(char *ShMemNm,
- unsigned long MemSize,
- char *QueueNm,
- unsigned long flag=QUE_PRIORITY,
- EventSemaphore *RS=NULL,
- _Action Mem=Create,
- _Action Queue=Create);
-
- PID GetPID() { return ReqDat.pid; }
- ULONG UserData() { return ReqDat.ulData; }
- void _exp_ PostSem();
- void _exp_ ResetSem();
- void _exp_ WaitOnSem();
-
- void* _exp_ Read(ULONG ElCode=0, BOOL wait=0);
- void* _exp_ Peek(ULONG ElCode=0);
-
- ULONG GetDataLen() { return DataLen; }
- BYTE GetPriority() { return Priority; }
-
- protected:
- EventSemaphore *ReadSem;
- REQUESTDATA ReqDat;
- ULONG DataLen;
- void *Entry;
- BYTE Priority;
- };
-
- The constructor for the server object creates both the shared memory and queue.
- It contains data members and member functions to manipulate the various
- parameters to the DosReadQueue() function, mentioned earlier. In addition to
- this, it also has a pointer to an event semaphore object. As this issue
- focuses on queues, I will not go into details on the semaphore objects.
-
- This object is a bit easier to use. After creating an instance of it, the read
- function is called to remove an element from the queue if one is present.
-
-
- ΓòÉΓòÉΓòÉ 3.2.4. Using the Objects ΓòÉΓòÉΓòÉ
-
- Using the Objects
-
- We will now test the queue server and client objects by creating two simple
- applications. The client application will prompt the user to numerical input.
- It will place this numerical input into the queue, at which point the server
- will echo the input to the display. Below is the source code to the queue
- server with comments added.
-
- /* These definitions are contained in QDefs.h
-
- #define QName "DemoQueue" //Name of the queue
- #define SMemName "DemoMem" //Name of the shared memory
-
- #define NemSize 1024*8 //size of the shared memory block
- //in bytes
-
- struct QueueMessage{ //format of the queue data packet
- int Number;
-
- };
- */
-
- int main(){
-
- int loop;
- QueueMessage *Mesg; // pointer to the data packet
-
- //create a shared event semphore
- EventSemaphore Sem((char*)NULL,Semaphore::SemCreate,DC_SEM_SHARED);
-
- //create the queue object.
- //This also creates the shared memory and the actual queue
- ServerQueueObj InQueue(SMemName,NemSize,QName,0,&Sem);
-
-
- loop=1;
-
- //reset the event semaphore
- Sem.Reset();
-
- cout<<"Queue Server Active !"<<endl;
-
- while(loop){
-
- //attempt to read an element from the queue
- Mesg=(QueueMessage *) InQueue.Read(0,1);
-
- //read from queue until an element occurs
- while(InQueue.GetError() ){
- //this semaphore will be posted as soon as an element is put in the queue
- Sem.Wait();
- Mesg=(QueueMessage *) InQueue.Read(0,1);
- }
-
-
- //terminate is the number was -1, else echo the number
- if(Mesg->Number == -1){
- loop=0;
- cout<<"Terminating"<<endl;
- }
- else{
- cout<<"Number= "<<Mesg->Number<<endl;
- }
- }
-
- return 0;
- }
-
- The queue encapsulation has made using the queues much less tedious. The code
- for the client is very similar. Therefore, it is left as an exercise for the
- curious to discover how it works.
-
- There are limitations with the current queue implementation. They are:
-
- o Only 1 client may use the shared memory pool at a time.
- o Only 1 thread in the client process may use the shared memory pool at a time.
-
- The following files are included with this issue:
-
- MAKE.CMD Rexx .CMD file to make the server and client. Assumes the
- compiler is IBM C-Set++.
- QCLIENT.CPP Main routine for the client.
- QSERVE.CPP Main routine for the server.
- QUEUEOBJ.CPP Queue object member function definitions.
- QUEUEOBJ.H QueueObj class definition.
- QDEFS.H Header used in the client a server source files.
- SEMTIMOBJ.CPP Semaphore member functions.
- SEMTIMOBJ.H Semaphore object definitions.
- QSERVE.EXE Server executable.
- QCLIENT.EXE Client executable.
-
- Although this code is compiled using C-Set++, other compilers should be able to
- compile the source files.
-
- Note: QSERVE.EXE must be run before QCLIENT.EXE .
-
-
- ΓòÉΓòÉΓòÉ 3.2.5. Summary ΓòÉΓòÉΓòÉ
-
- Summary
-
- This concludes our first attempt at encapsulating the queue objects. We have
- developed queue objects which encapsulate the creation and manipulation of
- queues and the shared memory they require. In a future issue, the next version
- of the queue objects will be presented. The goal for the next version is to
- eliminate the constraints that exist in this version.
-
-
- ΓòÉΓòÉΓòÉ 3.3. Introduction to PM Programming ΓòÉΓòÉΓòÉ
-
-
- ΓòÉΓòÉΓòÉ 3.3.1. Introduction ΓòÉΓòÉΓòÉ
-
- Written by Larry Salomon, Jr.
-
- Introduction
-
- The purpose of this column is to provide the readers out there wh9 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 you 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.
-
-
- ΓòÉΓòÉΓòÉ 3.3.2. Last Month ΓòÉΓòÉΓòÉ
-
- Last Month
-
- Last month, we finished dissecting the (initial) HELLO program that was
- introduced in volume 2, issue 1. This month we will starts to look at a
- significantly changed version of HELLO in order to set the direction of this
- column for the next few issues. To recap, though, HELLO's purpose was simply
- to demonstrate the minimum number of calls required to do anything useful.
- Other than that, it didn't do anything useful. :)
-
- Up to this point, we have introduced and continued discussion on two areas of
- PM application development - messages and functions. Now we are going to add
- two more - dialog box implementation and resource files.
-
-
- ΓòÉΓòÉΓòÉ 3.3.3. Dialog Boxes ΓòÉΓòÉΓòÉ
-
- Dialog Boxes
-
- In most programs, it is necessary to gather (even frequently) data from the
- user. This may be as simple as getting a filename, or it may be as complex as
- (gulp) filling out an online tax form. In any case, what is the method by
- which input is obtained? CUA defines the mechanism to be a dialog box. What
- is a dialog box, you ask? To be precise, it is a window whose sole purpose is
- to receive data from the user in order for the application to continue.
- Because it is a window, it has the window characteristics discussed when this
- column began.
-
- Every one has seen a dialog box or two; whether it is when your word processor
- prompts you for a file name to load, or when your spreadsheet program asks you
- to select a printer, you cannot escape these facts of (GUI) life. However,
- using them is one thing - implementing them is quite another.
-
- There are two components to dialog box implementation - screen layout and code.
- We will begin to look at each in this issue, and will continue in more detail
- in future issues as we also look at the various controls.
-
- Multilingual
-
- Okay, so you are a Pascal hacker from DOS trying to learn C under OS/2. To
- make it worse, you have to also learn PM, according to your boss. But if that
- weren't enough, now you can tack on another language to learn, another skill to
- master. It is called the resource language, and it is the language in which
- dialog boxes (among other things) are designed.
-
- Simply put, a dialog box is a collection of child windows. Each window is
- defined separately, with a specific order. At the next level, there are groups
- of windows, beginning with the first one having a special style - WS_GROUP -
- and ending just prior to the beginning of the next group or with the end of the
- dialog box definition. Finally, a dialog definition begins with a DIALOG
- keyword, followed by a BEGIN keyword, and ends with an END keyword.
-
- A sample dialog definition follows:
-
- DLGTEMPLATE DLG_GRAPHTYPE LOADONCALL MOVEABLE DISCARDABLE
- BEGIN
- DIALOG "Create graph", DLG_GRAPHTYPE, 64, 23, 195, 170, NOT FS_DLGBORDER |
- WS_VISIBLE, FCF_SYSMENU | FCF_TITLEBAR | FCF_SIZEBORDER
- BEGIN
- GROUPBOX "Graph type", -1, 10, 105, 170, 60
- LTEXT "Field(s) to graph", -1, 10, 90, 100, 8
- CONTROL "", DGT_UB_BARGRAPH, 15, 110, 50, 45, WC_BUTTON, BS_USERBUTTON | WS_GROUP | WS_TABSTOP | WS_VISIBLE
- CONTROL "", DGT_UB_LINEGRAPH, 70, 110, 50, 45, WC_BUTTON, BS_USERBUTTON | WS_GROUP | WS_TABSTOP | WS_VISIBLE
- CONTROL "", DGT_UB_SCATTERGRAPH, 125, 111, 50, 45, WC_BUTTON, BS_USERBUTTON | WS_GROUP | WS_TABSTOP | WS_VISIBLE
- LISTBOX DGT_LB_HEADERLIST, 10, 30, 175, 60, LS_HORZSCROLL | LS_EXTENDEDSEL | WS_GROUP
- LISTBOX DGT_LB_FULLIST, 10, 30, 175, 60, LS_HORZSCROLL | LS_EXTENDEDSEL | WS_GROUP | NOT WS_VISIBLE
- DEFPUSHBUTTON "~Graph", DGT_PB_GRAPH, 10, 10, 40, 13, WS_GROUP
- PUSHBUTTON "~Settings...", DGT_PB_SETTINGS, 55, 10, 40, 13
- PUSHBUTTON "Cancel", DLG_PB_CANCEL, 100, 10, 40, 13
- PUSHBUTTON "Help", DLG_PB_HELP, 145, 10, 40, 13
- END
- END
-
- As you can see, there are many more details regarding these definitions;
- fortunately, you don't have to know them. There is a utility called DLGEDIT
- which allows you to design, on the screen, your dialog boxes and save the
- result. We won't describe how to use this utility; see the accompanying
- documentation for this. What is the result? The human-readable source is
- saved with the extension .DLG and the binary form is saved with the extension
- .RES. In order to explain more, we need to broaden the scope of our discussion
- to...
-
- Resource Files
-
- A resource file - with the extension .RC - contains the definition of the
- various resources used by the application and each of these resources can be
- loaded or accessed via a Win*() function specific to that resource type. The
- resource language also includes C-style preprocessor commands to allow the
- inclusion of C header files containing #define statements for various constants
- that are needed by both the resource file and the application.
-
- For dialogs, Microsoft decided that the Dialog Box Editor shouldn't have to
- deal with parsing the entire resource file, so it added dialog definition files
- (.DLG) to the fold. Integrating the two can get unwieldy - at the end of the
- .RC file, you indicate the dialog file to include:
-
- RCINCLUDE MYDLGS.DLG
-
- ...but at the beginning of the dialog file, you must somehow also include the
- #define statements for each dialog, so there is a different statement:
-
- DLGINCLUDE 1 MYDLGS.H
-
- Whoa! Why the number on the DLGINCLUDE statement but not the RCINCLUDE
- statement? The answer is that Microsoft thought that different dialogs would
- have different sets of constants, so the number is the identifier of the dialog
- (meaning you can have different #include files refer to different dialog box
- definitions).
-
- If the reasoning seems flawed to you, that is because it is. My personal taste
- is to append the .RC file with the contents of the .DLG file. Preference is
- the key word here. Since the dialog editor reads the binary file (.RES), it
- doesn't matter how we manipulate the human-readable source.
-
-
- ΓòÉΓòÉΓòÉ 3.3.4. Dialog Code ΓòÉΓòÉΓòÉ
-
- Dialog Code
-
- In addition to a dialog screen definition, there is code to be written. Since a
- dialog is much like a "main window", you can imagine that the code is some form
- of a window procedure and this is correct. It is called a dialog procedure and
- differs from a window procedure in the following ways:
-
- 1. The dialog procedure never receives a WM_CREATE message. Instead, it gets a
- WM_INITDLG message.
- 2. The default dialog procedure is not WinDefWindowProc(); it is instead
- WinDefDlgProc() and takes the same parameters.
- 3. A dialog is not explicitly destroyed by the application (usually). Instead,
- it is dismissed via the WinDismissDlg() function.
-
- WM_INITDLG
-
- This message occurs when a dialog box is being created.
-
- Parameters
-
- param1
-
- hwndFocus (HWND)
-
- The handle of the window to receive the input focus when
- initialization is completed.
-
- param2
-
- pvData (PVOID)
-
- Points to the application data specified as the last parameter on the
- call to WinDlgBox() or WinLoadDlg().
-
- Returns
-
- reply
-
- bFocusChanged (BOOL)
-
- Focus change indicator:
-
- FALSE Give the input focus to the window in param1
- TRUE The input focus was assigned by the application to another
- window so do not give the input focus to any window.
-
- We will examine in detail how dialog procedures are written and used in later
- issues.
-
- Dialog boxes come in two flavors, and the type determines the function used to
- display the dialog box.
-
- Modal dialog boxes are those that require interaction by the user before the
- application or system can continue. An "Open file" dialog is an example of
- this.
-
- Modeless dialog boxes are those that do not require input for the application
- to continue. A toolpad is an example of this.
-
- For the former, WinDlgBox() is used to display and process the dialog. The
- latter uses WinLoadDlg(). Both functions take the same parameters.
-
- ULONG WinDlgBox(HWND hwndParent,
- HWND hwndOwner,
- PFNWP pfnDlgProc,
- HMODULE hmModule,
- ULONG ulDlgId,
- PVOID pvData);
-
- hwndParent The parent window of the dialog box
- hwndOwner The desired owner window of the dialog. This is rarely the
- true owner of the dialog (see below). For calls to
- WinDlgBox(), the true owner is disabled until the dialog is
- dismissed.
- pfnDlgProc Specifies the dialog procedure
- hmModule Handle to the DLL where the dialog definition resides
- ulDlgId ID of the dialog to be displayed
- pvData Application data, passed to the dialog procedure via the
- WM_INITDLG message
-
- The true owner of a dialog is calculated by the system in the following manner
- - each successive parent window of hwndOwner is queried until one is found
- whose immediate parent is hwndParent. If found, this becomes the true owner,
- otherwise hwndOwner becomes the true owner.
-
- Dismissal versus Destruction
-
- When a dialog is dismissed, it is by default not destroyed until the
- application exits. This is for performance reasons; since a large percentage
- of dialogs are reused by an application many times, this reduces the time
- between the call to WinDlgBox() and the time the dialog is actually displayed.
-
- The ramifications are that if you allocate resources in the dialog procedure,
- you cannot wait until the WM_DESTROY message to free them, or else you are
- asking for trouble. When do you do it? I have yet to find a good answer to
- that question...
-
-
- ΓòÉΓòÉΓòÉ 3.3.5. Summary ΓòÉΓòÉΓòÉ
-
- Summary
-
- This month, we began to look at dialog boxes and resource files. These are
- collectively large topics, so we will continue next month with these. In the
- meantime, a sample application has been provided as hello.zip; this is an
- enhancement of the previous Hello world application that was discussed in
- previous issues.
-
- Further down the road, we will begin looking at each of the individual
- controls, what their purpose is, and how they are used (primarily from the
- context of a dialog box). Stay tuned!
-
-
- ΓòÉΓòÉΓòÉ 3.4. 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 Corrections
- o Gotcha Notes!
- o Questions and Answers
- o Snippet(s) of the Month
- o Documentation Chop Shop
- o Want Ads
-
-
- ΓòÉΓòÉΓòÉ 3.4.1. Corrections ΓòÉΓòÉΓòÉ
-
- Corrections
-
- Last month, I incorrectly credited Gordon Zeglinski as the author of the
- functions in Snippet(s) of the Month. The true author is Richard Papo, the
- author of MemSize. He can be reached via email at 72607.3111@compuserve.com .
-
- My apologies for any inconveniences.
-
-
- ΓòÉΓòÉΓòÉ 3.4.2. Gotcha Notes! ΓòÉΓòÉΓòÉ
-
- Gotcha Notes!
-
- This is a part of this column that will appear from time to time, whenever
- someone else (or myself) manages to stumble upon something that is not obvious,
- yet doesn't belong anywhere else in the column. Things that end up here are
- not documentation errors or vagaries, but just things to be careful of. If you
- have something that would be of help to another of your fellow developers,
- please send it in.
-
- The first submission this month deals with MDI applications, minimized icons,
- and the WM_QUERYTRACKINFO message. As many of you know, this message is sent
- to the frame whenever any tracking-related event - most notably moving and
- sizing - occurs to allow the frame to set parameters for the event. The point
- is that, by subclassing the frame window and intercepting this message, you can
- control some very important things such as minimum or maximum size of the
- window simply by setting one or more fields in the TRACKINFO structure pointed
- to by PVOIDFROMMP(mpParm2).
-
- After investigating a bug reported by one of the users of an application I
- wrote, I discovered - much to my chagrin - that if a window is minimized and
- the user decides to move the icon, the frame receives this message. If, as was
- the case in my application, the frame is subclassed and the ptlMaxTrackSize is
- blindly set without any concern about the type of operation in progress, the
- icon will disappear when the operation is finished!
-
- The way to avoid this is to check that you are not minimized (by calling
- WinQueryWindowULong(hwndWnd,QWL_STYLE) and checking for the WS_MINIMIZED bit)
- before proceeding to set any fields in the TRACKINFO structure.
-
- On a related topic, it should be noted that this message is not sent to the
- frame when the user maximizes the frame. The message that is sent to the frame
- is WM_ADJUSTWINDOWPOS.
-
- ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
-
- Eberhard Mattes noted that, for menu items with the style MIS_BITMAP, it isn't
- stated that bitmaps of menus are loaded from the .EXE file and not resource
- DLL. He presents a solution to this in the Snippet(s) of the Month section of
- this column.
-
-
- ΓòÉΓòÉΓòÉ 3.4.3. Questions and Answers ΓòÉΓòÉΓòÉ
-
- Questions and Answers
-
- You have to love it when it seems that everyone else is writing your column for
- you. ;) This answer to a frequently asked question comes from Juergen Hermann
- (jh@ccave.ka.sub.org)
-
- The answer to the question "how to bind a default icon to an .EXE file" is that
- the system looks for an icon resource with the (magic) number 1, so you have to
- use the following in your resource file.
-
- ICON 1 PRELOAD MYAPP.ICO
-
- It was pointed out that this will cause trouble if you define a constant for
- the number 1 because it conflicts with DID_OK, which is defined by the Toolkit.
- My experience has indicated that the first icon resource found in the
- executable is used, but since the first icon resource in my application are
- usually the lowest numbered also, this could instead by the criteria. Given
- that you can't get much lower than 1 (0?), the behavior I have seen would be
- consistent with what is described above.
-
-
- ΓòÉΓòÉΓòÉ 3.4.4. Snippet(s) of the Month ΓòÉΓòÉΓòÉ
-
- Snippet(s) of the Month
-
- Eberhard Mattes, well-known for his numerous contributions including the EMX
- development system, sent us the following code to load a bitmap and set it into
- a menu item. The code is adapted from DVIPM.
-
- /* The bitmap of a menu entry having style MIS_BITMAP is loaded from
- the main executable. This function loads it from the resource DLL.
-
- Input: hwndFrame Handle of the frame window owning the menu
- hMod Module handle of the DLL containing the bitmap
- resource
- idMenu ID of the menu item (must have style MIS_BITMAP)
- idBitmap ID of the bitmap
- */
- void load_menu_bitmap (HWND hwndFrame, HMODULE hmod,
- USHORT idMenu, USHORT idBitmap)
- {
- HPS hps;
- HWND hwndMenu;
- HBITMAP hbm;
-
- hps = WinGetScreenPS (HWND_DESKTOP);
- hwndMenu = WinWindowFromID (hwndFrame, FID_MENU);
- hbm = GpiLoadBitmap (hps, hmod, idBitmap, 0, 0);
- WinSendMsg (hwndMenu, MM_SETITEMHANDLE,
- MPFROMSHORT (idMenu), MPFROMLONG (hbm));
- WinReleasePS (hps);
- }
-
-
- ΓòÉΓòÉΓòÉ 3.4.5. Documentation Chop Shop ΓòÉΓòÉΓòÉ
-
- Documentation Chop Shop
-
- The documentation of MM_SETITEMHANDLE appears to be wrong. It says that
- mpParm1 is the item index, but instead it should be the ID of the menu item
- with which it is to be associated. This message seems to search all child
- submenus for the menu item.
-
-
- ΓòÉΓòÉΓòÉ 3.4.6. Want Ads ΓòÉΓòÉΓòÉ
-
- 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. This
- month's article is a start, but is not enough! :)
-
- 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.
-
- Multimedia (warm) - last month we had two articles on this topic. However,
- they both dealt with sound, which we all know is not the only alternative media
- type. Articles on anything else - MIDI, video, etc. - are needed.
-
- 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"...
-
-
- ΓòÉΓòÉΓòÉ 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 in Almaden, of which I don't have the address.
-
- On Compuserve
-
- All back issues are available in the OS/2 Developers Forum 2.
-
- IBM Internal
-
- o IBM's internal gopher server in Almaden, of which I don't have the address.
- 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.
-
-
- ΓòÉΓòÉΓòÉ 5. Contributors to this Issue ΓòÉΓòÉΓòÉ
-
- Are You a Potential Author?
-
- 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
- before 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 five days prior to 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 BjФrn Fahller
- o Larry Salomon, Jr.
- o Kenton W. Shaver
- o Marc van Woerkom
- o Carsten Whimster
- o Gordon Zeglinski
- o Network distributors
-
-
- ═══ 5.1. BjФrn Fahller ═══
-
- BjФrn Fahller
-
- BjФrn Fahller is about to finish his Master's degree in Computer Science at
- LuleЖ University, Sweden. Before the university studies, he worked with ASIC
- design for the telecommunications industry, as well as miscellaneous
- programming jobs. His major fields of interest are Human-Computer interaction
- and object oriented design.
-
- After having worked with a short software project for the University of British
- Columbia, in Vancouver, Canada, the summer of '93, he is now trying to get back
- to Beautiful B.C. by searching for jobs there.
-
- Since a little over a year ago he has also tried to run the OS/2 ftp site,
- ftp.luth.se, but is constantly fighting the disk space problem.
-
- He can be reached on the Internet at bjorn@ludd.luth.se
-
-
- ΓòÉΓòÉΓòÉ 5.2. Larry Salomon, Jr. ΓòÉΓòÉΓòÉ
-
- Larry Salomon
-
- 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 being distributed by IBM with the Professional Developers Kit
- CD-ROM. 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. Kenton W. Shaver ΓòÉΓòÉΓòÉ
-
- Kenton can be contacted at kenton+@CMU.EDU.
-
-
- ΓòÉΓòÉΓòÉ 5.4. Marc van Woerkom ΓòÉΓòÉΓòÉ
-
- Marc Ernst Eddy 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.5. Carsten Whimster ΓòÉΓòÉΓòÉ
-
- Carsten Whimster
-
- I am an undergraduate computer science student at the University of Waterloo,
- and an OS/2 enthusiast as of OS/2 2.0. I am currently in my third year, taking
- mainly operating system, language, compiler, and graphics courses, as much as
- possible. :)
-
- This summer I will be working at the University as a tutor in CS241, an
- introductory course to compilers.
-
- I am a beginning OS/2 PM programmer with a few projects on the go, and many
- more in my head. I try to buy as many books as I can afford on the subject,
- but if you work for a publisher, I would certainly not turn down any donations,
- and if the books are interesting and development-oriented, I will try to review
- them as time permits.
-
- I am a TEAM-OS/2 member, and stay busy trying to keep up with several OS/2
- groups on the Internet.
-
- I am also an original member of the Kitchener-Waterloo OS/2 Users' Group, and
- have recently volunteered to help out organizing the events.
-
- It's a miracle my girl-friend puts up with my doing all these things :)
-
- You may reach me...
-
- ...via email:
-
- bcrwhims@undergrad.math.uwaterloo.ca - Internet
-
- gopher://descartes.math.uwaterloo.ca:70/h0/mathSOC/.csc/.www/.bcrwhimster/homepage.html
- - Mosaic homepage
-
- ...via snail mail:
-
- Carsten Whimster
- 319 Erb Street West, 3rd floor
- Waterloo, Ontario
- Canada
- N2L 1W4
-
-
- ΓòÉΓòÉΓòÉ 5.6. Gordon Zeglinski ΓòÉΓòÉΓòÉ
-
- 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.7. Network distributors ΓòÉΓòÉΓòÉ
-
- 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!