home *** CD-ROM | disk | FTP | other *** search
-
- ΓòÉΓòÉΓòÉ 1. Title Page ΓòÉΓòÉΓòÉ
-
- Welcome to EDM/2 - The Electronic OS/2 Developers Magazine!
- Portions copyright (c) by Larry Salomon Jr.
- Volume 1, issue 7
-
- 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
-
- Yes, the mailing distribution was goofed up. Many of you received parts 1 and
- 4 only. This is because the mailer runs Sendmail in the background but forgets
- to wait for it to complete before deleting the files it is supposed to be
- mailing. Oops. My apologies for the inconvenience.
-
- The fix to the problem has been volunteered by Gess Shankar, who runs the OS/2
- Multimedia Mailing List. He offered to run the mailing distribution using his
- tried-and-true mailing list machine. This will allow for much more robust
- subscription processing as well as the ability to request back issues; those of
- you who are currently subscribed via the "old way" have already been moved to
- the new machine, so there is no need to resubscribe. To receive more
- information (for new readers), send an empty mail message to
- EDM2@knex.via.mind.org and you will receive the information file that tell you
- how to subscribe, etc.
-
- I still have not heard from Gavin Baker so I will assume him to be "missing in
- action"; because of this I will be writing the "Introduction to Presentation
- Manager Programming" column and will start over from the beginning. Speaking
- of people missing in action, I received a note from Steve (Luzynski). He is
- doing well and now has a full-time job as a computer retail salesperson which
- leaves him with "no free time." I guess that answers the question I had about
- really being the editor or just being a stop-gap until he came back.
-
- By now, many if not all of you have heard of WordPerfect's announcement to halt
- development on WordPerfect version 6.0 for OS/2. Who knows what this portends?
- I certainly do not, but I do know that I am glad I am not in Orem, Utah right
- now...You are probably thinking, "what the hell does this have to do with OS/2
- development." Consider that a company of that size makes a decision like this
- for one of three reasons: political alliances could indicate a conflict of
- interest, marketing demand could indicate it is not worth the investment, or
- development strategies in the environment could make the product obsolete in a
- short time. My hunch is that they know something we don't about IBM's
- direction but we had better find out!
-
- There is both a correction and a clarification that are noteworthy in the
- Scratch Patch column. Also, we need your submissions for the Snippet(s) of
- the Month portion of that column! Not only is group participation better, but
- this old dog hasn't learned all of the possible tricks in OS/2 programming. I
- am always looking to grow, too!
-
- While talking to Dick Conklin a few days ago, he asked me to mention that you
- might qualify for a free subscription to OS/2 Developer, another OS/2 magazine
- aimed at developers. Call 1-800-WANT-OS2 and ask them for the form to fill
- out. If you are a member of the Developers Assistance Program (DAP), be sure
- to indicate this on the form to guarantee your acceptance.
-
- Some other tweaks on the magazine were made, notably, the icon file has been
- renamed to EDMI.ICO and the installation program has been changed accordingly.
- This will allow you to remove all of the previous icon files and only keep the
- latest on your disk. Also, an ASCII file containing the table of contents will
- be distributed with each issue as CONTENTS.NDX to allow you to see the titles
- of the articles for that month. The contents of issues 1 through 6 can be
- found in the Reader's Choice Awards in this issue and can also be found in the
- .ZIP file as OLDNDXS.NDX Many thanks to the readers who suggested these
- changes.
-
- Finally, I knew this was going to happen and it did. I forgot to add two
- articles to this month's index - OS/2 Installable File Systems - Part 3 and
- Using OS/2 2.x bitmap files. Because of this, I am resending this; I would
- hate for them to not be considered for the Reader's Choice Awards because of my
- screw-up.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2. This Issue's Features ΓòÉΓòÉΓòÉ
-
- The following articles constitute this issue's features:
-
- o OS/2 Installable File Systems - Part 3
- o Rexx-ercising Your Applications - Part 2
- o Using OS/2 2.x bitmap files
- o Writing a Direct Manipulation Spy
-
-
- ΓòÉΓòÉΓòÉ 2.1. OS/2 Installable File Systems - Part 3 ΓòÉΓòÉΓòÉ
-
- Written by Andre Asselin
-
- Introduction
-
- Whew! Between my regular job (I don't call it my day job anymore, because it
- seems like I've been spending more and more nights there too), this month's
- code not working, Larry breathing down my neck for an article, and the joys of
- Christmas shopping, keeping sane recently has not been easy. I'll tell you,
- Murphy was right - if it wasn't for the last minute, a lot of things wouldn't
- get done.
-
- Last time, I said that I'd present a state diagram that showed how the
- IFS/control program couldn't get deadlocked. I'm afraid that'll have to wait
- until next time. The code was very uncooperative this month, and I ended up
- not having enough time to finish it. Instead, I concentrated on getting the
- code into a real skeleton that you could just insert your own guts into, and
- also started inserting some guts for our versioning file system. In doing
- that, I of course ran into some problems...
-
- But before I start my detective story, I'd like to make official what has been
- happening for the past few months: this series is officially bi-monthly. As
- much as I'd like to do a monthly series, I'm afraid I don't have the time. As
- always, though, if you have specific questions that you need answered before
- this series gets to them, feel free to write, and I'll try my best to explain.
-
- Also, for those that don't know, I did an IPF version of the IFS document. It
- is available on Compuserve in the DAP section of the OS2DF2 forum (section 14),
- and is called IFSDOC.ZIP. You'll have to become a member of IBM's World Wide
- DAP to get access to that section, but the good news is joining is free and
- easy. Just GO OS2DAP on Compuserve and answer the questions. In about a week
- you should receive a welcome letter and have access to that section.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.1.1. Introduction ΓòÉΓòÉΓòÉ
-
- Written by Andre Asselin
-
- Whew! Between my regular job (I don't call it my day job anymore, because it
- seems like I've been spending more and more nights there too), this month's
- code not working, Larry breathing down my neck for an article, and the joys of
- Christmas shopping, keeping sane recently has not been easy. I'll tell you,
- Murphy was right - if it wasn't for the last minute, a lot of things wouldn't
- get done.
-
- Last time, I said that I'd present a state diagram that showed how the
- IFS/control program couldn't get deadlocked. I'm afraid that'll have to wait
- until next time. The code was very uncooperative this month, and I ended up
- not having enough time to finish it. Instead, I concentrated on getting the
- code into a real skeleton that you could just insert your own guts into, and
- also started inserting some guts for our versioning file system. In doing
- that, I of course ran into some problems...
-
- But before I start my detective story, I'd like to make official what has been
- happening for the past few months: this series is officially bi-monthly. As
- much as I'd like to do a monthly series, I'm afraid I don't have the time. As
- always, though, if you have specific questions that you need answered before
- this series gets to them, feel free to write, and I'll try my best to explain.
-
- Also, for those that don't know, I did an IPF version of the IFS document. It
- is available on Compuserve in the DAP section of the OS2DF2 forum (section 14),
- and is called IFSDOC.ZIP. You'll have to become a member of IBM's World Wide
- DAP to get access to that section, but the good news is joining is free and
- easy. Just GO OS2DAP on Compuserve and answer the questions. In about a week
- you should receive a welcome letter and have access to that section.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.1.2. It used to work... ΓòÉΓòÉΓòÉ
-
- The code I presented last month was the beginnings of a skeleton for a ring
- 0/ring 3 IFS. The design and concept were all hashed out, but the actual
- skeleton code for all the ring 0 stubs and for the ring 3 router weren't
- complete. That was the first thing I worked on - filling in all the missing
- skeleton code. The first thing I had to do was expand the union in R0R3SHAR.H
- that defines all the data passed between the IFS and control program to include
- structures for all the FS_ calls. Once that was done, the next thing to do was
- cut and paste the code for the ring 0 FS_ATTACH into all the other FS_
- functions so that they would route their calls to the control program.
- Finally, the control program's router had to have entries added for all the
- other FS_ calls. Talk about work that's hard to get psyched for! Cutting and
- pasting is not my idea of a stimulating programming session.
-
- Once that was done, the next thing to do was to actually start filling in some
- of those stubs. The first one to do is FS_ATTACH, since that is the first call
- this type of IFS will receive. After that, a good second one is FS_CHDIR,
- since directories are very basic to any IFS. The code I filled in isn't all
- that elaborate - just something to start testing with. At that point, though I
- decided it was time to give it a whirl and see how it ran. After fixing a few
- syntax errors, it was time for the big moment...and it didn't work anymore!
-
- OS/2 booted fine and loaded the IFS. The banner came up, and the system didn't
- crash. Things were looking good until I ran the ring 3 control program, when
- it failed trying to attach to the IFS. Funny, that worked before. Looking up
- the error code revealed it to be ERROR_INVALID_PARAMETER. I figured it was
- probably just a typo or something.
-
- A week later I still hadn't a clue what was causing the problem. I narrowed it
- down to a call to VMLock in the IFS, but all it's parameters looked good. I
- thought it might be that OS/2 had gone flaky on my system, so I tried it on
- another system at work. Same thing. I thought that maybe my assembly DevHlp
- functions were messing up, so I wrote direct calls to the DevHlps in the code.
- Same thing. I thought maybe the lock handle VMLock uses couldn't be in
- swappable memory, so I put it in movable, and then fixed memory. Same thing.
- Nothing seemed to want to cure this problem.
-
- At that point I got the brilliant idea of taking the code from the last
- article, compiling and running it, and seeing what changes I'd made. Well, I
- found out much to my dismay, that the code from last month had the same
- problem. I considered how lucky I was that the flood of angry email letters
- everyone sent me decrying my foul code must have somehow gotten filtered, and
- never made it to my electronic mail box. So after counting my blessings, I
- decided to get a beer. Not that that would help me find the problem, but it
- would help me think up an excuse to tell Larry why my article would be late.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.1.3. The C preprocessor strikes again ΓòÉΓòÉΓòÉ
-
- Funny thing about problems where you have no clue what the cause is - you kind
- of want to hibernate for a month or two and hope somebody else fixes it.
- Unfortunately among the list of things I have, a co-author is not one of them,
- so I would have solve this one myself.
-
- I won't pretend that what I did next had any kind of methodical systematic
- approach behind it. My whole focus at that point was to just make the call
- work somehow, so I inserted a return right after the call and started playing
- with the parameters. The first break came when I could make the call work by
- doing two VMAlloc's before the call, one for the data block to be locked down,
- and the other for the memory to put the lock handle into. A little more
- playing narrowed it down to just the VMAlloc for the lock handle. If I kept
- the call to VMLock the same as it was originally, but inserted that VMAlloc
- before it, it worked fine. Okay, so that pointed to a problem with the VMAlloc
- for the lock handle, which was done in the initialization routine. I thought
- maybe it's DevHlp routine was messed up and took a look at it. It looked fine.
- I decided to take a long look at the flags again to see if there was something
- I was missing, and while looking at the PDD reference, I decided to see if the
- values for the flags in the .H file matched those in the book. Lo and behold -
- VMAF_GLOBALSPACE was defined the opposite of what it should have been. The
- initialization routine was allocating memory for the lock handles in memory
- local to the process that calls the IFS at initialization time, instead of
- globally. VMLock had been complaining about not being able to address the lock
- handle! The whole problem was that I was looking at the code for the VMAlloc
- which clearly included the flag to allocate global memory, but the flag value
- was wrong. Bitten again by the C preprocessor.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.1.4. Up and running, almost ΓòÉΓòÉΓòÉ
-
- Once I got that bug figured out, I was finally back to where I was before. The
- control program could load and attach to the IFS, and I could issue a
- DosFSAttach() call to associate a drive letter with the IFS. Now was the big
- chance to try and see if the new FS_CHDIR code would work. I typed CD T:\1 and
- got back an error code of ERROR_PATH_NOT_FOUND. Hmmm. That's strange. When I
- did the attach, I said for it to attach drive T to drive C, and I knew that
- directory existed on drive C. Time to add debugging code. At least since the
- problem was in the ring 3 piece, I didn't have to reboot after every recompile.
- Waiting for OS/2 to shutdown and restart every ten minutes was getting to be
- tiring.
-
- After the debugging code was added, I found that the FS_CHDIR call was failing
- because DosQueryPathInfo() said that the directory I was passing in wasn't a
- directory. Double hmmm. I decided to have the control program print out what
- attributes DosQueryPathInfo() thought my directory had. It seems
- DosQueryPathInfo() was being very generous and saying my directory had lots of
- attributes, even some of the reserved ones, but not the directory attribute.
- Running it with Turbo Debugger and looking at the structure showed the same
- thing. This was too weird. I decided to pull the DosQueryPathInfo() call out
- into a little test program and add some code to dump the raw data that
- DosQueryPathInfo() returned. The code looked like this:
-
- static FILESTATUS3 buf;
- int i;
-
- :
- for (i=0; i<sizeof(buf); i++) {
- printf("%u ", ((unsigned char *)&buf)[i]);
- }
-
- Now something else weird was happening - it was printing out too many bytes.
- So I played with it a little, trying some variations before I just decided to
- look at the data.
-
- Well, to understand the data, you have to know the structure, so I looked it up
- in the Control Program reference. A FILESTATUS3 has 3 FDATE and FTIME
- structures at the head of the structure, followed by two fields related to the
- file size, and finally the file attributes. Looking up an FDATE in the book
- said it consists of 3 USHORT's, one each for day, month, and year. Okay, fine,
- but now the dump code wasn't printing enough data. Time to look at the header
- files.
-
- Looking at the BSEDOS.H header file in the 2.1 Toolkit showed the following
- definition for an FDATE (FTIME was similar):
-
- #ifdef __IBMC__
- typedef struct _FDATE /* fdate */
- {
- UINT day : 5;
- UINT month : 4;
- UINT year : 7;
- } FDATE;
- typedef FDATE *PFDATE;
- #else
- typedef struct _FDATE /* fdate */
- {
- USHORT day : 5;
- USHORT month : 4;
- USHORT year : 7;
- } FDATE;
- typedef FDATE *PFDATE;
- #endif
-
- Now I see where the Control Program reference was wrong - it left off those
- important little ': 5' things on the end. An FDATE wasn't 3 USHORT's, it was
- one! After that discovery, I got to wondering what the definition was in the
- header files Borland ships with it's compiler. So I pulled up BSEDOS.H in the
- bcos2\include directory, and found this:
-
- #if defined(__IBMC__) || defined(__BORLANDC__)
- typedef struct _FDATE /* fdate */
- {
- UINT day : 5;
- UINT month : 4;
- UINT year : 7;
- } FDATE;
- typedef FDATE *PFDATE;
- #else
- typedef struct _FDATE /* fdate */
- {
- USHORT day : 5;
- USHORT month : 4;
- USHORT year : 7;
- } FDATE;
- typedef FDATE *PFDATE;
- #endif
-
- Well, guess what, a UINT is not the same size as a USHORT in 32-bit code! It
- seems someone at Borland forgot that. As a result, every structure that used an
- FDATE or FTIME was messed up (including FILEFINDBUF3, which was what I was
- trying to use). Getting rid of the '||' and everything after it did the trick
- - now DosQueryPathInfo() was working like it should. For your reference, lines
- 399 and 418 in the BSEDOS.H that Borland ships need to have this fix applied.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.1.5. Debugging tools ΓòÉΓòÉΓòÉ
-
- There were some other problems after those two, but they weren't nearly as
- interesting. One thing I learned that I did find very interesting though, is
- that FS_EXIT in the IFS is not called if the ring 3 control program traps.
- That turns out to be unfortunate because OS/2 won't put up the trap info, or
- let the program die, until the memory that the IFS VMLock'ed has been
- VMUnlock'ed, which is the whole purpose of the code for FS_EXIT in the IFS.
- Because of that limitation, I'm considering modifying the design so that the
- IFS allocates the shared memory instead of the control program, but that will
- have to wait until next time. In the mean time, I added another operation to
- the IFS's FS_FSCTL entry point, FSCTL_FUNC_KILL, which will unlock the shared
- memory and clear the semaphores. FSKILL.C is a utility program that will make
- that call. This is extremely handy when debugging because you're bound to get
- things hosed up, and it's nice to have a way to unhose them besides rebooting.
-
- I also added a another new program, FSDET.C, which will detach a drive from the
- IFS. After running FSATT a few times and associating a bunch of drive letters
- with the IFS, it's nice to have a way to disassociate those drive letters, too.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.1.6. FS_ATTACH processing ΓòÉΓòÉΓòÉ
-
- Finally, this month, I'd like to talk about how FS_ATTACH and FS_CHDIR should
- be implemented. Unfortunately, the IFS reference isn't real clear on how they
- should work.
-
- FS_ATTACH is the function that is called whenever OS/2 needs to associate or
- disassociate a drive letter with your IFS. It's also called when OS/2 is
- querying information about a particular attach. FS_ATTACH supports the
- DosFSAttach() and DosQueryFSAttach() calls.
-
- When OS/2 wants to attach a drive, FS_ATTACH is called with flag set to
- FSA_ATTACH. pDev will contain the drive letter in the form 'R:', and pParm and
- pLen will correspond to the pDataBuffer and ulDataBufferLen parameters on
- DosFSAttach(). For our IFS, this area will contain a directory that the drive
- letter should be associated with.
-
- The first thing the IFS has to do is verify that pDev contains a drive letter,
- and not a device name. IFS's can also be used to write character mode device
- drivers, and for that type of IFS, FS_ATTACH is used to associate the IFS with
- a device driver name. All you have to do to check is see if the first
- character of pDev is a '\' - if it is, somebody is trying to attach a device
- driver name to the IFS, so you should just return with ERROR_NOT_SUPPORTED.
-
- The next thing you would if you were writing a pure ring 0 IFS would be to
- verify that you can access the pParm data area, since OS/2 does not do that
- automatically for you. In our split ring 0/ring 3 IFS, the ring 0 side handles
- that automatically, so the ring 3 side doesn't have to worry about it.
-
- Next, the IFS can proceed with the attach processing. For our IFS, we do a
- DosQueryPathInfo() to check that the directory the user passed in the pParm
- area exists and is really a directory. If it is, we allocate enough memory to
- hold the directory and copy in there. We then use part of the CDFSD to hold
- the pointer to that memory.
-
- So what's a CDFSD? IFS's have four types of structures it deals with, one each
- for volume parameters, current directories, open files, and file searches.
- Each one of those structures has a file system independent part, and a file
- system dependent part. The file system independent part is the same across
- every IFS, while each IFS can use the file system dependent part however it
- wants. So to answer the question, a CDFSD is a file system dependent current
- directory, and we use it to store a pointer to the text of the directory the
- user asked us to attach to. Descriptions of these structures can be found in
- the IFS reference, although some of the fields are a bit difficult to
- understand. As we need them, though, I'll explain what each one does.
-
- At attach time, an IFS should also fill in the VPFSD, which is the file system
- dependent volume parameters. For our IFS, all we need is the same pointer
- that's in the CDFSD. This will be used on the FS_FSINFO and FS_FLUSHBUF calls
- when we implement those.
-
- For a detach, flag will be set to FSA_DETACH. The IFS should flush any
- remaining buffers and deallocate any memory associated with the drive letter.
- Currently, our IFS doesn't do a free on the memory we allocated at attach time
- because it could get into trouble. Consider this scenario: a user runs the
- control program and attaches a drive (the control program allocates memory to
- hold the directory). The user then kills the control program without detaching
- the drive, and then re-runs it. At this point, OS/2 still considers the attach
- the user did to be valid, even though the control program wouldn't. Suppose
- now the user does a detach. If the control program tried to free the memory in
- the CDFSD, it would trap because it never allocated that memory - the previous
- instance of the control program did. There is a way around that problem, but
- for now we will just not free the memory.
-
- For a query, FS_ATTACH will be called with flag set to FSA_ATTACH_INFO. pParm
- will point to a structure that looks like this:
-
- struct attach_info {
- unsigned short cbFSAData;
- char rgFSAData[1];
- }
-
- The IFS should fill in the rgFSAData with whatever information it wants to
- return, and cbFSAData with the length of the information. Our IFS just returns
- the directory that the user originally specified when he attached. The
- information a particular IFS returns is completely up to the IFS, but generally
- it is some sort of ASCII text. To see what other IFS's return, you can use
- FSATT with no parameters - it'll go through all the drive letters, do a
- DosQueryFSAttach() on each, and print out what it returns.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.1.7. FS_CHDIR ΓòÉΓòÉΓòÉ
-
- OS/2 handles current directories extremely intelligently. It treats a current
- directory not as just some piece of text, but as something that has to be
- allocated and freed every time a user switches directories. If two different
- processes are in the same current directory, OS/2 can share a single current
- directory structure between them and will free it only when both processes no
- longer are using it. It also only keeps one current directory structure for
- the root directory (the one that we filled in at FS_ATTACH time). This makes
- FS_CHDIR a little more challenging to implement, but not too terribly
- complicated.
-
- When a user issues a CD command, the OS/2 will call FS_CHDIR with flag set to
- CD_EXPLICIT. This indicates to the IFS that it should mutate the CDFSD that it
- receives into the CDFSD appropriate for the directory being switched to. What
- OS/2 actually does is it copies the old CDFSD into new memory and calls the IFS
- pointing to the new memory. The IFS should do whatever verification it needs
- to do, and then change the CDFSD appropriately, unless we are switching to the
- root directory. If we are switching to the root directory, OS/2 will throw
- away whatever the IFS returns in CDFSD, and use the CDFSD that the IFS setup at
- FS_ATTACH time. The IFS should not free any resources kept with the old CDFSD.
-
- The text of the new current directory is passed in the pDir pointer, and to
- help optimize processing, iCurDirEnd points to the end of the old current
- directory in pDir. For example, if the old current directory was "F:\DIRECT1",
- and we're switching to "F:\DIRECT1\DIRECT2", iCurDirEnd will point to second
- '\'. If iCurDirEnd is -1, it means that the user CD'd up the tree, such as
- from "F:\LEVEL1\LEVEL2" to "F:\LEVEL1", or to "F:\LEVEL1\LEVEL2A", and this
- optimization isn't possible.
-
- What our IFS does for this call is simply verify that the directory the user is
- trying to switch to exists and really is a directory. We don't actually keep
- the text of the directory around, since we can recreate it whenever we need to
- by concatenating the Dir field of the CSFSD and the cdi_curdir field of the
- CDFSI (cdi_curdir is the text of the current directory).
-
- When OS/2 want to free a current directory structure, it calls FS_CHDIR with
- flag set to CD_FREE. The IFS should release any resources associated with the
- current directory passed in. For us, we never allocated anything, so we don't
- need to free anything.
-
- Finally, if OS/2 want to verify a current directory, it calls FS_CHDIR with
- flag set to CD_VERIFY. The IFS should verify that the current directory is a
- valid directory name, and that it exists. If it isn't a valid directory, it
- should release any resources associated with the current directory (i.e. it
- should pretend it got an FS_CHDIR/CD_FREE call). Our IFS simply calls
- DosQueryPathInfo() to verify that the directory still exists and that in fact
- it is a directory.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.1.8. Next Time ΓòÉΓòÉΓòÉ
-
- Well, that's it for now. Next time I'll go over that state diagram, and also
- start talking about the FS_FIND* routines. At that point we'll have an IFS
- where you'll be able to switch directories and do DIR's. Good stuff!
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.2. Rexx-ercising Your Applications - Part 2 ΓòÉΓòÉΓòÉ
-
- Written by Gordon W. Zeglinski
-
- Introduction
-
- In part 1, we seen how to extend REXX by creating external functions and how to
- execute REXX programs from within our applications. RexxStart() wasn't
- thoroughly explored in part 1, so in this issue we will examine two more
- permutations of RexxStart(). Also, in this issue, we will look at using
- external command handlers and using the REXX macrospace.
-
- Enough preamble, let's get to it!
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.2.1. Introduction ΓòÉΓòÉΓòÉ
-
- Written by Gordon W. Zeglinski
-
- In part 1, we seen how to extend REXX by creating external functions and how to
- execute REXX programs from within our applications. RexxStart() wasn't
- thoroughly explored in part 1, so in this issue we will examine two more
- permutations of RexxStart(). Also, in this issue, we will look at using
- external command handlers and using the REXX macrospace.
-
- Enough preamble, let's get to it!
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.2.2. More on RexxStart() ΓòÉΓòÉΓòÉ
-
- Last time, we set the INSTORE parameter to NULL, to execute REXX procedures
- from files. By permuting the INSTORE parameter, we can execute REXX procedure
- from memory or the macrospace.
-
- If you remember REXXSAMP.CPP from part 1, the variable Prog[] in the main()
- function, declares an in-memory version of this program. Prog[] is then
- assigned to the RXSTRING INSTORE[0].
-
- #define INCL_RXFUNC /* external function values */
- #define INCL_VIO
- #include <rexxsaa.h>
- #include <iostream.h>
- #include <string.h>
-
- ULONG _System SysCls(UCHAR *name, ULONG numargs, RXSTRING args[],
- PSZ *queuename, RXSTRING *retstr);
-
-
- ULONG _System SysCls(UCHAR *name, ULONG numargs, RXSTRING args[],
- PSZ *queuename, RXSTRING *retstr)
- {
- BYTE bCell[2];
-
- // If arguments, return non-zero to indicate error
- if (numargs)
- return 1;
-
- bCell[0] = 0x20; /* Space Character */
- bCell[1] = 0x07; /* Default Attrib */
- VioScrollDn( 0, 0, (USHORT)0xFFFF, (USHORT)0XFFFF,
- (USHORT)0xFFFF, bCell, (HVIO) 0);/* CLS */
- VioSetCurPos(0, 0, (HVIO) 0); /* Pos cursor */
-
- //return "0" to the caller
- retstr->strlength=1;
- strcpy(retstr->strptr,"0");
-
-
- return 0;
- }
-
- int main(){
- char Input[200];
- LONG return_code; /* interpreter return code */
- RXSTRING argv[1]; /* program argument string */
- RXSTRING retstr; /* program return value */
- SHORT rc; /* converted return code */
- RXSTRING INSTORE[2];
- char Prog[]=
- "/* RexxSamp.cmd */\r\n"
- "/* Sample EDM/2 REXX program by Gordon Zeglinski */\r\n"
- "\r\n"
- "Call RxFuncAdd \'SysSleep\', \'REXXUTIL\', \'SysSleep\'\r\n"
- "\r\n"
- "say \"Sample EDM/2 REXX program started\"\r\n"
- "\r\n"
- "/*execute a cmd based command */\r\n"
- "\'dir /w\'\r\n"
- "\r\n"
- "/* wait 5 seconds */\r\n"
- "Call SysSleep 5\r\n"
- "\r\n"
- "/*clear screen using sample function */\r\n"
- "call EDM_SysCls\r\n"
- "\r\n"
- "say\r\n"
- "say\r\n"
- "say \"Screen cleared by sample external REXX function\"\r\n\x1A";
-
-
- RexxRegisterFunctionExe((PSZ)"EDM_SysCls",(PFN) &.SysCls);
-
-
- INSTORE[0].strptr=Prog;
- INSTORE[0].strlength=strlen(Prog);
- INSTORE[1].strptr=NULL;
- INSTORE[1].strlength=0;
-
-
- cout<<"Sample EDM/2 Rexx Demonstration Program Part 1"<<endl<<"by Gordon Zeglinski"<<endl;
- cout<<"Executing REXX programs via INSTORE parameter"<<endl;
- cout<<endl<<"INSTORE[1].strptr\t"<<(void*)INSTORE[1].strptr;
- cout<<endl<<"INSTORE[1].strlength\t"<<INSTORE[1].strlength<<endl;
-
-
- if(!strlen(Input))
- strcpy(Input,"RexxSamp.cmd");
-
- cout<<"Executing Sample Program "<<endl;
- cout<<"-----------------"<<endl;
-
-
- retstr.strptr=new char [1024];
- retstr.strlength=1024;
-
- return_code = RexxStart(0, // No arguments
- argv, // dummy entry
- Input, // File name
- INSTORE, // InStore
- "CMD", // use the "CMD" command processor
- RXCOMMAND, // execute as a command
- NULL, // No exit handlers
- &.rc, // return code from REXX routine
- &.retstr); // return string from REXX routine
-
- delete [] retstr.strptr;
-
- cout<<"After Execution";
- cout<<endl<<"INSTORE[1].strptr\t"<<(void*)INSTORE[1].strptr;
- cout<<endl<<"INSTORE[1].strlength\t"<<INSTORE[1].strlength<<endl;
-
- return 0;
- }
-
- Figure 1. Using INSTORE to execute REXX procedures in memory
-
- The above program should have the same output as REXXSAMP.CMD. We haven't done
- anything too revolutionary yet, but it's a good start. Next we'll see how to
- set up INSTORE to execute macros.
-
- #define INCL_RXFUNC /* external function values */
- #define INCL_RXMACRO
- #define INCL_VIO
- #include <rexxsaa.h>
- #include <iostream.h>
- #include <string.h>
-
- ULONG _System SysCls(UCHAR *name, ULONG numargs, RXSTRING args[],
- PSZ *queuename, RXSTRING *retstr);
-
-
- ULONG _System SysCls(UCHAR *name, ULONG numargs, RXSTRING args[],
- PSZ *queuename, RXSTRING *retstr)
- {
- BYTE bCell[2];
-
- // If arguments, return non-zero to indicate error
- if (numargs)
- return 1;
-
- bCell[0] = 0x20; /* Space Character */
- bCell[1] = 0x07; /* Default Attrib */
- VioScrollDn( 0, 0, (USHORT)0xFFFF, (USHORT)0XFFFF,
- (USHORT)0xFFFF, bCell, (HVIO) 0);/* CLS */
- VioSetCurPos(0, 0, (HVIO) 0); /* Pos cursor */
-
- //return "0" to the caller
- retstr->strlength=1;
- strcpy(retstr->strptr,"0");
-
-
- return 0;
- }
-
-
- int main(){
- char Input[200];
- LONG return_code; /* interpreter return code */
- RXSTRING argv[2]; /* program argument string */
- RXSTRING retstr; /* program return value */
- SHORT rc; /* converted return code */
- RXSTRING INSTORE[2];
- char NumArg;
-
- RexxRegisterFunctionExe((PSZ)"EDM_SysCls",(PFN) &SysCls);
-
- INSTORE[0].strptr=NULL;
- INSTORE[0].strlength=0;
- INSTORE[1].strptr=NULL;
- INSTORE[1].strlength=0;
-
-
- cout<<"Sample EDM/2 Rexx Demonstration Program 2"<<endl<<"by Gordon Zeglinski"<<endl;
- cout<<"Type rexxsamp.cmd <ENTER> to execute supplied smaple"<<endl;
- cin>>Input;
-
- if(!strlen(Input))
- strcpy(Input,"RexxSamp.cmd");
-
- cout<<"Executing Sample Program "<<Input<<endl;
- cout<<"-----------------"<<endl;
-
- retstr.strptr=new char [1024];
- retstr.strlength=1024;
-
- argv[0].strptr="";
- argv[0].strlength=0;
-
- cout <<"Storing Macro"<<endl;
-
- return_code =RexxAddMacro("MACRO1", Input, RXMACRO_SEARCH_BEFORE);
-
- cout <<"Executing Macro"<<endl;
-
- return_code = RexxStart(0, // No arguments
- argv, // dummy entry
- "MACRO1", // File name
- INSTORE, // NULL InStore
- "CMD", // use the "CMD" command processor
- RXCOMMAND, // execute as a command
- NULL, // No exit handlers
- &rc, // return code from REXX routine
- &retstr); // return string from REXX routine
-
- delete [] retstr.strptr;
-
- return 0;
- }
-
- Figure 2. Using INSTORE to Execute Macros
-
- Now we have explore two new ways to use RexxStart(). In part 1 and part 2 of
- this article, we have now seen how to use RexxStart() to:
-
- o execute REXX programs from a file
- o execute REXX programs from memory
- o execute REXX programs from the macrospace
-
- So what is this macrospace thing and what does RexxAddMacro() function do?
-
- For the answer to these and other exciting questions, keep reading...
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.2.3. The REXX Macrospace ΓòÉΓòÉΓòÉ
-
- The macrospace is a storage area in memory where REXX can keep functions for
- rapid execution. The application can add, remove, query, and change the search
- order of macros using the functions:
-
- o (ULONG)RexxAddMacro(PSZ FunctionName,PSZ SourceFile,ULONG SearchOrder)
- o (ULONG)RexxDropMacro(PSZ FunctionName)
- o (ULONG)RexxQueryMacro(PSZ FunctionName,PUSHORT Position)
- o (ULONG)RexxReorderMacro(PSZ FunctionName,ULONG Position)
-
- For RexxAddMacro(), the following parameters are specified:
-
- FunctionName This is the name of the function that is to be
- used in the RexxStart function call
- SourceFile This is the name of the file in which the macro's
- source code is stored.
- SearchOrder either RXMACRO_SEARCH_BEFORE (search before
- external functions or source files) or
- RX_MACRO_SEARCH_AFTER (search after external
- functions or source files)
-
- Return Value:
-
- RXMACRO_OK no error
- RXMACRO_NO_STORAGE macrospace is full
- RXMACRO_SOURCE_NOT_FOUND SourceFile is invalid
- RXMACRO_INVALID_POSITION SearchOrder is invalid
-
- For RexxDropMacro(), the following parameters are specified:
-
- FunctionName This is the name of the function that was used in
- the RexxAddMacro() function call
-
- Return Value:
-
- RXMACRO_OK no error
- RXMACRO_NOT_FOUND macro name is not registered
-
- For RexxQueryMacro(), the following parameters are specified:
-
- FunctionName This is the name of the function whose existence
- is being queried
- Position If the macro is found, its search order is
- returned here.
-
- Return Value:
-
- RXMACRO_OK no error
- RXMACRO_NOT_FOUND macro name is not registered
-
- For RexxReorderMacro(), the following parameters are specified:
-
- FunctionName This is the name of the function that is to be
- used in the RexxStart() function call
- SearchOrder either RXMACRO_SEARCH_BEFORE or
- RX_MACRO_SEARCH_AFTER
-
- Return Value:
-
- RXMACRO_OK no error
- RXMACRO_NOT_FOUND macro name is not registered
- RXMACRO_INVALID_POSITION SearchOrder is invalid
-
- Manipulating the MACRO Storage Area
-
- In addition to manipulating the functions within the storage area, REXX also
- allows us to manipulate the storage area itself. The storage area can be saved
- to disk, loaded from disk and erased using the functions:
-
- o (ULONG)RexxSaveMacroSpace(ULONG Num,PSZ* List,PSZ FileName)
- o (ULONG)RexxLoadMacroSpace(ULONG Num,PSZ* List,PSZ FileName)
- o (ULONG)RexxClearMacroSpace(VOID)
-
- For RexxSaveMacroSpace(), the following parameters are specified:
-
- Num The number of function names in List (if Num is
- 0, then save the whole macrospace)
- List List of function names to be saved (if Num is 0,
- then this can be NULL)
- FileName The name of the file in which the macrospace will
- be saved.
-
- Return Value:
-
- RXMACRO_OK no error
- RXMACRO_NOT_FOUND a name in List is not registered
- RXMACRO_EXTENSION_REQUIRED FileName must have an extension
- RXMACRO_FILE_ERROR An error occurred while accessing the file given
- by FileName
-
- The parameters for RexxLoadMacroSpace() are the same as those for
- RexxSaveMacroSpace()
-
- Return Value:
-
- RXMACRO_OK no error
- RXMACRO_NO_STORAGE macrospace ran out while loading in the saved
- file
- RXMACRO_NOT_FOUND a name in List is not in the saved file
- RXMACRO_ALREADY_EXISTS a name in List is already in the macrospace
- RXMACRO_FILE_ERROR an error occurred while accessing the file given
- by FileName
- RXMACRO_SIGNATURE_ERROR the file is not a valid saved macrospace file or
- it is corrupted.
-
- The use of these three functions are illustrated in the file rexx23.cpp. The
- macrospace saved files are not portable across different versions of the REXX
- interpreter. In this case, the original source files would have to be read in
- using RexxAddMacro().
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.2.4. External Subcommand Handlers ΓòÉΓòÉΓòÉ
-
- Unlike the macrospace functions, which is more for the user than the
- application developer, the external subcommand handler functions are used by
- the developer to extend the REXX language. The following functions are
- provided to the developer:
-
- o (ULONG)RexxRegisterSubcomDll(PSZ Name,PSZ ModuleName,PSZ FunctionName,PUCHAR
- Data,ULONG Drop)
- o (ULONG)RexxRegisterSubcomExe(PSZ Name,PFN FunctionAdr,PUCHAR Data)
- o (ULONG)RexxDeregisterSubcom(PSZ Name,PSZ ModuleName)
- o (ULONG)RexxQuerySubcom(PSZ Name,PSZ ModuleName,PUSHORT Flag,PUCHAR Data)
-
- For RexxRegisterSubcomDll(), the following parameters are specified:
-
- Name environment name
- ModuleName name of the DLL
- FunctionName name of the function in the DLL
- Data 8 bytes of user data. (Can be NULL)
- Drop either RXSUBCOM_DROPPABLE (any process can
- deregister the handler) or RXSUBCOM_NONDROP (only
- the current process can deregister the handler)
-
- Return Value:
-
- RXSUBCOM_OK no error
- RXSUBCOM_DUP the environment name has already been defined (to
- address this handler, its library name must be
- specified)
- RXSUBCOM_NOEMEM Not enough memory
- RXSUBCOM_BADTYPE Drop is invalid
-
- For RexxRegisterSubcomExe(), the following parameters are specified:
-
- Name environment name
- FunctionAdr the address of the function
- Data 8 bytes of user data. (Can be NULL)
-
- Return Value:
-
- RXSUBCOM_OK no error
- RXSUBCOM_DUP the environment name has already been defined
- RXSUBCOM_NOTREG not registered because of duplicate name
- RXSUBCOM_NOEMEM not enough memory
-
- For RexxDeregisterSubcom(), the following parameters are specified:
-
- Name environment name
- ModuleName name of the DLL in which the handler function
- resides
-
- Return Value:
-
- RXSUBCOM_OK no error
- RXSUBCOM_NOTREG the environment name has not been registered
- RXSUBCOM_NOCANDROP drop permission is not granted
- RXSUBCOM_BADTYPE Subcom is invalid
-
- For RexxQuerySubcom(), the following parameters are specified:
-
- Name environment name
- ModuleName name of the DLL in which the handler function
- resides
- Flag either RXSUBCOM_OK or RXSUBCOM_NOTREG depending
- upon whether Name is registered or not.
- Data The address to an 8 byte location in memory to
- receive the user data
-
- Return Value:
-
- RXSUBCOM_OK no error
- RXSUBCOM_NOTREG the environment name has not been registered
- RXSUBCOM_BADTYPE
-
- Creating External Subcommand Handler Functions
-
- External command handlers must use the following format:
-
- ULONG _System command_handler(PRXSTRING Command,PUSHORT Flag, PRXSTRING Retstr)
-
- The arguments have the following meaning:
-
- Command a null-terminated RXSTRING containing the issued
- command
- Flag the subroutine uses this flag to return the
- completion status of the command. It must be
- either RXSUBCOM_OK, RXSUBCOM_ERROR, or
- RXSUBCOM_FAILURE
- Retstr Address of an RXSTRING for the return code.
- Retstr is a character string return code that
- will be assigned to the REXX special variable RC
- when the subcommand handler returns. By default,
- Retstr points to a 256-byte RXSTRING. If
- necessary, a longer RXSTRING can allocated with
- DosAllocMem(). If the subcommand handler sets
- Retval to an empty RXSTRING, REXX will assign the
- string "0" to RC.
-
- Flag Interpretation
-
- RXSUBCOM_OK Indicates that the command was successfully
- executed.
- RXSUBCOM_ERROR Indicates that the command is recognized by the
- handler but a syntax error occurred (incorrect
- parameters for instance). The REXX interpreter
- will raise this condition if SIGNAL ON ERROR or
- CALL ON ERROR traps have been created. If TRACE
- ERRORS has been issued, REXX will trace the
- command when the subcommand handler returns.
- RXSUBCOM_FAILURE Incidactes that a subcommand failure has
- occurred. Usually this is used if Command is
- unrecognized. The REXX interpreter will raise
- this condition if SIGNAL ON FAILURE or CALL ON
- FAILURE traps have been created. If TRACE
- FAILURES has been issued, REXX will trace the
- command when the subcommand handler returns.
-
- If the command has parameters, it is up to the subcommand handler to parse the
- command for the arguments. The following simple subcommand handler processes
- the command "CLS".
-
- ULONG _System EDM_Handler(PRXSTRING Command,PUSHORT Flags,PRXSTRING retstr){
- BYTE bCell[2];
-
- if(!strcmp("CLS",Command->strptr ) ){
- bCell[0] = 0x20;
- bCell[1] = 0x07;
- VioScrollDn( 0, 0, (USHORT)0xFFFF, (USHORT)0XFFFF,
- (USHORT)0xFFFF, bCell, (HVIO) 0);
- VioSetCurPos(0, 0, (HVIO) 0);
-
- *Flags=RXSUBCOM_OK;
- retstr->strlength=1;
- strcpy(retstr->strptr,"0");
- return 0;
- }
- *Flags=RXSUBCOM_FAILURE;
- retstr->strlength=1;
- strcpy(retstr->strptr,"1");
- return 1;
- }
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.2.5. Putting It All Together ΓòÉΓòÉΓòÉ
-
- The file rexx24.cpp contains a sample program demonstrating the use of external
- subcommand handlers, external functions, and executing in memory REXX programs.
- It is rather long, and very similar to the other sample REXX programs that are
- included in this article, so it won't be included here. However, let's look at
- two code snippets that illustrate new points.
-
- The external subcommand handler is registered by the function call:
-
- RexxRegisterSubcomExe((PSZ)"EDM",(PFN)&EDM_Handler,NULL);
-
- The external subcommand handler we have seen in the previous section is
- registered under the name "EDM". To use this handler, our RexxStart() must be
- modified to specify the "EDM" environment. The following section of code
- illustrates this:
-
- return_code = RexxStart(0, // No arguments
- argv, // dummy entry
- "Sample", // procedure name
- INSTORE, // InStore
- "EDM", // use the "EDM" command processor
- RXCOMMAND, // execute as a command
- NULL, // No exit handlers
- &rc, // return code from REXX routine
- &retstr); // return string from REXX routine
-
- Functions, Macros, and Commands
-
- We have now examined a good portion of the features REXX provides developers.
- A few questions now arise like: what's the difference between commands and
- functions, and where do macros fit into this?
-
- Let's start by looking at the difference between functions and commands.
- Functions require that the user (the person using your application/library) to
- use a bulky syntax. For example, to call a function that clears the screen,
- let's call it "CLS", the user would have to use the either of the following
- syntaxes.
-
- call CLS
-
- or
-
- dummy=CLS()
-
- In the first case, CLS() returns a result in the special REXX variable RESULT.
- In the second case, the variable dummy is assigned the return value from CLS().
- Both expressions are rather bulky considering that CLS() takes no arguments,
- and really doesn't have any return values other than TRUE or FALSE If
- developing a REXX programming environment, CLS would be a prime candidate for
- command status, requiring only the following statement to execute:
-
- CLS
-
- The return value for the command would be placed in the special REXX variable
- RC. As previously mentioned, subcommand handler must parse their own commands.
- So if arguments are needed, it is less work for the programmer to use
- functions.
-
- As mentioned before, macros really aren't there for the programmer, they are
- for the user. REXX provides several functions to allow the programmer to
- manipulate the macrospace. Macros are written in REXX, while external
- functions and subcommand handlers are written in C/C++, or some other compiled
- language.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.2.6. Summary ΓòÉΓòÉΓòÉ
-
- This concludes our look at Rexx/2 for this issue. Building upon the REXX
- information in part 1, we have covered new uses for RexxStart(), external
- subcommand handlers, and the REXX macrospace interface. We have not looked at
- exit handlers or the various REXX interfaces for interactive debugging.
-
- As usual, question and comments are welcome.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.3. Using OS/2 2.x bitmap files ΓòÉΓòÉΓòÉ
-
- Written by Timur Tabi
-
- Introduction
-
- This article discusses the OS/2 2.x bitmap file format and how to use it in
- your applications. OS/2 provides support for bitmaps contained in your
- executable's resources and they can be loaded with a single API call. However,
- there may be times when combining your bitmaps with the executable is not an
- option; unfortunately, OS/2 has no built-in support for bitmaps stored as .BMP
- files. This article provides the missing elements.
-
- It should be noted that the information article will eventually be obsolete.
- Rumor has it that IBM is working on a providing built-in support for loading
- bitmaps, including those in other graphic formats like JPEG. There are also
- plans for a real image editor, something that is badly needed.
-
- The bitmap file format is also used for icons and pointers, but these two are
- beyond the scope of this article. The PM Reference distinguishes between
- bitmaps and bit maps. Bitmaps are conventional bitmap images, the kind found
- in .BMP files, and they are the subject of this article. A bit map is either a
- bitmap, an icon, or a pointer.
-
- Nota Bene: A lot of the information on the bitmap file format is either
- undocumented or so rarely used that no one outside IBM (including me) actually
- knows what it is. If anyone has any information on the bitmap file format that
- is not in this article (for instance, the halftoning algorithms), please let me
- know. I'll include it in my second edition!
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.3.1. Introduction ΓòÉΓòÉΓòÉ
-
- Written by Timur Tabi
-
- This article discusses the OS/2 2.x bitmap file format and how to use it in
- your applications. OS/2 provides support for bitmaps contained in your
- executable's resources and they can be loaded with a single API call. However,
- there may be times when combining your bitmaps with the executable is not an
- option; unfortunately, OS/2 has no built-in support for bitmaps stored as .BMP
- files. This article provides the missing elements.
-
- It should be noted that the information article will eventually be obsolete.
- Rumor has it that IBM is working on a providing built-in support for loading
- bitmaps, including those in other graphic formats like JPEG. There are also
- plans for a real image editor, something that is badly needed.
-
- The bitmap file format is also used for icons and pointers, but these two are
- beyond the scope of this article. The PM Reference distinguishes between
- bitmaps and bit maps. Bitmaps are conventional bitmap images, the kind found
- in .BMP files, and they are the subject of this article. A bit map is either a
- bitmap, an icon, or a pointer.
-
- Nota Bene: A lot of the information on the bitmap file format is either
- undocumented or so rarely used that no one outside IBM (including me) actually
- knows what it is. If anyone has any information on the bitmap file format that
- is not in this article (for instance, the halftoning algorithms), please let me
- know. I'll include it in my second edition!
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.3.2. The OS/2 2.x Bitmap File Format ΓòÉΓòÉΓòÉ
-
- OS/2 1.x used a bitmap file format very similar to the Windows 3.x DIB format.
- OS/2 2.x provides a newer format that offers more features like compression,
- different units of measurement, half-toning, different color encoding schemes,
- and user-defined space. Unfortunately, many of these advanced options are not
- documented and therefore rarely used.
-
- OS/2 2.x supports both the old and the new bitmap formats. However, use of the
- old format is strongly discouraged, and this article is only concerned with the
- new format.
-
- Bitmaps are divided into three parts: the bitmap information structure, the
- color table, and the actualy pel data.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.3.3. Bitmap information structure ΓòÉΓòÉΓòÉ
-
- Structure BITMAPINFOHEADER2 contains information that defines the size and type
- of the bitmap, but it says nothing about the colors used or the actual image.
-
- typedef struct _BITMAPINFOHEADER2 {
- ULONG cbFix;
- ULONG cx;
- ULONG cy;
- USHORT cPlanes;
- USHORT cBitCount;
- ULONG ulCompression;
- ULONG cbImage;
- ULONG cxResolution;
- ULONG cyResolution;
- ULONG cclrUsed;
- ULONG cclrImportant;
- USHORT usUnits;
- USHORT usReserved;
- USHORT usRecording;
- USHORT usRendering;
- ULONG cSize1;
- ULONG cSize2;
- ULONG ulColorEncoding;
- ULONG ulIdentifier;
- } BITMAPINFOHEADER2;
-
- cbFix The size of the structure. Set it to
- sizeof(BITMAPINFOHEADER2).
-
- [Editor's note - I do not normally do this, but since I
- know a fair amount about this topic, I will add that this
- is not necessarily true. The BITMAPINFOHEADER2 structure is
- defined semantically to be a variable-length structure
- meaning that you initialize up to the last field needed and
- set this field to be the size of the initialized portion of
- the structure. Thus, if you do not need any of the
- advanced features, you only initialize cx, cy, cPlanes, and
- cBitCount and set this field to 16, which is the sum of
- these fields' sizes.]
- cx The true width of the bitmap, in pixels
- cy The true height of the bitmap, in pixels
- cPlanes The number of colors planes. I've never seen this set to
- anything other than 1.
- cBitCount The number of bits/pixel, either 1, 4, 8, or 24.
- ulCompression The compression scheme used. These are documented in the
- PM Reference and can be any one of the following:
-
- o BCA_UNCOMP - Uncompressed
- o BCA_HUFFMAN1D - Huffman encoding, monochrome bitmaps only
- o BCA_RLE4 - Run-length encoded, 4 bits/pixel
- o BCA_RLE8 - RLE, 8/bit pixel
- o BCA_RLE24 - RLE, 24 bits/pixel
- cbImage The number of bytes that the pel data occupies. For an
- uncompressed image, this should be initialized to zero.
- cxResolution The horizontal resolution of the target device.
- cyResolution The vertical resolution of the target device.
- cclrUsed The number of colors in the color table. This field is
- used if the color table is smaller than the maximum number
- of colors. That is, if this is a 256-color bitmap, and the
- color table only defines 120 distinct colors in the bitmap,
- then this field is set to 120. If it's set to zero, then
- this is either a 24-bit bitmap of RGB values, or the color
- table is full-length.
- cclrImportant The number of colors in the color table that are required
- for satisfactory display of the bitmap. This field is used
- in case the system cannot map all of the colors in the
- table to the physical palette, so it tries to map only the
- first cclrImportant colors, and it approximates the rest.
- usUnits The units in which cxResolution and cyResolution are
- specified. Currently, only BRU_METRIC (pixels per meter) is
- supported.
- usReserved This is a reserved field and must be set to zero.
- usRecording The recording order of the pel data. The only supported
- value is BRA_BOTTOMUP, which means that the first row of
- pel data is the bottom row of the image.
- usRendering The halftoning algorithm for this image. None of these is
- documented anywhere.
-
- o BRH_NOTHALFTONED - This is the default.
- o BRH_ERRORDIFFUSION - (Damped) error diffusion.
- o BRH_PANDA - Processing Algorithm for Non-coded Document Acquisition.
- o BRH_SUPERCIRCLE - Super Circle.
- cSize1 A parameter for the halftoning algorithm. It's not used if
- there's no halftoning.
- cSize2 The second paramater for the PANDA and Super Circle
- algorithms.
- ulColorEncoding The type of values in the color table. The only supported
- value is BCE_RGB, which means the color table is an array
- of RGB2 structures.
- ulIdentifier Application-specific data. Use this ULONG for your own
- information. Great for storing pointers to additional data
- structures or functions, or even to C++ objects.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.3.4. The Color Table ΓòÉΓòÉΓòÉ
-
- The color table is an array of RGB2 structures, which are defined in pmbitmap.h
- as:
-
- typedef struct _RGB2 {
- BYTE bBlue;
- BYTE bGreen;
- BYTE bRed;
- BYTE fcOptions;
- } RGB2;
-
- The first three fields are the values of blue, red, and green, respectively.
- Each is an eight-bit value, and together they combine to form a 24-bit color
- value, which is the maximum that OS/2 can handle. There is also a 32-bit color
- standard, where another eight-bit value is used to indicate the transparency,
- but there is no support for this standard in OS/2.
-
- The fourth field in RGB2 is the options field. There are two flags available,
- PC_RESERVED and PC_EXPLICIT. PC_RESERVED is used for animating colors with the
- GpiAnimatePalette() API. The PM Reference online documentation provides this
- cryptic exaplanation for PC_EXPLICIT:
-
- "The low-order word of the color table entry designates a physical palette
- slot. This allows an application to show the actual contents of the device
- palette as realized for other logical palettes. This does not prevent the
- color in the slot from being changed for any reason."
-
- I have yet to find any mention of this field in any other documentation.
- Fortunately, its meaning is not important for our uses - the fcOptions field is
- always set to zero.
-
- The color table is nothing more than an array of RGB2 structures. The length of
- the array defines the number of colors in the bitmap. Ideally, the values of
- the color table will exactly match the current system palette. If not, OS/2
- will automatically handle conversions. In either case, you don't have to worry
- about it.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.3.5. The Bitmap Pel Data ΓòÉΓòÉΓòÉ
-
- The bitmap pel data is the actual bitmap data, stored as a two-dimensional
- array in row-major order. The dimensions of the bitmap are specified in the
- BITMAPINFOHEADER2 structure. Although a bitmap can be of any size, each row of
- data is padded to the nearest doubleword boundry. In other words, a 256-color
- bitmap (eight bits/pixel) that is five pixels wide would normally take five
- bytes for each row. In actuality, it takes up eight bytes per row, where the
- last three bytes are all zeros.
-
- Each pixel is represented by an N-bit value in the pel data. A 256-color
- bitmap uses one byte for each pixel, and a 24-bit bitmap uses three bytes per
- pixel. The color table usually contains entries only for those colors used in
- the bitmap, although it may contain the entire palette. A 24-bit bitmap does
- not require a color table, although the Icon Editor tends to include one
- anyway.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.3.6. Single and Multiple bitmap files ΓòÉΓòÉΓòÉ
-
- The OS/2 bitmap file format also supports multiple bitmaps per file. A single
- bitmap file is stored in this manner:
-
- BITMAPFILEHEADER2
- BITMAPINFOHEADER2
- Color table
- Pel Data
-
- BITMAPFILEHEADER2 is defined as:
-
- typedef struct _BITMAPFILEHEADER2 {
- USHORT usType;
- ULONG cbSize;
- SHORT xHotspot;
- SHORT yHotspot;
- ULONG offBits;
- BITMAPINFOHEADER2 bmp2;
- } BITMAPFILEHEADER2;
-
- usType The type of image, BFT_BMAP for our purposes
- cbSize Set to sizeof(BITMAPFILEHEADER2)
-
- [Don't forget that the bmp2 field is a variable length
- structure. Adjust this value accordingly. - Editor]
- xHotspot, yHotspot The hotspot for icons and pointers, not used for bitmaps
- offBits Points to the pel data for this bitmap
- bmp2 The BITMAPINFOHEADER2 structure as described in the section
- Bitmap information structure
-
- From this one structure, two out of the three parts of the bitmap are readily
- available - the info structure and the pel data. The third part, the color
- table, is located immediately after the info structure. The pointer
- manipulations are a bit tricky, but it only takes a few lines of code to get
- all three parts.
-
- When multiple bitmaps are present, each is enclosed in a BITMAPARRAYFILEHEADER2
- structure. For example, this is the layout of a bitmap file with two bitmaps:
-
- BITMAPARRAYFILEHEADER2 (for bitmap #1)
- BITMAPFILEHEADER2
- BITMAPINFOHEADER2
- Color table
- BITMAPARRAYFILEHEADER2 (for bitmap #2)
- BITMAPFILEHEADER2
- BITMAPINFOHEADER2
- Color table
- Pel Data (for bitmap #1)
- Pel Data (for bitmap #2)
-
- The BITMAPARRAYFILEHEADER2 structure is described below.
-
- typedef struct _BITMAPARRAYFILEHEADER2 {
- USHORT usType;
- ULONG cbSize;
- ULONG offNext;
- USHORT cxDisplay;
- USHORT cyDisplay;
- BITMAPFILEHEADER2 bfh2;
- } BITMAPARRAYFILEHEADER2;
-
- usType Set to BFT_BITMAPARRAY, which indicates this as an array of
- bit maps.
- cbSize Set to sizeof(BITMAPARRAYFILEHEADER2).
- offNext Points to the next BITMAPARRAYFILEHEADER2, or 0 if this is
- the last one.
- cxDisplay, cyDisplay The resolution of the target display for this bitmap. See
- the section Using Multiple Bitmaps for an example of how to
- use these fields.
- bfh2 The BITMAPFILEHEADER2 structure as described earlier in
- this section.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.3.7. Creating a Bitmap from a .BMP file ΓòÉΓòÉΓòÉ
-
- Creating an OS/2 bitmap from a .BMP file requires three steps:
-
- 1. Load the bitmap file into memory
- 2. Locate the three main pieces of the bitmap file
- 3. Use GpiCreateBitmap() to create a bitmap
-
- The first part is easy. The following code opens the file, finds its length,
- allocates enough memory for it, and loads it into that memory.
-
- #include <iostream.h>
- #include <io.h>
- #include <fcntl.h>
- #include <malloc.h>
-
- int load(const char *szName, char* &bitmap) {
- int fh=_open(szName,O_RDONLY | O_BINARY);
- if (fh == -1) {
- cout << "Error opening file " << szName << ".\n";
- return 0;
- }
-
- int length=_filelength(fh);
- if (length == -1) {
- cout << "Error determining length for " << szName << ".\n";
- _close(fh);
- return 0;
- }
- if (!length) {
- cout << szName << " has zero filesize.\n";
- _close(fh);
- return 0;
- }
-
- bitmap=(char *) malloc(length);
- if (!bitmap) {
- cout << "Error allocating " << length << " bytes.\n";
- _close(fh);
- return 0;
- }
-
- int bytesread=_read(fh,bitmap,length);
-
- if (!bytesread) {
- cout << "Read past end of file " << szName << ".\n";
- free(bitmap);
- _close(fh);
- return 0;
- }
- if (bytesread == -1) {
- cout << "Error reading file " << szName << ".\n";
- free(bitmap);
- _close(fh);
- return 0;
- }
- if (bytesread != length) {
- cout << "Could only read " << bytesread << " of " << length << " bytes.\n";
- free(bitmap);
- _close(fh);
- return 0;
- }
-
- if (_close(fh)) {
- cout << "Error closing " << szName << ".\n";
- free(bitmap);
- return 0;
- }
-
- return length;
- }
-
- The first parameter is the name of the bitmap file to load. The second is a
- reference to a pointer to a block of memory. This function allocates the
- correct amount of memory itself and returns the pointer to that block. If all
- goes well, then the length of the block is returned. Otherwise, a zero
- indicates failure.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.3.8. Parsing a Bitmap File ΓòÉΓòÉΓòÉ
-
- Once the bitmap is loaded into memory, its three subdivision must be located.
- The following code parses a single bitmap file:
-
- void parse(PBITMAPFILEHEADER2 pbfh, PBYTE pbBmpFile) {
- PBITMAPINFOHEADER2 pbih=&pbfh->bmp2;
- PRGB2 prgb=((PBITMAPINFO2) pbih)->argbColor;
- PBYTE pbPelData=pbBmpFile+pbfh->offBits;
- hbm=makebmp(pbih,prgb,pbPelData);
- }
-
- Function parse() takes two parameters: a pointer to a BITMAPFILEHEADER2
- structure and a pointer to the beginning of bitmap file. For a single bitmap
- file, these two are the same, but they are different for a multiple bitmap
- file.
-
- The first line locates the bitmap info structure (BITMAPINFOHEADER2). The
- second line finds the color table, and the third line finds the pel data. The
- last line passes these three values to makebmp(), which actually creates the
- bitmap and is covered in the next section.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.3.9. Making the Bitmap ΓòÉΓòÉΓòÉ
-
- Once the three parts of a bitmap are located, they need to be combined into a
- format acceptable by the GpiCreateBitmap() API. The following code does just
- that:
-
- #define INCL_WINWINDOWMGR
- #define INCL_GPIBITMAPS
- #define INCL_DEV
- #include <os2.h>
-
- #include <malloc.h>
- #include <memory.h>
-
- static int ipow(int b, int e) {
- int p=b;
- while (--e) p*=b;
- return p;
- }
-
- HBITMAP makebmp(PBITMAPINFOHEADER2 pbih2, PRGB2 prgb2, PBYTE pbPelData) {
-
- // Determine size of color table
- int iNumColors,numbits=pbih2->cPlanes * pbih2->cBitCount;
- if (numbits != 24)
- iNumColors = pbih2->cclrUsed ? pbih2->cclrUsed : ipow(2,numbits);
- else
- iNumColors = pbih2->cclrUsed;
- int iColorTableSize=iNumColors*sizeof(RGB2);
-
- // Allocate storage for BITMAPINFO2
- PBITMAPINFO2 pbi2=(PBITMAPINFO2) malloc(sizeof(BITMAPINFOHEADER2)+iColorTableSize);
- if (!pbi2) return 0;
-
- memcpy(pbi2,pbih2,sizeof(BITMAPINFOHEADER2)); // Copy First half
- memcpy((PBYTE) pbi2+sizeof(BITMAPINFOHEADER2),prgb2,iColorTableSize); // Second half
-
- HPS hps=WinGetPS(HWND_DESKTOP);
- HBITMAP hbm=GpiCreateBitmap(hps,pbih2,CBM_INIT,pbPelData,pbi2);
- WinReleasePS(hps);
- free(pbi2);
- return hbm;
- }
-
- Function ipow() computes b^e, which is used to compute the size of the color
- table, in case the bitmap info structure doesn't specify it.
-
- GpiCreateBitmap() doesn't use a BITMAPINFOHEADER2 structure. Instead, it uses
- a BITMAPINFO2 structure, which is nothing more than a BITMAPINFOHEADER2
- followed by a color table. The bitmap file format always places the color table
- immediately after the corresponding BITMAPINFOHEADER2 structure, so you would
- think that there is no need to pass a BITMAPINFOHEADER2 and a color table.
- However, we want makebmp() to be generic enough to take bitmap data that didn't
- necessarily come from a bitmap file.
-
- Function makebmp() performs the following tasks:
-
- o Determine the size of the color table
- o Allocate enough space to hold the BITMAPINFOHEADER2 and the color table
- o Copy both pieces to this block of data. This gives you a BITMAPINFO2.
- o Create a presentation space compatible with the display
- o Create the bitmap.
- o Release the presenation space and the allocated memory, since neither is
- needed any more.
-
- Determining the size of the color table.
-
- If the bitmap is not a 24-bit bitmap, then there's a chance that the color
- table is 2^n entries long. If the cclrUsed field of the bitmap info structure
- is zero, then it is assumed that the table is full-length (i.e. 2^n entries
- long). Otherwise, there are cclrUsed entries.
-
- If it is a 24-bit bitmap, then there cannot be 2^24 entries, so the cclrUsed
- field is guaranteed to contain the length of the color table. If this value is
- zero, then there is no color table. Technically speaking, a 24-bit bitmap
- doesn't need a color table at all.
-
- Allocating the memory.
-
- Since a BITMAPINFO2 structure is equivalent to a a BITMAPINFOHEADER2 plus a
- color table, we need to allocated enough enough space for both. The size of a
- bitmap info structure is sizeof(BITMAPINFOHEADER2) [Remember the
- variable-length characteristic - Editor] and the size of the color table is
- equal to the number of entries times sizeof(RGB2).
-
- Initializing the BITMAPINFO2 block.
-
- A pair of memcpy()'s, first for the BITMAPINFOHEADER2 and then for the color
- table right after it, is all it takes.
-
- Creating the presentation space.
-
- In order to create a bitmap, OS/2 needs to know where you're planning on using
- it. Since we want to display these bitmaps on the screen, we need to create an
- appropriate presentation space. The easiest way to do this is to call
- WinGetPS() and pass it the handle of the desktop window.
-
- Creating the bitmap.
-
- We now have everything we need. Just pass all the parameters to
- GpiCreateBitmap().
-
- Cleanup.
-
- The bitmap that we create is self-sufficient, i.e. OS/2 makes a copy of all the
- data it needs, and the presentation space is only required during the creation,
- so we can release these two resources. That's it!
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.3.10. Using Multiple Bitmaps ΓòÉΓòÉΓòÉ
-
- If you've ever edited one of the standard icons that comes with OS/2, you'll
- notice there are several images defined. There's one that's 40x40 with 256
- colors, one that's 32x32 with 16 colors, and several others. When OS/2 displays
- an icon, it searches the file for the best fit.
-
- You can also use the same technique. Different bitmaps for different
- resolutions and colors can be created. OS/2 can scale and dither images
- automatically, but the results are often unsatisfactory. By defining a
- different bitmap for each resolution and color depth, you can always have
- perfect images.
-
- Unfortunately, this approach is error-prone. Usually you need to define a set
- of bitmaps that go together. And for each bitmap in the set, you'll want one
- for each resolution. If you create one bitmap for a certain resolution, you'll
- create to do the same for all other bitmaps. If you don't, then you risk
- having your bitmaps mismatched.
-
- Alternatively, you could have your program automatically scale the nearest
- match. But what is the nearest match? Which is closer to 800x600: 640x480 or
- 1024x768? And what if you're running 800x600 at 256 colors, and you have two
- bitmaps defined: 800x600x16 colors and 1024x768x256 colors. Which is more
- important, the resolution or the number of colors?
-
- If I ever write a second edition for this article, I will address this issue in
- more detail.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.3.11. The BMPINFO program ΓòÉΓòÉΓòÉ
-
- Included with this article is the source code to BMPINFO - a program which
- provides a detailed dump of all bit map files, including icons and pointers.
- This program is useful for testing programs which need to scan or create bitmap
- files. It can also be used to get a better understand of the bitmap file
- format. It supports single and multiple bitmaps, icons, and pointers in both
- the new and old bitmap file formats.
-
- Note that many programs which create bitmap files might not initialize all the
- fields correctly. For instance, versions of Joe View prior to 1.22 would not
- set the cbSize field correctly, so BMPINFO could not determine the the version
- of the file format.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.4. Writing a Direct Manipulation Spy ΓòÉΓòÉΓòÉ
-
- Written by Larry Salomon, Jr.
-
- Introduction
-
- Almost everyone has at least heard of or even used the development tool PM Spy.
- The equivalent of the Windows application Spy, it captures all of the messages
- sent to a window or queue and displays them in a nicely formatted listbox.
- Some time ago, while adding direct manipulation support to a rather complex
- application, I realized how difficult it is to actually do this; also, there
- were more than just a few questions on how to integrate with the Workplace
- Shell via "drag-n-drop".
-
- "Wouldn't it be nice," I thought, "if there were a tool to help people with
- rendering mechanisms, etc.?" I concluded that it would be nice, and I finally
- got the gumption to write one.
-
- Drop Me! is a tool that saves all of the information about the items being
- dragged over its window. When it receives notification that the drag operation
- has left the window, it displays the information in a container. All of the
- fields in the DRAGITEM structure are saved.
-
- When you think about it, this utility is a trivial one. However, this article
- is being written to discuss the trials and tribulation of its authoring.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.4.1. Introduction ΓòÉΓòÉΓòÉ
-
- Written by Larry Salomon, Jr.
-
- Almost everyone has at least heard of or even used the development tool PM Spy.
- The equivalent of the Windows application Spy, it captures all of the messages
- sent to a window or queue and displays them in a nicely formatted listbox.
- Some time ago, while adding direct manipulation support to a rather complex
- application, I realized how difficult it is to actually do this; also, there
- were more than just a few questions on how to integrate with the Workplace
- Shell via "drag-n-drop".
-
- "Wouldn't it be nice," I thought, "if there were a tool to help people with
- rendering mechanisms, etc.?" I concluded that it would be nice, and I finally
- got the gumption to write one.
-
- Drop Me! is a tool that saves all of the information about the items being
- dragged over its window. When it receives notification that the drag operation
- has left the window, it displays the information in a container. All of the
- fields in the DRAGITEM structure are saved.
-
- When you think about it, this utility is a trivial one. However, this article
- is being written to discuss the trials and tribulation of its authoring.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.4.2. Right to the Heart of the Matter ΓòÉΓòÉΓòÉ
-
- The application is rather straight forward; it is a PM application which
- creates a "standard window" and within that a container window is created as a
- child of the client window. The container is initialized in detail view.
-
- Let us consider the events that occur when direct manipulation occurs, from the
- target's perspective:
-
- 1. The user initiates the drag operation
-
- 2. The drag moves over the window
-
- 3. The user either terminates the drag operation by
-
- a) ...releasing the mouse button to perform the operation
- b) ...pressing F1 for direct manipulation help
- c) ...pressing ESC to cancel the operation
-
- 4. ...or the drag leaves the window
-
- From a message standpoint, the application receives a DM_DRAGOVER, DM_DROP,
- DM_DRAGHELP, or DM_DRAGLEAVE message for steps 2, 3a, 3b, and 4, respectively.
- Since the client is covered by the container, however, we must instead rely on
- it to notify us of these events.
-
- Fortunately, there are many notifications for direct manipulation provided by
- the container class. We are interested specifically in the CN_DRAGOVER and
- CN_DRAGLEAVE notifications; because we will not be accepting any drop
- operations, we do not care about the CN_DROP notification.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.4.3. "Doc, this headache is killing me" ΓòÉΓòÉΓòÉ
-
- Original Intentions
-
- Originally, I had implemented the utility in the following manner: when the
- CN_DRAGOVER message was received, convert each DRAGITEM field to a string and
- store the resulting structures in a linked list (assuming the list was not
- already initialized); when the CN_DRAGLEAVE message was received, if the list
- was not empty, empty the container, add the contents of the linked list to the
- container, and empty the linked list to prime ourselves for the next drag
- operation.
-
- This was much too complex. First, it requires that the Common/2 package
- (containing the linked list routines) be installed on the machine where it is
- being compiled. Second, why can we not simply insert the records into the
- container when we receive the CN_DRAGOVER message?
-
- Hmm, good question.
-
- Unnecessary Complexities
-
- My intent was not to "scare" the user by having this information pop up
- immediately when the mouse moved over the container. However, moving the
- CN_DRAGLEAVE processing to the end of the CN_DRAGOVER processing as a test
- revealed that the records, while added to the container, do not get displayed
- because the direct manipulation operation locks the screen until it is
- completed. Back to the drawing board.
-
- The result is what is included as dropme.zip. The source no longer requires
- Common/2, which results in an executable size that has been halved. Also, the
- code is less complex, which means it is easier to understand.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.4.4. For the Container Virgins ΓòÉΓòÉΓòÉ
-
- If you have never done any programming with the container control, I highly
- recommend you read the "Programming the Container Control" series (3 parts) in
- issues 3, 4, and 5 of EDM/2. I will briefly describe the specifics of the
- interactions with the container here, however.
-
- The container is created using WinCreateWindow() in the WM_CREATE processing
- and is resized in the WM_SIZE processing to insure that it completely covered
- the client. The initialization, which also occurs in WM_CREATE, involves
- allocating the FIELDINFO structures (via a CM_ALLOCDETAILFIELDINFO message),
- initializing them, and finally inserting them (via a CM_INSERTDETAILFIELDINFO
- message). Before exiting, the container is set to "detail view" mode (via a
- CM_SETCNRINFO message) specifying that the column headings should also be
- displayed.
-
- When the container notifies us that a drag operation is occuring over our
- window (by sending us a WM_CONTROL message specifying CN_DRAGOVER as the
- notification code), we allocate enough records for each item being dragged (via
- a CM_ALLOCRECORD message) and then loop through the items; DrgQueryDragitem()
- is called for each and the contents of the DRAGITEM structure are converted to
- strings to be displayed in the container. Finally, the current contents are
- emptied (via a CM_REMOVERECORD message) and the new records are inserted (via a
- CM_INSERTRECORD message).
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.4.5. Possible Enhancements ΓòÉΓòÉΓòÉ
-
- Now that we have our nifty utility, it would be nice if it had the ability
- to...
-
- o ...copy one or more records to the clipboard.
- o ...save the contents of the container to a file.
-
- The first item should not be difficult and, using the article "Threads in PM
- Applications" from last month's issue of EDM/2, the second item should not
- require too much time to implement either. These exercises are left to the
- reader.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 2.4.6. Epilogue ΓòÉΓòÉΓòÉ
-
- Here is the "Reader's Digest Condensed" version of a humorous incident that I
- had while writing this application. I would like to thank my good friend Peter
- Haggar (the original container developer) for taking time out of his busy
- schedule to give me a much-needed hand in debugging.
-
- "Hi Peter. This is Larry Salomon."
-
- "Hey Larry. How are you?"
-
- "Can't complain much. Yourself?"
-
- "Not too bad. What can I do for you?"
-
- "Well, I have the problem. It seems that when I try to insert some records
- into the container during a CN_DRAGLEAVE message, my application traps. I have
- checked everything in the insertion procedure and it looks fine. Is this some
- sort of critical section that I need to be aware of?"
-
- [Tap tap tap] (He brings up the source code)
-
- "Well, it seems that we do check to see if a drag operation is in progress and
- call the appropriate function to get the HPS since the screen is locked."
-
- [The rest of the "debugging over the phone" is deleted]
-
- [The next morning]
-
- "Hi Peter; it's me again."
-
- "Hey."
-
- "As I was driving home last night, I thought that because I'm not using the
- pszIcon field that the container might still be trying to access it and that is
- causing the trap. However, that is not the case. Here is everything that my
- application does. First, it allocates the FIELDINFO structures and inserts
- them into the container. I'm using CFA_STRING because I know the DRAGITEM will
- probably not be valid anymore. And then I..."
-
- "Are your field offsets pointing to a pointer to the string?"
-
- "Oh, shit..."
-
- "Yeah, we should've named the constant CFA_PSZ; it confuses everyone. By the
- way, isn't this [pitfall] in your book?"
-
- "Oh, shit. How embarrassing..."
-
- ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
-
- See you next time.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 3. Columns ΓòÉΓòÉΓòÉ
-
- The following columns can be found in this issue:
-
- o Introduction to PM Programming
- o Scratch Patch
-
-
- ΓòÉΓòÉΓòÉ 3.1. Introduction to PM Programming ΓòÉΓòÉΓòÉ
-
- Written by Larry Salomon, Jr.
-
- Introduction
-
- The purpose of this column is to provide to the readers out there who are not
- familiar with PM application development the information necessary to satisfy
- their curiousity, educate themselves, and give them an advantage over the
- documentation supplied by IBM. Of course, much of this stuff could probably be
- found in one of the many books out there, but the problem with books in general
- is that they don't answer the questions you have after you read the book the
- first time through.
-
- I will gladly entertain feedback from the readers about what was "glossed over"
- or what was detailed well, what tangential topics need to be covered and what
- superfluous crap should have been removed. This feedback is essential in
- guaranteeing that you get what you pay for. :)
-
- It should be said that you must not depend solely on this column to teach you
- how to develop PM applications; instead, this should be viewed as a supplement
- to your other information storehouses (books, the network conferences, etc.).
- Because this column must take a general approach, there will be some topics
- that would like to see discussed that really do not belong here. Specific
- questions can be directed to the Scratch Patch, where an attempt to answer them
- will be made.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 3.1.1. Introduction ΓòÉΓòÉΓòÉ
-
- Written by Larry Salomon, Jr.
-
- The purpose of this column is to provide to the readers out there who are not
- familiar with PM application development the information necessary to satisfy
- their curiousity, educate themselves, and give them an advantage over the
- documentation supplied by IBM. Of course, much of this stuff could probably be
- found in one of the many books out there, but the problem with books in general
- is that they don't answer the questions you have after you read the book the
- first time through.
-
- I will gladly entertain feedback from the readers about what was "glossed over"
- or what was detailed well, what tangential topics need to be covered and what
- superfluous crap should have been removed. This feedback is essential in
- guaranteeing that you get what you pay for. :)
-
- It should be said that you must not depend solely on this column to teach you
- how to develop PM applications; instead, this should be viewed as a supplement
- to your other information storehouses (books, the network conferences, etc.).
- Because this column must take a general approach, there will be some topics
- that would like to see discussed that really do not belong here. Specific
- questions can be directed to the Scratch Patch, where an attempt to answer them
- will be made.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 3.1.2. What is PM? ΓòÉΓòÉΓòÉ
-
- PM - an acronym for Presentation Manager - was originally a subsystem intended
- to provide a graphical user environment to make the system easier to use.
- Since OS/2 version 1.1 when it was introduced, it has grown to become more of a
- Siamese twin, joined at the hip. If viewed as a set of components, it can be
- broken down into the following:
-
- o Device functions
- o Direct manipulation functions
- o Graphics functions
- o Hooks
- o Message processing
- o Spooler functions
- o Window functions
- o Workplace Shell classes
-
- Figure 1) The PM components as listed in the Technical Reference
-
- An explaination of the components is provided below. The prefix used on the
- functions belonging to the groups is listed after the component name, when one
- exists.
-
- Device functions (Dev) - since OS/2 takes the outlook that all output devices
- are logical devices, this component is used to access and query the physical
- device that the application is using.
-
- Direct Manipulation functions (Drg) - commonly referred to as "drag-n-drop",
- these functions implement a common protocol for communicating what objects are
- being dragged from one window to another.
-
- Graphics functions (Gpi) - allows an application to draw on any output device,
- in addition to providing a host of other functions.
-
- Hooks - allow an application to intercept notification of and act on certain
- types of events before the intended recipient is receives notification.
-
- Message processing - communicates an event to an application.
-
- Spooler functions (Spl) - provides printer queue management functions.
-
- Window functions (Win) - this is a bit of a misnomer, because there are
- functions included in this group which really do not have a direct relationship
- with windows.
-
- Workplace Shell classes - included in OS/2 2.x, this component provides the
- System Object Model (SOM) classes that the user-interface uses, such as
- folders, palettes (color, font, and scheme), etc.
-
- We will be concentrating on the two components that you cannot avoid - Message
- processing and Window functions.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 3.1.3. Architecture 101 ΓòÉΓòÉΓòÉ
-
- Before we can go on, we need to step back and look at the 1000-foot view of PM
- "things", after which we can start defining the terms you have already been
- confused by.
-
- Object Oriented?
-
- Disclaimer - I am not a hard-core C++ programmer, so my use of terms used in
- object oriented books might be incorrect. As Maddie, the moderator of
- rec.humor.funny so blithly put it: you can get a refund of double what you
- paid for this. :)
-
- PM has a half-implemented object oriented design, which hopefully some of you
- will find familiar; the exception to this is the Workplace Shell classes which,
- although a part of PM, are completely object oriented because they are based on
- SOM. The half-implemented design can be described as such: pick a object,
- without thinking about specifics. Let's say a toaster. Now, consider that all
- toasters can
-
- o Toaster bread
- o Allow you to set the darkness of the toast
- o "Tell you" the darkness setting (by looking at it)
- o Insert bread
- o Remove toast
-
- Figure 2) Characteristics of a toaster
-
- This is the concept of a toaster, but not the toaster itself. For that, you
- must go to your local K-Mart, Caldor, etc. and buy a toaster. This toaster or
- is said to be an instance of the toaster concept in reality. An important
- distinction here is that the concept defines the characteristics, but not the
- way the characteristics are defined. In other words, each instance of a
- toaster has different ways of accomplishing the tasks above, but they all can
- accomplish the tasks in some manner. An example might be that one toaster
- receives bread through the top, while another has a front door that you open to
- insert the bread.
-
- Figure 3) Relationship between concept and reality.
-
- The concept in PM is the window class and the instance of the concept is the
- window. A window is defined to be a rectangular area in a Cartesian coordinate
- space, and has the ability to paint itself, be sized, be moved, etc.
-
- It's a Fashion Thing
-
- When you buy a automotive vehicle, you can specify that you want a radio,
- four-wheel drive, air conditioning; in PM the application can specify a window
- style, which controls (to an extent) how the window reacts/behaves in specific
- situations. These window styles can be specified when the window is created
- or, if every window of the class has a common set of styles, these can be
- specified when the class is defined and are then called class styles.
-
- Figure 4) Relationship between window class and window objects.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 3.1.4. Events ΓòÉΓòÉΓòÉ
-
- An event is the most important entity in PM application development; it has the
- same definition here as it does in the "real world". Events occur for any of a
- myriad of reasons - the mouse moved, a menu item was selected, a window was
- destroyed, etc. - and the application is notified by the system using a
- message.
-
- (What an abstract term. I remember reading about messages for the first time
- eons ago and wondering what the hell the term "message" meant.)
-
- A message is really no more than a ULONG sent to a function in your
- application. Each message can have up to eight parameters (all characters),
- and can optionally return a result. Additionally, an application not only
- receives messages, but it can also send messages. For system-defined messages,
- there are constants defined in pmwin.h, and there numerical values are in the
- range 1-(WM_USER-1). Any application-specific messages (user messages) should
- be defined as some value greater than or equal to the system constant WM_USER.
-
- A message's parameters are packed into a special type - MPARAM - and a
- message's result is packed into another type - MRESULT. From either, the
- various parameters can be extracted using one of the many "helper macros"
- defined.
-
- MPARAM mpParm1;
- MRESULT mrResult;
-
- datatypeFROMMP(mpParm1)
- datatypeFROMMR(mrResult)
-
- Figure 5) Syntax of the MPARAM/MRESULT extraction macro names
-
- In figure 5, datatype can be one of the following:
-
- PVOID Pointer to a VOID which should be cast to the appropriate
- type.
- HWND Window handle (see the next section)
- CHAR1 The first character (bits 0-7).
- CHAR2 The first character (bits 8-15).
- CHAR3 The first character (bits 16-23).
- CHAR4 The first character (bits 23-31).
- SHORT1 The first USHORT (bits 0-15).
- SHORT2 The second USHORT (bits 16-31).
- LONG A ULONG.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 3.1.5. Resources and Handles ΓòÉΓòÉΓòÉ
-
- In addition to windows, PM manages a lot of many other different things:
- icons, bitmaps, strings, menus, drawing boards (known as presentation spaces),
- etc. The management of these entities - known as resources - is assisted by
- many internal tables that we do not care about (other than for curiousity's
- sake maybe). Each resource is referred to by a handle, which is nothing more
- than a ULONG used as an index into one or more tables.
-
- The importance of this point is that, because these resource handles are
- typedef'd from a ULONG, the ability to specify the wrong type of handle as a
- parameter to a function is substantially increased. While the temptation to
- typecast the parameter to avoid any warnings or errors generated by your
- compiler is great, care must be exercised to insure that what you are doing
- agrees with the original intent of the function-resource relationship.
-
- As an example, a pointer and an icon are two similar resources, yet there is a
- WinLoadPointer() function and no WinLoadIcon() function. While few people know
- why, everyone knows that you can use WinLoadPointer() to load an icon (from the
- resource table, a concept that will be discussed in future issues. This is
- noted so that you do not think that it loads from a separate disk file, which
- it does not.). You cannot, however, use WinLoadPointer() to load a bitmap from
- the resource table, because pointers and bitmaps have fundamental differences
- in their purposes.
-
- One final note: a NULL handle is specified using the constant NULLHANDLE and
- not using NULL. Though you can cast NULL to the appropriate type, why type the
- extra keystrokes?
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 3.1.6. Putting it Together ΓòÉΓòÉΓòÉ
-
- Now that I've managed to confuse you with what seems to be a bunch of distantly
- related concepts, let us take a better look at windows and window classes to
- bind it together.
-
- A window class is defined to be an entity that consists of the following:
-
- o A name (string)
- o Zero or more class-styles (separate bits in a ULONG)
- o A window class procedure (usually incorrectly referred to as a window
- procedure)
- o Zero or more bytes of memory that is allocated for each window of the class
- (called window words)
-
- The class name is bound to the remainder of the class parameters, and is used
- as a referencing index. Thus, when you create windows of a particular class,
- you need specify only the class name and the system performs a table lookup to
- retrieve the specifics.
-
- The window class procedure which receives all of the events directed at a
- window of the class. Its prototype is defined below:
-
- MRESULT EXPENTRY myProcedure(HWND hwndWnd,
- ULONG ulMsg,
- MPARAM mpParm1,
- MPARAM mpParm2);
-
- Figure 6) Prototype of a window class procedure
-
- The parameters should look familiar except for one.
-
- hwndWnd The handle of the window receiving the message.
- ulMsg The message identifier being sent.
- mpParm1 Message parameter 1.
- mpParm2 Message parameter 2.
-
- This function only processes the messages it is interested in (typically done
- by the C "switch" construct). The other messages are handled by passing them
- to a special system function - WinDefWindowProc() - and returning its result.
-
- The window words are important concept that cannot be overlooked...so we won't
- overlook them. The idea is that n bytes of memory are allocated for every
- window of the class that is created. This memory can be accessed with the
- functions WinQueryWindowULong() and WinSetWindowULong() (specifying the
- constant QWL_USER), and their functional equivalents WinQueryWindowPtr() and
- WinSetWindowPtr(). Note that since the number of bytes to be queried/set is a
- multiple of 4, a multiple of 4 should be specified when the class is created.
-
- What is the point(er) of using window words? By storing a pointer to an
- application-defined structure (where the structure is dynamically allocated
- using malloc()/calloc()), you can maintain a separate set of data for each
- window of the class. See the article "Development of a New Window Class - Part
- 1" in issue 4 of EDM/2 for more information on window words.
-
- One last question: how is the class created? The class is created (said to be
- registered) using the WinRegisterClass() function, which we will see more of in
- future issues.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 3.1.7. Summary ΓòÉΓòÉΓòÉ
-
- In this issue, we have wiped the slate clean and started anew. We should have
- learned...
-
- o ...what the components of Presentation Manager are.
- o ...the relationship of an object class to an object instance and how windows
- fit into this view of the world.
- o ...the purpose of window styles and class styles and the difference between
- the two.
- o ...what an event, an MPARAM, and an MRESULT is.
- o ...what resources are and how an application refers to them.
- o ...what the components of a class are.
- o ...what window words are and why they are used.
-
- Next month, we will build our "first" PM application and take it apart to see
- what lurks beneath the surface.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 3.2. 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 Questions and Answers
- o Snippet(s) of the Month
- o Documentation Chop Shop
- o Want Ads
-
-
- ΓòÉΓòÉΓòÉ 3.2.1. Corrections ΓòÉΓòÉΓòÉ
-
- Since I meant to mention this last month but forgot, I have a correction for
- the "Help Manager and Online Documentation" article in the October issue. It
- is really not a correction, but instead a better way of doing message box help.
- The idea is to create a dummy HELPSUBTABLE and then for each message box help
- id, create a HELPITEM whose first and third identifiers are the message box
- help id, and the second identifier is of the dummy HELPSUBTABLE. The idea is
- that when the user requests help, the HELPSUBTABLE will not have the
- appropriate entry and so the Extended Help panel (the third parameter) will be
- displayed.
-
- The advantage of this is that of performance; hooks are performance eaters, so
- this is a nice reason to do this (according to Scott Kliger, the developer of
- the original Help Manager code). The disadvantage of this is that for larger
- applications with large numbers of different message boxes, the HELPTABLE can
- get rather unwieldy.
-
- Another clarification is about last month's "Threads in PM Applications"
- article, where the call to DosSleep() was made in the processing of the
- MYM_ENDTHREAD message. The use of this function within a PM application is
- strongly discouraged because it can lock the system up until the call returns,
- but if used with caution will cause no noticeable effect. This case is just
- that; the thread is already dying, so the loop with the DosSleep() body will
- execute a minimal number of times.
-
- Thanks go to Michael Hohner (fn0728@cd4680fs.rrze.uni-erlangen.de) for sending
- us both of these items.
-
- ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
-
- Also, in issue 5, I stated that, for user painted buttons, BDS_DEFAULT was
- incorrectly defined. Instead, it is correctly defined and I was
- misinterpreting the definition of default. Default in this sense does not mean
- "paint in the default manner", but instead means that the button is the default
- button and should be drawn with a darker border. This is a flag that can be
- specified in addition to the others (BDS_HILITED and BDS_DISABLED) which are
- mutually exclusive of each other.
-
- A value of 0 can still be received for fsState (in the USERBUTTON structure, so
- a new constant should be defined for this value (I named it BDS_NORMAL).
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 3.2.2. Questions and Answers ΓòÉΓòÉΓòÉ
-
- Gordon W. Zeglinski (zeglins@cc.umanitoba.ca) writes:
-
- Here's a Q that may make good EDM/2 material. I want to define a menu in
- memory, and then create it using WinCreateMenu. This will eventually be a
- submenu. It is impossible to create the submenu in resource files, as its
- contents are not know until runtime. The reason I want to create the whole
- submenu at once instead of placing them the entries into an existing submenu is
- because placing 18 entries into the sub menu is taking about 1 min on a 486
- DX33. A very slow process :(.
-
- Anyways, I digress...
-
- The documentation states that the WinCreateMenu item takes a Binary Menu
- template. After searching through much IBM documentation, I find no such
- binary template documented...
-
- So what is the format of this template ?
-
- Gordon
-
- (Before everyone gasps at the "1 minute on a 486/33" statement, let me add that
- Gordon subsequently found that his code was trying to delete several thousand
- non-existant menu items first, which was causing the severe time delay.)
-
- In order to answer the question, one first needs to look at the data structures
- involved. In pmwin.h, there are two little-known data structures known as MTI
- (none) and MT (LMTI), (the datatype names for pointers to these structures are
- shown in paratheses) which are acronyms for menu template item and menu
- template.
-
- typedef struct _mti {
- USHORT afStyle;
- USHORT pad;
- USHORT idItem;
- CHAR c[2];
- } MTI;
-
- typedef struct _mt {
- ULONG len;
- USHORT codepage;
- USHORT reserved;
- USHORT cMti;
- MTI rgMti[1];
- } MT, *LPMT;
-
- The MTI structure fields are explained below:
-
- afStyle one or more MIS_* constants
- pad reserved and must be zero.
- idItem menu item identifier.
- c variable length data depending on which bits in afStyle are
- set:
-
- MIS_TEXT specifies the NULL-terminated menu item text
- MIS_BITMAP If the first byte is '#', the remaining bytes specify
- the ASCII representation of the bitmap resource. If the
- first byte is zero, then the next four bytes define a
- bitmap handle that has already been loaded. Otherwise,
- there is no bitmap handle associated and the application
- must set one using the MM_SETITEM message.
- MIS_OWNERDRAW, MIS_SEPARATOR the data is ignored
- MIS_SUBMENU the data is a menu template
-
- The MT structure fields are explained below:
-
- len specifies the size of the menu template in bytes
- codepage specifies the code page to be used
- reserved reserved and must be zero.
- cMti specifies the number of MTI structures in rgMti
- rgMti specifies the MTI structures that comprise the menu items.
-
- As you can see, the menu template size will - in all likelihood - have to be
- calculated at run-time as well, making this a nasty section of code. Once you
- have calculated this, allocated the memory, and initialized the structures, you
- then do one of two things: if a top-level menu does not already exist, you
- call WinCreateMenu() and you are done; otherwise, you call WinCreateWindow() to
- create the submenu as in the following example:
-
- hwndSubMenu=WinCreateWindow(HWND_DESKTOP, // parent
- WC_MENU, // window class
- "", // no text
- 0, // no style :)
- 0, // no position
- 0, // :
- 0, // nor size
- 0, // :
- HWND_DESKTOP, // owner
- HWND_TOP, // z-order
- M_TESTMENU, // window id
- pmtMenu, // pCreateParms=menu template
- NULL); // no pres params
-
- After this, you need to insert the top-level item representing the pulldown by
- sending the menu control a MM_INSERTITEM message. The hwndSubMenu field of the
- MENUITEM structure gets the value returned by the WinCreateWindow() call.
-
- miSubMenu.iPosition=MIT_END;
- miSubMenu.afStyle=MIS_SUBMENU|MIS_TEXT;
- miSubMenu.afAttribute=0;
- miSubMenu.id=M_TESTMENU;
- miSubMenu.hwndSubMenu=hwndSubMenu;
- miSubMenu.hItem=NULLHANDLE;
-
- strcpy(achText,"~Test");
-
- WinSendMsg(hwndMenu,
- MM_INSERTITEM,
- MPFROMP(&miSubMenu),
- MPFROMP(achText));
-
- Note that the value of miSubMenu.id is the same as the id specified on the
- WinCreateWindow() call.
-
- Obviously, using menu templates can be a real headache, and the headache gets
- worse if you have "pull-right" menus. It might be better for you to determine
- where your performance bottleneck is in your current code and try to optimize
- that if possible.
-
- A sample program illustrating these concepts has been provided as MENUTEST.ZIP.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 3.2.3. Snippet(s) of the Month ΓòÉΓòÉΓòÉ
-
- (Sigh) There was only one submission this month, but unfortunately the person
- who sent it to me did not write it; remember, you must be the author and you
- must give me permission to publish your code!
-
- Even with this setback, I cannot admit defeat, so I have once again reached
- down into my black hat and pulled out two useful functions. As before, all of
- these are located in the file snippet.zip.
-
- BOOL splitFilename(PCHAR pchFile,PCHAR pchDrive,PCHAR pchPath,PCHAR pchName)
- //-------------------------------------------------------------------------
- // This function splits a filename into the drive, path, and name components.
- // This is the functional equivalent of the MSC 6.0 function _splitpath().
- //
- // Input: pchFile - points to the filename to split
- // Output: pchDrive - if not NULL, points to the buffer which receives
- // the drive portion of the filename
- // pchPath - if not NULL, points to the buffer which receives
- // the path portion of the filename
- // pchName - if not NULL, points to the buffer which receives
- // the name portion of the filename
- // Returns: TRUE if successful, FALSE otherwise
- //-------------------------------------------------------------------------
- {
- PCHAR pchBegin;
- PCHAR pchEnd;
-
- pchBegin=pchFile;
- pchEnd=strchr(pchBegin,':');
-
- if (pchDrive!=NULL) {
- *pchDrive=0;
-
- if (pchEnd!=NULL) {
- strncat(pchDrive,pchBegin,pchEnd-pchBegin+1);
- } /* endif */
- } /* endif */
-
- if (pchEnd!=NULL) {
- pchBegin=pchEnd+1;
- } /* endif */
-
- pchEnd=strrchr(pchBegin,'\\');
-
- if (pchPath!=NULL) {
- *pchPath=0;
-
- if (pchEnd!=NULL) {
- strncat(pchPath,pchBegin,pchEnd-pchBegin+1);
- } /* endif */
- } /* endif */
-
- if (pchEnd!=NULL) {
- pchBegin=pchEnd+1;
- } /* endif */
-
- if (pchName!=NULL) {
- strcpy(pchName,pchBegin);
- } /* endif */
-
- return TRUE;
- }
-
- BOOL isWindowOfClass(HWND hwndWnd,PCHAR pchClass)
- //-------------------------------------------------------------------------
- // This function determines whether or not the specified window is of
- // the specified class. This will work for either private classes
- // (registered with WinRegisterClass) or the public classes (specified
- // by a WC_* constant).
- //
- // Input: hwndWnd - specifies the window handle.
- // pchClass - points to either the window class or specifies a
- // WC_* constant to compare with.
- // Returns: TRUE if hwndWnd is of class pchClass, FALSE otherwise.
- //-------------------------------------------------------------------------
- {
- CHAR achClass[256];
- ATOM aAtom;
-
- //----------------------------------------------------------------------
- // For the WC_* classes, WinQueryClassName returns the string
- // representation of the integer atom as stored in the system atom
- // table. The string representation of *any* integer atom is expressed
- // in the form "#ddddd", where 'ddddd' are the decimal digits specifying
- // the value of the atom.
- //
- // We can check the first character of the class name returned to see
- // if the window is of a predefined type.
- //----------------------------------------------------------------------
- WinQueryClassName(hwndWnd,sizeof(achClass),achClass);
-
- if (achClass[0]=='#') {
- //-------------------------------------------------------------------
- // We have a WC_* window, so check to see if pchClass is also a
- // predefined class. If so, the high word is 0xFFFF.
- //-------------------------------------------------------------------
- if (HIUSHORT(pchClass)!=(USHORT)0xFFFF) {
- return FALSE;
- } /* endif */
-
- //-------------------------------------------------------------------
- // Get the integer representation of the atom and compare it
- // against the low word of pchClass, which is also the integer
- // representation of the class it represents.
- //-------------------------------------------------------------------
- aAtom=WinFindAtom(WinQuerySystemAtomTable(),achClass);
- if (aAtom==LOUSHORT(pchClass)) {
- return TRUE;
- } /* endif */
- } else {
- //-------------------------------------------------------------------
- // We don't have a WC_* window, so check to see if pchClass is not
- // also. If it isn't, compare the classes using strcmp().
- //-------------------------------------------------------------------
- if (HIUSHORT(pchClass)==(USHORT)0xFFFF) {
- return FALSE;
- } /* endif */
-
- if (strcmp(achClass,pchClass)==0) {
- return TRUE;
- } /* endif */
- } /* endif */
-
- return FALSE;
- }
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 3.2.4. Documentation Chop Shop ΓòÉΓòÉΓòÉ
-
- A few of you may have noticed, but in the OS/2 2.1 Toolkit, IBM once again
- forgot to define the container record attribute CRA_SOURCE. It is wise to
- define it directly in the pmstddlg.h file with the value 0x00004000L.
-
- ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
-
- When we received our copy of the new C-Set++ compiler, I installed it and
- immediately proceeded to test for a successful installation by writing a C++
- style "Hello world" program. After many unsuccessful attempts to get the
- program to link successfully, I was told that the compiler does not insert the
- default library object record for the C++ class libraries and that the user
- must do so explicitly. This is not stated in the documentation; the libraries
- are listed below:
-
- o DDE4MUIB.LIB, DDE4MUIC.LIB, and DDE4MUID.LIB for the standard and
- user-interface class libraries, statically linked.
- o DDE4CC for the container class libraries, statically linked.
-
- It should be noted that statically linking these libraries ballooned the "Hello
- world" application to 131584 bytes! It is thus recommended that you use the
- dynamically linked run-time (DDE4MUII.LIB for the standard/user-interface class
- libraries and DDE4CCI.LIB for the container class libraries) and distribute the
- DLL's (which are royalty free, but require that you agree to the conditions
- listed on page 12 of the "C/C++ Tools License Information" pamphlet) with your
- application.
-
- ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
-
- In issue 2, I described how dropping on a printer does not send you a pointer
- to the DRAGINFO structure, but instead to a DRAGITEM structure corresponding to
- one of the items dropped on the printer sending the message. Well, it appears
- that, in OS/2 2.1, the system forgets to grant you access to the structure (if
- you think about it, however, this makes more sense than before, but they should
- still pass the PDRAGINFO structure instead). How do you access the data?
- Well, fortunately, the DrgAccessDraginfo() function is not picky about whether
- or not its argument is a PDRAGINFO or a PDRAGITEM, so insure that you call this
- function before trying to access the structure's contents, or you are sure to
- receive a protection violation.
-
- As a related topic, when you are initializing the PDRIVDATA field of the
- DEVOPENSTRUC structure, after allocating the memory, do not forget to set the
- cb field equal to the size of the buffer. Otherwise, calling
- DevPostDeviceModes() to initialize this buffer will cause a protection
- violation.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 3.2.5. Want Ads ΓòÉΓòÉΓòÉ
-
- Below are the hot topics as of this issue's writing. Feel free to write on any
- of these.
-
- Workplace Shell Programming (hot) - lately, I have noticed two things: 1) lots
- of people want to learn how to write new Workplace Shell classes and 2) no one
- who knows anything about it is telling the rest of us how to do it. I'll even
- stoop down to accepting an article in ASCII format on this topic! :)
-
- Client/Server (hot) - using either named pipes (with or without a network) or
- sockets, client/server programming is all-the-rage these days. On a related
- note, some people have also expressed an interest in learning about interfacing
- with the various protocol drivers (e.g. NDIS, IPX/SPX, etc.). Any articles in
- this area are most welcome.
-
- Anything on Rexx/2 (hot) - many people have requested more articles on Rexx/2.
- This issue sees the first article on this topic, but we can always use more.
- Writing "Enhanced Editor" macros in Rexx/2 and interfacing with the Workplace
- Shell from Rexx/2 are still open topics.
-
- Using Input Hooks (warm) - this is a complicated topic which was brought up
- frequently in the comp.os.os2.programmer.misc newsgroup.
-
- Hit testing (warm) - one reader noted that the Jigsaw sample in both the IBM
- and Borland toolkits (are they not the same?) perform there own correlation and
- wondered why? Charles Petzold, in his OS/2 book "Programming the OS/2
- Presentation Manager" briefly describes correlation and hit-testing, but does
- not go into any detail nor does it describe the Gpi functions used for this
- purpose.
-
- 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.
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 4. Reader's Choice Awards ΓòÉΓòÉΓòÉ
-
- It is the end of our first calendar year in production and since we released
- our first issue in March of this year, we figured it made more sense to do this
- now instead of in February (otherwise, we will receive many questions asking
- why we didn't do this now). So, without further ado, 'tis time for...
-
- The First Annual
-
- The editors want to know what you think the top three articles written during
- the past year are. Please mail your responses to os2man@panix.com and indicate
- on what network you get EDM/2, if it is not the Internet (or the auto-mailer).
- For your convenience, the article titles have been included below.
-
- The "rules of the game" are...
-
- o Article series can be treated as either one entity or a group of separate
- articles.
- o Each issue's columns should be treated as separate articles and can be voted
- for separately.
- o If you wrote an article or a column, you may vote, but you may not vote for
- something that you authored.
- o The three people that receive the most votes are placed in order of the
- number of votes received. Each person can place only once; thus, if John Doe
- receives the most and second-most votes, while James Smith receives the
- third-most, John is awarded first place and James is awarded second.
- o All votes must be email-dated before January 1, 1994.
-
- The winners of the Reader's Choice Awards not only get recognition in the next
- issue of the magazine, but they will also get a goodie (or two) which will not
- be revealed until the next issue.
-
- An index of the past seven issues:
-
- o A Review of C++ Compilers (issue 5)
- o Advance GPI: Retained Segments and Transformations (issue 1)
- o Beginning Client/Server Programming: Named Pipes (issue 2)
- o C++ Encapsulation of PM (issue 4)
- o Customizing the Enhanced Editor (issue 6)
- o DOS Development Tools Under OS/2 (issue 3)
- o Development of a New Window Class - Part 1 (issue 4)
- o Development of a New Window Class - Part 2 (issue 5)
- o Getting Started with EMX/GCC (issue 1)
- o Introduction to PM - Gavin Baker (column)
- o Introduction to PM - Larry Salomon, Jr. (column)
- o OS/2 Installable File Systems - Part 1 (issue 3)
- o OS/2 Installable File Systems - Part 2 (issue 5)
- o OS/2 Installable File Systems - Part 3 (issue 7)
- o OS/2 Presentation Drivers in a Nutshell (issue 2)
- o Programming the Container Control - Part 1 (issue 3)
- o Programming the Container Control - Part 2 (issue 4)
- o Programming the Container Control - Part 3 (issue 5)
- o Questions and Answers (column)
- o Rexx-ercising Your Applications (issue 6)
- o Rexx-ercising Your Applications - Part 2 (issue 7)
- o Road Map for the Workplace Shell (issue 2)
- o Scratch Patch (column)
- o The Help Manager and Online Documentation (issue 5)
- o The Making of MineSweeper (issue 1)
- o The Unofficial Guide to the Palette Manager (issue 1)
- o Threads in PM Applications (issue 6)
- o Using OS/2 2.x bitmap files (issue 7)
- o Writing a C++ Thread Class (issue 6)
- o Writing a Direct Manipulation Spy (issue 7)
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 5. 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 gopher server, which I have not the address of, in Almaden.
-
- On Compuserve
-
- All back issues are available in the OS/2 Developers Forum 2. (someone correct
- me if this is incorrect)
-
- Select this to go to the next section
-
-
- ΓòÉΓòÉΓòÉ 6. Contributors to this Issue ΓòÉΓòÉΓòÉ
-
- Are You a Potential Author?
-
- As always, we are always looking for (new) authors. If you have a topic about
- which you would like to write, send a brief description of the topic
- electronically to any of the editors, whose addresses are listed below, by the
- 15th of the month in which your article will appear. This alerts us that you
- will be sending an article so that we can plan the issue layout accordingly.
- After you have done this, get the latest copy of the Article Submission
- Guidelines from ftp.cdrom.com in the /pub/os2/2_x/program/newsltr directory.
- (the file is artsub.zip) The completed text of your article should be sent to
- us no later than the last day of the month; any articles received after that
- time may be pushed to the next issue.
-
- The editors can be reached at the following email addresses:
-
- o Larry Salomon - os2man@panix.com (Internet).
-
- The following people contributed to this issue in one form or another (in
- alphabetical order):
-
- o Andre Asselin
- o Larry Salomon, Jr.
- o Timur Tabi
- o Gordon Zeglinski
- o Network distributors
-
-
- ΓòÉΓòÉΓòÉ 6.1. Andre Asselin ΓòÉΓòÉΓòÉ
-
- Andre Asselin recently graduated Cum Laude from Rensselaer Polytechnic
- Institute with a Bachelor of Science degree in Computer Science. He has worked
- with OS/2 since version 1.3, and also has extensive experience with MS-DOS and
- Microsoft Windows. He currently works in IBM's OS/2 TCP/IP Development group
- in Raleigh NC, where his responsibilities include the NFS client, a remote file
- system implemented as an IFS.
-
- Andre is also a member of Alpha Sigma Phi Fraternity, and enjoys hockey,
- soccer, and a good science fiction novel. He can be reached via email at
- asselin@vnet.ibm.com or on CompuServe at 71075,133.
-
-
- ΓòÉΓòÉΓòÉ 6.2. Larry Salomon, Jr. ΓòÉΓòÉΓòÉ
-
- Larry Salomon wrote his first Presentation Manager application for OS/2 version
- 1.1 in 1989. Since that time, he has written numerous VIO and PM applications,
- including the Scramble applet included with OS/2 and the I-Brow/Magnify/Screen
- Capture trio included with the IBM Professional Developers Kit CD-ROM currently
- being distributed by IBM. Currently, he works for International Masters
- Publishers in Stamford, Connecticut and resides in Bellerose, New York with his
- wife Lisa.
-
- Larry can be reached electronically via the Internet at os2man@panix.com.
-
-
- ΓòÉΓòÉΓòÉ 6.3. Timur Tabi ΓòÉΓòÉΓòÉ
-
- Timur Tabi is a graduate student at the George Washington University in
- Washington, D.C, majoring in Computer Science/Hardware and Systems. He is also
- the author of the The Ultimate OS/2 Game, a column in OS/2 Monthly that covers
- advanced PM programming and computer game design. After he graduates in May,
- he will join IBM's Multimedia development team in Boca Raton, Florida.
-
- Timur can be reached on Internet at timur@seas.gwu.edu and on Fidonet at
- 1:109/347.
-
-
- ΓòÉΓòÉΓòÉ 6.4. Gordon Zeglinski ΓòÉΓòÉΓòÉ
-
- Gordon Zeglinski is a freelance programmer/consultant who received his Master's
- degree in Mechanical Engineering with a thesis on C++ sparse matrix objects.
- He has been programming in C++ for 6 years and also has a strong background in
- FORTRAN. He started developing OS/2 applications with version 2.0 .
-
- His current projects include a client/server communications program that
- utilitizes OS/2's features which has entered beta testing. Additionally, he is
- involved in the development of a "real-time" automated vehicle based on OS/2
- and using C++ in which he does device driver development and designs the
- applications that comprise the control logic and user interface.
-
- He can be reached via the Internet at zeglins@cc.umanitoba.ca.
-
-
- ΓòÉΓòÉΓòÉ 6.5. Network distributors ΓòÉΓòÉΓòÉ
-
- These people are part of our distribution system to provide EDM/2 on networks
- other than the Internet. Their help to provide access to this magazine for
- others is voluntary and we appreciate them a lot!
-
- o Gess Shankar (gess@knex.via.mind.org) - Internet
- o Paul Hethman (hethman@cs.utk.edu) - Compuserve
- o David Singer (singer@almaden.ibm.com) - 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!