home *** CD-ROM | disk | FTP | other *** search
-
- ΓòÉΓòÉΓòÉ 1. Mar 1995 Title Page ΓòÉΓòÉΓòÉ
-
- EDM/2
- The Electronic Developer's Magazine for OS/2
- Portions copyright (c) by Larry Salomon Jr.
- Volume 3, issue 3
-
- Administrivia
-
- I hate months like this one. It's bad enough that the weather can't make up
- its mind whether to be warm or cold, but my schedule can't make up its mind in
- the morning whether I should have any free time that evening.
-
- New Beginnings
-
- After the fiasco surrounding my dismissal by my last employer, I managed to
- quickly find new employment with a wholly-owned subsidiary of Dow Jones,
- Telerate. The job is quite interesting, and it promises to teach me much about
- communications and device driver development, two areas in which I am sorely
- lacking in the amount of experience I have. Additionally, being an employee,
- even indirectly, of Dow Jones has its perks; there are always free copies of
- the Wall Street Journal in the lobby when I come in to work in the morning.
- The only bad thing about this job is that its location is in New Jersey, which
- makes for a 1 hour and 15 minute commute each way.
-
- Obviously, there are many other things I'd rather be doing with the three hours
- I spend on the trains...
-
- Use (tm) Your (tm) Words (tm) Warily (tm)
-
- Like trying to find words that aren't listed as trademarks of major
- corporations. Microsoft apparently wasn't daunted by the removal of its
- trademark on the word "windows"; they are now threatening litigation against a
- small shop in upstate New York for violating their trademark on the word
- "bookshelf." You read that right. The moral here is that you had better do
- some thorough investigation into trademarks that might be a part of any product
- you release commercially.
-
- EDM/2 probably violates a couple of hundred trademarks of this type. <grin> My
- guess is that, in a year or two, someone is going to trademark individual
- letters of the alphabet and will suddenly find themselves a multi- trillionaire
- due to the sudden income from royalties they'll be receiving.
-
- In Tune With Wall Street
-
- Now that the New York Stock Exchange has finally broken the 4000 mark, I'm
- expecting it to crash any day. Gordon's hard drive, however, could wait, and
- it decided to crash last month in anticipation; his column will be notably
- absent this month. As if that weren't enough, Carsten has too many projects
- and mid-term exams to worry about for school, so his column will not appear
- this month either. He does manage to make a brief cameo appearance, though. I
- didn't want to be a non-conformist and since I can use the time, my column will
- not appear this month. However, part 6 of the VIOWIN series does appear.
-
- Columns Are For The Weak Magazines
-
- But who needs columns anyway? <grin> This month, there is the new "Letters"
- section, where your email might appear. Also, we have some great articles, and
- who knows what else might turn up? Regarding the "Letters" section, it should
- be noted that email messages chosen for publication are subject to editing
- changes.
-
- Speaking of not knowing what will turn up, I still haven't heard from my
- contact at the Media Relations group regarding donations to EDM/2 for the
- winners of the Reader's Choice Awards. That is quite disappointing, but next
- time I will be more cautious in whom I approach regarding this topic. That
- doesn't alleviate my current problem, though; if the winners will have some
- patience, and if I ever get enough time to investigate other avenues, they will
- get their well- earned stocking-stuffer even if it is too late for Christmas
- 1994.
-
- Speaking of things turning up and then not turning up after all, a few of you
- might have noticed how the article on the KEYBOARD.DCP layout was pulled out
- last month after the issue had gone out. I had to re-release the issue because
- I forgot some "legal speak" that was required in addition to the OS/2
- Accredited logo (but will not be forgotten again). The article was pulled
- because Martin Lafaix indicated that it was a draft only; the final version of
- the article is in this issue.
-
-
- ΓòÉΓòÉΓòÉ 2. Copyright Notice ΓòÉΓòÉΓòÉ
-
- Copyright Notice
-
- EDM/2 is published by IQPac Inc. IQPac Inc. can be reached via U.S. Mail at
- the following address:
-
- IQPac Inc.
- 89-17 Moline Street
- Bellerose, New York 11428
- U.S.A.
-
- Editor-in-chief Larry Salomon Jr.
- Associate editor Carsten Whimster
- Contributing editor Gordon Zeglinski
-
- CEO/President Larry Salomon Jr.
-
- All material is copyrighted by its original author. No part of this magazine
- may be reproduced without permission from the original author.
-
- 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.
-
- Neither IQPac Inc. nor this publication 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.
-
- The OS/2 Accredited Logo is a trademark of International Business Machines
- Corporation and is used by IQPac Inc. under license. This On-line Publication
- is independently produced by IQPac Inc. and IBM is not responsible in any way
- for its contents.
-
- IQPac Inc. is an accredited member of the IBM Independent Vendor League.
-
- Copyright Notice - EDM/2 - Mar 1995 - Volume 3, Issue 3
-
-
- ΓòÉΓòÉΓòÉ 3. Letters ΓòÉΓòÉΓòÉ
-
- Letters
-
- To write to EDM/2, send your email to os2man@panix.com and use the Subject:
- line "Letters".
-
- To Protect or Not (To Bother)?
-
- Henrik Falk (hfalk@ibm.net) writes:
-
- "I have a little comment on your artichle in EDM/2 2-10 on copy-protection.
- Your idea about using the volume serial number for copy protection only works
- for copy programs which doesn't copy the serial number. One famous program of
- that category is DISKCOPY.
-
- I don't know if American computer users are that naive or they just don't know
- the fact that a good copy program copies the volume serial number as well.
-
- In Europe, we all use that kind of copy programs. Not because we are less
- software pirates than Americans but because we like powerful utilities.
- Europeans don't like copy-protection. <grin>
-
- I didn't mean to offend you with this letter! It was a great article and I
- think this soft-copy-protection discussion is fine. As a developer you'll have
- to protect you work in a certain degree."
-
- EDM/2 responds:
-
- A number of people have mentioned limitations with the copy-protection scheme
- presented in the article, but this is one of the more serious ones because it
- limits the effectiveness to preventing only those users who haven't acquired a
- "good" copy protection program. Thank you for your feedback.
-
- ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
-
- Ed Rozmiarek (edroz@vnet.ibm.com) writes:
-
- "I just downloaded EDM/2 Volume 3, Issue 1 and read that you never seem to get
- responses to your magazine. Well, I wanted to let you know that I have every
- issue and have at least browsed through every one. I have read several of the
- articles and found some tricks and techniques that I can use.
-
- I wanted to make a comment on December's article on copy protection. I know
- there is no fool-proof and perfect form of copy protection, but there is one
- problem with the method you described. It has to do with backup copies of the
- product diskettes. The first thing I do when I get a new product is make
- backup copies of the product diskettes and then install from the backups. (I
- have run into one to many program install problems that trash the install
- disk). With the method you described in the article, I would not be able to
- make backup diskettes since the backups would have a different Volume Serial
- Numbers and therefore would not install.
-
- One topic I would like to see in a future article is related to copy
- protection. It is, what is the best technique(s) for locking/unlocking
- features within a shareware program without having a completely different
- version. For example, the SIO drivers by Ray Gwynn have a "nag" screen that
- displays for the unregistered version. Once you register the screen goes away.
- If you download a newer version, you run a program to reregister the new
- version. Also, some programs may have limited features or options in the
- unregistered version. Once you enter some unique number, the features will
- unlock."
-
- Larry Salomon Jr. responds:
-
- It should be a trivial matter to include an additional license on the disks and
- write a "validator" program which, after the buyer makes the backup copy,
- reduces the license count by one on the original disks and initializes the
- license count to one on the backup copy.
-
- Disabling features and having "nag" screens are common techniques for
- encouraging users to register shareware, but these non-prohibiting methods of
- encouraging payment are usually too specific to each application to be dealt
- with properly in an "all encompasing" article. I'll give the matter some
- thought to see if a followup should be done for this tangent.
-
- More Feedback Than A Biorhythm
-
- Juergen Wagner (hoerni@nodos.snafu.sub.org) writes:
-
- "In EDM/2 Jan 1995 you asked for some feedback about PM- Programing.
-
- Well, here it is. I really have to say thank you for some really great hints
- on all this stuff. Since I am writing a UUCP package for OS/2 including
- Mailreader and Newsreader I need every help I can get. And you give me exactly
- what I need right now.
-
- You also asked, if we would like to see some topics in detail. Well, here is
- something that really bothers me. I need a dialog element, that gives me the
- functionality of a spread sheet. That is:
-
- 1. A field of Editlines, where the programmer can define how many they want
- 2. Variable wide of each column
- 3. Scrollbars to scroll through all of the Editlines
-
- I think of something looking like this:
-
- |---------|----------------|--------------|^
- | | | |
- |---------|----------------|--------------|
- | | | |
- |---------|----------------|--------------|v
- < >
-
- It should be possible, to edit each field and to put it in a dialog window. If
- you know how to do something like this, that would be an idea for EDM/2.
-
- Another topic could be Drag-and-Drop, and how to print with the Printer
- Subsystem, and a closer look at the container control.
-
- I hope, you can draw some ideas out of these suggestions.
-
- Keep on this good work."
-
- EDM/2 responds:
-
- Taken under advisement. Thank you for your feedback.
-
- ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
-
- Galen Rhodes (rhode923@uidaho.edu) writes:
-
- "You asked for feedback for the PM Programming section of EDM/2.
-
- First off, thanks for EDM/2! I am new to OS/2 programming. Having spent a
- great deal of time programming for the early 8-bit Commodore Computers (I
- wrote assembly language utilities for the C64 and C128), I have in the past 3
- years moved to DOS, Windows and now OS/2 because I enjoy it's power. I was
- shocked to see the lack of available information on OS/2 considering it's been
- out since the late 80's. I was also shocked bacause I was use to the
- excellent coverage the Commodore computers got. I find EDM/2 to be a very
- helpful resource for bringing me up to speed on OS/2. Please keep up the good
- work.
-
- I would like to see a very in-depth coverage of notebooks. This seems to be
- an area that gets very superficial, if no treatment at all. I enjoyed Larry
- Salomon's book on OS/2 PM Programming (The Art of OS/2 2.1 C Programming) but
- even it gave a very light touch on the subject and didn't cover it in as much
- detail as I would have liked.
-
- Also I would like to see the MMPM API covered as it pertains to PM
- programming. So far I have only seen a few references on the net to MMPM
- programming and they are mostly REXX examples."
-
- EDM/2 responds:
-
- Also taken under advisement. Thank you for your feedback.
-
- Future Topics and More on Copy Protection
-
- Brian DeWeese (71242.2616@compuserve.com) writes:
-
- "In the last issue of EDM3-1 (Jan.) you asked for ideas and comments at the
- end of "Intro to PM section." You mentioned that you had covered the same
- subject twice and no one had commented on that fact. Normally, I skip over
- that section since I'm a fairly experienced PM developer and that is an intro
- section. But I have looked at some of them and they are well written.
-
- Anyway, I have a couple of ideas for your column, but they may be too advanced
- of subjects for it.
-
- 1. Discuss and easy way to add toolbar like buttons on the title bar. These
- buttons are similar to the buttons used in the IPMD debugger. And they
- are simply created by retrieving the handle of the minmax buttons (they
- are actually a menu bar control) and adding some new menu item entries
- that are MIS_BITMAP.
-
- 2. Discuss implementing the new UCMenus toolbar available from IBM. It's a
- very robust control and it appears that several commercial/shareware
- packages will soon be released using this control. IBM's new EPM editor
- uses it. It can help sharpen the image of OS/2 apps.
-
- 3. Subclassing. This can be a tricky subject to tackle when you take into
- consideration that, A) you don't really what to store your "previous proc
- address" in a global variable, and B) consider the impact when your
- subclass may be one of several others, some of the others may be out of
- your control. How do you ensure that they call your subclass rather than
- the default winproc, etc. Lots of issues there. I've written some
- functions that store the prev proc address and private data in a link
- list per subclass and it address some of the issues but not all.
-
- a. Subclassing windows that don't belong to your application. What are
- the implications? etc.
-
- b. Superclassing, how and when should it be done.
-
- 4. Discuss dragging and dropping to/from the desktop (WPS). Performing Drag
- 'n Drop within your own application is one thing but I'd like to know
- more about the implications of integrating a non-WPS application with the
- WPS by accepting drag operations from it, etc.
-
- These ideas are a little vague and if any interest you, I'll be happy to
- discuss them with you.
-
- Also, I just now got around to reading EDM2-11 (Dec) and read your article on
- copy protection. You asked for comments. Copy protection is something that I
- have given a lot of thought to but have never come up with the 'ideal'
- solution. Your idea has merits but there are a couple of things that I do not
- like about it.
-
- 1. The user is not allowed to make backup copies of his software, at the
- min. 1 copy should be allowed to be created.
-
- 2. What about shareware that is downloaded as a .zip file and installed
- after unzipping it and never sees a diskette? Should it be crippled and
- if the user wishes to purchase it, then send him a diskette? etc.
-
- It's a controversial subject, one that I am interested in. Perhaps I'll start
- a thread on this subject somewhere and see what gets flushed out.
-
- Thank you for your work on the EDM issues. Your efforts are appreciated."
-
- EDM/2 responds:
-
- Items 1-4 are taken under advisement, but you are correct in your assessment
- that the topics are a bit advanced for an introductory programming column.
-
- Referring to item 1 in the section about copy protection, a suggested answer
- to this was presented earlier in this section. Item 2 is an interesting
- notion for which the following suggested solution is presented:
-
- Write a "self-extracting" installation program which has the .ZIP file as a
- user-defined resource. The program calls DosGetResource(), writes the .ZIP
- file to disk, and then spawns the UNZIP utility available from the Info-ZIP
- package. After the unzipping is complete, the .ZIP file is deleted.
-
- Thank you for your feedback.
-
-
- ΓòÉΓòÉΓòÉ 4. Building Smaller OS/2 Executables (Part 1) ΓòÉΓòÉΓòÉ
-
-
- ΓòÉΓòÉΓòÉ 4.1. Introduction ΓòÉΓòÉΓòÉ
-
- Building Smaller OS/2 Executables (Part 1)
-
- Written by Pete Cassetta
-
- Introduction
-
- Not so long ago, programmers used to pride themselves on how tight and fast
- their code was. RAM was expensive and scarce, so it was necessary to shoehorn
- as much functionality as possible into the precious memory available. Today,
- of course, all that has changed. RAM has become cheap and plentiful, and, even
- more importantly, modern PC operating systems such as OS/2 support virtual
- memory. This provides a huge address space to play in, with no more than a
- fairly minor performance hit when programs need more memory than is physically
- present and free in the system. Now that memory constraints are less of a
- concern, programmers naturally focus more attention on other issues such as
- making deadlines, adding functionality, and getting the bugs out (often in that
- order). If code size is considered at all, it usually lags well behind these
- other concerns.
-
- Users, on the other hand, pay a lot of attention to code size. Their hard disk
- space is always at a premium, and they complain loudly about the disk
- requirements of today's multi-megabyte programs. Also, while large programs
- may still run, users notice it when programs take a long time to load or force
- OS/2 to spend too much time swapping over-committed memory to disk. Making
- your program smaller will help endear it to your users and give it an edge in
- competing with other programs for the limited disk space on your potential
- users' computers.
-
- The good news is that OS/2 actually provides programmers with more facilities
- for producing optimally small executables than perhaps any other PC operating
- system available today. Although these features often go unused, they are
- really very quick and easy to implement. This series of articles will detail
- five simple steps you can take to get your OS/2 executables down to size.
-
- Executable
-
- I will use executable as a cover term to refer to both EXEs and DLLs. While
- some of my comments also apply to device drivers, these are really beyond the
- scope I wish to address, so I won't mention them specifically.
-
- Tradeoffs Between Size and Speed
-
- Before I lose the speed demons out there, I'd better reassure you that it's
- possible to optimize code size without introducing too much of a speed penalty.
-
- Every program has bottlenecks where special attention must be given to
- execution speed. These fall into two broad categories: I/O operations
- involving the disk, screen, or to a lesser extent, the printer, and
- computationally expensive operations such as searching, sorting, or updating
- data structures. I suggest code involving I/O operations is the place to
- optimize for size. The reason for this is that OS/2 programs tend to do I/O at
- a fairly high level, thanks to the rich set of I/O facilities OS/2 provides via
- its Dos, Gpi, Win, and other API functions. To optimize the speed of your I/O
- operations under OS/2 you usually need to improve your algorithms and possibly
- also the selection and sequence of API calls you use. Once you've done this,
- you are generally free to optimize the code you've written for size, since any
- speed degradation this introduces will be negligible. After all, only a
- minuscule portion of the time required by an I/O operation will be spent in
- your code; the significant time is spent within OS/2's system code.
-
- Keeping this in mind, it is helpful to organize your source code into modules
- which will be optimized for size and others where you'll go all-out for speed.
- This makes it relatively easy to select different compiler options for these
- two categories of code.
-
- Finally, while optimizing for size and speed are often mutually exclusive
- goals, there are a number of things you can do that will actually improve both
- size and speed. As I discuss each step below, I'll detail the side effects on
- execution speed that you're likely to encounter.
-
- Building Smaller OS/2 Executables (Part 1) - EDM/2 - Mar 1995 - Volume 3, Issue
- 3
-
-
- ΓòÉΓòÉΓòÉ 4.2. Five Simple Steps ΓòÉΓòÉΓòÉ
-
- Shown below are five steps you can take to reduce executable size. They are
- listed according to the order in which you should take them, especially if your
- time is limited. Earlier steps are both quicker to implement and more likely
- to show significant results for your effort.
-
- 1. Compress Your Resources
- 2. Put Your Linker to Work
- 3. Experiment with Compiler Switches
- 4. Try a Different Compiler
- 5. Use API Wrapper Functions
-
- I'll cover steps 1 and 2 in this article, and leave steps 3 - 5 for next time.
-
- At certain points I'll need to apply my comments to specific compiler
- packages. I own three OS/2 compilers, Borland C++ 1.5, IBM C Set++ 2.0 (CSD
- level 9), and Watcom C/C++ 10.0, so these are the ones I'll cover. My
- apologies if your compiler isn't included.
-
- Building Smaller OS/2 Executables (Part 1) - EDM/2 - Mar 1995 - Volume 3,
- Issue 3
-
-
- ΓòÉΓòÉΓòÉ 4.3. Step 1 - Compress Your Resources ΓòÉΓòÉΓòÉ
-
- The OS/2 resource compiler has a little-known switch, -x, which enables
- compression of resources as they are being bound to the executable. The
- compression is basically run-length encoding, so it gives best results for
- resource types that tend to have repeating sequences of data, e.g. bitmaps,
- icons, pointers, and fonts. If you haven't been using this switch, I encourage
- you to take a break right now and try it out on one of your programs. Simply
- re-bind the resources using a command like the following:
-
- rc -x myprog.res myprog.exe
-
- You may have noticed that this took longer than usual; resource binding is much
- slower when the -x switch is used. For this reason, you may want to reserve
- this switch for making release versions of your executables.
-
- Even Better Under Warp
-
- In OS/2 Warp, IBM has made a good thing better by adding a second compression
- algorithm for resources. The Warp toolkit comes with version 2.02.001 of the
- resource compiler. This version accepts the switch -x2, which enables both
- run-length encoding and the new algorithm, which I expect is a form of
- Lempel-Ziv-Welch. Note that you can still use -x, or its new synonym -x1, if
- you want run-length encoding only. The bad news is that versions of OS/2 prior
- to Warp don't know how to load resources bound with - x2. If you want your
- program to run under OS/2 2.x, you'll have to stick with -x1 for now.
-
- Compression Schemes
-
- Run-length encoding is a simple scheme whereby a sequence of identical data
- units is replaced by a flag, a count and then one copy of the data unit. So a
- sequence like "00 00 00 00 00 00" is compressed to "Flag 06 00". This can be
- quite effective for compressing graphical data, but rarely gives any
- significant savings for text.
-
- Lempel-Ziv-Welch is an algorithm that replaces repeated strings by shorter
- tokens. For example, the string " rep" occurs twice in the last sentence so it
- would be a good candidate for replacement by a token. This algorithm gives
- very good results for compressing a variety of data types. Variants of it are
- used in commercial products like PKZIP, Stacker and many others.
-
- A Look at the Results
-
- The following chart shows the results I obtained by using the -x1 and -x2
- switches while binding resources to a variety of executables:
-
- ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
- ΓöéExecutableΓöéOrigin ΓöéOriginal ΓöéSize with ΓöéSize with Γöé
- Γöé Γöé ΓöéSize Γöé-x1 Γöé-x2 Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- ΓöéI495.EXE ΓöéEDM 2-7 Γöé129,792 Γöé68,448 Γöé50,752 Γöé
- Γöé Γöé Γöé Γöé(53%) Γöé(39%) Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- ΓöéBPMCC.DLL ΓöéBorland Γöé99,456 Γöé84,608 Γöé64,640 Γöé
- Γöé ΓöéC++ 1.5 Γöé Γöé(85%) Γöé(65%) Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- ΓöéBCRES.DLL ΓöéBorland Γöé166,042 Γöé153,242 Γöé114,330 Γöé
- Γöé ΓöéC++ 1.5 Γöé Γöé(92%) Γöé(69%) Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- ΓöéFPWCAT.DLLΓöéOS/2 Warp Γöé237,901 Γöé155,581 Γöé124,573 Γöé
- Γöé Γöé Γöé Γöé(65%) Γöé(52%) Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- ΓöéMAHJONGG.EΓöéOS/2 Warp Γöé620,731 Γöé562,139 Γöé449,199 Γöé
- Γöé Γöé Γöé Γöé(91%) Γöé(72%) Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- ΓöéOS2-CIM.EXΓöéOS/2 Warp Γöé1,275,350 Γöé1,145,962 Γöé1,023,670 Γöé
- Γöé Γöé Γöé Γöé(90%) Γöé(80%) Γöé
- ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
-
- Figure 1 - Effect of compressing resources on the size of various executables.
-
- The first executable, I495.EXE, was supplied as sample code to an accompanying
- EDM/2 article, so it's not too surprising that it didn't use this switch. But
- note that all the others were taken from commercial products! I've found that
- most OS/2 products are released with uncompressed resources, needlessly wasting
- space on your hard disk and mine.
-
- As a disclaimer, I should mention that I chose these particular executables
- because they contain a lot of resources. The savings is far less spectacular
- in executables which have fewer resources. Also, you may be wondering where I
- got the RES files for all these executables. I had source code for I495.EXE,
- but for each of the others, I used Borland's Resource Workshop to extract the
- resources and then saved them as a RES file. I generated new executables with
- commands like the following:
-
- rc -x2 bpmcc.res bpmcc.dll
-
- Tradeoffs and Limitations
-
- Compressed resources do add run-time overhead, since OS/2 must decompress the
- resources as they are loaded. Keep in mind, however, that the time spent in
- decompression is at least partly offset by the time savings of reading fewer
- bytes from disk. In effect, using compressed resources shifts some of the
- run-time burden from I/O (reading resources from disk) to processor
- (decompression). Since processor speed is improving much more rapidly than
- hard disk speed these days, this seems to be a sensible tradeoff. In my own
- informal testing, I've found that programs with compressed resources tend to
- load up to 5% slower in a 386 machine, but they often load quicker in a machine
- with a faster processor.
-
- You should be aware that compressing your resources saves nothing but disk
- space; once loaded, the resources still consume the same amount of RAM as they
- would otherwise. Nevertheless, the disk space savings can be considerable, and
- I see no reason why every OS/2 executable shouldn't be built with compressed
- resources before release.
-
-
- ΓòÉΓòÉΓòÉ 4.4. Step 2 - Put Your Linker to Work ΓòÉΓòÉΓòÉ
-
- Having done all we can with resources, let's turn our attention to the link
- step. First I'll describe the four types of optimizations that can be done
- during a link, then I'll look at how specific linkers support these
- optimizations.
-
- Aligning Pages of Code and Data
-
- In OS/2 executables, every page of code or data begins at an offset from the
- beginning of the file that is an even multiple of the page alignment. The
- alignment must be a power of two, and many linkers use 512 as the default. 512
- is used because this is the size of a disk sector, and starting pages on sector
- boundaries ensures that OS/2 doesn't need to read any more sectors than
- necessary to load a page. (A sector is the smallest unit of disk space OS/2
- can read.) To see how this works, consider what happens when OS/2 must load a
- 32-byte page that begins at file offset 1000. In this case, two sectors must
- be read: the one which begins at offset 512 and contains the first 24 bytes of
- the page, and the one which begins at offset 1024 and contains the final 8
- bytes. Note that if this page were to begin at offset 1024, then OS/2 could
- load it with a single sector read.
-
- The downside here is that the linker must pad pages with null bytes in order to
- make their length an even multiple of the page alignment. As a result, larger
- alignment values increase executable size. If the above example were linked
- with a page alignment of 512, then the 32-byte page would need 512 - 32, or 480
- bytes of padding. So you see the tradeoff: linking with a smaller page
- alignment reduces executable size, but it may also cause more pages to span
- sector boundaries, increasing load time. Also, keep in mind that using a
- smaller page alignment saves disk space only; it has no effect on the amount of
- memory your program will need once loaded.
-
- Eliminating Internal Fixups in EXEs
-
- For every function call made in a program, the linker must somehow supply the
- function's address. Normally, the address isn't known at link time, so the
- linker creates what is known as a relocation record, or fixup for short. Fixups
- come in two flavors: internal and external. Internal fixups reference
- functions in the EXE or DLL being linked, while external fixups reference
- functions in external DLLs. Each fixup is resolved at load time, which means
- that the call statement is patched with the linear run-time address of the
- function being called. You should minimize the number of fixups whenever
- possible, because they are bulky and time-consuming to resolve.
-
- As it turns out, internal fixups aren't really necessary in EXEs. This is
- because OS/2 always loads programs at a starting address of 64K. If you tell
- the linker that your program will load at a starting address of 64K, it can
- determine the run-time addresses of all functions local to that EXE, and supply
- these addresses directly instead of creating fixups. Note that this applies
- only to EXEs; there is no way to reliably predict the load address of an OS/2
- DLL at link time.
-
- I've found that when internal fixups are eliminated, most EXEs shrink by 5 to
- 15% and load perceptibly quicker. Yet a surprising number of commercial OS/2
- products contain EXEs with internal fixups. Here are a couple of examples:
-
- Product EXEs with Internal Fixups
- Borland C++ 1.5 BRCC.EXE, IMPDEF.EXE, MAKE.EXE, WORKSHOP.EXE, ...
- OS/2 Warp FPWPIM.EXE, GOPHER.EXE, MAHJONGG.EXE, TELNETPM.EXE, ...
-
- Figure 2 - Examples of OS/2 executables which contain internal fixups.
-
- To check whether an EXE contains internal fixups, you can use either IBM's
- EXEHDR or Borland's TDUMP. If a program has no internal fixups, you'll see
- the following lines near the beginning of EXEHDR's display:
-
- Module type: Program
- NO internal fixups in executable image
-
- When internal fixups are present, the second line is omitted. If you run
- TDUMP on a program that has no internal fixups, you'll see something similar
- to the following:
-
- Module flags 00000312
- Instance init : No
- Internal fixups removed : Yes
-
- When internal fixups are present, the "Yes" is changed to a "No".
-
- Chaining Fixups
-
- As mentioned above, every time you call a function whose address isn't known
- at link time, the linker creates a fixup. So if your program makes 25 calls
- to WinSendMsg, the linker will create 25 fixups that all reference WinSendMsg
- in PMWIN.DLL. This is rather redundant, since much of the information in
- these 25 fixups is identical.
-
- Fixup chaining is a very elegant optimization that addresses this redundancy.
- Instead of generating 25 fixups, the linker just generates one fixup and
- creates a linked list to all the places in the executable where this fixup
- must be applied. This reduces the size of your executable, and also speeds up
- loading. The time savings comes about because the loader only needs to
- determine the address of the function once; it then traverses the list and
- applies this address at each node on the list.
-
- Compressing Code and Data
-
- Earlier we looked at resource compression. In a similar vein, OS/2 supports
- compression of code and data, which is done during the link step. OS/2 2.x
- supports run-length encoding only, but OS/2 Warp also supports a newer scheme
- that usually yields much better results.
-
- The compression tradeoffs discussed in relation to resources apply here as
- well. Programs with compressed code and data may load a bit slower in a 386
- machine, but often load faster in machines with faster processors. Also,
- compressing your code and data saves disk space only; it doesn't reduce your
- program's RAM requirements.
-
- Building Smaller OS/2 Executables (Part 1) - EDM/2 - Mar 1995 - Volume 3,
- Issue 3
-
-
- ΓòÉΓòÉΓòÉ 4.5. Applying Linker Optimizations ΓòÉΓòÉΓòÉ
-
- Now I'll take a look at various linkers. For each one, I'll mention which
- optimizations are supported, and how to enable or disable these optimizations.
-
- TLINK (Borland C++)
-
- Borland's linker, TLINK, has three switches that help reduce the size of a
- generated executable: /A:dd, /B:0x10000, and /Oc. /A:dd specifies the page
- alignment for code and data. Remember that the alignment must be a power of
- two, and dd specifies which power of two you wish to use. For EXEs, /B:0x10000
- specifies a load address of 64K (0x10000 in hexadecimal), eliminating all
- internal fixups. /Oc enables fixup chaining, which is disabled by default.
- Unfortunately, TLINK doesn't support compression of code or data.
-
- To see how these options affect the size of executables, I've linked the
- "Clock" sample program from Borland C++ 1.5, using a variety of switches:
-
- ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
- ΓöéAlignment ΓöéSize ΓöéSize with ΓöéSize with Γöé
- Γöé Γöé Γöé/B:0x10000Γöé/B:0x10000Γöé
- Γöé Γöé Γöé Γöé/Oc Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- Γöé/A Γöé89,350 Γöé76,820 Γöé75,388 Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- Γöé/A Γöé89,356 Γöé76,824 Γöé75,392 Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- Γöé/A Γöé89,412 Γöé76,884 Γöé75,444 Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- Γöé/A Γöé92,724 Γöé79,924 Γöé78,388 Γöé
- ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
-
- Figure 3 - Effect of various TLINK switches on the size of CLOCK.EXE.
-
- This table has a row for each alignment I tried, and columns for various
- combinations of the /B:0x10000 and /Oc switches. Recent recommendations I've
- seen from IBM call for using an alignment of 2 or 4 bytes. Since there's not
- much difference between the two, I generally use 4 bytes. I included the 16
- byte row for comparison purposes, since this value is commonly used by
- developers. As you can see from the final column, linking with /B:0x10000 /Oc
- gives a significant savings in size; these switches are highly recommended for
- release builds.
-
- LINK386 (IBM C Set++)
-
- IBM's linker, LINK386, has four switches that can help you reduce your code
- size: /A[LIGNMENT]:n, /BAS[E]:0x10000, /E[XEPACK:{1|2}], and
- /NOS[ECTORALIGNCODE]. The portion of each command which is enclosed in square
- brackets is optional; I'll use the shorter forms from now on.
-
- The first of these, /A:n, lets you specify the alignment for code and data
- pages. The parameter n is the desired alignment in bytes, e.g. 4, 16, etc.
- LINK386 uses 512 as its default alignment when /A:n isn't specified.
-
- I'll discuss /NOS next, since it relates to code alignment. This switch is
- new, first appearing in LINK386 version 2.02.001, which comes with the Warp
- toolkit. Previous versions of LINK386 used the /A:n switch for aligning both
- code and data pages. With version 2.02.001 (and presumably later versions) of
- LINK386, /A:n affects only data pages; pages of code are always aligned on 512
- byte boundaries so that they will begin on sector boundaries. To override this
- new behavior, you can supply the /NOS switch. This forces LINK386 to use the
- value given with /A:n for both code and data pages, just as earlier versions of
- LINK386 did. Note that this switch has no affect when you use an alignment of
- 512.
-
- Since IBM has changed the default behavior of LINK386, you might imagine they
- feel pretty strongly about keeping pages of code aligned on sector boundaries.
- This seems to be the case; all recent build recommendations I've seen from IBM
- suggest omitting the /NOS switch. Remember that code pages are discardable, so
- they may be loaded and reloaded a large number of times during a single
- execution of your program. I guess IBM feels it is very important to optimize
- this load process, and keeping pages aligned on disk sectors is a help.
-
- For EXEs, /BAS:0x10000 specifies a load address of 64K, eliminating internal
- fixups.
-
- Before I discuss the final switch, I should mention that fixup chaining is
- always enabled in LINK386; no switches are provided to disable it.
-
- The final switch, /E:{1|2}, allows you to compress your code and data. /E uses
- run-length encoding for the compression, and this option has been present in
- all versions of LINK386. Version 2.02.001 of LINK386 allows you to suffix this
- option with a number. /E:1 is a synonym for /E; /E:2 enables both run-length
- encoding and the new compression scheme.
-
- To see how these options affect the size of executables, I've linked the
- "Clock" sample program from the 2.x toolkit, using a variety of switches
- (/BAS:0x10000 was used for all links):
-
- ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
- ΓöéAlignment ΓöéSize ΓöéSize with ΓöéSize with ΓöéSize with ΓöéSize with ΓöéSize with Γöé
- Γöé Γöé Γöé/NOS Γöé/E:1 Γöé/E:1 /NOS Γöé/E:2 Γöé/E:2 /NOS Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- Γöé/A Γöé74,064 Γöé73,814 Γöé73,552 Γöé73,064 Γöé56,302 Γöé52,828 Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- Γöé/A Γöé74,068 Γöé73,820 Γöé73,556 Γöé73,072 Γöé56,308 Γöé52,848 Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- Γöé/A Γöé74,116 Γöé73,876 Γöé73,604 Γöé73,124 Γöé56,356 Γöé52,980 Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- Γöé/A Γöé76,852 Γöé76,852 Γöé76,340 Γöé76,340 Γöé59,444 Γöé59,444 Γöé
- ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
-
- Figure 4 - Effect of LINK386 switches on the size of CLOCK.EXE.
-
- This table has a row for each alignment I tried, and columns which show various
- combinations of the /E:{1|2} and /NOS switches. By scanning across the bottom
- row, you'll see that /NOS indeed has no effect with an alignment of 512.
- Notice that /NOS makes the most difference when used with the /E:2 switch; it
- has a much smaller impact elsewhere.
-
- WLINK (Watcom C/C++)
-
- Watcom's linker, WLINK, provides two options that affect executable size. The
- Alignment option lets you specify page alignment, and the Offset option lets
- you specify the load address for EXEs. Unlike the other linkers I've
- discussed, WLINK's defaults produce optimally small executables. You won't be
- able to reduce executable size any further by overriding these defaults.
- Unfortunately, WLINK doesn't support fixup chaining or compression of code and
- data.
-
- Building Smaller OS/2 Executables (Part 1) - EDM/2 - Mar 1995 - Volume 3, Issue
- 3
-
-
- ΓòÉΓòÉΓòÉ 4.6. To Be Continued... ΓòÉΓòÉΓòÉ
-
- So far I've shown that judicious selection of switches during linking and
- resource binding can reduce executable size significantly. Next time I'll look
- at the compile step and also discuss a simple source code modification that can
- produce further savings.
-
- In the meantime, if you haven't been using the techniques I've discussed in
- this article, I encourage you to give them a try. I'd enjoy hearing how much
- savings they give you. I'll also welcome any other comments, questions, or
- suggestions you might have on this topic.
-
- Building Smaller OS/2 Executables (Part 1) - EDM/2 - Mar 1995 - Volume 3, Issue
- 3
-
-
- ΓòÉΓòÉΓòÉ 5. The Design and Implementation of VIOWIN: Part 6 ΓòÉΓòÉΓòÉ
-
-
- ΓòÉΓòÉΓòÉ 5.1. Introduction ΓòÉΓòÉΓòÉ
-
- The Design and Implementation of VIOWIN: Part 5
-
- Written by Larry Salomon, Jr.
-
- Introduction
-
- For my job, I once had to write an application that ran only when OS/2 booted
- from the floppy diskettes. Because I had no access to the functionality PM
- provides, I resorted to a line-oriented interface, where messages were
- displayed on the screen and scrolled up when necessary. It was a good
- interface, I thought; it was fully NLS enabled and had intelligent defaults so
- the user basically only had to type in the name of the application.
- Unfortunately, the Quality Assurance team didn't concur with my opinion. "We
- want a nice interface!" one exclaimed. "Yeah, one with different windows and
- such!" another shouted.
-
- I was backed into a corner that I could only get out of one way.
-
- This series describes the design and implementation of VIOWIN, a library that
- implements a subset of the Win APIs provided by PM for fullscreen sessions.
- The reasoning behind writing this series is that it provided me and will
- hopefully provide you with some unique insights into how a windowing system is
- developed; and since it is based on PM, your familiarity with the already
- defined interface will increase your capability to fully understand what is
- being described.
-
- Obviously, this series assumes you have PM application development experience,
- but it isn't required.
-
- This Month
-
- This month, we will begin our look at the windows classes that are implemented
- in VIOWIN.
-
- The Design and Implementation of VIOWIN: Part 6 - EDM/2 - Mar 1995 - Volume 3,
- Issue 3
-
-
- ΓòÉΓòÉΓòÉ 5.2. Scaffolding ΓòÉΓòÉΓòÉ
-
- Scaffolding
-
- First, we must look at how and where the classes are registered. In issue 2-9,
- we briefly looked at how ordinal 1 was loaded by vwInitialize() and called to
- give the DLL an opportunity to perform any initialization it needed. We could
- have just as easily used the DLL initialization routine, which IBM's C-Set++
- product allows you to redefine.
-
- BOOL EXPENTRY vwInitDll(VOID)
- //-------------------------------------------------------------------------
- // This function registers the classes used by VIOWIN.
- //
- // Returns: TRUE if successful, FALSE otherwise
- //-------------------------------------------------------------------------
- {
- vwRegisterClass(VWWC_BUTTON,VwButtonClassProc);
- vwRegisterClass(VWWC_ENTRYFIELD,VwEntryfieldClassProc);
- vwRegisterClass(VWWC_LISTBOX,VwListboxClassProc);
- vwRegisterClass(VWWC_SCROLLBAR,VwScrollbarClassProc);
- vwRegisterClass(VWWC_STATIC,VwStaticClassProc);
-
- return TRUE;
- }
-
- It needs to be mentioned that the various window procedures are exported in the
- .DEF file, but this is probably unnecessary since the vwSendMsg() and
- vwPostMsg() routines simply call the procedure directly using the function
- address that was passed to vwRegisterClass().
-
- The Design and Implementation of VIOWIN: Part 6 - EDM/2 - Mar 1995 - Volume 3,
- Issue 3
-
-
- ΓòÉΓòÉΓòÉ 5.3. Entryfields ΓòÉΓòÉΓòÉ
-
- Entryfields
-
- Let us now begin to look at the VWWC_ENTRYFIELD class. Since we should all be
- familiar with its PM counterpart, we will skip the introduction to the class
- and will instead dive into the code itself. The first thing that should be
- discovered is the instance data used by the instantiations of the class.
-
- typedef struct _INSTDATA {
- ULONG ulSzStruct;
- BOOL bChanged;
- BOOL bDirty;
- BOOL bSetParms;
- SHORT sAnchor;
- SHORT sCursor;
- SHORT sFirstChar;
- PCHAR pchBuf;
- USHORT usSzBuf;
- } INSTDATA, *PINSTDATA;
-
- ulSzStruct is the size of the structure.
-
- bChanged is TRUE when the entryfield's contents have changed since the last
- EM_QUERYCHANGED message.
-
- bDirty is TRUE when a call to vwSetWindowText() exceeded the text limit and,
- thus, the text the VIOWIN system thinks the entryfield has is different from
- what the entryfield really has.
-
- bSetParms is used in conjunction with bDirty to synchronize the differences in
- window text.
-
- sAnchor is the anchor point.
-
- sCursor is the cursor point.
-
- sFirstChar is the first visible character index.
-
- pchBuf points to the buffer containing the text.
-
- usSzBuf is the size of the buffer.
-
- Utility Functions
-
- As you can well imagine, there are a number of tasks which are used frequently
- or are complex enough to warrant a separate function. These functions are
- listed below:
-
- scrollText() - scrolls the text of the window by one-half of the window's width
- either left or right.
-
- deltaCursor() - moves the cursor one position to the left or right.
-
- textChanged() - sets bChanged and bDirty and sends an EN_CHANGED notification
- to the owner.
-
- handleVkey() - processes virtual keystrokes.
-
- handleChr() - processes non-virtual keystrokes.
-
- The Design and Implementation of VIOWIN: Part 6 - EDM/2 - Mar 1995 - Volume 3,
- Issue 3
-
-
- ΓòÉΓòÉΓòÉ 5.4. The scrollText() Function ΓòÉΓòÉΓòÉ
-
- The scrollText() Function
-
- The code for the function is presented below.
-
- BOOL scrollText(HVWWND hwndWnd,BOOL bLeft)
- //-------------------------------------------------------------------------
- // This function scrolls the text left or right by half the window's
- // width.
- //
- // Input: hwndWnd - handle to the window
- // bLeft - TRUE if scroll goes to the left, FALSE if to the right
- // Returns: TRUE if successful, FALSE otherwise
- //-------------------------------------------------------------------------
- {
- PINSTDATA pidData;
- RECTL rclWnd;
-
- //----------------------------------------------------------------------
- // Get the instance data and the window rectangle
- //----------------------------------------------------------------------
- pidData=vwQueryWindowPtr(hwndWnd,1);
-
- vwQueryWindowRect(hwndWnd,&rclWnd);
- rclWnd.xRight--;
- rclWnd.yTop--;
-
- //----------------------------------------------------------------------
- // Calculate the new first character
- //----------------------------------------------------------------------
- if (bLeft) {
- pidData->sFirstChar-=(rclWnd.xRight-rclWnd.xLeft)/2;
- } else {
- pidData->sFirstChar+=(rclWnd.xRight-rclWnd.xLeft)/2;
- } /* endif */
-
- //----------------------------------------------------------------------
- // Do boundary checks
- //----------------------------------------------------------------------
- if (pidData->sFirstChar<0) {
- pidData->sFirstChar=0;
- } /* endif */
-
- if (pidData->sFirstChar>strlen(pidData->pchBuf)-1) {
- pidData->sFirstChar=strlen(pidData->pchBuf)-1;
- } /* endif */
-
- //----------------------------------------------------------------------
- // Paint and notify the owner
- //----------------------------------------------------------------------
- vwSendMsg(hwndWnd,WM_PAINT,0,0);
-
- vwSendMsg(VWHWND_DESKTOP,
- WM_CONTROL,
- MPFROM2SHORT(vwQueryWindowUShort(hwndWnd,QWS_ID),
- EN_SCROLL),
- MPFROMHWND(hwndWnd));
-
- return TRUE;
- }
-
- This function is fairly straightforward; it calculates the new "first visible
- character", performs a bounds check, sends the EN_SCROLL notification, and
- repaints the window.
-
- The Design and Implementation of VIOWIN: Part 6 - EDM/2 - Mar 1995 - Volume 3,
- Issue 3
-
-
- ΓòÉΓòÉΓòÉ 5.5. The deltaCursor() Function ΓòÉΓòÉΓòÉ
-
- The deltaCursor() Function
-
- The code for the function is presented below.
-
- BOOL deltaCursor(HVWWND hwndWnd,BOOL bLeft)
- //-------------------------------------------------------------------------
- // This function moves the cursor one character to the left or right
- //
- // Input: hwndWnd - handle to the window
- // bLeft - TRUE if cursor goes to the left, FALSE if to the right
- // Returns: TRUE if successful, FALSE otherwise
- //-------------------------------------------------------------------------
- {
- PINSTDATA pidData;
- RECTL rclWnd;
-
- //----------------------------------------------------------------------
- // Get the instance data and the window rectangle
- //----------------------------------------------------------------------
- pidData=vwQueryWindowPtr(hwndWnd,1);
-
- vwQueryWindowRect(hwndWnd,&rclWnd);
- rclWnd.xRight--;
- rclWnd.yTop--;
-
- if (bLeft) {
- //-------------------------------------------------------------------
- // Check to see if we're already at the beginning
- //-------------------------------------------------------------------
- if (pidData->sCursor>0) {
- pidData->sCursor--;
-
- //----------------------------------------------------------------
- // If the cursor went beyond the left edge, scroll the window
- //----------------------------------------------------------------
- if (pidData->sCursor<pidData->sFirstChar) {
- scrollText(hwndWnd,TRUE);
- } /* endif */
-
- //----------------------------------------------------------------
- // Update the cursor position
- //----------------------------------------------------------------
- vwCreateCursor(hwndWnd,
- pidData->sCursor-pidData->sFirstChar+1,
- 0,
- 1,
- 1,
- CURSOR_SETPOS);
-
- return TRUE;
- } else {
- vwAlarm(WA_ERROR);
- return FALSE;
- } /* endif */
- } else {
- //-------------------------------------------------------------------
- // Check to see if we're already at the end
- //-------------------------------------------------------------------
- if (pidData->sCursor<strlen(pidData->pchBuf)) {
- pidData->sCursor++;
-
- //----------------------------------------------------------------
- // If the cursor went beyond the right edge, scroll the window
- //----------------------------------------------------------------
- if (pidData->sCursor>pidData->sFirstChar+rclWnd.xRight-2) {
- scrollText(hwndWnd,FALSE);
- } /* endif */
-
- //----------------------------------------------------------------
- // Update the cursor position
- //----------------------------------------------------------------
- vwCreateCursor(hwndWnd,
- pidData->sCursor-pidData->sFirstChar+1,
- 0,
- 1,
- 1,
- CURSOR_SETPOS);
-
- return TRUE;
- } else {
- vwAlarm(WA_ERROR);
- return FALSE;
- } /* endif */
- } /* endif */
- }
-
- This function is a bit more complication, but should still be easy to
- understand. It calculates the new cursor position and calls scrollText() if
- the cursor goes off-screen.
-
- The Design and Implementation of VIOWIN: Part 6 - EDM/2 - Mar 1995 - Volume 3,
- Issue 3
-
-
- ΓòÉΓòÉΓòÉ 5.6. The textChanged() Function ΓòÉΓòÉΓòÉ
-
- The textChanged() Function
-
- The code for the function is presented below.
-
- VOID textChanged(HWND hwndWnd)
- //-------------------------------------------------------------------------
- // This function simply performs a common task of setting the changed
- // and dirty flags and sending the owner a changed notification.
- //
- // Input: hwndWnd - handle to the window
- //-------------------------------------------------------------------------
- {
- PINSTDATA pidData;
-
- pidData=vwQueryWindowPtr(hwndWnd,1);
-
- pidData->bChanged=TRUE;
- pidData->bDirty=TRUE;
-
- vwSendMsg(VWHWND_DESKTOP,
- WM_CONTROL,
- MPFROM2SHORT(vwQueryWindowUShort(hwndWnd,QWS_ID),EN_CHANGE),
- MPFROMHWND(hwndWnd));
- }
-
- This code is trivial.
-
- The Design and Implementation of VIOWIN: Part 6 - EDM/2 - Mar 1995 - Volume 3,
- Issue 3
-
-
- ΓòÉΓòÉΓòÉ 5.7. The handleVkey() Function ΓòÉΓòÉΓòÉ
-
- The handleVkey() Function
-
- The code for the function is presented below.
-
- BOOL handleVkey(struct _CHARMSG *pcmMsg,HVWWND hwndWnd)
- //-------------------------------------------------------------------------
- // This function processes the virtual keystrokes.
- //
- // Input: pcmMsg - points to the CHARMSG structure
- // hwndWnd - handle to the window
- //
- // Returns: TRUE if successful, FALSE otherwise
- //-------------------------------------------------------------------------
- {
- PINSTDATA pidData;
- RECTL rclWnd;
- ULONG ulMods;
- ULONG ulIndex;
-
- //----------------------------------------------------------------------
- // Get the instance data and the window rectangle
- //----------------------------------------------------------------------
- pidData=vwQueryWindowPtr(hwndWnd,1);
-
- vwQueryWindowRect(hwndWnd,&rclWnd);
- rclWnd.xRight--;
- rclWnd.yTop--;
-
- ulMods=KC_CTRL | KC_ALT | KC_SHIFT;
-
- //----------------------------------------------------------------------
- // Check for Control or Alt or Shift key down
- //----------------------------------------------------------------------
- switch (pcmMsg->fs & ulMods) {
- case 0:
- //-------------------------------------------------------------------
- // None of them were down
- //-------------------------------------------------------------------
- switch (pcmMsg->vkey) {
- case VK_LEFT:
- //----------------------------------------------------------------
- // Move the cursor
- //----------------------------------------------------------------
- if (!deltaCursor(hwndWnd,TRUE)) {
- return FALSE;
- } /* endif */
- break;
- case VK_RIGHT:
- //----------------------------------------------------------------
- // Move the cursor
- //----------------------------------------------------------------
- if (!deltaCursor(hwndWnd,FALSE)) {
- return FALSE;
- } /* endif */
- break;
- case VK_BACKSPACE:
- {
- PCHAR pchFirst;
-
- //-------------------------------------------------------------
- // Move the cursor and move the text in the buffer to the
- // left one character, effectively erasing the current
- // character
- //-------------------------------------------------------------
- if (deltaCursor(hwndWnd,TRUE)) {
- pchFirst=&pidData->pchBuf[pidData->sCursor];
- strcpy(pchFirst,pchFirst+1);
-
- vwSendMsg(hwndWnd,WM_PAINT,0,0);
- textChanged(hwndWnd);
- } else {
- return FALSE;
- } /* endif */
- }
- break;
- case VK_INSERT:
- //----------------------------------------------------------------
- // Toggle the insertion mode
- //----------------------------------------------------------------
- vwSendMsg(hwndWnd,
- EM_SETINSERTMODE,
- MPFROMLONG(!vwQuerySysValue(VWSV_INSERTMODE)),
- 0);
- break;
- case VK_DELETE:
- {
- PCHAR pchFirst;
-
- //-------------------------------------------------------------
- // Move the cursor and move the text in the buffer to the
- // left one character, effectively erasing the character
- // to the right of the cursor
- //-------------------------------------------------------------
- if (pidData->sCursor<strlen(pidData->pchBuf)) {
- pchFirst=&pidData->pchBuf[pidData->sCursor];
- strcpy(pchFirst,pchFirst+1);
-
- vwSendMsg(hwndWnd,WM_PAINT,0,0);
- textChanged(hwndWnd);
- } else {
- vwAlarm(WA_ERROR);
- return FALSE;
- } /* endif */
- }
- break;
- case VK_SPACE:
- if (pidData->sCursor<pidData->usSzBuf) {
- //-------------------------------------------------------------
- // Insert a space or overwrite the current character with a
- // space
- //-------------------------------------------------------------
- if (vwQuerySysValue(VWSV_INSERTMODE)) {
- if (strlen(pidData->pchBuf)<pidData->usSzBuf) {
- for (ulIndex=strlen(pidData->pchBuf)+1;
- ulIndex>pidData->sCursor;
- ulIndex--) {
- pidData->pchBuf[ulIndex]=pidData->pchBuf[ulIndex-1];
- } /* endfor */
- } else {
- vwAlarm(WA_ERROR);
- return FALSE;
- } /* endif */
- } /* endif */
-
- pidData->pchBuf[pidData->sCursor]=' ';
- deltaCursor(hwndWnd,FALSE);
-
- vwSendMsg(hwndWnd,WM_PAINT,0,0);
- textChanged(hwndWnd);
- } else {
- vwAlarm(WA_ERROR);
- return FALSE;
- } /* endif */
- break;
- case VK_HOME:
- //----------------------------------------------------------------
- // Move the cursor to the first position
- //----------------------------------------------------------------
- pidData->sFirstChar=0;
- pidData->sCursor=pidData->sFirstChar;
-
- vwSendMsg(hwndWnd,WM_PAINT,0,0);
-
- vwCreateCursor(hwndWnd,
- pidData->sCursor-pidData->sFirstChar+1,
- 0,
- 1,
- 1,
- CURSOR_SETPOS);
- break;
- case VK_END:
- //----------------------------------------------------------------
- // Move the cursor to the last position
- //----------------------------------------------------------------
- pidData->sCursor=strlen(pidData->pchBuf)-1;
- pidData->sFirstChar=pidData->sCursor-(rclWnd.xRight-rclWnd.xLeft)/2;
-
- if (pidData->sFirstChar<0) {
- pidData->sFirstChar=0;
- } /* endif */
-
- vwSendMsg(hwndWnd,WM_PAINT,0,0);
-
- vwCreateCursor(hwndWnd,
- pidData->sCursor-pidData->sFirstChar+1,
- 0,
- 1,
- 1,
- CURSOR_SETPOS);
- break;
- default:
- return FALSE;
- } /* endswitch */
- break;
- case KC_CTRL:
- //-------------------------------------------------------------------
- // Control key was down
- //-------------------------------------------------------------------
- switch (pcmMsg->vkey) {
- case VK_LEFT:
- //----------------------------------------------------------------
- // Ctrl-Left is the same as "home"
- //----------------------------------------------------------------
- pidData->sCursor=0;
- pidData->sFirstChar=pidData->sCursor;
-
- vwSendMsg(hwndWnd,WM_PAINT,0,0);
-
- vwCreateCursor(hwndWnd,
- pidData->sCursor-pidData->sFirstChar+1,
- 0,
- 1,
- 1,
- CURSOR_SETPOS);
- break;
- case VK_RIGHT:
- //----------------------------------------------------------------
- // Ctrl-Right is the same as "end"
- //----------------------------------------------------------------
- pidData->sFirstChar=strlen(pidData->pchBuf)-1;
- pidData->sCursor=pidData->sFirstChar;
-
- vwSendMsg(hwndWnd,WM_PAINT,0,0);
-
- vwCreateCursor(hwndWnd,
- pidData->sCursor-pidData->sFirstChar+1,
- 0,
- 1,
- 1,
- CURSOR_SETPOS);
- break;
- default:
- return FALSE;
- } /* endswitch */
- break;
- default:
- return FALSE;
- } /* endswitch */
-
- return TRUE;
- }
-
- This function is one of the workhorses (the other being the handleChr()
- function). It classifies the keystroke according to the modifiers (Ctrl, Alt,
- or Shift) and processes the keys individually. It relies heavily on
- deltaCursor().
-
- The Design and Implementation of VIOWIN: Part 6 - EDM/2 - Mar 1995 - Volume 3,
- Issue 3
-
-
- ΓòÉΓòÉΓòÉ 5.8. The handleChr() Function ΓòÉΓòÉΓòÉ
-
- The handleChr() Function
-
- The code for the function is presented below.
-
- BOOL handleChr(struct _CHARMSG *pcmMsg,HVWWND hwndWnd)
- //-------------------------------------------------------------------------
- // This function processes the non-virtual keystrokes.
- //
- // Input: pcmMsg - points to the CHARMSG structure
- // hwndWnd - handle to the window
- //
- // Returns: TRUE if successful, FALSE otherwise
- //-------------------------------------------------------------------------
- {
- PINSTDATA pidData;
- ULONG ulMods;
- ULONG ulIndex;
-
- pidData=vwQueryWindowPtr(hwndWnd,1);
- ulMods=KC_CTRL | KC_ALT | KC_SHIFT;
-
- switch (pcmMsg->fs & ulMods) {
- case 0:
- case KC_SHIFT:
- if (pidData->sCursor<pidData->usSzBuf) {
- //----------------------------------------------------------------
- // Insert the character or overwrite the current character with
- // the character
- //----------------------------------------------------------------
- if (vwQuerySysValue(VWSV_INSERTMODE)) {
- if (strlen(pidData->pchBuf)<pidData->usSzBuf) {
- for (ulIndex=strlen(pidData->pchBuf)+1;
- ulIndex>pidData->sCursor;
- ulIndex--) {
- pidData->pchBuf[ulIndex]=pidData->pchBuf[ulIndex-1];
- } /* endfor */
- } else {
- vwAlarm(WA_ERROR);
- return FALSE;
- } /* endif */
- } /* endif */
-
- pidData->pchBuf[pidData->sCursor]=pcmMsg->chr;
- deltaCursor(hwndWnd,FALSE);
- vwSendMsg(hwndWnd,WM_PAINT,0,0);
- textChanged(hwndWnd);
-
- vwCreateCursor(hwndWnd,
- pidData->sCursor-pidData->sFirstChar+1,
- 0,
- 1,
- 1,
- CURSOR_SETPOS);
- } else {
- vwAlarm(WA_ERROR);
- return FALSE;
- } /* endif */
- break;
- default:
- return FALSE;
- } /* endswitch */
-
- return TRUE;
- }
-
- This function inserts the character pressed into the buffer or overwrites the
- character at the current cursor position with the character pressed.
-
- The Design and Implementation of VIOWIN: Part 6 - EDM/2 - Mar 1995 - Volume 3,
- Issue 3
-
-
- ΓòÉΓòÉΓòÉ 5.9. The Window Procedure ΓòÉΓòÉΓòÉ
-
- The Window Procedure
-
- The window procedure is shown below.
-
- MRESULT EXPENTRY VwEntryfieldClassProc(HVWWND hwndWnd,
- ULONG ulMsg,
- MPARAM mpParm1,
- MPARAM mpParm2)
- {
- PINSTDATA pidData;
-
- pidData=vwQueryWindowPtr(hwndWnd,1);
-
- switch (ulMsg) {
- case WM_CREATE:
- //-------------------------------------------------------------------
- // Allocate and initialize the instance data
- //-------------------------------------------------------------------
- pidData=calloc(1,sizeof(INSTDATA));
- if (pidData==NULL) {
- return MRFROMSHORT(TRUE);
- } /* endif */
-
- vwSetWindowPtr(hwndWnd,1,pidData);
-
- pidData->ulSzStruct=sizeof(pidData);
-
- pidData->bChanged=FALSE;
- pidData->bDirty=FALSE;
- pidData->bSetParms=FALSE;
- pidData->sAnchor=0;
- pidData->sCursor=0;
- pidData->sFirstChar=0;
- pidData->pchBuf=NULL;
- pidData->usSzBuf=0;
-
- vwSendMsg(hwndWnd,EM_SETTEXTLIMIT,MPFROMSHORT(32),0);
- break;
- case WM_DESTROY:
- free(pidData);
- break;
- case WM_PAINT:
- {
- RECTL rclWnd;
- CHAR achText[256];
- ULONG ulFore;
- ULONG ulBack;
- PCHAR pchFirst;
- USHORT usLenVisible;
- ULONG ulStyle;
-
- //----------------------------------------------------------------
- // Get the window rectangle
- //----------------------------------------------------------------
- vwQueryWindowRect(hwndWnd,&rclWnd);
- rclWnd.xRight--;
- rclWnd.yTop--;
-
- //----------------------------------------------------------------
- // Initialize the buffer that we will draw to "show" ourselves
- //----------------------------------------------------------------
- memset(achText,' ',sizeof(achText));
- achText[sizeof(achText)-1]=0;
-
- //----------------------------------------------------------------
- // Get the colors and toggle them if we have the focus
- //----------------------------------------------------------------
- ulFore=vwQueryForeColor(hwndWnd);
- ulBack=vwQueryBackColor(hwndWnd);
-
- if (vwQueryFocus()==hwndWnd) {
- ulFore^=0x000000FF;
- ulBack^=0x000000FF;
- } /* endif */
-
- //----------------------------------------------------------------
- // Put the brackets in the buffer
- //----------------------------------------------------------------
- achText[0]='[';
- achText[rclWnd.xRight]=']';
-
- //----------------------------------------------------------------
- // Copy the text to the buffer. If unreadable, set the
- // text in the buffer to '*'.
- //----------------------------------------------------------------
- pchFirst=&pidData->pchBuf[pidData->sFirstChar];
- usLenVisible=min(rclWnd.xRight-2,strlen(pchFirst));
-
- ulStyle=vwQueryWindowULong(hwndWnd,QWL_STYLE);
-
- if ((ulStyle & ES_UNREADABLE)!=0) {
- memset(&achText[1],'*',usLenVisible);
- } else {
- memcpy(&achText[1],pchFirst,usLenVisible);
- } /* endif */
-
- vwDrawText(hwndWnd,
- -1,
- achText,
- NULL,
- ulFore,
- ulBack,
- DT_LEFT|DT_TOP|DT_ERASERECT);
- }
- break;
- case WM_CHAR:
- {
- ULONG ulFlags;
-
- ulFlags=KC_KEYUP;
-
- if ((CHARMSG(&ulMsg)->fs & ulFlags)!=0) {
- return vwDefWindowProc(hwndWnd,ulMsg,mpParm1,mpParm2);
- } /* endif */
-
- if ((CHARMSG(&ulMsg)->fs & KC_VIRTUALKEY)!=0) {
- if (!handleVkey(CHARMSG(&ulMsg),hwndWnd)) {
- return vwDefWindowProc(hwndWnd,ulMsg,mpParm1,mpParm2);
- } /* endif */
- } else {
- if (!handleChr(CHARMSG(&ulMsg),hwndWnd)) {
- return vwDefWindowProc(hwndWnd,ulMsg,mpParm1,mpParm2);
- } /* endif */
- } /* endif */
- }
- return MRFROMSHORT(TRUE);
- case WM_QUERYDLGCODE:
- return MRFROMLONG(DLGC_ENTRYFIELD);
- case WM_SETFOCUS:
- if (SHORT1FROMMP(mpParm2)) {
- vwSendMsg(VWHWND_DESKTOP,
- WM_CONTROL,
- MPFROM2SHORT(vwQueryWindowUShort(hwndWnd,QWS_ID),
- EN_SETFOCUS),
- MPFROMHWND(hwndWnd));
-
- vwCreateCursor(hwndWnd,
- pidData->sCursor-pidData->sFirstChar+1,
- 0,
- 1,
- 1,
- 0);
- vwShowCursor(hwndWnd,TRUE);
- } else {
- vwSendMsg(VWHWND_DESKTOP,
- WM_CONTROL,
- MPFROM2SHORT(vwQueryWindowUShort(hwndWnd,QWS_ID),
- EN_KILLFOCUS),
- MPFROMHWND(hwndWnd));
-
- vwShowCursor(hwndWnd,FALSE);
- vwDestroyCursor(hwndWnd);
- } /* endif */
- break;
- case WM_SETWINDOWPARAMS:
- {
- PWNDPARAMS pwpParms;
- MRESULT mrRc;
-
- pwpParms=(PWNDPARAMS)PVOIDFROMMP(mpParm1);
-
- mrRc=vwDefWindowProc(hwndWnd,ulMsg,mpParm1,mpParm2);
-
- //----------------------------------------------------------------
- // This is the tricky part:
- //
- // If the application calls vwSetWindowText(), then we want to
- // execute normally. However, WM_QUERYWINDOWPARAMS calls
- // vwSetWindowText() whenever the "dirty" flag is set, so
- // we don't want to do this processing in that case.
- //----------------------------------------------------------------
- if ((pwpParms->fsStatus==WPM_TEXT) && (!pidData->bSetParms)) {
- *pidData->pchBuf=0;
- strncat(pidData->pchBuf,pwpParms->pszText,pidData->usSzBuf-1);
-
- if (pidData->usSzBuf<pwpParms->cchText) {
- pidData->bDirty=TRUE;
- } else {
- pidData->bDirty=FALSE;
- } /* endif */
-
- pidData->bChanged=TRUE;
- pidData->sCursor=0;
- pidData->sFirstChar=0;
-
- vwSendMsg(hwndWnd,WM_PAINT,0,0);
- } /* endif */
-
- return mrRc;
- }
- case WM_QUERYWINDOWPARAMS:
- {
- PWNDPARAMS pwpParms;
-
- pwpParms=(PWNDPARAMS)PVOIDFROMMP(mpParm1);
-
- switch (pwpParms->fsStatus) {
- case WPM_CCHTEXT:
- case WPM_TEXT:
- //-------------------------------------------------------------
- // If we're "dirty", set bSetParms to TRUE and call
- // vwSetWindowText() to synch VIOWIN's version of our
- // text with our own copy.
- //-------------------------------------------------------------
- if (pidData->bDirty) {
- pidData->bSetParms=TRUE;
- vwSetWindowText(hwndWnd,pidData->pchBuf);
- pidData->bSetParms=FALSE;
-
- pidData->bDirty=FALSE;
- } /* endif */
- break;
- default:
- break;
- } /* endswitch */
-
- return vwDefWindowProc(hwndWnd,ulMsg,mpParm1,mpParm2);
- }
- case EM_QUERYCHANGED:
- {
- BOOL bChanged;
-
- bChanged=pidData->bChanged;
- pidData->bChanged=FALSE;
- return MRFROMSHORT(bChanged);
- }
- case EM_QUERYSEL:
- break;
- case EM_SETSEL:
- break;
- case EM_SETTEXTLIMIT:
- {
- PCHAR pchBuf;
-
- //----------------------------------------------------------------
- // Allocate a new buffer, free the current one, and set
- // pidData->pchBuf to the new buffer
- //----------------------------------------------------------------
- pchBuf=calloc(1,SHORT1FROMMP(mpParm1)+1);
- if (pchBuf==NULL) {
- return MRFROMSHORT(FALSE);
- } /* endif */
-
- if (pidData->pchBuf!=NULL) {
- *pchBuf=0;
- strncat(pchBuf,pidData->pchBuf,SHORT1FROMMP(mpParm1));
-
- free(pidData->pchBuf);
- } /* endif */
-
- pidData->pchBuf=pchBuf;
- pidData->usSzBuf=SHORT1FROMMP(mpParm1);
- return MRFROMSHORT(TRUE);
- }
- case EM_CUT:
- break;
- case EM_COPY:
- break;
- case EM_PASTE:
- break;
- case EM_QUERYFIRSTCHAR:
- return MRFROMSHORT(pidData->sFirstChar);
- case EM_SETFIRSTCHAR:
- {
- SHORT sFirstChar;
-
- sFirstChar=SHORT1FROMMP(mpParm1);
- if ((sFirstChar>0) && (sFirstChar<strlen(pidData->pchBuf))) {
- pidData->sFirstChar=sFirstChar;
- return MRFROMSHORT(TRUE);
- } else {
- return MRFROMSHORT(FALSE);
- } /* endif */
- }
- case EM_QUERYREADONLY:
- {
- ULONG ulStyle;
-
- ulStyle=vwQueryWindowULong(hwndWnd,QWL_STYLE);
- return MRFROMSHORT((ulStyle & ES_READONLY)!=0);
- }
- case EM_SETREADONLY:
- {
- ULONG ulStyle;
-
- ulStyle=vwQueryWindowULong(hwndWnd,QWL_STYLE);
- ulStyle=ulStyle & (~ES_READONLY) |
- (SHORT1FROMMP(mpParm1) ? ES_READONLY : 0);
- vwSetWindowULong(hwndWnd,QWL_STYLE,ulStyle);
- return MRFROMSHORT(TRUE);
- }
- case EM_SETINSERTMODE:
- {
- BOOL bInsert;
-
- bInsert=(SHORT1FROMMP(mpParm1)!=0);
- vwSetSysValue(VWSV_INSERTMODE,bInsert);
- }
- break;
- default:
- return vwDefWindowProc(hwndWnd,ulMsg,mpParm1,mpParm2);
- } /* endswitch */
-
- return MRFROMLONG(FALSE);
- }
-
- The window procedure, too, is fairly trivial, but there are two noteworthy
- pieces: the processing for the WM_SETWINDOWPARAMS and WM_QUERYWINDOWPARAMS
- messages. Here is where the bDirty and bSetParms flags are used. Since it
- would be costly, in terms of performance, to call vwSetWindowText() everytime a
- character is pressed, the entryfield maintains its own copy of the text and
- only updates the system's version of the text when someone requests it via
- vwQueryWindowText(). When this function is called, the system sends us a
- WM_QUERYWINDOWPARAMS message, during which we check bDirty. If it is TRUE, we
- set bSetParms and call vwSetWindowText(). This results in us receiving a
- WM_SETWINDOWPARAMS message, but we don't want to update ourselves because of
- the annoying flicker that will take place. So, only if bSetParms is not set do
- we update the internal buffer.
-
- The Design and Implementation of VIOWIN: Part 6 - EDM/2 - Mar 1995 - Volume 3,
- Issue 3
-
-
- ΓòÉΓòÉΓòÉ 5.10. Conclusion ΓòÉΓòÉΓòÉ
-
- Conclusion
-
- This month, we looked at the code for the entryfield. Next month, we will look
- at the next window class, VWWC_STATIC. Please send me your thoughts on the
- approach I've taken this month, so that I can modify it as necessary.
-
- Please note that the source is not included in any of the .ZIP files that
- accompany this issue. When all of the window classes have been discussed, the
- source for the entire class library will be given as well as the source for the
- VIOWIN library.
-
- The Design and Implementation of VIOWIN: Part 6 - EDM/2 - Mar 1995 - Volume 3,
- Issue 3
-
-
- ΓòÉΓòÉΓòÉ 6. KEYBOARD.DCP File Format ΓòÉΓòÉΓòÉ
-
-
- ΓòÉΓòÉΓòÉ 6.1. Introduction ΓòÉΓòÉΓòÉ
-
- KEYBOARD.DCP File Format
-
- Written by Martin Lafaix
-
- Introduction
-
- What's That?
-
- Keyboard layouts are stored in KEYBOARD.DCP These layouts are used when
- converting a raw scancode (that is, the value sent by the keyboard controller)
- to its corresponding value (that is, a symbol, like "A", or a virtual key code,
- like F1 or CapsLock).
-
- Knowing this file format would allowed us to create customized layouts, or new
- layouts for specific keyboards, or whatever. Wouldn't it be nice?
-
- Another advantage would be the possibility for us to write our own keyboard
- handler; that is, something which converts a scancode to a key value.
- Naturally, we can already do that, but we have to define the keyboard layout,
- which is, er, boring ? Reinventing the wheel is not always funny!
-
- Why?
-
- Well, to please our beloved editor. <grin>
-
- Contents
-
- This article describes the OS/2 2.x KEYBOARD.DCP file format. It contains three
- main parts: the first one being the description properly-speaking, the second
- one explaining how to translate a raw scancode to an OS/2 key code and the
- third one describing a keyboard layout manipulation tool.
-
- Credit
-
- The first part of this paper is mainly based upon Ned Konz's SWAPDCP tool,
- available from your favorite ftp site.
-
- KEYBOARD.DCP File Format - EDM/2 - Mar 1995 - Volume 3, Issue 3
-
-
- ΓòÉΓòÉΓòÉ 6.2. KEYBOARD.DCP File Format ΓòÉΓòÉΓòÉ
-
- KEYBOARD.DCP File Format
-
- How is KEYBOARD.DCP Organized?
-
- The first four bytes of KEYBOARD.DCP contain the index table offset (0-based),
- ito.
-
- The first two bytes of the index table contain the index entry count, iec.
- Following this index entry count are iec Index Entries. Each index entry is as
- follow:
-
- typedef struct
- {
- WORD word1;
- BYTE Country[2]; /* i.e. "US" */
- BYTE SubCountryID[4]; /* i.e. "153 " */
- WORD word2;
- WORD XTableID; /* i.e. 0x1b5 (437) */
- WORD KbdType;
- ULONG HeaderLocation; /* of beginning of table (header)
- */
- } IndexEntry;
-
- Figure 1. The Index Entry structure.
-
- Field Description
- word1 unknown
- Country The country name abbreviation ("US", "FR", ...). Note:
- The byte ordering is reversed. That is, the first
- character of the abbreviation is in Country[1], and the
- second character is in Country[0].
- SubCountryID The country's keyboard layout ID ("153 ", "189 ", "120 ",
- ...). In some country (UK, France, Italy, ...) there
- exists different "main keyboard" layouts. This field
- reflects this information.
- word2 unknown
- XTableID The keyboard's layout codepage (437, 850, ...).
- KbdType The keyboard type (0 for a 89 keys keyboard, 1 for a
- 101/102 keys keyboard).
- HeaderLocation The corresponding layout table offset (0-based).
-
- So, to find a specific keyboard layout, we have to (1) read the first four
- bytes to find the index table and (2) locate the specified index entry
- (Country, SubCountryID, XTableID and keyboard type). If such an entry exists,
- its HeaderLocation field contains the Keyboard Layout Table Entry offset.
-
- The Keyboard Layout Table Entry
-
- Each keyboard layout table entry contains a header, followed by key and accent
- definitions. The header is as follows:
-
- /* code page header */
- typedef struct XHeader
- {
- WORD XTableID; /* code page number */
-
- /* note: 32-bit wide field */
- struct
- {
- /* which shift key or key combo affects Char3 of each KeyDef */
- BITFIELD ShiftAlt :1; /* use shift-alt instead of ctrl-alt */
- BITFIELD AltGrafL :1; /* use left alt key as alt- graphics */
- BITFIELD AltGrafR :1; /* use right alt key as alt- graphics */
- /* other modifiers */
- BITFIELD ShiftLock :1; /* treat caps lock as shift lock */
- BITFIELD DefaultTable :1; /* default table for the language */
- BITFIELD ShiftToggle :1; /* TRUE:. toggle, FALSE:. latch shiftlock */
- BITFIELD AccentPass :1; /* TRUE:. pass on accent keys and beep,
- FALSE:. just beep */
- BITFIELD CapsShift :1; /* caps-shift uses Char5 */
- BITFIELD MachDep :1; /* machine-dependent table */
- /* Bidirectional modifiers */
- BITFIELD RTL :1; /* Right-To-Left orientation */
- BITFIELD LangSel :1; /* TRUE:. National language layout
- FALSE:. English language layout */
- /* default layout indicator */
- BITFIELD DefaultLayout:1; /* default layout for the country */
- } XTableFlags1;
-
- WORD KbdType; /* keyboard type */
- WORD KbdSubType; /* keyboard sub-type */
- WORD XtableLen; /* length of table */
- WORD EntryCount; /* number of KeyDef entries */
- WORD EntryWidth; /* width in bytes of KeyDef entries */
- BYTE Country[2]; /* country ID, i.e. "US" */
- WORD TableTypeID; /* Table type, 0001=OS/2 */
- BYTE SubCountryID[4]; /* sub-country ID, ASCII, i.e. "153 " */
- WORD Reserved[8];
- } XHeader;
-
- Figure 2. The Table header structure.
-
- Field Description
- XTableID The keyboard layout codepage.
- XTableFlags Layout's flags (see the Layout Flags subsection below).
- KbdType The keyboard type (0 = 89 keys, 1 = 101/102 keys).
- KbdSubType The keyboard subtype (??? 0).
- XtableLen The table length. The length (in bytes) includes this
- header.
- EntryCount The number of KeyDef entries.
- EntryWidth The width in bytes of KeyDef entries.
- Country The country ID (bytes reversed, that is, you got " SU" for
- US).
- TableTypeID The table type (1 for OS/2).
- SubCountryID The subcountry ID ("153 ", "189 ", "120 ", ...).
- Reserved Unknown.
-
- This table header is followed by EntryCount KeyDef entries. Each KeyDef entry
- is as follows:
-
- /* Key definition, one per scan code in table */
- typedef struct
- {
- WORD XlateOp;
-
- BYTE Char1;
- BYTE Char2;
- BYTE Char3;
- BYTE Char4;
- BYTE Char5;
- } KeyDef;
-
- Figure 3. The KeyDef structure.
-
- Field Description
- XlateOP The 9 lower bits specify the key type (see the key type
- subsection below).
-
- The high 7 bits specify which "accent key" is allowed.
-
- Note: if there's more than seven accent keys, and if an
- accent key with an ID greater than 7 is allowed, the
- seventh bit of the high 7 bits will be set and we will
- have to check the corresponding Accent Table Entry to find
- out the validity of the combination.
- char1 The "standard" value
- char2 The "shifted" value
- char3 The "Alted" value
- char4
- char5
-
- The specific meaning of the charx fields depends on the XlateOP value, as
- explained in the key type subsection. The default value of the EntryWidth
- field (in the header) is 7, but, if this value is bigger, then, there are
- additional charx fields in the KeyDef structure. (Namely, you have EntryWidth
- - sizeof(XlateOP) charx fields, with sizeof(XlateOP) being 2.)
-
- These EntryCount KeyDef entries are then followed by the Accent Table, which
- contains the seven Accent Table Entries (one per possible accent -- if there's
- more than seven accents, the seventh entry contains the additional entries).
-
- Each Accent Table Entry is as follows:
-
- /* Accent Table Entry, one per accent, up to seven accent */
- typedef struct
- {
- CHAR charOrg; /* The key's ASCII value, i.e. "a" */
- CHAR charRes; /* The resulting ASCII value, i.e. "Е" */
- } TRANS;
-
- typedef struct
- {
- BYTE AccentGlyph; /* What to show while waiting
- for a key */
- BYTE byte1;
- BYTE byte2;
- BYTE byte3;
- BYTE byte4;
- BYTE byte5;
- TRANS aTrans[20]; /* The allowed substitutions */
- } AccentTableEntry;
-
- Figure 4. The AccentTableEntry structure.
-
- The seventh entry has a slightly different format. If there's more than 6
- accents, its first byte contains the length of the seventh Accent Table Entry.
- This entry is then followed by a byte whose contents is the length of the
- eighth entry, and so on:
-
- +--+-------------------------+--+-----------+--+-------------
- |l7| entry #7 |l8| entry #8 |l9| entry #9
- :
- :
- +--+-------------------------+--+-----------+--+-------------
- <- l7 bytes -> <- l8 bytes -> <- l9 bytes ->
-
- Figure 5. The seventh Accent Table Entry structure.
-
- There's no "end of entry" indicator. Use the XTableLen field to check it:
- AccentTableEntryLen = XTableLen - sizeof(XHeader) - EntryCount * EntryWidth.
-
- The first six entries take 6*sizeof(AccentTableEntry) = 276 bytes. The
- remaining Accent Table entries fit in AccentTableEntryLen-276 bytes. When the
- sum of the size of the additional entries (that is, l7 + l8 + ...) reaches
- this value, it's done.
-
- If there's less than seven accents, the first byte of the seventh entry is
- 0x00.
-
- So, the accent-key+ASCII-key translation process is quite easy: when an
- accent key is pressed, just remember the accent code, and optionally display
- the corresponding glyph (AccentGlyph). Then, wait for another key to be
- pressed. If this key accepts the remembered accent (that is, the
- corresponding bit in the 7 high bits of XlateOP is set), locate the
- corresponding charRes in the aTrans array of the Accent Table Entry (yes,
- you'll have to browse this array until you find the right charOrg!). If the
- pressed key does not accept the remembered accent (or if you can't find the
- corresponding charOrg in aTrans), just beep. You're done!
-
- Layout Flags
-
- Various flags describe the layout's behavior.
-
- Flag Description
- ShiftAlt When this flag is 1, it allows you to use "Shift+Alt"
- instead of "Ctrl+Alt" when accessing the third glyph of a
- key. (With 89-keys keyboards.)
- AltGrL When this flag is 1, the left "Alt" key is used for
- "AltGr". (With 101/102-keys keyboards.)
- AltGrR When this flag is 1, the right "Alt" key is used for
- "AltGr". (With 101/102-keys keyboards.)
- ShiftLock When this flag is 1, "CapsLock" acts as a "ShiftLock" key.
- That is, when CapsLock is ON, pressing a "Shift" key unset
- it. When this flag is 0, pressing a "Shift" key
- temporarily toggle the CapsLock state, but it is restored
- when releasing the "Shift" key.
- DefaultTable When this flag is 1, the layout uses the default country
- codepage.
- ShiftToggle With 89-keys keyboards, set this flag in conjunction with
- ShiftLock. That is, when ShiftLock is 1, set ShiftToggle
- to 1, and when ShiftLock is 0, set ShiftToggle to 0. On
- 101/102-keys keyboards, set this flag to 0.
- AccentPass When this flag is set to 1, accents keys (aka. dead keys)
- are allowed.
- CapsShift Unknown. It's 1 for all Swiss keyboards, 0 otherwise.
- MachDep Set this flag to 1 when there's more than one physical
- layout sharing the same country code. See the
- DefaultLayout flag below.
- RTL When this flag is 1, the layout use the Bidirectional
- Languages support. (RTL stands for Right-to-Left.)
- LangSel When this flag is 1, the layout is a National one (Arabic
- or Hebrew, usually). When this flag is 0, the layout is
- an English one. Note: When RTL is 0, this flag is not used
- - set it to 0.
- DefaultLayout When MachDep is 1, set this flag to 1 to denote the fact
- that this layout is the default one. Otherwise, set it to
- 0.
-
- Key Type
-
- The "Key type" value specifies the meaning of the charx fields in the KeyDef
- entries.
-
- Note: In the following table, "xxx'ed" means holding down xxx while pressing
- the key. And, if an entry contains "???", well, its meaning is not completely
- known...
-
- Value Signification
- 0 An empty entry. That is, no key produces this scan code.
- 0x01 AlphaKey. This is an alphabetical key. The char1 field
- contains the unshifted key value. The char2 field
- contains the shifted key value. If the "accent" bits are
- not null, they specify the allowed accents.
-
- Each AlphaKey can generates a "Ctrl'ed" value, when used
- in conjunction with a "Ctrl" key. In this case, the
- generated value is char1-96.
- 0x02 SpecKey. This key generates an unshifted value (char1)
- and a shifted value (char2). It does not generates an
- "Alted", "AltGr'ed" or "Ctrl'ed" value.
- 0x03 SpecKeyC. This key generates can generate a value when
- "AltGr'ed". char3 contains this (optional) value. If
- char3 is non null, then, it's the value. If char3 is less
- than 32, the value is an accent. Otherwise, it's a
- "normal" symbol.
-
- char1 and char2 contain the unshifted and shifted key
- value. When CapsLock is ON, the order is reversed. That
- is, the unshifted value is char2 while the shifted value
- is char1.
- 0x04 SpecKeyA. This key can generate a value (char3) when
- "AltGr'ed". char1 and char2 contain the unshifted and the
- shifted key value, respectively. It does not depend on
- the CapsLock value (that is, char1 is always the unshifted
- value while char2 is always the shifted value, whether
- CapsLock is ON or not).
-
- If char3 is less than 32, the value is a "control" code.
- It's not an accent (compare with the previous key type,
- SpecKeyC).
- 0x05 SpecKeyCA ???
- 0x06 FuncKey. The char1 field contains the function key
- number. All other fields contain 0.
- 0x07 PadKey. This is a "NumPad" key. The char1 field contains
- the padkey indices (0 = "7", 1 = "8", 2 = "9", 3 = "-", 4
- = "4", 5="5", 6="6", 7="+", 8="1", 9="2", 10="3", 11="0"
- and 12 = ".").
-
- Note: This follows the "old" keyboard (89 keys) layout.
-
- The char2 field contains the ASCII character. All other
- fields contains 0.
- 0x08 SpecCtlKey. This keys generates "control" code (that is,
- ASCII code in range 0..31). char1 contains the unshifted
- control code, while char2 contains the shifted control
- code. All other fields contain 0.
- 0x09 The PrtSc key.
- 0x0a The SysReq key.
- 0x0b AccentKey. This key generates "accent" code. char1 is
- the unshifted accent (in range 1..7) and char2 is the
- shifted accent (also in range 1..7). char5 has the value
- of char1. If char3 is not null, it's the value generated
- when "alted". char4 is 0.
- 0x0c ShiftKey. A shift or control key. If char1 is 0x1 it's
- the right "Shift" key. It's the left one if char1 is 0x2.
- If char1 is 0x4, it's a "Ctrl" key. In this case, char2
- is 0x1 and char3 is 0x4, and char4 & char5 are 0.
- Otherwise, char2..char5 are 0.
- 0x0d ToggleKey ???
- 0x0e The Alt key .
- 0x0f The NumLock key.
- 0x10 The CapsLock key.
- 0x11 The ScrollLock key.
- 0x12 XShiftKey ???
- 0x13 XToggleKey ???
- 0x14 SpecKeyCS. When CapsLock is OFF, this key generates char1
- when unshifted and char2 when shifted. When CapsLock is
- ON, this key generates char4 when unshifted and char5 when
- shifted. When used in conjunction with the "AltGr" key,
- this key generates char3 (whether CapsLock is ON or not).
- If char3 is less than 32, it's an accent.
-
- Otherwise, and if char3 is not null (in which case nothing
- is produced), it's a "normal" symbol.
- 0x15 SpecKeyAS ??? (my guess: it's like SpecKeyCS, except
- that char3 is a "control" code (that is, an ASCII value in
- range 1..31).
- 0x1a ExtExtKey. The new "cursor" keys. That is, the keys
- which are missing in a 89-keys keyboard.
- <other> unknown
-
- KEYBOARD.DCP File Format - EDM/2 - Mar 1995 - Volume 3, Issue 3
-
-
- ΓòÉΓòÉΓòÉ 6.3. ScanCode to Key Value Conversion ΓòÉΓòÉΓòÉ
-
- ScanCode to Key Value Conversion
-
- In this section, we'll describe the keyboard scancode to ASCII char conversion
- scheme. Doing such a conversion is required when you want to write your own
- keyboard handler, or when, for any other reason, you have to deal directly with
- scancodes.
-
- Portion of code will be given in REXX. Please refer to SHOWDCP.CMD for missing
- functions.
-
- And, it's just a scheme, it's not a complete and fully-functional scancode to
- key value converter. <grin>
-
- Determining the Required Keyboard Layout
-
- The first thing to do is to load the correct keyboard layout. We first have to
- find the current Country/CodePage value by using the
- DosQueryCp/DosQueryCtryInfo functions (Refer to Control Program Guide and
- Reference for more information on those two functions).
-
- We then have to find the current keyboard type - that is, an old (89 keys) one
- or a "new" one (101/102 keys). If you know how to do this, please, let me
- know!
-
- The last step is to find the user's desired keyboard layout. The easiest way
- to do this is probably to scan the CONFIG.SYS, or to provide a command
- parameter. (We need both country abbrev and subcountry code.)
-
- We could then load the corresponding keyboard layout.
-
- /* Loading the keyboard layout
- **
- ** rcp is current codepage
- ** rcn is current country abbrev (US, FR, ...)
- ** rss is current subcountry (153, 189, 120)
- ** rty is current keyboard type
- */
- ito = readl()
- call charin infile,ito
- iec = readw()
- do iec
- call getindex
- if (country = rcn) & (rss = subcntr) & (rcp = cp) & (rty =
- type) then
- leave
- end /* do */
-
- if (country \= rcn) | (rss \= subcntr) | (rcp \= cp) | (rty \=
- type) then
- do
- say "Keyboard layout not found!"
- exit
- end
-
- call getentry offset
-
- Having read the layout header, we then have to read the corresponding KeyDefs:
-
- do i = 1 to EntryCount
- call getkeydef
- /* here, we have to store is somewhere... */
- ...
- end
-
- And then come the last initialization step:
-
- Determining the Accent Key Conversion Table
-
- free = tablelen - 40 - entrycount * entrywidth
- j = 1
- empty = 1
- do while free > 0
- call getaccententry j
- /* We here have to store it somewhere ... */
- ...
- j = j + 1
- free = free - len
- end /* do */
-
- We are now ready...
-
- Converting a Scancode to a Key Value
-
- The first thing to do is to maintain some Boolean values containing various
- special keys status (CapsLock, NumLock, ScrollLock and Alt/Shift/Ctrl). We
- have to remember the last accent key pressed, too.
-
- We then have to handle each key type.
-
- /* scan is the current key scancode */
- type = key.scan.keytype
- select
- /* One of the many "toggle" key */
- when type = 'CAPSLOCK' then CapsLock = \CapsLock
- ...
- when type = 'ALPHAKEY' then do
- if \PendingAccent then
- select
- when CtrlPressed then code = key.scan.char1 - 96
- when AltPressed then code = '00'x||scan
- when ShiftPressed & \CapsLock then code = key.scan.char2
- when ShiftPressed then code = key.scan.char1
- when CapsLock then code = key.scan.char2
- otherwise
- code = key.scan.char1
- end /* select */
- else
- select
- when \allowedAccent() | CtrlPressed | AltPressed then
- call BEEP
- when ShiftPressed & \CapsLock then
- code = addAccent(key.scan.char2)
- when ShiftPressed then
- code = addAccent(key.scan.char1)
- when CapsLock then
- code = addAccent(key.scan.char2)
- otherwise
- code = addAccent(key.scan.char1)
- end
- end
- ...
- otherwise
- /* Not a known type! */
- say 'Type 'type' unknown!'
- end
-
- The complete implementation is, err, left as an exercise. <grin>
-
- KEYBOARD.DCP File Format - EDM/2 - Mar 1995 - Volume 3, Issue 3
-
-
- ΓòÉΓòÉΓòÉ 6.4. Using Keyboard Layouts ΓòÉΓòÉΓòÉ
-
- Using Keyboard Layouts
-
- A very important note: be sure to have a safe copy of your original
- KEYBOARD.DCP before any experimentation! You've been warned. <grin>
-
- In SHOWDCP.ZIP is included a REXX script which allows you to explores/modify
- keyboard layouts. It's usage is as follow:
-
- showdcp usage
-
- Usage: showdcp [param] file [country [subcountry [cp [type]]]] [file2]
-
- -h - Access Help ;
- -v[n] - View matching layouts. n is the detail level ;
- -x - Extract matching layouts ;
- -a - Add layout to file ;
- -ds,t,c - Define a key ;
- -sk1,k2 - Swap key k1 and key k2.
-
- country = country code (US, FR, ...) or *
- subcountry = subcountry code (189, 120, ...) or *
- cp = code page (437, 850, ...) or *
- type = keyboard type (0 = 89 keys, 1 = 101/102 keys) or *
-
- Figure 6. The showdcp command usage
-
- Param Description
- -h Shows usage information (see Figure 6);
- -v[n] Displays keyboard layouts which match the given
- specification. n is the detail level, in range 0- 4 (1 is
- the default):
-
- 0 displays matching entry count,
-
- 1 adds Index Entry information,
-
- 2 adds Table Entry information,
-
- 3 adds KeyDefs definitions,
-
- 4 adds AccentTable definitions;
- -x Extracts the matching layouts in file2;
- -a Adds the layouts contained in file2 to file (if file does
- not exist, it's created);
- -ds,t,d Defines the key associated with scancode s. t is the key
- type and d is the definition. It's an hexadecimal string
- (see Example 3 below);
- -sk1,k2 Swaps keys definition. k1 and k2 are the scancode to
- swap.
-
- Example 1
-
- If you want to create a restricted KEYBOARD.DCP file which contains all US
- layouts, but nothing else, enter the following commands:
-
- showdcp -x c:\os2\keyboard.dcp US * * * dummy
- showdcp -a mylayout.dcp dummy
-
- And then, replace the DEVINFO=KBD... line in your CONFIG.SYS with:
-
- DEVINFO=KBD,US,D:\TMP\MYLAYOUT.DCP
-
- Example 2
-
- If you want to find all layouts which use the 863 (Canadian French) codepage,
- enter:
-
- showdcp -v c:\os2\keyboard.dcp * * 863
-
- You'll get something like:
-
- Operating System/2 Keyboard.dcp file viewer
- Version 1.05.000 Jan 25 1995
- (C) Copyright Martin Lafaix 1994, 1995
- All rights reserved.
-
- Index Country SubCountry CodePage Offset Type ...
- --------------------------------------------------------
- 1 CF 058 863 17611 0 0 0
- 2 CF 058 863 18862 1 1 0
-
- Example 3
-
- If you want to change the definition of the "A" key in the standard French
- layout so that the key caps are reversed, enter:
-
- copy c:\os2\keyboard.dcp mykbd.dcp
- showdcp -d16,1E05,4161000000 MYKBD.DCP FR 189 * 1
-
- If you want to try the newly defined layout, and assuming your boot drive is
- "C:", enter:
-
- copy c:\os2\keyboard.dcp keyboard.org
- copy mybkd.dcp c:\os2\keyboard.dcp
- keyb fr189
-
- Then, experiment with it (with the French layout, the "A" key is on the US "Q"
- key). And, after that, restore your initial configuration:
-
- keyb us
- copy keyboard.org c:\os2\keyboard.dcp
-
- The "-d" parameter revisited
-
- The "-d" parameter is immediately followed by the key scancode. It's a
- decimal number. It's then followed by a comma. The key type comes next. It's
- a 16bits hexadecimal value. Its 9 low bits contains the key type properly
- speaking, while the 7 high bits contain the allowed accents. The key type is
- followed by another comma, which is followed by the key definition. It's an
- hexadecimal string. The first two hexadecimal digits corresponds to the char1
- field, and so on. In the previous example, we are assigning 0x41 to the char1
- field ("A"), 0x61 ("a") to char2, and 0x00 to all remaining fields (char3,
- char4 and char5). If the key definition string does not defines all fields,
- the value of the non-specified fields is not modified. In the previous
- example, we could have used "4161" instead of "4161000000".
-
- Be really careful when using the "-d" parameter.
-
- Summary
-
- Please tell me what you think!
-
- I hope you find this article useful and informative. If you like what I have
- done, please let me know; if not, please tell me why. I will use your
- comments to make upcoming papers better.
-
- Thank you!
-
- KEYBOARD.DCP File Format - EDM/2 - Mar 1995 - Volume 3, Issue 3
-
-
- ΓòÉΓòÉΓòÉ 7. REXX, The Developer's Best Friend ΓòÉΓòÉΓòÉ
-
-
- ΓòÉΓòÉΓòÉ 7.1. Introduction ΓòÉΓòÉΓòÉ
-
- REXX, The Developer's Best Friend
-
- Written by Paul Gallagher
-
- Introduction
-
- Whenever I think of REXX, I picture a golden brown Bassett hound. He's always
- by my side, panting and slobbering a bit, eager to run around and do some
- tricks for me. When I throw a ball, off he scampers, eager to please. His legs
- are a bit short, but boy do they pump him along.
-
- Yep, that's m' boy, good ol' REXX. A developer couldn't have a more faithful
- companion.
-
- OS/2 provides a tremendous environment for the software developer. I'm sure we
- all know that by now! As the toothpaste advertisements go, "OS/2. Recommended
- by more software developers than any other operating system".
-
- This article will focus on one feature of OS/2 that can really help improve the
- way you build software, perhaps more than any other - REXX. Now, I'm not so
- much talking about developing in REXX as using REXX to assist your development
- in some other language (which could be REXX however, but could equally be C or
- C++).
-
- We're going to do some basic to intermediate REXX scripting which I'll present
- in tutorial format. At the end we'll have a few fully functional developer's
- tools. That's only half the story though; I intend to take a few detours along
- the way and get philosophical about the software development process and
- developer tools.
-
- I guess I have two audiences in mind. If you are fairly new to REXX, or have
- never used it before, then I hope the tutorial nature of the presentation will
- help you along the learning curve. Many developers could be reasonably
- familiar with REXX though. If you are in this camp, then (if nothing else) I
- hope I can stimulate your imagination somewhat, and perhaps encourage you to
- use REXX for tasks that you wouldn't have otherwise considered.
-
- REXX, The Developer's Best Friend - EDM/2 - Mar 1995 - Volume 3, Issue 3
-
-
- ΓòÉΓòÉΓòÉ 7.2. The REXX Renaissance ΓòÉΓòÉΓòÉ
-
- The REXX Renaissance
-
- Some Background for Beginners
-
- It seems interest in REXX has never been stronger, and I suspect that's largely
- thanks to OS/2 (remember, REXX is available on a wide range of platforms). The
- Internet newsgroup "comp.lang.rexx" is lively and EDM/2 is full of reviews of
- REXX-based tools. The IBM Employee Written Software (EWS) scheme has provided
- some great REXX support; there are now libraries that enable REXX to access a
- large portion of the OS/2 API, including networking features.
-
- The key to REXX is its refreshing combination of simplicity and power. It is
- particularly strong in handling text strings and patterns. For example, the
- simple REXX statement
-
- Parse Var tempstring word1 word2 therest
-
- will split the contents of the variable "tempstring" into three variables
- (stored in "word1", "word2" and "therest"). Everything in the source string up
- until the first space character goes into "word1", the text between the first
- and second space goes into "word2" and the remaining characters (if any) get
- put into "therest". Note also that our use of the space character as the
- delimiter is purely arbitrary - it could have been '#', '^' or any other
- character (or combinations of characters).
-
- REXX is arguably one of the best text processing languages around. It is
- easier and more flexible than AWK, quicker and easier to develop than C and
- other 3GLs, and more widely supported than some specialist text processing
- engines (e.g. the DOS Shareware program "Parse-O-Matic").
-
- Developing software is a complex activity with many aspects. However, when you
- consider that producing code, particularly for 3GL languages, is largely a
- "text processing" activity, you perhaps get an inkling that REXX may have
- something to offer when it comes to helping your software development
- activities.
-
- REXX, The Developer's Best Friend - EDM/2 - Mar 1995 - Volume 3, Issue 3
-
-
- ΓòÉΓòÉΓòÉ 7.3. Developer Productivity? Me? ΓòÉΓòÉΓòÉ
-
- Developer Productivity? Me?
-
- Developer productivity - A Personal Issue
-
- Most people reading this article would call themselves software developers, at
- least to some extent. Perhaps you are paid to write software; I'm sure many
- more wish they were, but only get to write code at home while attending to
- their other responsibilities at work.
-
- Any developer, whether working for profit or just for fun, must surely be
- interested in being as productive as possible. After all, the more time you
- save, the more work you can do (and as we all know, developing in GUI,
- networked environments usually takes a lot of work - in terms of lines of
- code).
-
- Have you ever sat back and made an objective assessment of your 'productivity'?
- If you're paid to program, then I'm sure your managers make sure that the
- productivity issue is always in your face. But are you like me? You have good
- habits at work and then everything is out the window when you're sitting at
- your home computer?
-
- Anyone answer in the affirmative? <grin>
-
- Well, perhaps I'm not that bad (I hope <grin>), but I recognize that I tend to
- suffer from another ailment - "DOS-upsized-tunnel-vision". Like many of you,
- my first programming experience was under DOS. I am used to having low
- expectations of the operating environment. DOS .BAT files can only do so much.
- To do anything beyond copying and concatenating files etc, you need to turn to
- additional tools - tools you have to find (or buy) yourself. I guess partly in
- response to the paucity of standard tools, compiler products tended to come
- with a lot of functionality built-in (like Borland's IDE). Even adding Windows
- to the equation didn't help: it was a boon to the functionality that the
- average programmer could build into a program, but Windows itself didn't help
- us develop any better (far from it - productivity plummeted).
-
- Now that I've "upsized" to a decent operating environment - OS/2 - I tend to
- forget that my development environment doesn't end at the edge of the IDE.
-
- I would suggest that two features of OS/2 - the Workplace shell and REXX - set
- it apart from most other operating systems/environments when it comes to the
- software development process. The Workplace Shell gives us a powerful space in
- which to organize the files and programs that "belong" to a project, and REXX
- gives us a supremely powerful scripting tool to automate many of the basic
- activities involved in setting up and working on a project.
-
- REXX, The Developer's Best Friend - EDM/2 - Mar 1995 - Volume 3, Issue 3
-
-
- ΓòÉΓòÉΓòÉ 7.4. My Scripting Style ΓòÉΓòÉΓòÉ
-
- My Scripting Style
-
- This article includes a good half-dozen scripts, but they are all based on the
- same basic template. Rather than tediously describe the scripts in full, over
- and over, I'll take this opportunity to introduce you REXX scripts a la Paul
- Gallagher.
-
- Here's the 8-point blueprint I use:
-
- 1. Header
-
- This is basically a non-executable section containing comments, description and
- version history notes. I generally define three global variables here:
- programNameStr (a text description of the script), copyrightStr (appropriate
- copyright message) and versionStr (version message). These variables are
- mainly used in banner messages and help screens.
-
- You will note a number of cryptic tags (beginning with "***" at around column
- 35) in the header - these are actually instructions for the version control
- software I use (TLib). They only pop up in a few places so I have not removed
- them for publication. (They also let you know that these are real scripts!)
-
- Example:
-
- /*-------------------------------------------------------------------------*/
- '@echo off'
- programNameStr= "Create new project (example only)"
- copyrightStr= "Copyright (c) Paul Gallagher 1995"
-
- /* ***keywords*** "Version: %v Date: %d %t"*/
- versionStr= ""
- /*
- ; ***keywords*** "%l"
- ; LOCK STATUS ""
- ;
- ; ***keywords*** "%n"
- ; Filename "newproj.cmd"
- ; Platform OS/2 (REXX)
- ;
- ; Authors Paul Gallagher (paulg@resmel.bhp.com.au)
- ;
- ; Description
- ;
- ; Revision History
- ; ***revision-history***
- ; ***revision-history***
- ;--------------------------------------------------------------------------*/
-
- 2. Load REXX utility functions [optional]
-
- Next, I load the REXX utility package, if any of the functions will be required
- by the script. There tend to be a few schools of thought in relation to
- loading/unloading external functions. I prefer to load the entire standard
- utility package - and not unload functions at the end of the script. I do this
- since the load/unload is a global operation - not limited to the one process.
-
- Example:
-
- /*---------------------------------------------------------------------------
- ; Load REXXUTIL
- ;--------------------------------------------------------------------------*/
- If RxFuncQuery('SysLoadFuncs') <> 0 Then
- If RxFuncAdd('SysLoadFuncs','RexxUtil','SysLoadFuncs') <>0 Then Do
- Say 'Unable to init REXX Utility function loader.'
- Exit
- End
- Call SysLoadFuncs
-
- 3. Install selected error traps
-
- As with any other programming language, it is important to ensure that programs
- fail as gracefully as possible as possible when severe errors are encountered.
- All error conditions cause program flow to jump to ExitProc - the point from
- which a reasonably clean exit can occur.
-
- Example:
-
- /*---------------------------------------------------------------------------
- ; Set error traps
- ;--------------------------------------------------------------------------*/
- signal on failure name ExitProc
- signal on halt name ExitProc
- signal on syntax name ExitProc
-
- 4. Initial parse of command line [optional]
-
- If the command arguments are of any interest at all, they are initially loaded
- into a variable called params. The code that follows does a quick check for
- anything that looks like the user is after help - if that is the case, then the
- HelpInfo procedure is called prior to jumping to the script exit point.
-
- Example:
-
- /*---------------------------------------------------------------------------
- ; Do initial parse of command line and call help message if
- required
- ;--------------------------------------------------------------------------*/
- /* get the command line arguments */
- Parse Arg params
- /* call help routine if required */
- If POS(TRANSLATE(params),"-?"'00'x"/?"'00'x"-HELP"'00'x"/HELP") > 0 Then Do
- Call HelpInfo
- Signal ExitProc
- End
-
- The check for a "help" parameter illustrates a neat trick that is handy for
- command parsing. TRANSLATE(params) simply does an uppercase conversion. All
- of the valid "help" commands are concatenated as a string with intervening null
- characters. The POS function is then used to test whether the parameters
- passed to the program match any of the "help" commands. Thus, the parameters
- "-?", "/?", "/help", "-H", etc. will all be recognized as calls for help.
-
- 5. Main program
-
- This is where the "generic" stuff ends, and the real program starts.
-
- 6. General Exit Procedure
-
- The main exit point of the script is named ExitProc. Normal execution of the
- main program will simply see control flow to this point, but error conditions
- will generally see execution jump to this point. Prior to exiting completely,
- variables are dropped (mainly to be neat than for any other reason) - but other
- tasks could be included here if required.
-
- Example:
-
- /*---------------------------------------------------------------------------
- ; General exit procedure
- ;--------------------------------------------------------------------------*/
- ExitProc:
- Drop params programNameStr copyrightStr versionStr
- Exit
-
- 7. Standard help procedure
-
- A standard help procedure is provided, and usually enhanced for each individual
- script. It is intended to simply display an appropriate message, but other
- features could be added if required.
-
- Example:
-
- /*---------------------------------------------------------------------------
- ; routine to display help message
- ;--------------------------------------------------------------------------*/
- HelpInfo: Procedure Expose programNameStr copyrightStr
- versionStr
- Say
- Say "*===================================================================*"
- Say " "programNameStr
- Say " "versionStr
- Say " "copyrightStr
- Say
- Say "*===================================================================*"
- Return
-
- 8. Additional functions/procedures
-
- The standard template ended at point 7. What follows are all the functions and
- procedures written for a specific application.
-
- Coding and documentation standards
-
- In addition to following the blueprint outlined above, the code adheres to some
- basic presentation guidelines:
-
- Program blocks indented by 2 characters
- Keywords typed in proper case (e.g. "If" or "Select")
- Function names typed in uppercase (e.g. "TRANSLATE")
- Variables name components in proper case, but name started with lower
- case (e.g. "programName")
- All comments that document algorithms are indented by 35 characters (i.e.
- comments appear to the right of the page). Comments that precede a
- procedure or block of code begin at column 1.
-
- REXX, The Developer's Best Friend - EDM/2 - Mar 1995 - Volume 3, Issue 3
-
-
- ΓòÉΓòÉΓòÉ 7.5. Setting Up Your Development Environment ΓòÉΓòÉΓòÉ
-
- Setting Up Your Development Environment
-
- In this section I'll talk about a selection of REXX scripts that can help you
- setup and maintain a development environment.
-
- INFICONS.CMD
-
- A great deal of developer information comes packaged as INF files (just like
- EDM/2). As always, INF files tend to get scattered all over your disk - and
- searching for the right one can be a difficult task since they invariably have
- 8 character filenames (and if they come from IBM, the name will probably be
- particularly cryptic. <grin>)
-
- This is a fairly simple script searches for INF files on your disk. The script
- takes an optional parameter to specify the path and/or filemask used in the
- search for INF files. By default it will search C: drive, examining all
- subdirectories for *.INF files. I must admit that this is not the first script
- to be written with this purpose in mind - but it's the only one I know of that
- assigns the INF's true title to the icon created.
-
- After parsing the command line to determine the appropriate search mask
- (variable mask), the main algorithm to search the disk and process files is as
- follows:
-
- If SysFileTree(mask, 'file', 'FSO') > 0 then
- /* out of memory message */
- Say SysGetMessage(8)
- Else Do
- /* if files found then process */
- if file.0>0 Then Do
-
- /* ... create folder for all INFs if it doesn't already exist */
-
- /* loop through all INFs found */
- Do i=1 to file.0
-
- /* ... for each INF, get its title & create icon */
-
- End
- End
- Else
- /* file not found message */
- Say SysGetMessage(2)
- End
-
- This is a fairly generic algorithm to search-and-process files that could be
- used in other situations.
-
- Workplace Shell icons are created for all INFs found using the SysCreateObject
- function. Icons are created in a folder called "INF Files" on the desktop.
- The enclosing folder is created with the following command:
-
- Call SysCreateObject "WPFolder", "INF Files", "<WP_DESKTOP>",,
- "OBJECTID=<INF_FILES>"
-
- Translated this means that this command creates a WPFolder object on the
- desktop called "INF Files" and assigns to it the object ID <INF_FILES>. Note
- the extra comma on the first line - this is the method REXX uses to indicate
- the command continues on the next line.
-
- INF icons are created with the command:
-
- Call SysCreateObject "WPProgram", title' ['path']', "<INF_FILES>",,
- "EXENAME=VIEW.EXE;PARAMETERS="name";STARTUPDIR="path";",, "UPDATE"
-
- where path is the fully qualified path of the INF file, name is the filename,
- and title is its true title. Basically, an icon has been created for the
- VIEW.EXE program which is passed the INF filename as a parameter and is
- executed in the INF file's directory.
-
- The UPDATE parameter to the SysCreateObject function requests that object
- attributes be updated, rather than additional objects created if a duplicate is
- encountered. In writing this script I discovered that if a WPProgram object
- had a multi-line name, then an object match was never detected and additional
- objects always created - something to watch out for.
-
- The INF file's true title is obtained by peeking inside the INF file. The
- title is a null terminated string up to 48 characters long, starting at offset
- 6B(hex). The "peek" is easily achieved with two lines of code:
-
- Call CHARIN file.i,1,X2D('6B')
- Parse Value CHARIN(file.i,,48) with title'00'x
-
- The first line "gobbles" the first 6B characters (Note: file.i is the INF file
- name). The next line reads the 48 characters that constitute the title field,
- and uses the Parse command to assign all characters up to the first null to the
- variable "title".
-
- Once we have the INF filename ("name", located in directory "path") and true
- title ("title"), creating an icon for it requires a simple variation on the
- SysCreateObject we have seen already:
-
- Call SysCreateObject "WPProgram", title' ['path']', "<INF_FILES>",,
- "EXENAME=VIEW.EXE;PARAMETERS="name";STARTUPDIR="path";",, "UPDATE"
-
- NEWPROJ.CMD
-
- Starting a new programming task or project involves a bit of administration to
- get you going. Aside from perhaps cleaning up your desk a bit, emptying the
- ashtray and so on, you need to set up an appropriate computing environment (set
- up a project directory, check-out some standard libraries etc). I'm here to
- tell you that REXX can't help with the first tasks (unfortunately) - but it can
- certainly help with the later.
-
- As an example, here's what I'll typically do when starting a new C or C++
- project:
-
- 1. Create a project directory (I'll also create directories for the
- different target platforms - DOS, OS/2 etc)
- 2. I use TLib version control software, so I'll create a project-specific
- TLib configuration file, and create a project directory in my TLib
- archive area.
- 3. I may use TLib to check out a copy of my (personal) standard library for
- use in the project
- 4. Create a Workplace Shell folder for the project. I'll make this folder a
- "work area", and fill it up with some objects: a shadow of the project
- directory (from 1); an OS/2 Command Line icon with startup directory set
- to the project directory; icons for any compilers or tools I intend on
- using - with startup directories set to the project directory where
- appropriate.
- 5. Copy some program stubs as a starting point for coding.
-
- Phew! I haven't even got started yet, but if I had to do this manually I
- would already have wasted a good half an hour. Fortunately it's all pretty
- mundane stuff - easy to automate with REXX. Unfortunately, it's also all very
- idiosyncratic - chances are, what I've described is similar to what you do,
- but we'll always have our differences. For that reason, the NEWPROJ.CMD
- script I describe here is only an example - it will need some heavy
- customization for it to meet you own requirements.
-
- After asking for the name of the new project, NEWPROJ.CMD does two things:
- creates some directories (the project's "home" directories); and then some
- Workplace Shell objects.
-
- Directories are created using the utility function SysMkDir:
-
- Call SysMkDir prjDir
-
- We could just have easily (but with a speed penalty) invoked the built-in
- command 'md' (i.e. 'mkdir'):
-
- 'md' prjDir
-
- To "personalize" this script, you may wish to create some basic project files
- at this stage: customized software configuration files; initialize some
- project log files etc etc.
-
- The Workplace Shell objects are a bit more interesting. Firstly, the script
- creates a folder on the desktop called "REXX - The developer's best friend";
- this folder will be a container for all the individual project folders.
-
- Call SysCreateObject "WPFolder", "REXX - The developer's best friend",,
- "<WP_DESKTOP>","OBJECTID=<REXX-TDBF>;"
-
- After creating a project folder (as a workarea, i.e. when this folder is
- closed, all child objects are also closed)...
-
- SysCreateObject("WPFolder", n "Project", "<REXX-TDBF>",,
- "OBJECTID=<SPRJ-"n">;WORKAREA=YES;","UPDATE")
-
- it is populated with a selection of objects. Firstly, it creates icons for
- the Borland C++ compilers - OS/2, DOS and Windows version (if you use these
- products you should examine the NEWPROJ script for details of the required
- SysCreateObject commands). An OS/2 Command Prompt icon is also created, as
- well as a shadow of the project directory ("d"; "n" is the project name).
-
- Call SysCreateObject "WPProgram", "OS/2 Command Prompt", "<SPRJ-"n">",,
- "EXENAME=*;STARTUPDIR="d";","UPDATE"
- Call SysCreateShadow d, "<SPRJ-"n">"
-
- As I have already mentioned, NEWPROJ.CMD is probably of little use as it
- stands, but is a good basis on which to build your own "New Project
- Initiation" script.
-
- STUBS.CMD
-
- Most developers have a pretty clear idea about how they like their source
- files organized. You may follow the more common conventions, or use a style
- of your own. Either way, you don't want to be typing all the same fluff for
- each new file created.
-
- The most common solution to this problem is to keep a selection of templates.
- Rather than type a header, copyright info and so on - just copy the template
- and away you go. Two things could be improved though: you may end up with a
- clutter of templates, and the template may still need a bit of tweaking on a
- case-by-case basis.
-
- STUBS.CMD allows you to automate your use of templates ("stubs"). Running
- STUBS first presents the user with a menu from which they can select the
- template to be generated. After providing a new filename, the script
- generates a file based on the selected template. Of course, being a REXX
- script it can include all kinds of smarts when generating the template - such
- as including a copyright notice with this years date (instead of the date from
- when you created the template. <grin>
-
- So far its a good concept - but you may ask how I (as the guy who wrote
- STUBS.CMD) know how you want your templates produced. The answer is - I
- don't! In fact, STUBS.CMD comes as a basic menu shell but contains no actual
- templates. The one useful command it provides you (other than "exit") is
- "modify". Here is the basic menu structure:
-
- Do Forever
- /* display menu */
- Say
- Say "Select from the following commands:"
- /*FLAG1* DO NOT DELETE THIS LINE - New menu items inserted above*/
- Say " -----------------------------------------------------"
- Say " MODIFY: add a new template to this file"
- Say " EXIT: end processing"
- Call CHAROUT ,'> '
- Pull Cmd
- /* process menu option */
- Select
- When ABBREV("EXIT",Cmd) Then
- Signal ExitProc
- When ABBREV("MODIFY",Cmd) Then
- Call AdminMODIFY
- /*FLAG2* DO NOT DELETE THIS LINE - New menu items inserted below*/
- Otherwise
- Nop
- End
- End
-
- As you can see, very simple. A menu is printed, acommand is accepted, and
- then the command is processed. Using the ABBREV function to examine a command
- makes for a very user-friendly interface reminiscent of VMS. (Isn't that an
- oxymoron?) Users can type as few or as many letters of a command as they
- like; insofar as the letters they have typed match the actual keyword, then
- the command is recognized.
-
- The MODIFY command allows you to add a new template to STUBS.CMD. You provide
- an example file, and the script reads it in, creating the necessary procedures
- and code modifications in STUBS.CMD to support the new template type. You
- will notice the hard-coded "tags" in the preceding code - these help the
- script locate the menu code that requires extending. The code to write the
- new templates is encapsulated in procedures which are simply appended to the
- file.
-
- When adding a new template, you are asked for three bits of information: the
- filename of the file to be used as the example template; the keyword to be
- used as the menu command; and a description of the template - this is used in
- the menu display.
-
- To modify STUBS.CMD, you first need to locate the scripts true file name and
- path. This is easily done using the "Source" variant of the "Parse" command:
-
- Parse Source . . SourceFile
-
- Next, the SourceFile ("STUBS.CMD") is read into a queue. The queue provides a
- temporary storage area prior to writing back a modified STUBS.CMD, and is much
- more convenient than messing about with temporary files and so on.
-
- /* read stubs.cmd to queue */
- Do While LINES(SourceFile) > 0
- line = LINEIN(SourceFile)
- queue line
- End
-
- Once the file has been fully enqueued, write-back begins by first
- repositioning the file pointer to the start of the file...
-
- Call LINEOUT SourceFile,,1
-
- and then pulling the the source from the queue, writing each line back to file
- - inserting the new menu display and processing commands as we go:
-
- Do While QUEUED() > 0
- Parse Pull line
-
- /* insert new menu item */
- if (POS("/*FLAG1",line)=1) Then Do
- Call LINEOUT SourceFile,' Say " 'key': 'description'"'
- End
- /* write current line */
- Call LINEOUT SourceFile,line
-
- /* insert new menu- processing commands */
- if (POS("/*FLAG2",line)=1) Then Do
- Call LINEOUT SourceFile,' When ABBREV("'key'",Cmd) Then'
- Call LINEOUT SourceFile,' Call Create'key
- End
- End
-
- After that, the new template-writing procedure is appended to the source file.
- At its core, this is a simplified variation of the procedure used in the
- QUOTE.CMD script to read the example file and convert it to commands that can
- re- write the example (see the section on text filters for more details). In
- part:
-
- /* insert sample script */
- pre="Call LINEOUT f,'"
- post="'"
- Do While LINES(sample) > 0
- line = LINEIN(sample)
- new=""
- /* replace conflicting quote chars in
- source line */
- Do While POS("'",line)>0
- Parse Var line frag"'"line
- new=new''frag"''"
- End
- new=new''line
- /* write command to write 'clean' line */
- Call LINEOUT SourceFile,' 'pre''new''post
- End
-
- In my experience, such self-modifying scripts appear to be quite safe.
- Although I don't know the details of the process, I suspect that the REXX
- command processor reads and semi-compiles a script before beginning execution
- (the compiled form of the script is also stored in extended attributes for
- re-use). This means a script can safely read and modify itself without
- altering the execution path of the current program instance.
-
- How to best use STUBS? I suggest that you marshal your most commonly used
- templates and put a bit of effort into cleaning them up. Once they've checked
- out OK, use the MODIFY command to add them to STUBS.
-
- At this stage, STUBS.CMD will be a self-contained script that can re-create
- your original templates on command. You can stop right there, however you may
- wish to delve in and customize the template-writing procedures to add extra
- capabilities.
-
- For example, my personal STUBS.CMD can create C, C++ and REXX templates. I
- have converted all hard-coded dates (in the copyright legends for example) to
- calculated values so that the templates never go out of date. The C/C++
- template procedures contain other enhancements. For example, after asking for
- a name stem, they produce both an include (*.h) and associated source files
- (*.c or *.cpp). The source files #include the matching header file.
-
- More Ideas
-
- I've already hinted that I use REXX to assist with version and project control
- activities (to great effect). In fact, my own variant of the NEWPROJ.CMD
- script is growing in scope and capabilities almost daily - it maintains
- version control configurations, checks-in/out and updates standard libraries,
- packages software for distribution and so on. As you would appreciate, I
- cannot really present these systems in this article because they are too
- specialized and peculiar to my personal development environment. But there's
- a lesson in there somewhere: I'm sure that most of you would be in a similar
- situation of having unique requirements, and I can only encourage you to
- examine what you do and consider whether REXX can give you a hand!
-
- REXX, The Developer's Best Friend - EDM/2 - Mar 1995 - Volume 3, Issue 3
-
-
- ΓòÉΓòÉΓòÉ 7.6. Text Filters ΓòÉΓòÉΓòÉ
-
- Text Filters
-
- I made the point that REXX is particularly good at text processing, and indeed,
- it is a natural choice for implementing a whole raft of "text filter" type
- utilities.
-
- Text filters are basically programs that read a text file from the standard
- input stream, modify or process it in some way (line by line), and write the
- resultant file to the standard output stream. This means that the standard
- operating system pipe and redirection functions can be used to select input and
- output files or device handles. Typical text filter usage is as follows, using
- the OS/2 SORT.EXE filter as an example:
-
- type c:\config.sys | sort > lpt1:
-
- The basic filter structure in REXX is a very simple 'Do' loop which reads from
- the standard input (using the LINEIN function) until no more lines are
- available (i.e. LINES() = 0).
-
- /* loop until no lines available at
- standard input */
- Do While LINES() > 0
- /* read current line */
- line = LINEIN()
-
- /* ... perform some processing */
-
- End
-
- LINE.CMD
-
- This is the good old "print line number x" program (it actually prints line "x"
- and the 3 lines before and after). Implementing this requires only a few extra
- lines added to the basic filter script. Basically, we count lines and only
- print the ones we want.
-
- TAIL.CMD
-
- Again, an "oldie but a goodie" - print the last x lines of a file. When
- setting out to implement this specification, there are two obvious problems:
- we don't know how many lines are in the file - until after we've read it; and
- what's the best way of buffering the file so that you can come back and process
- it after you've worked out which lines to print.
-
- The queue interface in REXX gives us an elegant way of solving these problems.
- As we read the source file, we write the lines to a first-in, first-out (FIFO)
- queue using the QUEUE keyword. Once we've read x lines, we PULL (discard) a
- line from the front of the queue for each new one we add - this way the queue
- becomes a sliding window containing the most recently read x lines.
-
- Do While LINES() > 0
- line = LINEIN()
- lc=lc+1
- /* enqueue the new line */
- Queue line
- /* if we already have our quota, also
- discard a line from the front of
- the queue */
- If (lc>params) Then
- Pull line
- End
-
- By the time we get to the end of the file, the queue contains exactly what we
- need to print. A simple process:
-
- Do While QUEUED() > 0
- Parse Pull line
- Say line
- End
-
- QUOTE.CMD
-
- This script helps you prepare text for inclusion as print statements in C/C++
- or REXX programs. For example, the line
-
- I said "I'm here!"
-
- may be converted to the C statement
-
- printf("I said \"I'm here!\"");
-
- In fact there are four output formats supported:
-
- 1. C stdio
-
- printf("I said \"I'm here!\"");
-
- 2. C++ iostreams
-
- cout << "I said \"I'm here!\"" << endl;
-
- 3. REXX - Say keyword
-
- Say 'I said "I''m here!"'
-
- 4. REXX - CHAROUT function
-
- Call LINEOUT f,'I said "I''m here!"'
-
- The QUOTE.CMD script uses the basic text filter model to process the input
- stream. Lines are prepared for output using five variables:
-
- 1. ("pre") a prefix is prepended to the line
- 2. ("post") a suffix is appended to the line
- 3 & 4. Nominated quote characters ("qchar") that appear within the text
- itself are substituted by a quote character replacement string
- ("reqchar").
- 5. The process of replacing quote characters requires another variable
- ("qqchar"), which takes the value of "qchar" in REXX-quoted format (more
- on that later!)
-
- So, to produce a C++ iostream compatible output, we define these 5 quantities
- as follows:
-
- pre='cout << \"'
- post='\" << endl;'
- qchar='"'
- qqchar='''"''' /* the value of qqchar is '"' i.e. qchar */
- reqchar='\"'
-
- By using this generic "quoting" model, it is easy to add new variations.
-
- The trickiest part of the procedure (shown below) is the replacement of
- embedded quote characters ("qchar") with a substitute string ("reqchar"). If
- it was to be a simple character for character replacement, then the TRANSLATE
- function could have come in handy, however there are situations where the
- quote character needs to be replaced with more than one character. The
- solution I have chosen is to repeatedly parse the input line into left and
- right fragments separated by the first occurrence of the quote character. The
- right-hand fragment becomes the subject of the next iteration - this process
- continues until the quote character can no longer be found in the remaining
- fragment. Because the delimiter between left and right portions is a variable
- quantity, we need to construct a command and request REXX to interpret it on
- the fly - this allows us to make the delimiter appear as a constant in the
- Parse template.
-
- Do While LINES() > 0
- line = LINEIN()
- new=""
-
- /* replace qchar occurrences in source
- text with reqchar */
- Do While POS(qchar,line)>0
- cmd = "Parse Var line frag"qqchar"line"
- interpret cmd
- new=new''frag''reqchar
- End
-
- /* tack on any remaining line to new */
- new=new''line
- /* print the quoted line */
- say pre''new''post
- End
-
- You may not need the QUOTE.CMD itself - but this example of string
- substitution may be of assistance in other scripts you write. Note: I'm not
- altogether happy with this approach to string substitution; if anyone has a
- better suggestion I'd be glad to hear from them!
-
- More Ideas
-
- I've presented a few generic text filters. It's pretty obvious though that
- the sky's the limit when you start considering special purpose filters. Here
- are some suggestions.
-
- There are quite a few code formatters already available (mostly free) but
- these may not do exactly what you want. Perhaps you or the companies you work
- for have some special coding requirements. Developing a code formatter can be
- a fairly involved process (depending on how deep into the syntax you needed to
- peek to make the required modifications), but it would be easier in REXX than
- C!
-
- Do you need to update copyright notices embedded in source files? If you use
- a consistent format, then a REXX script to do the update for you would be
- trivial.
-
- Are you half way through a project you decide to change your variable or
- function naming conventions. If you have many source modules, a quick REXX
- script may be the easiest way of implementing the change.
-
- REXX, The Developer's Best Friend - EDM/2 - Mar 1995 - Volume 3, Issue 3
-
-
- ΓòÉΓòÉΓòÉ 7.7. Conclusion ΓòÉΓòÉΓòÉ
-
- Conclusion
-
- REXX is a fantastic language for text processing - enormously powerful (yet
- simple) features such as the Parse command set it apart from its competitors.
-
- Coupled with the ability to manipulate Workplace Shell objects, REXX clearly
- has a lot to offer the developer - not only as a language for coding the end
- product, but as I've explored in this article: as a tool for assisting
- development.
-
- Hopefully the scripts presented - if not immediately useful to you - will give
- you ideas for other applications.
-
- REXX, The Developer's Best Friend - EDM/2 - Mar 1995 - Volume 3, Issue 3
-
-
- ΓòÉΓòÉΓòÉ 7.8. APPENDIX: Summary of REXX Scripts Presented ΓòÉΓòÉΓòÉ
-
- APPENDIX: Summary of REXX Scripts Presented
-
- INFICONS.CMD
-
- Invocation:
-
- INFICONS [path][filemask]
-
- Example usage:
-
- INFICONS
- INFICONS C:\OS2\BOOK
-
- This script will search for INF files on disk, and create icons for them in a
- folder called "INF Files". The icons will be named according to the INF file's
- true title (not its filename).
-
- LINE.CMD
-
- Invocation:
-
- LINE x
-
- Example usage:
-
- type file.txt | LINE 50
-
- This script prints the specified line number of a file to the screen, along
- with the 3 lines immediately before and after.
-
- NEWPROJ.CMD
-
- Invocation:
-
- NEWPROJ
-
- QUOTE.CMD
-
- Invocation:
-
- QUOTE [c|c++|rexx|rexxf]
-
- Example usage:
-
- type file.txt | LINE 50
-
- This script formats lines for inclusion in C/C++ or REXX procedures. The
- parameter indicates the formatting system used.
-
- STUBS.CMD
-
- Invocation:
-
- STUBS
-
- TAIL.CMD
-
- Invocation:
-
- TAIL x
-
- Example usage:
-
- type file.txt | TAIL 5
-
- This script prints the last 'x' lines of a text file to the screen.
-
- REXX, The Developer's Best Friend - EDM/2 - Mar 1995 - Volume 3, Issue 3
-
-
- ΓòÉΓòÉΓòÉ 8. RMX-OS2: An In-Depth View (Part 3) ΓòÉΓòÉΓòÉ
-
-
- ΓòÉΓòÉΓòÉ 8.1. Introduction ΓòÉΓòÉΓòÉ
-
- RMX-OS2: An In-Depth View (Part 3)
-
- Written by Johan Wikman
-
- Introduction
-
- This is the third article in my series of articles about RMX. RMX is a system
- that allows OS/2 PM applications to run remotely, i.e., to run on one computer
- and be used on another. A somewhat more thorough overview of RMX is available
- in EDM/2 Volume 3 Issue 1.
-
- One central feature of RMX is the replacement of the local procedure calls
- (LPCs) an application makes to PM with equivalent remote procedure calls (RPCs)
- over a network to a remote computer. In this article I will describe how that
- replacement is done.
-
- RMX-OS2: An In-Depth View (Part 3) - EDM/2 - Mar 1995 - Volume 3, Issue 3
-
-
- ΓòÉΓòÉΓòÉ 8.2. Dynamic Link Libraries ΓòÉΓòÉΓòÉ
-
- Dynamic Link Libraries
-
- Anybody who has programmed for OS/2 must have - at some point - come across
- dynamic link libraries or DLLs. But let us, nevertheless, take a closer look
- at the differences between static and dynamic linking.
-
- Static Linking
-
- When an application is statically linked, all code the application needs is
- collected into the application itself. Source files are first compiled into
- object modules that are combined - if of more general use - into a library. The
- executable is built by linking object modules and libraries as shown in the
- following figure.
-
- There are obvious drawbacks with this approach. If several applications use
- the same routine from the library, it effectively means that there are N copies
- of that routine on disk and potentially in memory. Also, if a bug is found in
- the routine, then all applications using that routine must be rebuilt.
- However, as the application contains all code it needs there is never the risk
- that it stops working because some DLL is missing. This may be a highly
- desirable with certain programs.
-
- Dynamic Linking
-
- When DLLs are used the build process is slightly different. Object modules of
- general use are linked into a DLL instead of being combined into a library.
- From the DLL (or alternatively from a definitions file) an import library is
- generated that is used when the application is linked. When the application is
- run the DLL is dynamically linked and the application uses the code in the DLL
- as if it would be a part of the application itself.
-
- Back in 1989 Ray Duncan listed in his book Advanced OS/2 Programming the
- following benefits of dynamic linking:
-
- Code and read-only data in DLLs is shared invisibly between all processes
- or clients that use the libraries.
- Code and data in DLLs can be loaded on demand, so it does not occupy
- physical memory until needed.
- DLL routines can be referenced dynamically, i.e., by name, so a library
- and the applications that use it can be independently maintained and
- improved.
- Centralization of frequently used routines in DLLs reduces the size of
- each application program file and conserves disk space.
-
- So what has this got to do with RMX?
-
- RMX-OS2: An In-Depth View (Part 3) - EDM/2 - Mar 1995 - Volume 3, Issue 3
-
-
- ΓòÉΓòÉΓòÉ 8.3. RMX and DLLs ΓòÉΓòÉΓòÉ
-
- RMX and DLLs
-
- Although Ray Duncan's list of DLL benefits is valid, it is not complete. One
- important point that must be added is:
-
- Provided the prototypes of the exported functions of a DLL are known, a
- third party can provide a replacement DLL - with added value - to be used
- instead of the original DLL.
-
- This is in essence precisely what RMX is all about. RMX provides replacement
- DLLs that offer remote execution capability. That is, for each PM DLL to be
- replaced there is a corresponding RMX DLL that contains the same functions
- with the same prototypes and where the functions are exported using the same
- ordinals. So, an application can not detect any difference between the PM
- DLLs and the RMX DLLs. The user suddenly just can use the application
- remotely.
-
- Replacing DLLs
-
- Ok, so we want to replace a DLL an application is using (or actually would be
- using if it was started). How should we do that? There are at least three
- ways:
-
- 1. Simply replace the original DLL with the replacement DLL.
- 2. Place the replacement DLL - with the same file and module name - earlier
- in the LIBPATH than the original DLL.
- 3. Patch the executable so that it uses the replacement DLL instead of the
- original DLL.
-
- With ordinary DLLs - i.e., DLLs that are only used by applications and not by
- the system - all alternatives are usable, although the first one obviously is
- the easiest.
-
- With system DLLs - e.g. PMWIN.DLL and PMGPI.DLL - the situation is more
- complicated. First of all they can't simply be replaced as they are always in
- use, if not by an application then by WPS and the system itself. Also, the
- idea of replacing the DLLs of the system is not particularly appealing.
-
- It is also not possible to allow the system to use the original PM DLLs and
- force applications to use the replacement DLLs. The reason is that once a DLL
- is loaded OS/2 does no longer look for it from the LIBPATH. So, even if a DLL
- with the name PMWIN.DLL is in the current directory and the value of LIBPATH
- is .;C:\OS2\DLL;... the original PMWIN.DLL which already is in memory will be
- used when an application is started.
-
- That leaves the third alternative. Patching an application is not all that
- nice, but it is actually quite trivial to do and, above all, it works. But
- before I continue with the actual process of patching, I'll mention a fourth
- way which actually would be the nicest.
-
- I'm convinced it would be possible to write a loader application that when
- started would explicitly read the code and data pages of another executable,
- patch the code in memory and execute it. Hence, no patching of the actual
- executable files would be needed.
-
- RMX-OS2: An In-Depth View (Part 3) - EDM/2 - Mar 1995 - Volume 3, Issue 3
-
-
- ΓòÉΓòÉΓòÉ 8.4. RMX DLLs ΓòÉΓòÉΓòÉ
-
- RMX DLLs
-
- RMX does not provide replacement DLLs for all DLLs of OS/2. Only some of the
- ones that deal with PM have to be replaced. Currently rmxpatch.exe - which is
- the application that does the actual patching - replaces the following DLLs:
-
- Original Replaced with
- PMWIN.DLL RXWIN.DLL
- PMGPI.DLL RXGPI.DLL
- PMSHAPI.DLL RXSHAPI.DLL
- PMCTLS.DLL RXCTLS.DLL
- HELPMGR.DLL RXLPMGR.DLL
-
- Replacing these DLLs is enough to get most PM programs up and running. It
- turned out to be necessary to replace HELPMGR.DLL because amazingly many
- programs refuse to start if they fail to initialise the help system. Obviously
- some functionality - e.g. drag and drop as PMDRAG.DLL is not on the list - is
- missing but more often than not it doesn't significantly influence the
- usability of the application.
-
- RMX-OS2: An In-Depth View (Part 3) - EDM/2 - Mar 1995 - Volume 3, Issue 3
-
-
- ΓòÉΓòÉΓòÉ 8.5. Reading Executable Info ΓòÉΓòÉΓòÉ
-
- Reading Executable Info
-
- Ok, let's get on with the real stuff.
-
- Executable Header
-
- Before anything can be done, we need to find out exactly which DLLs the
- executable (application or DLL) uses. In order to do that we must read the
- header of the executable. Although RMX is targeted only towards 32-bit
- applications we have to, in order to find the 32-bit header, read the old DOS
- executable header first. The layout of a 32-bit application looks like
-
- 00h +------------------+ <--+
- | DOS 2 Compatible | |
- | EXE Header | |
- 1Ch +------------------+ |
- | unused | |
- +------------------+ |
- 24h | OEM Identifier | |
- 26h | OEM Info | |
- | | |-- DOS 2.0 Section
- 3Ch | Offset to | | (Discarded)
- | Linear EXE | |
- | Header | |
- 40h +------------------+ |
- | DOS 2.0 Stub | |
- | Program & | |
- | Reloc. Table | |
- +------------------+ <--+
- | |
- xxh +------------------+ <--+
- | Executable | |
- | Info | |
- +------------------+ |
- | Module | |
- | Info | |
- +------------------+ |-- Linear Executable
- | Loader Section | | Module Header
- | Info | | (Resident)
- +------------------+ |
- | Table Offset | |
- | Info | |
- +------------------+ <--+
- | Object Table | |
- +------------------+ |
- | Object Page Table| |
- +------------------+ |
- | Resource Table | |
- +------------------+ |
- | Resident Name | |
- | Table | |
- +------------------+ |-- Loader Section
- | Entry Table | | (Resident)
- +------------------+ |
- | Module Format | |
- | Directives Table | |
- | (Optional) | |
- +------------------+ |
- | Resident | |
- | Directives Data | |
- | (Optional) | |
- | | |
- | (Verify Record) | |
- +------------------+ |
- | Per-Page | |
- | Checksum | |
- +------------------+ <--+
- | Fixup Page Table | |
- +------------------+ |
- | Fixup Record | |
- | Table | |
- +------------------+ |-- Fixup Section
- | Import Module | | (Optionally Resident)
- | Name Table | |
- +------------------+ |
- | Import Procedure | |
- | Name Table | |
- +------------------+ <--+
- | Preload Pages | |
- +------------------+ |
- | Demand Load | |
- | Pages | |
- +------------------+ |
- | Iterated Pages | |
- +------------------+ |
- | Non-Resident | |-- (Non-Resident)
- | Name Table | |
- +------------------+ |
- | Non-Resident | |
- | Directives Data | |
- | (Optional) | |
- | | |
- | (To be Defined) | |
- +------------------+ <--+
- | Debug Info | |-- (Not used by Loader)
- +------------------+ <--+
- where the actual DOS 2 compatible header contains the following fields:
-
- struct exe {
- WORD eid;
- ...
- WORD ereloff;
- ...
- };
-
- If we read a proper old header then at offset 0x3C we find the offset of the
- 32-bit header. But only if the value of the eid field is EXEID (defined in
- exe.h) and the value of the erelof field is 0x40.
-
- Once we have got that offset (which is from the beginning of the file) we can
- read the 32-bit executable header.
-
- Modules
-
- Our target is to find the names of all DLLs the executable implicitly uses but
- unfortunately that is not quite enough. There are in practice three kinds of
- PM applications:
-
- 1. 16-bit applications that uses the 16-bit PM functions.
- 2. 32-bit applications that uses the 16-bit PM functions.
- 3. 32-bit applications that uses the 32-bit PM functions.
-
- E.g., the Warp applications mahjongg.exe belongs to the third cathegory while
- klondike.exe belongs to the second. As RMX only supports pure 32-bit
- applications we have to find out which interface of PM the application uses
- before we can patch it. The required information can be find out by looking
- at the fixups that have to be applied when the application is loaded by OS/2.
- All needed information is in the fixup section of the executable (copied here
- for ease of reference).
-
- +------------------+ <--+
- | Fixup Page Table | |
- +------------------+ |
- | Fixup Record | |
- | Table | |
- +------------------+ |-- Fixup Section
- | Import Module | | (Optionally Resident)
- | Name Table | |
- +------------------+ |
- | Import Procedure | |
- | Name Table | |
- +------------------+ <--+
-
- The fixup page table provides a mapping from logical page number to an offset
- into the fixup record table for that page.
-
- The fixup record table contains entries for all fixups in the executable. To
- make things more complicated there are four types of fixups: an internal
- reference, imported by ordinal, imported by name or an internal reference via
- an entry table. So, the fixup record table has to be scanned and for each
- reference that refers to a PM DLL we must establish whether it is a pure
- 32-bit call.
-
- The import module name table is a list of all modules the executable imports
- through dynamic link references.
-
- The import procedure name table contains the procedures the executable imports
- by name.
-
- Everything we need for extracting this information is in the 32-bit header:
-
- struct e32_exe
- {
- ...
- unsigned long e32_fixupsize; // Fixup section size
- ...
- unsigned long e32_fpagetab; // Offset of Fixup Page Table
- unsigned long e32_frectab; // Offset of Fixup Record Table
- unsigned long e32_impmod; // Offset of Import Module Name Table
- unsigned long e32_impmodcnt; // Number of entries in Import Module
- // Name Table
- unsigned long e32_impproc; // Offset of Import Proc. Name Table
- unsigned long e32_pagesum;
- ...
- };
-
- Before we begin we define a structure where the data of interest is collected:
-
- #define RMX_MAXDLLNAME 128
-
- typedef struct RMXMODULE_
- {
- CHAR achName[RMX_MAXDLLNAME];
- ULONG ulOffset;
- BOOL fixup8;
- BOOL fixup16Selector;
- BOOL fixup1616Pointer;
- BOOL fixup16Offset;
- BOOL fixup1632Pointer;
- BOOL fixup32Offset;
- BOOL fixup32SROffset;
- } RMXMODULE;
-
- achName is a buffer where the module name is stored.
-
- ulOffset is the position - from the beginning of the executable - where the
- name of this module is. This information will be used when the exe is
- patched.
-
- fixup8, fixup16Selector, fixup1616Pointer, fixup16Offset, fixup1632Pointer,
- fixup32Offset and fixup32SROffset are booleans that specify what kind of
- fixups must be applied when the DLL ,this module represents, is loaded by the
- system.
-
- Before we begin here are two variables that are used throughout the
- presentation.
-
- ULONG offExe32 = ...;
- struct e32_exe exe32 = ...;
-
- OffExe32 is the offset of the 32-bit header from the beginning of the file.
- This is needed because all (but one) offsets in the header are from the
- beginning of the header and not from the beginning of the file. Exe32 is the
- 32-bit header.
-
- The offset of the fixup section is obtained by adding the offset of the first
- table in the fixup section - the fixup page table - to the offset of the
- 32-bit header. The size of the fixup section is obtained directly from the
- header.
-
- ULONG offFixups = offExe32 + exe32.e32_fpagetab;
- ULONG cbFixups = exe32.e32_fixupsize;
-
- Now that we know the offset and the size of the fixup section we can read it.
-
- BYTE* pbFixups = (BYTE*) malloc(cbFixups);
-
- DosRead(hExe, pbFixups, cbFixups, &cBytesRead);
-
- As we really want to have the information in the form of RMXMODULEs we must
- allocate some memory.
-
- ULONG ulModuleSize = exe32.e32_impmodcount * sizeof(RMXMODULE);
-
- RMXMODULE* pModules = (RMXMODULE*)malloc(ulModuleSize);
-
- The next thing to do is to fill in the module name and file offset. The
- offset of the module name table (from the beginning of the fixup section) is
- the difference between the offset (from the beginning of the header) of the
- module name table and the offset (also from the beginning of the header) of
- the fixup page table.
-
- ULONG offModule = exe32.e32_impmod - exe32.e32_fpagetab;
- BYTE* pbRawModules = pbFixups + offModules;
-
- Now we have enough data in order to fill in the name and offset fields of the
- RMXMODULEs.
-
- for (int i = 0; i < exe32.e32_impmodcnt; i++)
- {
- // The length of the module name is in the first byte.
-
- ULONG
- ulSize = *pbRawModules;
- RMXMODULE
- *pModule = &pModules[i];
-
- // And the actual name is then from the second byte forward.
-
- pbRawModules++;
-
- memcpy(pModule->achName, pbRawModules, ulSize);
-
- // The strings are not NULL terminated which is why the NULL
- // must be set explicitly.
-
- pModule->achName[ulSize] = 0;
- pModule->ulOffset = offFixups + (pbRawModules - pbFixups);
-
- pbRawModules += ulSize;
- }
-
- The next task is to check out the fixups. The fixup table provides a mapping
- of a logical page number to an offset into the fixup record table for that
- page. The table contains one additional entry that is an offset to the end of
- the fixup record table. As the offsets are 4 bytes, the number of pages is:
-
- ULONG cPages = (exe32.e32_frectab - exe32.e32_fpagetab) / 4 - 1;
-
- As the page table is first in the fixup chunk we can simply cast the fixups
- pointer into an unsigned long pointer and, voila, we have the page table. The
- actual records are obtained by adding the proper offset to the beginning of
- the fixup section.
-
- ULONG* poffPages = (ULONG*) pbFixups;
- BYTE* pbRecords = pbFixups + (exe32.e32_frectab - exe32.e32_fpagetab);
-
- In the fixup record table there are four kind of entries. The length and
- structure is different for each kind of entry but the following figure shows
- the general layout:
-
- +-----+-----+-----+-----+
- 00h | SRC |FLAGS|SRCOFF/CNT*|
- +-----+-----+-----+-----+-----+-----+
- 03h/04h | TARGET DATA * |
- +-----+-----+-----+-----+-----+-----+
- | SRCOFF1 @ | . . . | SRCOFFn @ |
- +-----+-----+---- ----+-----+-----+
-
- * These fields are variable size.
- @ These fields are optional.
-
- The SRC byte specifies the type of the fixup to be performed on the fixup
- source and the type is specified as follows:
-
- 0Fh = Source mask.
- 00h = Byte fixup (8-bits).
- 01h = (undefined).
- 02h = 16-bit Selector fixup (16-bits).
- 03h = 16:16 Pointer fixup (32-bits).
- 04h = (undefined).
- 05h = 16-bit Offset fixup (16-bits).
- 06h = 16:32 Pointer fixup (48-bits).
- 07h = 32-bit Offset fixup (32-bits).
- 08h = 32-bit Self-relative offset fixup (32-bits).
- 10h = Fixup to Alias Flag.
- 20h = Source List Flag.
-
- The FLAGS field specifies how the target data should be interpreted and the
- flags are defined as follows:
-
- 03h = Fixup target type mask.
- 00h = Internal reference.
- 01h = Imported reference by ordinal.
- 02h = Imported reference by name.
- 03h = Internal reference via entry table.
- 04h = Additive Fixup Flag.
- 08h = Reserved. Must be zero.
- 10h = 32-bit Target Offset Flag.
- 20h = 32-bit Additive Fixup Flag.
- 40h = 16-bit Object Number/Module Ordinal Flag.
- 80h = 8-bit Ordinal Flag.
-
- So, in order to find out which fixups are applied for each DLL we have to loop
- through all relocation entries and see what they tell us.
-
- for (i = 0; i < cPages; i++)
- {
- BYTE
- *pbCurrentRecord = pbRecords + poffPages[i],
- *pbNextRecord = pbRecords + poffPages[i + 1];
-
- while (pbCurrentRecord < pbNextRecord)
- {
- BYTE
- flTarget = pbCurrentRecord[1]; // NOTE: 1 not 0
- ULONG
- ulSize = 0;
-
- switch (flTarget & NRRTYP)
- {
- case NRRINT:
- ulSize = ParseInternalReference(pbCurrentRecord);
- break;
-
- case NRRORD:
- ulSize = ParseOrdinalImportReference(pbCurrentRecord, pModules);
- break;
-
- case NRRNAM:
- ulSize = ParseNameImportReference(pbCurrentRecord, pModules);
- break;
-
- case NRRENT:
- ulSize = ParseInternalEntryReference(pbCurrentRecord);
- }
-
- pbCurrentRecord += ulSize;
- }
- }
-
- The NNR constants are defined in exe386.h. Ok, let's then have a look at the
- different Parse-functions.
-
- ULONG PreParseReference(BYTE* pbRecord);
-
- The following fields are common to all reference entries regardless of what
- their actual type is:
-
- +-----+-----+-----+-----+
- 00h | SRC |FLAGS|SRCOFF/CNT*|
- +-----+-----+-----+-----+
- ...
-
- * These fields are variable size.
-
- What this function actually does is to figure out how much space the common
- parts actually occupies. As the SRC and FLAGS entries are always present the
- size is at least 2 bytes.
-
- ULONG ulSize = 2;
-
- The item following the SRC and FLAGS field is either a byte specifying how
- many offsets there are - in which case the actual relocation entry is followed
- by a list of offsets (the SRCOFF1 ... SRCOFFn in the figure presented
- earlier) - or a word specifying the single source offset. Which one it is, is
- specified by the presens or absence of the source list flag in the SRC byte.
-
- BYTE sourceType = pbRecord[0];
-
- if (sourceType & NRCHAIN) // "Source List" flag.
- {
- ulSize += 1; // The byte itself.
-
- BYTE
- sourceCount = pbRecord[2];
-
- ulSize += sourceCount * 2; // The actual offsets..
- }
- else
- ulSize += 2;
-
- So, depending on the way the offsets are presented this function returns
- either 4, or 5, 7, 9, ... and so on.
-
- ULONG ParseInternalReference(BYTE* pRecord);
-
- If it is an internal reference the relocation record looks like:
-
- +-----+-----+-----+-----+
- 00h | SRC |FLAGS|SRCOFF/CNT*|
- +-----+-----+-----+-----+-----+-----+
- 03h/04h | OBJECT * | TRGOFF * @ |
- +-----+-----+-----+-----+-----+-----+
- | SRCOFF1 @ | . . . | SRCOFFn @ |
- +-----+-----+---- ----+-----+-----+
-
- * These fields are variable size.
- @ These fields are optional.
-
- The call to PreParseReference figures out the size of the common parts.
-
- ULONG ulSize = PreParseReference(pbRecord);
- BYTE
- sourceType = pbRecord[0],
- flTarget = pbRecord[1];
-
- OBJECT is an index into the current module's object table that specifies the
- target object. Depending on a bit in flTarget it is either a byte or a word.
-
- if (flTarget & NR16OBJMOD)
- ulSize += 2;
- else
- ulSize += 1;
-
- The target offset field - TRGOFF - is present only if this record does not
- represent a 16-bit selector fixup.
-
- if ((sourceType & NRSTYPE) == NRRSEG)
- ;
- else
- {
-
- If it is present it is either a 2 or 4 byte entry depending on whether the
- 32-bit target offset bit has been set.
-
- if (flTarget & NR32BITOFF)
- ulSize += 4;
- else
- ulSize += 2;
- }
-
- ULONG ParseOrdinalImportReference(BYTE* pbRecord, RMXMODULE* pModule);
-
- If it is an ordinal import reference the relocation record looks like:
-
- +-----+-----+-----+-----+
- 00h | SRC |FLAGS|SRCOFF/CNT*|
- +-----+-----+-----+-----+-----+-----+-----+-----+
- 03h/04h | MOD ORD# *|IMPORT ORD*| ADDITIVE * @ |
- +-----+-----+-----+-----+-----+-----+-----+-----+
- | SRCOFF1 @ | . . . | SRCOFFn @ |
- +-----+-----+---- ----+-----+-----+
-
- * These fields are variable size.
- @ These fields are optional.
-
- The common stuff is handled by PreParseReference.
-
- ULONG ulSize = PreParseReference(pbRecord);
- BYTE
- sourceType = pbRecord[0],
- flTarget = pbRecord[1];
-
- Depending on which way the source offsets are represented, the index of the
- module ordinal - MODORD - is either 3 or 4.
-
- ULONG index = 3;
-
- if (!(sourceType & NRCHAIN))
- index++;
-
- The module index is either a byte or a word, depending on whether the 16-bit
- module ordinal flag has been set.
-
- if (flTarget & NR16OBJMOD)
- {
- usModule = *((USHORT*) &pbRecord[index]);
- index += 2;
- ulSize += 2;
- }
- else
- {
- usModule = pbRecord[index];
- index += 1;
- ulSize += 1;
- }
-
- The actual ordinal is then either 1, 2 or 4 bytes depending on whether certain
- bits have been set or not.
-
- ULONG ordinal = 0;
-
- if (flTarget & NR8BITORD) // "8-bit Ordinal" flag
- {
- ordinal = (UCHAR) pbRecord[index];
- ulSize += 1;
- }
- else if (flTarget & NR32BITOFF) // "32-bit Target Offset" flag
- {
- ordinal = *((ULONG*) &pbRecord[index]);
- ulSize += 4;
- }
- else
- {
- ordinal = *((USHORT*) &pbRecord[index]);
- ulSize += 2;
- }
-
- I don't use the ordinal for anything, but for a tdump or exehdr kind of
- utility you could simply print it out. The additive fixup value - ADDITIVE -
- is optionally present and its size is either 2 or 4 bytes.
-
- if (flTarget & NRADD)
- {
- if (flTarget & NR32BITADD)
- ulSize += 4;
- else
- ulSize += 2;
- }
-
- Finally we call UpdateFixupInfo in order to update the proper RMXMODULE entry.
-
- UpdateFixupInfo(sourceType, &pModules[usModule - 1]);
-
- ULONG ParseNameImportReference(BYTE* pbRecord, RMXMODULE* pModules);
-
- If it is an name import reference the relocation record looks like:
-
- +-----+-----+-----+-----+
- 00h | SRC |FLAGS|SRCOFF/CNT*|
- +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
- 03h/04h | MOD ORD# *| PROCEDURE NAME OFFSET*| ADDITIVE * @ |
- +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
- | SRCOFF1 @ | . . . | SRCOFFn @ |
- +-----+-----+---- ----+-----+-----+
-
- * These fields are variable size.
- @ These fields are optional.
-
- The beginning of this function is identical to ParseOrdinalImportReference, so
- I won't duplicate it here, but continue after the module index has been
- figured out.
-
- The procedure name offset is a direct offset into the import procedure name
- table and could be used for printing out the names of the functions the
- executable imports. Keep in mind that the strings are pascal strings (initial
- byte specifying the length, no NULL at end). The procedure offset is either a
- word or a long depending on the 32-bit target offset flag.
-
- ULONG procedure = 0;
-
- if (flTarget & NR32BITOFF) // "32-bit Target Offset" flag
- {
- procedure = *((ULONG*) &pbRecord[index]);
- index += 4;
- ulSize += 4;
- }
- else
- {
- procedure = *((USHORT*) &pbRecord[index]);
- index += 2;
- ulSize += 2;
- }
-
- The entry is then, just like in the ordinal import case, followed by an
- optional additive fixup value.
-
- if (flTarget & NRADD)
- {
- if (flTarget & NR32BITADD)
- ulSize += 4;
- else
- ulSize += 2;
- }
-
- Again, the corresponding RMXMODULE entry is updated with the information.
-
- UpdateFixupInfo(sourceType, &pModules[usModule - 1]);
-
- ULONG ParseInternalEntryReference(BYTE* pbRecord);
-
- If it is an internal entry reference, the relocation entry looks like:
-
- 00h | SRC |FLAGS|SRCOFF/CNT*|
- +-----+-----+-----+-----+-----+-----+
- 03h/04h | ORD # * | ADDITIVE * @ |
- +-----+-----+-----+-----+-----+-----+
- | SRCOFF1 @ | . . . | SRCOFFn @ |
- +-----+-----+---- ----+-----+-----+
-
- * These fields are variable size.
- @ These fields are optional.
-
- As usual the initial stuff is handled by PreParseReference.
-
- ULONG ulSize = PreParseReference(pbRecord);
- BYTE
- flTarget = pbRecord[0];
-
- The ordinal is always there, either as a 1 or 2 byte value and the additive
- fixup value is optionally there, either as a 2 or 4 byte value.
-
- if (flTarget & NR16OBJMOD) // "16-bit Object Number/Module Ordinal" flag.
- ulSize += 2;
- else
- ulSize += 1;
-
- if (flTarget & NRADD) // "Additive Fixup" flag
- {
- if (flTarget & NR32BITADD) // "32-bit Additive Fixup" flag
- ulSize += 4;
- else
- ulSize += 2;
- }
-
- VOID UpdateFixupInfo(BYTE sourceType, RMXMODULE* pModule);
-
- Let's then have a look at what UpdateFixupInfo does. It is very trivial;
- based on the source type flags it sets the appropriate flags in RMXMODULE to
- TRUE.
-
- switch (sourceType & NRSTYP)
- {
- case NRSBYT:
- pModule->fixup8 = TRUE;
- break;
-
- case NRSSEG:
- pModule->fixup16Selector = TRUE;
- break;
-
- case NRSPTR:
- pModule->fixup1616Pointer = TRUE;
- break;
-
- case NRSOFF:
- pModule->fixup16Offset = TRUE;
- break;
-
- case NRPTR48:
- pModule->fixup1632Pointer = TRUE;
- break;
-
- case NROFF32:
- pModule->fixup32Offset = TRUE;
- break;
-
- case NRSOFF32:
- pModule->fixup32SROffset = TRUE;
- }
-
- The whole concept here is very brute-force. Instead of scanning through all
- relocation records for a module, we could stop immediately when something is
- found that makes patching impossible. But that would make everything more
- complex and the scanning of all relocation records really doesn't take too
- long.
-
- RMX-OS2: An In-Depth View (Part 3) - EDM/2 - Mar 1995 - Volume 3, Issue 3
-
-
- ΓòÉΓòÉΓòÉ 8.6. Patching ΓòÉΓòÉΓòÉ
-
- Patching
-
- So, having obtained all RMXMODULEs of an executable we scan through them and if
- the name of the module matches one of the DLLs we want to replace, we use the
- offset to find the proper place in the executable file and simply write the new
- name. However, there are a few problems:
-
- The name of the replacement DLL must have the same length as the name of
- the original DLL.
- If OS/2 would use the checksum entries that are present in the executable
- header, then this crude approach would not work.
-
- There is a fix for these problems. If the patching would be performed as a
- proper link, i.e., a completely new executable header is created, there
- wouldn't be any constraints on the name and the checksum would also not be a
- problem. However, that would require a great deal more effort and it'll have
- to wait until I have more time<grin>.
-
- RMXPATCH.EXE
-
- Along with this article is the full source for rmxpatch.exe. You can't use it
- for anything particular yet as I haven't distributed RMX itself, but it would
- probably be fairly easy to use rmxpatch as a base for a tdump or exehdr kind
- of utility. And anyway, once RMX is available (real soon now <grin>) rmxpatch
- can be used for patching and unpatching PM programs.
-
- If you start rmxpatch.exe without any arguments it prints:
-
- usage: rmxpatch (-p|-u) [-d] file [file ...]
-
- -p: Patch the files for RMX.
- -u: Unpatch the files, i.e., remove references to RMX.
- -d: Do it. Without this flag rmxpatch only shows
- what it would do had the flag been given.
-
- So, without -d rmxpatch only shows what it would do, but it doesn't patch
- anything. I chose to make it rather difficult to patch a program so that
- people wouldn't patch programs by mistake. Let's see then what the output of
- rmxpatch look like. I use here mine.exe - the excellent mine sweeper game -
- as example.
-
- [C:\]rmxpatch -p mine.exe
- mine.exe:
- Offset From To Comment
- ---------------------------------------
- 6193 dllc101 ignored
- 6201 DOSCALLS ignored
- 6210 PMGPI RXGPI replacable
- 6216 PMWIN RXWIN replacable
- 6222 PMSHAPI RXSHAPI replacable
- 6230 HELPMGR RXLPMGR replacable
-
- Success: the executable can be patched.
-
- With -u the output obviously is different as there is nothing to do.
-
- [C:\]rmxpatch -u mine.exe
- mine.exe:
- Offset From To Comment
- ---------------------------------------
- 6193 dllc101 ignored
- 6201 DOSCALLS ignored
- 6210 PMGPI ignored
- 6216 PMWIN ignored
- 6222 PMSHAPI ignored
- 6230 HELPMGR ignored
-
- Success: the executable can be patched.
-
- As expected, there is nothing to unpatch on a program. It would have been
- possible to have deduced from the DLL names what the user actually wants to
- do, but I chose to require the user to tell it explicitly. A smaller risk for
- confusion that way. To actually patch the program -d is required.
-
- [C:\]rmxpatch -p -d mine.exe
- mine.exe:
- Offset From To Comment
- ---------------------------------------
- 6193 dllc101 ignored
- 6201 DOSCALLS ignored
- 6210 PMGPI RXGPI successfully replaced
- 6216 PMWIN RXWIN successfully replaced
- 6222 PMSHAPI RXSHAPI successfully replaced
- 6230 HELPMGR RXLPMGR successfully replaced
-
- Success: the executable was successfully patched.
-
- The unpatching works in a similar way.
-
- RMX-OS2: An In-Depth View (Part 3) - EDM/2 - Mar 1995 - Volume 3, Issue 3
-
-
- ΓòÉΓòÉΓòÉ 8.7. Conclusion ΓòÉΓòÉΓòÉ
-
- Conclusion
-
- That was briefly about the mechanism used for replacing DLLs and the tool
- required for patching applications. Feel free to use the source of the tool
- for anything you want and don't hesitate to mail me if there is something you
- are wondering about. I haven't decided yet what the next article will be about
- so you'll just have to wait<grin>.
-
- RMX-OS2: An In-Depth View (Part 3) - EDM/2 - Mar 1995 - Volume 3, Issue 3
-
-
- ΓòÉΓòÉΓòÉ 9. /dev/EDM/BookReview ΓòÉΓòÉΓòÉ
-
-
- ΓòÉΓòÉΓòÉ 9.1. Introduction ΓòÉΓòÉΓòÉ
-
- /dev/EDM2/BookReview
-
- Written by Carsten Whimster
-
- Introduction
-
- /dev/EDM2/BookReview is a monthly column which focuses on development oriented
- books and materials. The column is from the point of view of an intermediate
- PM C programmer and intermediate REXX programmer. Pick up whichever book
- strikes your fancy, and join the growing group of people following our PM
- programming columns. I have reviewed a number of beginner's books, and will
- try to concentrate a bit more on intermediate techniques and special topics
- from now on.
-
- Please send me your comments and thoughts so that I can make this column as
- good as possible. I read and respond to all mail.
-
- This month has quite possibly been the busiest of my life, and there will be no
- review. I am halfway done a review of The GUI- OOUI War - Windows vs. OS/2,
- Mandel, though, so this will appear next month.
-
- /dev/EDM2/BookReview - EDM/2 - Mar 1995 - Volume 3, Issue 3
-
-
- ΓòÉΓòÉΓòÉ 9.2. Errata ΓòÉΓòÉΓòÉ
-
- Errata
-
- My WWW page is back on-line, albeit a little short on up-to- dateness, and
- thoroughness of execution, but for those of you with actual time on your hands,
- it might prove slightly interesting. The address is in my author page.
-
- Due to having to write a file system two weeks ago, and a virtual memory system
- this week coming up, I haven't had enough time to do anything real in my life
- for a long time, and there is no review this month. If you'd like, you can
- send me mail at the address on my page at the end, if you want to encourage me,
- suggest something, or just plain communicate.
-
- /dev/EDM2/BookReview - EDM/2 - Mar 1995 - Volume 3, Issue 3
-
-
- ΓòÉΓòÉΓòÉ 9.3. Books Reviewed ΓòÉΓòÉΓòÉ
-
- Books Reviewed
-
- Real-World Programming for OS/2 2.1, Blain, Delimon, and English
-
- - SAMS Publishing. ISBN 0-672-30300-0. US$40, CAN$50.
- - Intermediate to Advanced PM C programmers
- - B+
-
- Lots of good code examples, but sometimes it is too complex for novices.
- Accurate. Well organized. The index needs a little beefing up. Good,
- but not entirely complete how-to reference. Good purchase.
-
- Learning to Program OS/2 2.0 Presentation Manager by Example, Knight
-
- - Van Nostrand Reinhold. ISBN 0-442-01292-6. US$40, CAN$50.
- - Beginning PM C Programmers
- - B-
-
- This book can be both frustrating and very rewarding. It is not very
- large, and a bit pricey, but has some excellent chapters on certain
- beginning topics, such as messages, resources, IPF, and dialog boxes.
- Strictly for beginners. This book has only one (large) sample program!
-
- Writing OS/2 2.1 Device Drivers in C, 2nd Edition, Mastrianni
-
- - Van Nostrand Reinhold. ISBN 0-442-01729-4. US$35, CAN$45.
- - Advanced C Programmers, familiar with hardware programming
- - A-
-
- The only thing a device driver programmer would not find in here is how
- to write SCSI, ADD, and IFS drivers. Most everything else is in here,
- along with skeleton examples. An optional DevHlp library of C-callable
- functions can be purchased by those who don't have time to write their
- own.
-
- OS/2 Presentation Manager GPI, Winn
-
- - Van Nostrand Reinhold. ISBN 0-442-00739-6. US$35, CAN$45.
- - Intermediate to advanced PM C programmers
- - C+
-
- This book needs updating for OS/2 2.x. It is a well-written in- depth
- coverage of the OS/2 way of programming for graphics. It is not an
- introductory PM or graphics programming book. You should know the basics
- of PM programming already.
-
- The Art of OS/2 2.1 C Programming, Panov, Salomon, and Panov
-
- - Wiley-QED. ISBN 0-471-58802-4. US$40, CAN$50.
- - Beginning OS/2 and PM programmers
- - B+
-
- This is a great introductory PM programming book. It covers basic OS/2
- issues like threads before it jumps into PM programming. The coverage is
- quite thourough, with just enough reference material to make it useful
- after you read it through the first time. The upcoming revised edition
- should be a killer.
-
- Mastering OS/2 REXX, Gargiulo
-
- - Wiley-QED. ISBN 0-471-51901-4. US$40, CAN$50.
- - Intermediate OS/2 users and beginning programmers
- - B
-
- This book is very easy to understand. If you program with any
- regularity, look elsewhere, but if you need an easily read, well-
- explained beginner's book, look no further. Some more detailed, and
- complex real-world examples might be useful as you learn the material.
- Good coverage of REXX's capabilities.
-
- REXX Reference Summary Handbook, Goran
-
- - C F S Nevada. ISBN 0-9639854-1-8. US$20, CAN$25.
- - Beginning to advanced REXX programmers
- - A
-
- This little handbook is packed full of useful information. Includes
- chapters on both built-in and some popular commercial libraries.
- Well-written and comprehensively indexed. A must for REXX programmers.
-
- Application Development Using OS/2 REXX, Rudd
-
- - Wiley-QED. ISBN 0-471-60691-X. US$40, CAN$50
- - Intermediate to advanced REXX programmers
- - A-
-
- Excellent coverage of everything REXX, with the occasional sparse
- section. It is a decent reference book, and has enough unusual material
- that it will probably find its way into many REXX programmers' libraries.
-
- OS/2 Presentation Manager Programming, Petzold
-
- - Ziff-Davis Press. ISBN 1-56276-123-4. US$30, CAN$42
- - Beginning PM C programmers
- - A-
-
- This book has the most thorough introduction to PM basics in any book I
- have read so far. It leaves the field wide open for a more thorough
- advanced PM book later. Very well written, very thorough, and very
- understandable. Only a lack of in-depth treatment of control windows and
- common dialog boxes keep it from being perfect.
-
- Designing OS/2 Applications, Reich
-
- - John Wiley & Sons. ISBN 0-471-58889-X. US$35, CAN$45
- - All programmers
- - A
-
- This book is about design, and making intelligent decisions in this
- process. It describes the OS/2 programming environment well, and thus
- helps you make the proper choices in designing applications. Highly
- recommended to anyone creating more than just command-line tools and very
- small programs.
-
- OS/2 Programming, Schildt and Goosey
-
- - Osborne, McGraw-Hill. ISBN 0-07-881910-5. US$30, CAN$40
- - Introductory PM C programmers
- - B+
-
- This book is a good, but slightly thin introductory PM book. It has good
- explanations of many things that other books tend to skip over, and
- covers a reasonably well selected subset of the API. Unfortunately, it
- doesn't come with a disk, so if you are looking for programs to
- cut-and-paste, look elsewhere.
-
- NOTES
-
- This list contains all books I have reviewed, so that you can find what you
- are looking for at a glance. I will be careful to rate books fairly. If I
- feel a need to adjust ratings, I will adjust all of them at the same time, and
- write a note explaining why I felt this necessary. Please note that books
- aimed at different audiences should only be compared with great care, if at
- all. I intend to concentrate on the strong points of the books I review, but
- I will point out any weaknesses in a constructive manner.
-
- LEGEND
-
- BOOK: The name of the book, and the author(s).
-
- PUBLISHING INFORMATION: Publishing company, ISBN, and approximate price.
-
- AUDIENCE: This is a description of the audience I think the book targets
- best. This is not intended as gospel, just a guideline for people not
- familiar with the book.
-
- MARK: My opinion of the success of the book's presentation, and how well it
- targets its audience. Technical content, accuracy, organization, readability,
- and quality of index all weigh heavily here, but the single most important
- item is how well the book covers what it says it covers. Many books try to
- cover too much, and get a lower mark as a result.
-
- A+ Ground-breaking, all-around outstanding book.
- A Excellent book. This is what I want to see happen a lot.
- A- Excellent book with minor flaws.
- B+ Very good book with minor flaws or omissions.
- B Good book with some flaws and omissions.
- B- Good book, but in need of improvement.
- C+ Mediocre book with some potential, but in need of some updating.
- C Mediocre book with some good sections, but badly in need of fixing.
- C- Mediocre book, little good material, desperately in need of an
- overhaul.
- D Don't buy this book unless you need it, and nothing else exists.
- F Don't buy this book. Period.
-
- COMMENTS: This is a very brief summary of the review proper.
-
- /dev/EDM2/BookReview - EDM/2 - Mar 1995 - Volume 3, Issue 3
-
-
- ΓòÉΓòÉΓòÉ 9.4. Content Index ΓòÉΓòÉΓòÉ
-
- Content Index
-
- This Content Index is designed to let you find the book that covers the topics
- you need to learn about. It will eventually have a lot of categories, with
- each book being rated along each row. These tables will be quite large, and
- will continually grow, so please give me your feedback regarding what
- categories you would like to see, and which you don't. It may take me a while
- to flesh them out, so have a little patience.
-
- NOTE: books which cover the same material can look similar in this table, but
- be different in real life. The style of a book, for example, can not be seen
- from a quick table, so make sure that you follow up by reading the reviews of
- the books you find here. Finally, be sure that the books you are comparing are
- aimed at the same audiences.
-
- PM C BOOKS
-
- ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
- ΓöéBOOK ΓöéMARK ΓöéKernel ΓöéDevice ΓöéVIO andΓöéPM ΓöéGPI ΓöéFonts ΓöéPrint Γöé
- Γöé Γöé ΓöéBasics ΓöéDriver ΓöéAVIO ΓöéIntro Γöé Γöé Γöé Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- ΓöéRWP ΓöéB+ Γöé2 Γöé0 Γöé0 Γöé4 Γöé4 Γöé4 Γöé3 Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- ΓöéPME ΓöéB- Γöé1 Γöé0 Γöé0 Γöé2 Γöé2 Γöé2 Γöé0 Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- ΓöéODD ΓöéA Γöé0 Γöé5 Γöé0 Γöé0 Γöé1 Γöé0 Γöé1 Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- ΓöéGPI ΓöéC+ Γöé0 Γöé0 Γöé0 Γöé0 Γöé5 Γöé2 Γöé3 Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- ΓöéTAO ΓöéB+ Γöé3 Γöé2 Γöé1 Γöé4 Γöé1 Γöé2 Γöé0 Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- ΓöéPMP ΓöéA- Γöé1 Γöé0 Γöé1 Γöé5 Γöé3 Γöé4 Γöé2 Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- ΓöéOSP ΓöéB+ Γöé2 Γöé0 Γöé0 Γöé3 Γöé2 Γöé1 Γöé0 Γöé
- ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
-
- REXX BOOKS:
-
- ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
- ΓöéBOOK ΓöéMARK ΓöéREXX ΓöéWPS ΓöéReferenceΓöé
- Γöé Γöé ΓöéIntro Γöé Γöé Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- ΓöéMOR ΓöéB Γöé4 Γöé0 Γöé2 Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- ΓöéRSH ΓöéA Γöé1 Γöé2 Γöé5 Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- ΓöéADO ΓöéA- Γöé3 Γöé2 Γöé4 Γöé
- ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
-
- BOOK LEGEND:
-
- ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
- ΓöéRWP ΓöéReal-World Programming for OS/2 2.1 Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- ΓöéPME ΓöéLearning to Program OS/2 2.0 Presentation Manager by Example Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- ΓöéODD ΓöéWriting OS/2 2.1 Device Drivers in C, 2nd Edition Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- ΓöéGPI ΓöéOS/2 Presentation Manager GPI Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- ΓöéTAO ΓöéThe Art of OS/2 2.1 C Programming Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- ΓöéMOR ΓöéMastering OS/2 REXX Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- ΓöéRSH ΓöéREXX Reference Summary Handbook Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- ΓöéADO ΓöéApplication Development Using OS/2 REXX Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- ΓöéPMP ΓöéOS/2 Presentation Manager Programming Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- ΓöéDOA ΓöéDesigning OS/2 Applications Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- ΓöéOSP ΓöéOS/2 Programming Γöé
- ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
-
- RATINGS LEGEND:
-
- ΓöîΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
- Γöé0ΓöéNo coverage Γöé
- Γö£ΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- Γöé1ΓöéVery light coverage Γöé
- Γö£ΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- Γöé2ΓöéIntroductory coverage Γöé
- Γö£ΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- Γöé3ΓöéGood Coverage Γöé
- Γö£ΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- Γöé4ΓöéIn-depth coverage Γöé
- Γö£ΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- Γöé5ΓöéAuthoritative Γöé
- ΓööΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
-
- /dev/EDM2/BookReview - EDM/2 - Mar 1995 - Volume 3, Issue 3
-
-
- ΓòÉΓòÉΓòÉ 9.5. Coming Up ΓòÉΓòÉΓòÉ
-
- Coming Up
-
- I am running a little low on books at the moment, but there is relief in sight.
- Next month I will be finishing the book The GUI- OOUI War - Windows vs. OS/2,
- Mandel. The following are the books I intend to review, in no particular
- order.
-
- OS/2 Presentation Manager GPI, 2nd edition, Winn
- OS/2 Unleashed, Moskowitz and Kerr
- The Design of OS/2, 2nd Edititon, Kogan and Deitel
- Designing High Powered OS/2 Applications, Reich (tentative title)
-
- I am considering reviewing the IBM OS/2 Redbooks, since they are readily and
- cheaply available, and look like good reference.
-
- If anyone has a book they want to see reviewed, I will be happy to oblige.
- Just mail me and tell me. Publishers can send me books at the address on my
- personal page at the end of the magazine, and I will review all OS/2
- development-related and advanced user books I receive.
-
-
- ΓòÉΓòÉΓòÉ 10. Contributors to this Issue ΓòÉΓòÉΓòÉ
-
- Are You a Potential Author?
-
- We are always looking for (new) authors. If you have a topic about which you
- would like to write, send a brief description of the topic electronically to
- any of the editors, whose addresses are listed below, by the 15th of the month
- before the month in which your article will appear. This alerts us that you
- will be sending an article so that we can plan the issue layout accordingly.
- After you have done this, get the latest copy of the Article Submission
- Guidelines from hobbes.nmsu.edu in the /os2/newsltr directory. (The file is
- artsub.zip.) The completed text of your article should be sent to us no later
- than five days prior to the last day of the month; any articles received after
- that time may be pushed to the next issue.
-
- The editors can be reached at the following email addresses:
-
- Larry Salomon - os2man@panix.com (Internet).
- Carsten Whimster - bcrwhims@undergrad.math.uwaterloo.ca (Internet).
-
- The following people contributed to this issue in one form or another (in
- alphabetical order):
-
- Pete Cassetta
- Paul Gallagher
- Martin Lafaix
- Larry Salomon, Jr.
- Carsten Whimster
- Johan Wikman
- Gordon Zeglinski
- Network distributors
-
- Contributors - EDM/2 - Mar 1995 - Volume 3, Issue 3
-
-
- ΓòÉΓòÉΓòÉ 10.1. Pete Cassetta ΓòÉΓòÉΓòÉ
-
- Pete Cassetta
-
- Pete Cassetta is a freelance software developer who specializes in OS/2 and
- Windows. He began his first OS/2 program in January, 1988, using OS/2 1.0, and
- has used every version of OS/2 since then. He presently resides in San
- Antonio, Texas with his wife Lydia.
-
- You may reach Pete on CompuServe at 75554,3502 (75554.3502@compuserve.com from
- the Internet), or at the following address:
-
- Pete Cassetta
- 624 W. Magnolia Ave. Apt. 1
- San Antonio, TX 78212
- (210) 737-3930
-
- Contributors - EDM/2 - Mar 1995 - Volume 3, Issue 3
-
-
- ΓòÉΓòÉΓòÉ 10.2. Paul Gallagher ΓòÉΓòÉΓòÉ
-
- Paul Gallagher
-
- Paul Gallagher is from Melbourne, Australia and works in computing support. An
- avowed Pascal fan from way back, he switched to C/C++ when it became clear
- Pascal wasn't going to be well supported on OS/2. The supreme sacrifice.
- <grin> That's how much he loves OS/2!
-
- Correspondence is welcome by Internet mail to: <paulg@resmel.bhp.com.au>
-
- Contributors - EDM/2 - Mar 1995 - Volume 3, Issue 3
-
-
- ΓòÉΓòÉΓòÉ 10.3. Martin Lafaix ΓòÉΓòÉΓòÉ
-
- Martin Lafaix
-
- Martin Lafaix is (among other things <grin>) a Team OS/2 member, and he first
- met OS/2 in the 1.3 days. His main interest points with OS/2 are Programming
- environments and developement tools.
-
- He is the (proud) author of MLEPM, an EPM add-on which provides PopUp menus,
- highlighting, ... and of the soon-to-be-released MLRXSHL, a set of tools for
- command-line users (including a command-shell front-end, with filename
- completion, a FILELIST-like directory browser, ...).
-
- He can be reached via email at: lafaix@mimosa.unice.fr
-
- or via surface-mail at:
-
- Martin Lafaix
- 16 rue de Dijon
- 06000 Nice
- France
-
- Contributors - EDM/2 - Mar 1995 - Volume 3, Issue 3
-
-
- ΓòÉΓòÉΓòÉ 10.4. Larry Salomon, Jr. ΓòÉΓòÉΓòÉ
-
- Larry Salomon Jr.
-
- Larry Salomon Jr. has been developing OS/2 applications since version 1.1 in
- 1989. He has written numerous applications, including the Scramble applet that
- was included in OS/2 versions 2.0-2.11, and the I-Brow, Magnify, and Screen
- Capture trio that has been distributed on numerous CD-ROMs.
-
- Larry is also the coauthor of the successful book, The Art of OS/2 2.1 C
- Programming (Wiley-QED). Finally, he is the CEO/President of IQPac Inc. which
- is responsible for the publishing of EDM/2 and he is a frequent contributor to
- the publication.
-
- Larry can be reached electronically via the Internet at os2man@panix.com.
-
- Contributors - EDM/2 - Mar 1995 - Volume 3, Issue 3
-
-
- ΓòÉΓòÉΓòÉ 10.5. Carsten Whimster ΓòÉΓòÉΓòÉ
-
- Carsten Whimster
-
- Carsten is an undergraduate Computer Science student at the University of
- Waterloo. He is currently in third year, and is enjoying it immensely. He
- uses Watcom C/C++ 10.0 and Watcom VX-REXX 2.0b.
-
- Carsten is the author of some commandline utilities and POV-Panel/2, a popular
- shareware dashboard-like front-end for the POV-Ray 2.x compilers. POV-Panel/2
- can be found on ftp-os2.cdrom.com in pub/os2/32bit/graphics and some of the
- other major OS/2 ftp sites. He is also a TEAM-OS/2 member, and has adopted a
- little computer store called The Data Store in Waterloo, Ontario.
-
- You may reach Carsten...
-
- ...via email:
-
- bcrwhims@undergrad.math.uwaterloo.ca - Internet
-
- ...Web homepage (I am just setting it up, so it may or may not be on-line):
-
- http://www.undergrad.math.uwaterloo.ca/~bcrwhims - WWW
-
- ...via snail mail (notice the changed address):
-
- Carsten Whimster
- 318A Spruce Street
- Waterloo, Ontario
- Canada
- N2L 3M7
-
- Contributors - EDM/2 - Mar 1995 - Volume 3, Issue 3
-
-
- ΓòÉΓòÉΓòÉ 10.6. Johan Wikman ΓòÉΓòÉΓòÉ
-
- Johan Wikman
-
- Johan Wikman received his Master's degree in Computer Science with a thesis
- "Adding remote execution capability to operating systems". He has been
- programming for OS/2, using C++, ever since 1990.
-
- Currently he works for Nokia Telecommunications where he takes part in a
- project that developes network management software in a Unix environment. In
- his sparetime he continues working on RMX-OS2, a system that provides remote
- execution for OS/2.
-
- Johan can be reached electronically via the Internet at
- johan.wikman@ntc.nokia.com, or via ordinary mail:
-
- Johan Wikman
- Smedjeviksvagen 23 B 22
- FI-00200 Helsinki
- FINLAND
-
- Contributors - EDM/2 - Mar 1995 - Volume 3, Issue 3
-
-
- ΓòÉΓòÉΓòÉ 10.7. Gordon Zeglinski ΓòÉΓòÉΓòÉ
-
- Gordon Zeglinski
-
- Gordon Zeglinski is a freelance programmer/consultant who received his Master's
- degree in Mechanical Engineering with a thesis on C++ sparse matrix objects.
- He has been programming in C++ for 6 years and also has a strong background in
- FORTRAN. He started developing OS/2 applications with version 2.0 .
-
- His current projects include a client/server communications program that
- utilitizes OS/2's features which has entered beta testing. Additionally, he is
- involved in the development of a "real-time" automated vehicle based on OS/2
- and using C++ in which he does device driver development and designs the
- applications that comprise the control logic and user interface.
-
- He can be reached via the Internet at zeglins@cc.umanitoba.ca.
-
- Contributors - EDM/2 - Mar 1995 - Volume 3, Issue 3
-
-
- ΓòÉΓòÉΓòÉ 10.8. Network distributors ΓòÉΓòÉΓòÉ
-
- Network Distributors
-
- These people are part of our distribution system to provide EDM/2 on networks
- other than the Internet. Their help to provide access to this magazine for
- others is voluntary and we appreciate them a lot!
-
- Paul Hethmon (hethmon@apac.ag.utk.edu) - Compuserve
- Gess Shankar (gess@knex.mind.org) - Internet
- Jason B. Tiller (PeerGynt@aol.com) - America On-line
- David Singer (singer@almaden.ibm.com) - IBM Internal
- Andre Asselin (ASSELIN AT RALVM12) - IBM Internal
-
- If you would like to become a "network distributor", be sure to contact the
- editors so that we can give you the credit you deserve!
-
- Contributors - EDM/2 - Mar 1995 - Volume 3, Issue 3
-
-
- ΓòÉΓòÉΓòÉ 11. How Do I Get EDM/2? ΓòÉΓòÉΓòÉ
-
- How Do I Get EDM/2?
-
- EDM/2 can be obtained in any of the following ways:
-
- On the Internet
-
- All back issues are available via anonymous FTP from the following sites:
- - hobbes.nmsu.edu in the /os2/newsltr directory.
- - ftp.luth.se in the /pub/os2/programming/newsletter directory.
- - generalhq.pc.cc.cmu.edu in the /pub/newsletters/edm2 directory.
- The EDM/2 mailing list. Send an empty message to edm2-info@knex.mind.org
- to receive a file containing (among other things) instructions for
- subscribing to EDM/2. This is a UUCP connection, so be patient please.
- IBM's external gopher/WWW server in Almaden. The address is
- index.almaden.ibm.com and it is in the "Non-IBM-Originated" submenu of
- the "OS/2 Information" menu; the URL is
- "gopher://index.almaden.ibm.com/1nonibm/os2nonib.70".
-
- On Compuserve
-
- All back issues are available in the OS/2 Developers Forum 2.
-
- IBM Internal
-
- IBM's internal gopher/WWW server in Almaden. The address is
- n6tfx.almaden.ibm.com and it is in the "Non-IBM-Originated Files" menu;
- the URL is "gopher://n6tfx.almaden.ibm.com/1!!nonibm/nonibm.70".
- IBM's REQUEST command on all internal VM systems. Enter the VM command
- REQUEST LIST FROM ASSELIN AT RALVM12 and a list of the requestable
- packages will be sent to you; in this list are the names of the packages
- containing the EDM/2 issues.
-
- How do I Get EDM/2? - EDM/2 - Mar 1995 - Volume 3, Issue 3