home *** CD-ROM | disk | FTP | other *** search
-
- ΓòÉΓòÉΓòÉ 1. Feb 1995 Title Page ΓòÉΓòÉΓòÉ
-
- EDM/2
- The Electronic Developer's Magazine for OS/2
- Portions copyright (c) by Larry Salomon Jr.
- Volume 3, issue 2
-
- Administrivia
-
- Oh boy, it has been one wild month. Many things have happened, and I have
- survived to tell the tale.
-
- First, the Bad News...
-
- First, the bad news must come out: the Internet service provider I use had a
- disk crash; while only one set of home directories was affected, you can bet
- your bottom dollar that I was a part of that set. Since I had not yet
- downloaded the submissions for this issue when the crash occurred, I lost
- everything. To make it worse, due to complications, the nightly backups were
- bad too, so I could only be restored to the last full backup which was on
- January 25. The end result is that this issue is a bit slimmer and later than
- usual.
-
- ...Then, the Good News
-
- Fortunately, the good news significantly outweighs the bad news. The first
- thing you will notice, if you look at the Copyright Notice section, is that
- EDM/2 now is published by IQPac Inc. which I formed with the intent of taking
- this magazine to new heights. IQPac Inc. will not yet be paying anyone, but
- plans to do so as soon as a consistent revenue stream is established. The best
- thing about having a corporation is that I can now justify to my wife - the
- accountant - the need to buy a lot of new hardware; I've already purchased a
- LaserJet 4MP and plan to get a ScanJet IIcx and a #9 GXE64 Pro in the near
- future. <grin>
-
- The next thing you will notice, again if you look at the Copyright Notice
- section, is that EDM/2 is now an OS/2 Accredited publication. This is great,
- as far as I'm concerned, because it gives us a lot more credibility as a
- publication. Look for IQPac Inc. in the next edition of Sources and Solutions.
-
- Another point for our side came during the fiasco when Mr. Labant retired from
- IBM. I sent off to Lou Gerstner, for my own reasons, a four page fax
- containing a lot of stuff; included in that "stuff" was a mention of IQPac Inc.
- and EDM/2. I did subsequently receive a reply from one of Mr. Gerstner's staff
- members acknowledging the favorable reception of the fax by Mr. Gerstner.
-
- Wanna Laugh?
-
- In the "Good for a Chuckle" department, pick up a copy of the new ARCsolo for
- OS/2 version 1.5 (Cheyenne Software) when it hits your shelves. If you look at
- the screen shots on the box, you'll see that the name of the tape is "EDM/2."
- That's what you get when you put me in charge of taking screen shots. <grin>
-
- A Slight Change
-
- Due to the fact that this issue is so late, I have decided to push the next
- part of the VIOWIN series until next issue, which isn't that far away. My
- apologies for this inconvenience.
-
- And Another Change
-
- Since Gordon Zeglinski's column has of late focused on SOM related topics, we
- felt it would be more accurate if the name of his column was changed from C++
- Corner to OOPS Avenue.
-
- And Yet Another Change
-
- EDM/2 next month will see the beginning of a "Letters to the Editors" page,
- where you can voice your opinions about the magazine, columnists, or
- programming issues in general. Make sure you send your email to me -
- os2man@panix.com - with a subject line similar to "Letters to the Editors".
-
- One Bigger Change
-
- I don't normally re-release an issue due to problems that I made when it was
- first released, but this issue had special problems in it:
-
- The legally-required statements that correspond to the OS/2 Accredited
- Logo were inadvertently left out. These were added.
- Martin Lafaix's article was intended to be a rough draft. It has been
- removed and will be included in the next issue (hopefully).
-
- And the Votes are Tallied
-
- Finally, the votes for the Reader's Choice Awards were much greater in number,
- but still not as much as I'd hoped. Considering that I estimate that EDM/2
- has over 2000 readers, 112 (valid) votes (some of the readers did not take
- full advantage of the 3 votes they were able to cast) is still small.
- However, it's much better than the 14 I received last year! The results are
- displayed below. There is a sad note to this and that is that I have yet to
- get committments from anyone willing to donate anything to the winners. I am
- currently waiting to hear from my liason in the Media Relations group in IBM,
- so I'll keep the winners posted as I receive more information.
-
- Place Article/Column (Votes)
- 1. "WPS Programming the Easy Way" (18 votes)
- 2. "Introduction to PM Programming" (15 votes)
- 3. "/dev/EDM/BookReview" (14 votes)
- 4. "C++ corner" (13 votes)
- 5. "Workplace Shell Development 101" (10 votes)
- 6. "Making Noise with MMPM/2" (6 votes)
- 7. "Sprites and Animation" (6 votes)
- 8. "TCP/IP Socket Programming in REXX" (5 votes)
- 9. "Scratch Patch" (5 votes)
- 10. "The Design and Implementation of VIOWIN" (4 votes)
- 11. "Coding for Dollars: Copy Protection and Prevention" (4 votes)
- 12. "Visual REXX Faceoff" (3 votes)
- 13. "Resources and Decompiling Them" (3 votes)
- 14. "Using SYSLEVEL Files in Your Applications" (2 votes)
- 15. "Adding Sounds to Your OS/2 Application" (2 votes)
- 16. "Utilizing Hooks for Added Capabilities" (1 vote)
- 17. "Controlling Yourself: A Framework for Configurable Options" (1
- vote)
-
- I didn't know what to expect, but the winner was not something I was too
- surprised about. My column coming in 2nd place is something I did not expect,
- though, and though Carsten does an excellent job in his column, I did not
- expect him to place either.
-
- After further reflection of the results, it seems unfair that a column should
- be eligible, since it (theoritically) has 12 chances to win your vote. So,
- after a quick confirmation with Carsten and Gordon, the columns were removed
- from the results. This means that the top three articles of the year were:
-
- 1. "WPS Programming the Easy Way", Frank Matthijs
- 2. "Workplace Shell Development 101", BjФrn Fahller
- 3. "Making Noise with MMPM/2", Marc van Woerkom
-
- Congratulations to the winners!
-
- The Funny Pages
-
- I have saved some of the funnier votes and they are displayed below. I
- sincerely hope that I do not offend anyone by displaying these, although I
- have left out the names of the submitters. The first item is that "OS/2
- Installable File Systems" and "Threads in PM Applications" both received 2
- votes each, even though they were written last year. The rest are below.
-
- "Um, what? I did not know about any voting. Could you refresh me on what to
- vote for?"
-
- "Well, since EMX 09a is the only development environment I've used for OS/2 so
- far, I'd have to vote for it."
-
- "My nomination: OS2 Warp Unleashed. (problem is it's not released yet) The
- v2.11 book was excellent. Second vote would go to Petzold's OS/2 Presentation
- Manager Programming by ZD Press."
-
- "I'll be happy to vote but I've only just subscribed within the last few days.
- I will say that I'm very excited about the existance of and on-line
- availability of your publication."
-
- "Before you chastise me for not reading your mailings.......we lost serious
- amounts of data on our server about a week ago and that was my first receipt
- of your magazine. If you would like to repost, I would be happy to vote for
- something."
-
- "I vote for Larry Salomon, Jr."
-
- "Ok I'll send you a note. I am not voting because I just got all of the
- issues installed on our LAN for the rest of my department and I haven't had
- time to read all of them yet. While I have your attention do you know of a
- paint program for OS/2 that will create OS/2 bmp's. One of these days I might
- be able to get you a article on client/server methods and practices, I was a
- developer at IBM on the DOS/Windows TCP/IP product and now that I am with MCI
- I write PM based LAN apps using TCP/IP and NETBui. I find it interesting that
- there has been no mention of the GPF screen/code generator. I live and die by
- this app, it's probably the best spent $1000+ I have ever made on computer
- software. Hell maybe I'll do a review of it for ya. Enough of this rambling
- for now, I have 2 months of work to do in 1!"
-
- "This may not be possible given the rules of the EDM/2 publishing guidelines,
- but my vote would be, precisely because of the information expressed above,
- that *YOU* receive the Reader's Choice award this year. IMO, you do the
- lion's share of the work, and probably receive little, if any, formal
- recognition for your efforts. I hereby nominate Larry Salomon for the 1994
- EMD/2 Reader's Choice award, based on his outstanding contribution to the
- success of the EDM/2 newsletter, and the work involved in publishing this
- excellent reference for those of us struggling to get things working."
-
- "I couldn't pick out just one article, sorry. I just want to give you a vote
- of appreciation for EDM."
-
- That's it for this month! Enjoy what has made it into this issue and we'll
- see you next month!
-
- Title Page - EDM/2 - Feb 1995 - Volume 3, Issue 2
-
-
- ΓòÉΓòÉΓòÉ 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 - Feb 1995 - Volume 3, Issue 2
-
-
- ΓòÉΓòÉΓòÉ 3. The Infinately Floating Spinbutton ΓòÉΓòÉΓòÉ
-
-
- ΓòÉΓòÉΓòÉ 3.1. Introduction ΓòÉΓòÉΓòÉ
-
- The Infinately Floating Spinbutton
-
- Written by Marc Mittelmeijer and Eric Slaats
-
- Introduction
-
- Most of the OS/2 controls are very useful and flexible. But every once in a
- while you bounce against the limits of what's possible with the given controls.
- (Handling floats in spinbuttons and containers for one thing is an issue.)
- When we were building an interface for a neural network problem we needed a
- spinbutton control to handle an infinite range of (undetermined) numeric
- values.
-
- This isn't a big problem; the spinbutton can accept boundaries when handling
- integers which can be set or reset using the SPBM_SETLIMITS message. The
- problem occurred when we wanted the spinbutton to handle floats. The control
- is incapable of handling floats. Of course its possible to convert floats to
- strings and a spinbutton can accept strings. But this would mean a limited
- range of predefined (float to string) values. We needed a infinite range of
- undetermined values! In this article we'll discuss our solution to this
- problem. But first, we need to dissect the spinbutton.
-
- The Infinately Floating Spinbutton - EDM/2 - Feb 1995 - Volume 3, Issue 2
-
-
- ΓòÉΓòÉΓòÉ 3.2. How Do Spinbuttons Work? ΓòÉΓòÉΓòÉ
-
- How Do Spinbuttons Work?
-
- The spinbutton is one the most useful controls OS/2 has to offer. It's
- described in the book The Art of OS/2 2.1 programming but for all those lost
- souls who do not have a copy, here is a short description.
-
- A spinbutton is a combination of an entryfield and a set of up and down arrow
- buttons. The buttons can be used to spin through a list of predefined choices.
- If the spinbutton isn't read only, the user can type in a value of choice.
- (Which doesn't necessarily have to be in the predefined list of choices.)
- Spinbuttons are typically used to cycle through choices as months of the year,
- days of the week, hours of the day.
-
- An example of the use of spinbuttons is found in the settings of the clock
- applet. Here spinbuttons are used to set the time and the date.
-
- Spinbuttons can be defined in a number of ways. One of the most attractive
- features (which will not be described here) is the ability to link several
- spinbuttons together, so they react to one set of up and down buttons. The
- following styles can be used creating a spinbutton.
-
- Style Description
- SPBS_ALLCHARACTERS Any char can be typed in. This is the default.
- SPBS_NUMERICONLY Only the digits 0-9 are accepted.
- SPBS_READONLY Nothing can be typed in the spinfield.
- SPBS_MASTER When more spinfields are coupled this spinbutton provides
- the up and down keys and acts as a master which controls
- the others.
- SPBS_SERVANT A servant spinbutton will have no buttons and will spin
- under the master's control.
- SPBS_JUSTLEFT Left justify the spinbutton text.
- SPBS_JUSTRIGHT Right justify the spinbutton text.
- SPBS_JUSTCENTER Center the spinbutton text.
- PBS_NOBORDER Surpress drawing a border.
- SPBS_FASTSPIN The speed in which the spinbutton cycles through the
- choices will increase (every two seconds) when one of the
- buttons is held down.
- SPBS_PADWITHZEROS The entryfield is padded with zeros (up to a maximum of
- 11) to the first non zero digit.
-
- A set of values for the spinbutton can be set up in two ways. You can define
- upper and lower integer boundaries with a message to the spinbutton and let it
- spin between them or you can supply the spinbutton with an array of predefined
- (character) values.
-
- These two types of spinbuttons are demonstrated in the following simple
- program. We used a dialog box to demonstrate the spinbuttons because this
- eliminated the hassle necessary to build a 'normal' window. The .RC file for
- dialog box is build using the resource workshop of Borland C++ for OS/2 and is
- straightforward.
-
- //------------------------------------------------------------------------
- // FILE: Spinbut1.rc
- //------------------------------------------------------------------------
-
- #include "Spinbut1.H"
-
- DLGTEMPLATE SPINDLG
- BEGIN
- DIALOG "Spin example1", 100, 14, 92, 122, 71, NOT FS_DLGBORDER | FS_SIZEBORDER | WS_VISIBLE, FCF_SYSMENU | FCF_TITLEBAR | FCF_MINBUTTON | FCF_MAXBUTTON
- BEGIN
- CONTROL "", SPINBUT1, 9, 40, 73, 12, WC_SPINBUTTON, SPBS_MASTER | SPBS_ALLCHARACTERS | SPBS_JUSTLEFT | WS_VISIBLE
- CONTROL "", SPINBUT2, 8, 8, 74, 12, WC_SPINBUTTON, SPBS_MASTER | SPBS_NUMERICONLY | SPBS_JUSTLEFT | SPBS_FASTSPIN | WS_VISIBLE
- CONTROL "With array of values", 103, 11, 55, 98, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | DT_MNEMONIC | WS_VISIBLE | WS_GROUP
- CONTROL "With integer boundaries", 104, 9, 22, 106, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | DT_MNEMONIC | WS_VISIBLE | WS_GROUP
- END
- END
-
- This .RC file defines a dialog box with two spinbuttons and some text above
- each. Both spinbuttons are defined as SPBS_MASTER. This means they both have
- arrow-buttons. SPINBUT1 will take any character as input,SPINBUT2 will accept
- only numeric (integer) input. This dialog is the base for the following
- program which will set SPINBUT1 to a set of predefined values and SPINBUT2 to
- a range between two given boundaries.
-
- This dialog box will look like this:
-
- //-------------------------------------------------------------------------
- // FILE: Spinbut1.h
- //-------------------------------------------------------------------------
- #define SPINBUT1 101
- #define SPINBUT2 102
- #define SPINDLG 1
-
- //-------------------------------------------------------------------------
- // FILE: Spinbut1.cpp
- //-------------------------------------------------------------------------
- #define INCL_WIN
- #include <os2.h>
- #include "spinbut1.h"
-
- PCHAR achSpin1Array[] = { "One",
- "Two",
- "Three",
- "Four",
- "Five",
- "Six",
- "Seven",
- "Eight",
- "Nine",
- "Ten",
- "And once again"
- };
-
- //-------------------------------------------------------------------------
- // Prototypes
- //-------------------------------------------------------------------------
- MRESULT EXPENTRY SpinDlg (HWND, ULONG ,MPARAM, MPARAM);
-
- //-------------------------------------------------------------------------
- // Main
- //
- // Sets up a simple dialogbox to demonstrate spinbuttons. By using a dialog
- // none of the usual window control has to be included.
- //-------------------------------------------------------------------------
- void main(void)
- {
- HAB hab;
- HMQ hmq;
-
- hab = WinInitialize(0);
- hmq = WinCreateMsgQueue(hab,0);
-
- WinDlgBox(HWND_DESKTOP,
- HWND_DESKTOP,
- SpinDlg,
- NULLHANDLE,
- SPINDLG,
- 0);
-
- WinDestroyMsgQueue(hmq);
- WinTerminate(hab);
- }
-
-
- //-------------------------------------------------------------------------
- // dialog procedure
- //-------------------------------------------------------------------------
- MRESULT EXPENTRY SpinDlg(HWND hwndDlg, ULONG ulMsg, MPARAM mpParm1, MPARAM mpParm2)
- {
- switch (ulMsg)
- {
- case WM_INITDLG:
- {
- WinSendDlgItemMsg(hwndDlg, // Spinbut1 to predefined
- SPINBUT1,
- SPBM_SETARRAY,
- MPFROMP (achSpin1Array),
- MPFROMSHORT (11));
-
- WinSendDlgItemMsg(hwndDlg, // Spinbut2 to range
- SPINBUT2,
- SPBM_SETLIMITS,
- MPFROMLONG (100),
- MPFROMLONG (0));
- }
- }
- return WinDefDlgProc(hwndDlg, ulMsg, mpParm1, mpParm2);
- }
-
- The complete working code can be found in SPINBUT1.ZIP .
-
- In the dialog procedure, the only message that is intercepted is the
- WM_INITDLG message. This message is send when the dialog box is created.
- This message functions just like the WM_CREATE message, only it's specially
- for dialog boxes. When the WM_INITDLG message is generated, we can initiate
- the two spinbuttons.
-
- SPINBUT1 is initiated using the SPBM_SETARRAY message. In this message the
- array achSpin1Array is attached to the spinbutton. If the spinbutton is used,
- the values as they are declared in this array are shown.
-
- SPINBUT2 is initiated using the SPBM_SETLIMITS message. In this case the
- upper limit is 100 and the lower limit is 0. The spinbutton will show the
- integer values from 0 to 100.
-
- The Infinately Floating Spinbutton - EDM/2 - Feb 1995 - Volume 3, Issue 2
-
-
- ΓòÉΓòÉΓòÉ 3.3. The Infinite Spinbutton. ΓòÉΓòÉΓòÉ
-
- The Infinite Spinbutton.
-
- Our goal was to build a spinbutton with no predefined boundaries. Also the
- next value a spinbutton shows after a button-up or button down action should be
- derived from the current value. So if a user types in a value and uses the
- buttons afterwards, the new value should be derived from the typed value.
-
- NOTE: the spinbutton we're building will be working with numeric values. So
- the derived values are also numeric. It's possible to take the same technique
- and use it with alphanumeric values.
-
- The trick used to accomplish this is to set up a new value array for the
- spinbutton each time the value in the button is changed. This value array
- should have three entries, the middle one being the current value. If a button
- is pressed, the button shows one of the other two values. At that point a new
- array of values should be calculated and should be attached to the spinbutton.
-
- "1.04" is the current value in the spinbutton. The value array looks something
- like this:
-
- ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
- Γöé'1.03'Γöé'1.04'Γöé'1.05'Γöé
- ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
-
- The up button is pressed. So '1.05' will be the next current value. '1.05'
- should also be the next center value in the value array. The array will change
- to:
-
- ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
- Γöé'1.04'Γöé'1.05'Γöé'1.06'Γöé
- ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
-
- To accomplish such a task, we could intercept the SPBN_UPARROW and the
- SPBN_DOWNARROW notification messages and change the values in the value array
- when the up or down arrow has been pressed. The code to accomplish this may
- look like this:
-
- case WM_CONTROL:
- {
- switch (SHORT2FROMMP(mpParm1))
- {
- case SPBN_DOWNARROW:
- case SPBN_UPARROW:
- {
- char achRetValue[12];
-
- WinSendDlgItemMsg(hwndDlg, SPINBUT1,
- SPBM_QUERYVALUE,
- MPFROMP (achRetValue),
- MPFROM2SHORT (sizeof(achRetValue), 0));
-
- sprintf(achValues[0], "%.*f", 2, atof(achRetValue)-0.01);
- sprintf(achValues[1], "%.*f", 2, atof(achRetValue));
- sprintf(achValues[2], "%.*f", 2, atof(achRetValue)+0.01);
-
- WinSendDlgItemMsg(hwndDlg, SPINBUT1, // Spinbut1 to predefined
- SPBM_SETARRAY,
- MPFROMP (achValues),
- MPFROMSHORT (3));
-
- WinSendDlgItemMsg(hwndDlg, SPINBUT1, // Make middle one current
- SPBM_SETCURRENTVALUE,
- MPFROMLONG (1),
- MPFROMLONG (0));
- }
- return MRFROMSHORT (TRUE);
- }
- }
-
- NOTE: the array which will be attached to the spinbutton must contain 3
- values!
-
- The complete code for this example can be found in SPINBUT2.ZIP.
-
- This example code will increment or decrement the spinbutton value with 0.01.
- Of course, this is a choice, it's very simple to change the code so the change
- will be 0.5.
-
- After a button click, the spinbutton will switch to the next value in the value
- array and will generate a notification message. The code reacts to the
- SPBN_DOWNARROW and the SPBN_UPARROW. With the SPBM_QUERYVALUE the spinbutton
- is queried. The returned value in achRetValue is a value of the value array.
- With the returned value the new values for the achValue array will be set. In
- this example a (float) in/decrement of .01 is chosen. The changed array has to
- be attached to the spinbutton, and the middle value has to be made current.
-
- Building an infinite spinbutton this way has one major flaw. The button will
- not derive it's next value from a value entered by a user. The reason is:
- when the spinbutton gets a SPBM_QUERYVALUE, it will read from the attached
- value array using an index. It won't read from the entry field attached to the
- spinbutton. So the value entered will be discarded if it isn't in the values
- array! Also, it would be nice to leave all the notification messages untouched
- and take a universal approach in constructing an infinite spinbutton. In the
- next section we will analyze this problem.
-
- The Infinately Floating Spinbutton - EDM/2 - Feb 1995 - Volume 3, Issue 2
-
-
- ΓòÉΓòÉΓòÉ 3.4. Adding Spoilers and Other Gadgets ΓòÉΓòÉΓòÉ
-
- Adding Spoilers and Other Gadgets
-
- In the previous section the major trick to create an infinite spinbutton which
- can handle floats is explained. There is a more elegant way to achieve an
- infinite spinbutton, which will also react to user input. Before we use a new
- bag of tricks, let's first examine a spinbutton more precisely.
-
- There is a nice tool to examine windows in an application. It's called PMTREE
- and it is an applet from IBM. We got it from the Developers Connection CD-ROM,
- but it is also available from a number of anonymous sites. If we examine a
- spinbutton with this tool, we will see that a spinbutton is build from several
- childwindows. The top-most window in a spinbutton is an entryfield window.
- This is useful information, because this gives us a way to directly query the
- value in a spinbutton. Even if a user entered a value which can't be found in
- the attached array, the entered value will be retrieved.
-
- Retrieving the Value in the Entryfield
-
- We can query the string in an entryfield by using WinQueryWindowText(). To use
- this API, we need the window-handle of the entryfield. We know the entryfield
- is the top child of the spinbutton childwindows. Thus, if we know the handle
- of the spinbutton, we can retrieve the handle of the corresponding entryfield
- using the WinQueryWindow() function and specifying QW_TOP as the parameter.
-
- WinQueryWindow (hwndSpin, QW_TOP);
-
- By combining these two functions, we can think of some code which will retrieve
- the value in the spinbutton-entryfield given the handle of the spinbutton.
-
- char achSpinValue[32];
-
- WinQueryWindowText(WinQueryWindow(hwndSpin, QW_TOP),
- sizeof(achSpinValue),
- achSpinValue);
-
- If the value in the spinfield is known, the value array can be set up. We
- calculate the float value from the string returned by WinQueryWindowText() and
- use this value to calculate the value array values.
-
- flSpinValue = atof(chSpinValue);
- sprintf(achValues[0],"%.*f", 2, flSpinValue + -0.01);
- sprintf(achValues[1],"%.*f", 2, flSpinValue + 0);
- sprintf(achValues[2],"%.*f", 2, flSpinValue + 0.01);
-
- If we use the code this way, we can only in/decrement the value in the
- spinbutton with 0.01. It's more interesting to in/decrease the value in the
- spinbutton with a fraction of its current value. The next code increases the
- value with 2% for values outside the range -1,1. It can easily be modified to
- work with another value, or even with a parameter.
-
- //-----------------------------------------------------------------------
- // Set the multiplication factor and fill values array
- //-----------------------------------------------------------------------
- if (fabs(flSpinValue) < 1)
- factor = 0.01;
- else
- factor = (float) ceil(fabs(flSpinValue/50));
-
- sprintf(achValues[0],"%.*f", 2, flSpinValue + -factor);
- sprintf(achValues[1],"%.*f", 2, flSpinValue + 0);
- sprintf(achValues[2],"%.*f", 2, flSpinValue + factor);
-
- The next step is to attach the array to the spinbutton and set the middle value
- as the current. This can be done using the messages SPBM_SETARRAY and
- SPBM_SETCURRENTVALUE.
-
- And now for the big trick! PM generates a SPBM_SPINUP or SPBM_SPINDOWN message
- if one of the spinbutton arrow buttons are pressed. If we set the new value
- array before the messages SPBM_SPINUP and SPBM_SPINDOWN are processed, we know
- for certain that the values array will be build upon the current value in the
- spinbutton, whether or not it was user-entered! So the processing of
- SPBM_SPINUP or SPBM_SPINDOWN will use this new value array and display the
- correct value.
-
- But how can we intercept these messages so that the value array can be changed
- before it's used? The technique to achieve this is called subclassing.
-
- The Infinately Floating Spinbutton - EDM/2 - Feb 1995 - Volume 3, Issue 2
-
-
- ΓòÉΓòÉΓòÉ 3.5. And Now For Some Subclassing ΓòÉΓòÉΓòÉ
-
- And Now For Some Subclassing
-
- Really the most elegant way of creating an infinite spinbutton is to use
- subclassing. Subclassing is a technique in which a call to the standard
- procedure for a particular kind of window is intercepted. This way, a
- programmer can define their own handling of a message. We want to intercept
- the messages SPBM_SPINUP and SPBM_SPINDOWN. These messages are send by the PM
- when the arrowbuttons attached to a spinbutton are pressed and cause the value
- in the spinbutton to skip one value in the attached array.
-
- The spinbutton can be subclassed when handling the WM_INITDLG message. The
- function to call is WinSubclassWindow(). With this function we can insert a
- function which will be called instead of the standard window function.
-
- NOTE: because the standard function won't be called, the subclass function
- should call the standard function for all the messages it doesn't compute!
-
- pfnwOldProc = WinSubclassWindow(WinWindowFromID(hwndDlg, SPINBUT1), CalcSpin);
-
- pfnwOldProc is the return pointer to the original window function. This
- pointer value should be available in the subclass function so the subclass
- function can call the original window function. The general way to deal with
- this is putting the pointer in a window word. Because we're not talking about
- window words and we're a little lazy, we define a global variable for it.
-
- The name of the subclass function is CalcSpin. This function has the same
- anatomy as a normal window procedure. It's prototype looks like this:
-
- MRESULT EXPENTRY CalcSpin (HWND, ULONG ,MPARAM, MPARAM);
-
- At this point we should be able to write the complete subclass function. The
- function always calls the original window procedure. Only if the messages
- SPBM_SPINUP or SPBM_SPINDOWN are received, the function should interfere. The
- subclass function looks like this.
-
- //---------------------------------------------------------------------------------
- // Subclass for spinbutton
- //---------------------------------------------------------------------------------
- MRESULT EXPENTRY CalcSpin(HWND hwndDlg, ULONG ulMsg, MPARAM mpParm1, MPARAM mpParm2)
- {
- if (ulMsg == SPBM_SPINUP || ulMsg == SPBM_SPINDOWN)
- {
- float flSpinValue, factor;
- char chSpinValue[12];
-
- WinQueryWindowText(WinQueryWindow(hwndDlg,QW_TOP),12,chSpinValue);
- flSpinValue = atof(chSpinValue);
- //-----------------------------------------------------------------------
- // Set the multiplication factor and fill values array
- //-----------------------------------------------------------------------
- if (fabs(flSpinValue) < 1)
- factor = 0.01;
- else
- factor = (float) ceil(fabs(flSpinValue/50));
-
- sprintf(achValues[0],"%.*f", 2, flSpinValue + -factor);
- sprintf(achValues[1],"%.*f", 2, flSpinValue + 0);
- sprintf(achValues[2],"%.*f", 2, flSpinValue + factor);
-
- //-----------------------------------------------------------------------
- // Set the new array for the spinbutton and make the middle item the
- // cuurent one so the user can go up or down.
- //-----------------------------------------------------------------------
- pfnwOldProc(hwndDlg, SPBM_SETARRAY, MPFROMP (achValues), MPFROMSHORT(3));
- pfnwOldProc(hwndDlg, SPBM_SETCURRENTVALUE, (MPARAM)1, (MPARAM)0);
- }
- return pfnwOldProc(hwndDlg, ulMsg, mpParm1, mpParm2);
- }
-
- The complete working code can be found in SPINBUT3.ZIP.
-
- It should be noted that subclassing takes a performance penalty because more
- functions have to be called for every message generated! So the EXAMPLE2
- version which works with notification messages might be slightly faster. On a
- 486DX system this difference should be insignificant.
-
- The Infinately Floating Spinbutton - EDM/2 - Feb 1995 - Volume 3, Issue 2
-
-
- ΓòÉΓòÉΓòÉ 3.6. Add the Spare Tire! ΓòÉΓòÉΓòÉ
-
- Add the Spare Tire!
-
- Now that we have a spinbutton which is reacting to user input, and can handle
- floats, it would be nice to have some functions to read a float from the
- spinbutton, or to put a float in the spinbutton.
-
- The first function reads the current value in the spinbutton and returns a
- float value. In this function we'll use the knowledge that the entryfield is
- the first child of a spinbutton.
-
- //--------------------------------------------------------------------------------------------
- // ReadSpin
- //
- // Read the value in the sle field of the and return the float equevalent
- //--------------------------------------------------------------------------------------------
- float ReadSpin(HWND hwndDlg, ULONG ulId)
- {
- HWND hwndSpinSle; // Handle off sb sle field (child sb)
- HWND hwndSpinbut; // Handle Spin button
- char Output[16];
-
- hwndSpinbut = WinWindowFromID(hwndDlg, ulId); // Query spinbutton handle
- hwndSpinSle = WinQueryWindow(hwndSpinbut, QW_TOP); // Query first child spinbuuton
- // (= entry field)
- WinQueryWindowText(hwndSpinSle, 16, Output);
-
- return (atof(Output));
- }
-
- The WriteSpin() function uses the same knowledge. It puts a given float value
- as text in the entryfield of the spinbutton. Because the way it's constructed
- the subclass will use this value if an up or down arrow is used.
-
- NOTE: if you want the message SPBM_QUERYVALUE to return a valid value, you've
- got to set the value array in the writespin function and attach it to the
- spinbutton. This can easily be achieved by using code from calcspin.
-
- //--------------------------------------------------------------------------------------------
- // WriteSpin
- //
- // Write the float value in Input as a char[12] in the sle of a Spinbutton
- //--------------------------------------------------------------------------------------------
- void WriteSpin(HWND hwndDlg, ULONG ulId, float WriteValue)
- {
- char Value[16];
-
- sprintf(Value,"%.*f", 2, WriteValue);
- WinSetWindowText(WinQueryWindow(WinWindowFromID(hwndDlg, ulId), QW_TOP), Value);
- }
-
- A complete working code example is found in the file SPINBUT4.ZIP.
-
- In the SPINBUT4 program both of the above functions are used to fill and read a
- spinbutton.
-
- Concluding Notes
-
- In this article we described some possible algorithm for spinbuttons to handle
- an infinite range of values which can be floating point numbers. Two different
- approaches were discussed, using notification messages and using subclassing.
-
- The infinite spinbutton could be refined by adding input control so the user
- can't enter illegal characters. This can be done by capturing the WM_CHAR
- message.
-
- The examples are build and compiled using Borland C++ version 1.5.
-
- The Infinately Floating Spinbutton - EDM/2 - Feb 1995 - Volume 3, Issue 2
-
-
- ΓòÉΓòÉΓòÉ 4. RMX-OS2: An In-Depth View (Part 2) ΓòÉΓòÉΓòÉ
-
-
- ΓòÉΓòÉΓòÉ 4.1. Introduction ΓòÉΓòÉΓòÉ
-
- RMX-OS2: An In-Depth View (Part 2)
-
- Written by Johan Wikman
-
- Introduction
-
- This is the second article in a series where I am describing RMX. RMX is a
- system for allowing OS/2 PM applications to run remotely, i.e., to run on one
- computer and be used an another. A somewhat more thorough overview of RMX is
- available in the previous issue of EDM/2.
-
- One central feature of RMX is the replacement of the local procedure calls an
- application makes to PM with equivalent remote procedure calls over a network
- to a remote computer. In this article I will describe the low level transfer
- mechanism that is used for implementing the RPCs.
-
- RMX-OS2: An In-Depth View (Part 2) - EDM/2 - Feb 1995 - Volume 3, Issue 2
-
-
- ΓòÉΓòÉΓòÉ 4.2. Remote Procedure Calls ΓòÉΓòÉΓòÉ
-
- Remote Procedure Calls
-
- Although most of you probably know what remote procedure calls are, I
- nevertheless provide here a brief introductory lesson. If RPCs are familiar
- you can simply skip this section.
-
- Whenever we have two communicating processes, the data exchanged must somehow
- be transferred between them. Consider a situation where a client process asks
- a server process to add two integers and return the result back. Pseudo code
- for achieving that may look like:
-
- 1. write both integers to a buffer
- 2. send the buffer to server
- 3. block while waiting for server to reply
- 4. extract result from buffer
-
- This works, but it would be tedious if the client process writer would have to
- write all that code (and its a lot more in reality when error checking, etc.
- is included) only to get two integers added. If this sequence is examined
- closer, it is pretty obvious that it resembles very much an ordinary function
- call. If we just wrap the pseudo code above in a function, a client process
- need not even be aware that the function is executed remotely.
-
- The general procedure when a client calls an RPC is as follows: 1) the client
- calls a stub procedure, 2) the stub procedure marshals the arguments into
- suitable transfer format and sends them to the server, 3) the server unpacks
- the arguments and executes the actual function, 5) the server marshals the
- return value and sends it back to the client, 6) the client extracts the
- return value and returns it to the client process in an ordinary way.
-
- So, my task in RMX was to replace every PM function with an RPC as described
- above. This was the-other-way-around compared with how RPCs usually are
- implemented. The usual procedure is to describe the RPCs in a special purpose
- language and then generate client and server stubs and C- prototypes from that
- description. But there was nothing I could do about that since PM API was
- already defined and had to provide an RPC implementation for it.
-
- RMX-OS2: An In-Depth View (Part 2) - EDM/2 - Feb 1995 - Volume 3, Issue 2
-
-
- ΓòÉΓòÉΓòÉ 4.3. What is required for RPCs ΓòÉΓòÉΓòÉ
-
- What is required for RPCs
-
- In order to implement RPCs there are some fairly obvious primitives that are
- needed. A client and a server both need to send data to and read data from
- their peer. These primitives are not sufficient, however, as a connection - in
- whatever way it is implemented - between a client and a server does not come
- into being by itself. In practice we need to be able to:
-
- 1. create a connection (server)
- 2. place a connection into listening state (server)
- 3. disconnect a connection (server)
- 4. open a connection (client)
- 5. close a connection (server/client)
- 6. read data from a connection (server/client)
- 7. write data to a connection (server/client)
-
- As the client and server usually are running on different machines, the client
- also needs a way to find out the name of the server connection.
-
- When I started working on RMX I had, of practical reasons, to implement the
- communications mechanism using named pipes. At the same time, however, I did
- not want to build the use of named pipes into RMX as it would have made it
- quite difficult to later start supporting TCP/IP or something else. So what I
- did, was to specify a communications API according to the needs of RMX and
- implement it using named pipes. As the communications DLL is dynamically
- loaded (based on the value of an environment variable) during start-up, it can
- easily be replaced by another DLL - e.g. implemented on top of TCP/IP - and
- no changes are needed in RMX. This is provided, of course, that the new DLL
- fulfils the requirements of the API exactly.
-
- Recently I found the complete TCP/IP package on a Developers Connection CD I
- have, and after a few days of pondering (with the help of Client/Server
- Programming with OS/2 2.1 by Orfali and Harkey _ an excellent book) I managed
- to put together an TCP/IP based RMX communications DLL. So, my initial design
- turned out to work. <grin>
-
- RMX-OS2: An In-Depth View (Part 2) - EDM/2 - Feb 1995 - Volume 3, Issue 2
-
-
- ΓòÉΓòÉΓòÉ 4.4. RMX Communications API ΓòÉΓòÉΓòÉ
-
- RMX Communications API
-
- Ok, so here is the API. I only present the prototypes here and continue with
- the implementation on top of named pipes and TCP/IP in the next section.
-
- RmxGetServiceName
-
- This is used by the server to obtain the name to use when creating a connection
- and by the client to obtain the well-known entry point of the server.
-
- RmxCreate
-
- This is used by the server to create a connection. The name the server uses is
- always obtained from RmxGetServiceName(). The connection created must be
- blocking (calls to a empty connection must block the caller).
-
- RmxCreateUnique
-
- This is used by the server to create a new unique connection. The name the
- function uses for creating the connection is given back to the caller so that
- it - by some means - can pass it to a client.
-
- RmxOpen
-
- This is used by the client to open an existing connection.
-
- RmxClose
-
- This is used is by both the client and the server to close a connection. The
- server always first calls RmxDisConnect() and only then RmxClose().
-
- RmxConnect
-
- This is used by the server to place a connection into listening state. In
- practice it causes the server to block and wait for clients to connect.
-
- RmxDisConnect
-
- This is used by the server to acknowledge that the client has closed the
- connection.
-
- RmxWrite
-
- This is used by the server and the client to write data to the peer.
-
- RmxRead
-
- This is used by both the server and client to read data from the peer.
-
- Error codes
-
- As the idea was to be able to implement the RMX communications API on top of
- any real communications mechanism, I had to define a bunch of error codes that
- hopefully would suffice for all implementations. But in fact, as RMX treats
- most errors as fatal, a boolean would have been enough. For instance, if the
- connection to the display computer is broken, there is no other option but to
- terminate the application.
-
- Error Code Description
- RMXAPI_OK The function was successfully executed.
- RMXERR_ENLARGE_BUFFER The provided buffer was too small. Enlarge
- and call again.
- RMXERR_UNKNOWN_SERVICE The service requested is not known.
- RMXERR_GENERAL An all purpose error code.
- RMXERR_BAD_NAME The name is invalid or illegal.
- RMXERR_BAD_NETWORK The network or the server is not running.
- RMXERR_CONNECTION_NOT_CONNECTED The server has disconnected the connection.
- RMXERR_CONNECTION_BUSY The connection is busy. Wait and retry.
- RMXERR_INVALID_HANDLE The handle provided is no good.
- RMXERR_OUT_OF_MEM Out of memory.
- RMXERR_OUT_OF_RESOURCES Out of some critical resource.
-
- RMX-OS2: An In-Depth View (Part 2) - EDM/2 - Feb 1995 - Volume 3, Issue 2
-
-
- ΓòÉΓòÉΓòÉ 4.5. How RMX uses the API ΓòÉΓòÉΓòÉ
-
- How RMX uses the API
-
- Until now, I've been talking solely about a client and a server although RMX in
- fact also consists of an engine. The purpose of RMX is to allow a PM
- application to run remotely. So, if we have a client application on computer A
- and want to use the application on computer B it means in practice that on B
- there must be a server that will execute the RPCs of the client application.
-
- So far so good, but what if we want to run several application remotely? Should
- the same server provide services to all remote applications? It would work,
- although it would be a pain to keep all data in the server belonging to one
- remote application inaccessible from another remote applications.
-
- Furthermore, would one of the remote applications cause the server to crash (it
- still does happen <grin>) then all remote applications would be affected. One
- solution is to have one server - with a well-known name - whose only task is to
- start a dedicated engine on behalf of a client's request. The engine creates a
- unique connection whose name is transferred over the server connection back to
- the client. The client closes the connection to the server and opens a
- connection to the engine.
-
- Ok, so how is does the communication take place on function call level? An
- observant reader notices that RmxCreateUnique() is not mentioned at all, and
- that a function RmxGetUniqueName() seems to be used. The reason is that
- initially there was no RmxCreateUnique(), but the same functionality was
- obtained by first calling RmxGetUniqueName() and then RmxCreate(). While I was
- implementing the TCP/IP DLL I noticed that it is a lot simpler to combine the
- two. I just hadn't the time to update the picture.
-
- The connection setup follows the following steps:
-
- 1. When the server is started it calls RmxGetServiceName() to get the name
- it should listen to.
- 2. The server creates the connection, using RmxCreate(), and issues
- RmxConnect() on the connection. This causes the server to block.
- 3. When the client is started it calls RmxGetServiceName() to get the name
- of the server. It obtains the name of the host from the environment
- variable RMXDISPLAY (In the demo client application provided with this
- article, the hostname is given on the command line.)
- 4. The client then opens a connection using RmxOpen(). This will cause the
- RmxConnect() function in the server to return.
- 5. The server immediately issues a call to RmxRead(), which again will cause
- the server to block.
- 6. The client sends the required connection setup info to the server using
- RmxWrite(). This will cause the RmxRead() call in the server to return.
- 7. Immediately after the call to RmxWrite(), the client calls RmxRead() in
- order to get the reply from the server. This will cause the client to
- block.
- 8. If the start-up parameters are acceptable, the server starts a new
- engine. The engine calls RmxGetUniqueName() to get a unique new name and
- uses RmxCreate() to create the connection.
- 9. The engine passes the engine name back to the server which, using
- RmxWrite(), sends it back to the client. Meanwhile the engine calls
- RmxConnect() which will cause it to block.
- 10. The client will now return from RmxRead(). It will immediately call
- RmxClose() to close the connection.
- 11. The server calls RmxDisConnect() to close the connection instance.
- 12. The client uses the value of the environment variable RMXDISPLAY and the
- returned engine name for opening a connection to the engine using
- RmxOpen().
- 13. When the client calls RmxOpen() the engine will return from RmxConnect().
- 14. The client enters a loop where it calls RmxWrite() to send requests to
- the engine and calls RmxRead() to receive replies. The engine will
- similarly enter a loop where it calls RmxRead() to get a request from a
- client, process the request and send a reply using RmxWrite().
- 15. Although not shown in the figure, the client will eventually call
- RmxClose() to close the connection. The engine will detect this, exit
- its own loop, call RmxDisConnect() and RmxClose() and finally exit.
-
- RMX-OS2: An In-Depth View (Part 2) - EDM/2 - Feb 1995 - Volume 3, Issue 2
-
-
- ΓòÉΓòÉΓòÉ 4.6. RMXPIPE.DLL ΓòÉΓòÉΓòÉ
-
- RMXPIPE.DLL
-
- Ok, let's look at the named pipe implementation. All source is available in
- RMXCOMMS.ZIP delivered with this issue of EDM/2.
-
- Types
-
- One thing that has to be specified when named pipes are used is the size of the
- named pipe buffers. I chose to use buffers of 4096 bytes (one page). Bigger
- buffers would give better throughput when big chunks of data is being
- transferred - with the cost of more memory being used - but as in RMX mostly
- small chunks are being sent, 4K was just about right.
-
- const ULONG SIZE_PAGE = 4096;
- const ULONG SIZE_PIPE = SIZE_PAGE;
-
- There is a need for a buffer outside the actual named pipes as well. As I
- wanted the entire data structure to reside within a (multiple if need be) page,
- I defined the buffer size using the pipe size.
-
- const ULONG SIZE_BUFFER = SIZE_PIPE - sizeof(HPIPE);
-
- struct CONNECTION
- {
- HPIPE hPipe;
- BYTE bytes[SIZE_BUFFER];
- };
-
- Now CONNECTION fits conveniently within a page. A user should not see this
- structure, however, as it will be different depending on the underlying
- communications mechanism. Therefore the only thing a user sees is a,
-
- typedef void* HCONNECTION;
-
- that can only be used as a handle.
-
- ULONG RmxGetServiceName(PCSZ pcszService, ULONG* pulSize, PSZ pszName);
-
- In the TCP/IP world there is standard way of finding server applications on
- remote hosts. In the directory /etc (\TCPIP\etc on OS/2) there is a file
- services that contains well-known entry points for different services.
- Unfortunately there is no similar convention with named pipes.
-
- At first I had the RMX named pipe names hard coded in RMXPIPE.DLL but while
- writing this article I realised that hard coding the names effectively would
- mean that nobody else could use RMXPIPE.DLL directly but would have to modify
- it. Consequently I decided to implement a similar kind of functionality as the
- TCP/IP \etc\services.
-
- As there is no obvious place in OS/2 to put a file like that I chose to require
- that it has to be in the same directory as RMXPIPE.DLL and that its name has to
- be RMXPIPE.DAT. Currently it looks like:
-
- #$Header$
- #
- # This file associates official service names with a pipe
- name.
- #
- # The form for each entry is:
- # <official service name> <pipe name>
- #
- # RMXPIPE.DLL will automatically provide the initial
- required '\PIPE'
- # of a name so it need not be explicitly provided.
- #
-
- rmxserver \rmx\server # RMX Server Process
- rmxstarter \rmx\starter # RMX Starter Daemon
- rmxtester \rmx\tester # RMX Test Server
-
- The implementation of RmxGetServiceName() simply scans this file and returns
- the corresponding pipe name.
-
- The server uses the returned name directly with RmxCreate(). The client cannot
- in general use the name directly with RmxOpen(), as the name uniquely
- identifies the service within the computer but not on the network.
-
- The name of the host is required for uniquely identifying the service on the
- network. In RMX this is solved by requiring that the host name is provided in
- the environment variable RMXDISPLAY. If RMXDISPLAY is specified, RMX attempt
- to redirect the application to the remote host, if it is not, the application
- is run locally.
-
- [C:\]set RMXDISPLAY=\\LOKE
- [C:\]start app
-
- The client application provided along this article takes the host as a command
- line argument.
-
- ULONG RmxCreate(PCSZ pcszName, HCONNECTION* phConn);
-
- RmxCreate() is used by a server process to create a connection. The only
- argument it takes is the name of the connection and a pointer to a variable
- where the connection handle will be returned. Most IPC mechanism can be
- configured in a million different ways - just look at the prototype for
- DosCreateNPipe() for instance - but as I only wanted to create connections of a
- specific kind there was no reason to have any additional arguments in the
- prototype of RmxCreate(). What RMX needs from a connection is:
-
- blocking behaviour, reads on an empty connection blocks
- write through, no buffering
- read-write mode, possible to read and write using the same handle
- unlimited instances, multiple instances with the same name can be created
-
- Let's see how these requirements can be achieved with named pipes. The
- prototype of DosCreateNPipe() looks like:
-
- APIRET APIENTRY DosCreateNPipe(PCSZ pszName,
- PHPIPE pHpipe,
- ULONG openmode,
- ULONG pipemode,
- ULONG cbInbuf,
- ULONG cbOutbuf,
- ULONG msec);
-
- The interesting arguments are openmode and pipemode. Openmode defines the mode
- in which the pipe is opened and pipemode defines the mode of the pipe. To get
- the behaviour I wanted I defined the open flags as:
-
- const ULONG NP_OPEN_FLAGS = NP_NOWRITEBEHIND |
- NP_NOINHERIT |
- NP_ACCESS_DUPLEX;
-
- and the pipe flags as:
-
- const ULONG NP_PIPE_FLAGS = NP_WAIT |
- NP_TYPE_BYTE |
- NP_READMODE_BYTE |
- NP_UNLIMITED_INSTANCES;
-
- Using these constants the pipe can be created.
-
- ULONG
- rc;
- HPIPE
- hPipe;
-
- rc = DosCreateNPipe(pcszName,
- &hPipe,
- NP_CREATE_FLAGS,
- NP_PIPE_FLAGS,
- SIZE_PIPE,
- SIZE_PIPE,
- 0);
-
- As the HCONNECTION that will be returned from RmxCreate() is in fact a
- CONNECTION, some memory must also be allocated. As this takes place in a DLL
- I chose to allocate the memory using DosAllocMem() directly, the main reason
- being that I don't know how safe it is to call runtime library functions
- inside a DLL that will be used by multithreaded application, that are not
- necessarily even compiled with the same compiler.
-
- ULONG
- rc;
- PVOID
- pvMemory;
-
- do
- rc = DosAllocMem(&pvMemory, sizeof(CONNECTION),
- PAG_READ | PAG_WRITE | PAG_COMMIT);
- while (rc == ERROR_INTERRUPT);
-
- CONNECTION *pconn = (PCONNECTION) pvMemory;
-
- memset(pconn, sizeof(CONNECTION), 0);
-
- pconn->hPipe = hPipe;
-
- ULONG RmxCreateUnique(ULONG* pulSize,
- PSZ pszName,
- HCONNECTION* phConn);
-
- RmxCreateUnique() is used by the engine to create a unique connection. The
- caller needs to provide a buffer sufficiently large where the used name will
- be returned. Internally the biggest question is how to create a unique name.
- A reasonably reliable method is to use the process id, thread ordinal and the
- time.
-
- const CHAR acUniqueName[] =
- "\\PIPE\\RMXPIPE\\UNIQUE\\";
-
- PPIB
- ppib;
- PTIB
- ptib;
-
- DosGetInfoBlocks(&ptib, &ppib);
-
- DATETIME dateTime;
-
- DosGetDateTime(&dateTime);
-
- USHORT
- pid = (USHORT) ppib->pib_ulpid,
- tord = (USHORT) ptib->tib_ordinal,
- seconds = (USHORT) dateTime.seconds, // <= 59
- hundredths = (USHORT) dateTime.hundredths; // <= 99
-
- sprintf(pszName, "%s%hu%hu%hu%hu",
- acUniqueName, pid, tord, seconds, hundredths);
-
- and then create the named pipe in a loop that eventually either succeeds
- properly or fails completely.
-
- do
- {
- GetUniqueName(achUniqueName);
-
- rc = RmxCreate(achUniqueName, phConn);
- }
- while (rc == RMXERR_CONNECTION_BUSY);
-
- ULONG RmxOpen(PCSZ pcszHost, PCSZ pcszPort, HCONNECTION* hConn);
-
- RmxOpen() is used by a client process to open a connection. The arguments it
- takes are the name of the host and the port to open and a pointer to a
- variable where the connection handle will be returned. When named pipes are
- used the host name will be the name of a server and the port the name of a
- named pipe.
-
- To get the qualified name we simply need to concatenate the host and the port
- names. In order to do that we need a buffer. Instead of first allocating a
- buffer for this purpose and then another for the actual connection handle we
- first allocate the connection handle buffer and use it for storing the string.
- The allocation is done pretty much the same way as in RmxCreate().
-
- CONNECTION
- *pconn = (PCONNECTION) pvMemory;
- PSZ
- pszName = (PSZ) pconn;
-
- pszName[0] = 0;
-
- if (pcszHost)
- strcpy(pszName, pcszHost);
-
- strcat(pszName, pcszPort);
-
- The host name can be NULL which effectively means that we attempt to open the
- pipe on the same computer. Once the name is built we can open the actual
- pipe.
-
- In RMX this function will be called behind the scenes of an application which
- may be doing a lot of things, e.g. open a large number of files. Hence we
- make sure the function does not fail because there are no handles left.
-
- const ULONG NP_MODE_FLAGS = OPEN_FLAGS_WRITE_THROUGH |
- OPEN_FLAGS_FAIL_ON_ERROR |
- OPEN_FLAGS_NO_CACHE |
- OPEN_FLAGS_SEQUENTIAL |
- OPEN_SHARE_DENYNONE |
- OPEN_ACCESS_READWRITE |
- OPEN_FLAGS_NOINHERIT;
-
- HPIPE
- hPipe;
- ULONG
- rc;
-
- do
- {
- ULONG
- ulActionTaken; // Not used.
-
- rc = DosOpen(pszName,
- &hPipe,
- &ulActionTaken,
- 0L,
- FILE_NORMAL,
- FILE_OPEN,
- NP_MODE_FLAGS,
- 0L);
-
- if (rc == ERROR_TOO_MANY_OPEN_FILES)
- {
- LONG
- lReqCount = 1; // We need one additional handle
-
- ULONG
- ulCurMaxFH; // Not used.
-
- DosSetRelMaxFH(&lReqCount, &ulCurMaxFH);
- }
- }
- while (rc == ERROR_TOO_MANY_OPEN_FILES);
-
- Then we set the state of the pipe the same way as it was set in RmxCreate().
- I am not entirely sure whether this is necessary or if the state is
- automatically set to the same as what was used when the pipe was created, but
- better safe than sorry.
-
- if (rc == NO_ERROR)
- {
- // The connection must be blocking.
- rc = DosSetNPHState(hPipe, NP_WAIT |
- NP_READMODE_BYTE);
- ...
-
- ULONG RmxClose(HCONNECTION hConn);
-
- RmxClose() is simply implemented by deallocating the memory that was allocated
- in RmxCreate(), and closing the pipe
-
- CONNECTION
- *pconn = (PCONNECTION) hConn;
-
- DosClose(pconn->hPipe);
- DosFreeMem(pconn);
-
- ULONG RmxConnect(HCONNECTION hConn);
-
- This function is mapped directly to DosConnect(). It blocks until a client
- opens the pipe.
-
- CONNECTION
- *pconn = (CONNECTION*) hConn;
- ULONG
- rc = 0;
-
- do
- rc = DosConnectNPipe(pconn->hPipe);
- while (rc == ERROR_INTERRUPT);
-
- ULONG RmxDisConnect(HCONNECTION hConn);
-
- Like RmxConnect() is mapped to DosConnect(), RmxDisConnect() is mapped to
- DosDisConnect().
-
- CONNECTION
- *pconn = (CONNECTION*) hConn;
-
- DosDisConnectNPipe(pconn->hPipe);
-
- ULONG RmxWrite(HCONNECTION hConn, PCBYTE pbBuffer,
- ULONG ulBytesToWrite);
-
- Okay, now we are getting to the more interesting stuff. This functions takes
- a handle to a connection, a pointer to a buffer and an unsigned long
- specifying how many bytes to write. Contrary to usual write-functions, it
- does not return the number of bytes written. This is because in RMX, where
- this function is used for implementing RPCs, the function must either succeed
- or completely fail. Also, if the number of bytes to transfer is too large for
- the underlying implementation (in this case named pipes) the function must
- itself deal with the task of splitting the message into smaller packets.
-
- CONNECTION
- *pconn = (PCONNECTION) hConn;
- BYTE
- *pbData = pconn->bytes;
-
- The length of the entire message is sent in the first 4 bytes as an unsigned
- integer.
-
- *((ULONG*) pbData) = ulBytesToWrite;
-
- ULONG
- ulBytesInBuffer = SIZE_BUFFER - sizeof(ULONG);
- ULONG
- ulBytesToCopy = ulBytesInBuffer < ulBytesToWrite ?
- ulBytesInBuffer : ulBytesToWrite;
-
- memcpy(pbData + sizeof(ULONG), pbBuffer, ulBytesToCopy);
-
- Okay, so what is happening here? After the size, we copy as much actual data
- as can fit into the data area of the connection handle. An alternative would
- be to first send the size, and then send the data directly out of the buffer
- provided by the caller. But that would mean that no matter how small a
- message, it would always be sent in at least two separate packets. In the
- code that follows note that during the first DosWrite(), pbData points to the
- connection buffer, but on subsequent calls it points directly to the buffer
- given by the caller.
-
- ULONG
- ulBytesLeft = ulBytesToWrite + sizeof(ULONG),
- ulBytesToSend = ulBytesToCopy + sizeof(ULONG);
- LONG
- lBytesSent = -sizeof(ULONG);
- ULONG
- rc = RMXAPI_OK;
-
- do
- {
- ULONG
- ulBytesSent;
-
- rc = DosWrite(hPipe, pbData, ulBytesToSend, &ulBytesSent);
-
- lBytesSent += ulBytesToSend;
- ulBytesLeft -= ulBytesToSend;
-
- if (ulBytesLeft != 0)
- {
- pbData = (PSZ)pbBuffer + lBytesSent;
-
- ulBytesToSend = SIZE_PIPE < ulBytesLeft ?
- SIZE_PIPE : ulBytesLeft;
- }
- }
- while (ulBytesLeft != 0);
-
- The funny looking plus and minus sizeof(ULONG) is there because the first
- message contains the size information which is not part of the actual message
- and must be excluded when we figure out whether data needs to be copied still.
- This ought to be cleaned up somwhat, I get nervous when signed and unsigned
- quantities are mixed in this fashion.
-
- ULONG RmxRead(HCONNECTION hConn, PBYTE pbBuffer, ULONG ulSize,
- ULONG* pulBytesRead);
-
- This function takes a handle to a connection, a pointer to a buffer, an
- unsigned long that specifies the size of the buffer, and a pointer to an
- unsigned long where the size of the read message is returned. If the buffer
- is not big enough the function returns an error code indicating that the
- buffer must be enlarged and the required size is returned in the variable
- pointed to by pulBytesRead.
-
- If the variable indicating the length is 0, this is a true new call to
- RmxRead().
-
- CONNECTION
- *pconn = (PCONNECTION) hConn;
- HPIPE
- hPipe = pconn->hPipe;
- ULONG
- *pulLength = (ULONG*) pconn->bytes;
-
- If it is 0, we read the size as a separate operation. We have to do that
- because we do not want to read to the size to the buffer provided by the
- caller, but to the buffer associated with the handle.
-
- if (*pulLength == 0)
- {
- ULONG
- ulBytesLeft = sizeof(ULONG),
- ulBytesRead = 0;
-
- do
- {
- PBYTE
- pbData = ((PBYTE) pulLength) + ulBytesRead;
-
- DosRead(hPipe, pbData, ulBytesLeft, &ulBytesRead);
-
- ulBytesLeft -= ulBytesRead;
- }
- while (ulBytesLeft != 0);
-
- if (*pulLength > ulSize)
- {
- *pulBytesRead = *pulLength;
- return RMXERR_ENLARGE_BUFFER;
- }
- }
-
- Perhaps it is overkill to read 4 bytes in a loop like that, but at least we'll
- be certain that we get the data we are interested in. The loop for reading
- the actual message data looks pretty much the same. Note that pbData now
- points to the buffer provided by the caller.
-
- ULONG
- ulBytesLeft = *pulLength,
- ulBytesRead = 0,
- ulTotalBytesRead = 0;
-
- do
- {
- PBYTE
- pbData = pbBuffer + ulTotalBytesRead;
-
- rc = DosRead(hPipe, pbData, ulBytesLeft, &ulBytesRead);
-
- ulBytesLeft -= ulBytesRead;
- ulTotalBytesRead += ulBytesRead;
- }
- while (ulBytesLeft != 0);
-
- *pulBytesRead = *pulLength;
-
- The length is set to 0 to indicate that the entire message has been read.
-
- *pulLength = 0;
-
- RMX-OS2: An In-Depth View (Part 2) - EDM/2 - Feb 1995 - Volume 3, Issue 2
-
-
- ΓòÉΓòÉΓòÉ 4.7. RMXTCPIP.DLL ΓòÉΓòÉΓòÉ
-
- RMXTCPIP.DLL
-
- Okay, now we begin to look at the equivalent TCP/IP implementation. When I
- initially specified the communications API I followed the named pipe model
- fairly closely. The reason was that I wanted it to be easy to implement the
- RMX API on top of them, but also because I had very little experience with
- TCP/IP and I didn't exactly know how they work. But, I was confident that it
- would later be possible to fit them into the RMX model.
-
- If we look at named pipes and TCP/IP in a multithreaded client-server
- situation, the basic scenario can be pictured as (_beginthread() is called when
- a client opens the connection the server blocks on):
-
- From this figure it is fairly obvious that there is not a direct mapping
- between named pipes and sockets. And while it is trivial to implement the RMX
- API using named pipes, sockets require a little bit more thought.
-
- The main problem is the difference in how multiple connections with the same
- name is handled. With named pipes the server simply calls DosCreateNPipe() and
- DosConnect() multiple times. The name given to DosCreateNPipe() is each time
- the same. With sockets you first create a socket using socket(), bind() and
- listen(), and then you call accept() multiple times. Each call to accept()
- returns a new socket handle. Like most other things in computer science this
- can be solved by adding an extra level of indirection.
-
- ULONG RmxGetServiceName(PCSZ pcszService, ULONG* pulSize, PSZ pszName);
-
- Now that we are running on top of TCP/IP we can directly use the
- \tcpip\etc\services file. The file should contain entries like:
-
- rmxserver 4711/tcp
- rmxstarter 4712/tcp
- rmxsecurity 4713/tcp
- rmxtester 4714/tcp
-
- The first column contains the public name of a server, and the second the port
- number and the protocol to be used. The port numbers are something that
- basically the world has to agree upon, so I just chose a few numbers that did
- not seem to be used by anybody. They are easily changed if they happen be in
- conflict with something else. The socket library directly provides functions
- for dealing with this file. As the port is returned in network format we have
- to convert it to host format using ntohs().
-
- servent
- *s = getservbyname((PSZ) pcszService, "tcp");
-
- if (!s)
- return RMXERR_UNKNOWN_SERVICE;
-
- sprintf(pszName, "%d", ntohs((unsigned short) s->s_port));
-
- ULONG RmxCreate(PCSZ pcszName, HCONNECTION* phConn);
-
- RmxCreate() is used by a server process to create a connection. And as it is
- supposed to be called several times with the same name we cannot directly map
- it onto the usual socket creation functions. The first thing we do is to
- convert the given name to a port number (the user supposedly has called
- RmxGetServiceName() before this).
-
- int
- port = atoi(pcszPort);
-
- return CreateConnection(htons((unsigned short) port), phConn);
-
- All work is done by CreateConnection() which is also used by RmxCreateUnique().
- As we need to be able to detect whether the caller actually wants to create a
- new socket or if he only wants to have a new instance of the same socket we
- need to have an additional data structure for that purpose.
-
- struct SOCKET
- {
- unsigned short port; // The port number in network format.
- int socket; // The socket handle.
- unsigned users; // Number of users of this instance.
- SOCKET* next; // Pointer to next instance (or NULL).
- SOCKET* prev; // Pointer to previous instance (or NULL).
- };
-
- const ULONG SIZE_BUFFER = SIZE_CONNECTION -
- sizeof(SOCKET*) -
- sizeof(int);
-
- struct CONNECTION
- {
- SOCKET* sharedSocket;
- int privateSocket;
- BYTE bytes[SIZE_BUFFER];
- };
-
- The first thng CreateConnection() does is to get an instance of SOCKET. I
- don't show it here (it's all available in the source code) but PickSOCKET()
- scans through all existing SOCKET instances and if it finds one instance where
- the port number is the same as the one requested, it increases the use count
- and returns it. Otherwise it creates a new instance.
-
- SOCKET
- *pSocket = PickSOCKET(port);
-
- if (pSocket->socket == 0)
- rc = InitializeSOCKET(pSocket);
-
- PCONNECTION
- pconn = AllocConnection();
-
- pconn->sharedSocket = pSocket;
-
- *phConn = pconn;
-
- If it was an all new instance there is no real socket associated with it yet.
- The socket is created in InitializeSOCKET() that as its first action calls
- CreateSocket(). CreateSocket() creates the socket handle and deals with system
- call interruptions. It is in a separate function because it is also used by
- RmxOpen().
-
- static int CreateSocket()
- {
- int
- serrno = 0,
- socket = 0;
-
- do
- {
- socket = ::socket(AF_INET, SOCK_STREAM, 0);
-
- if (socket < 0)
- serrno = sock_errno();
- }
- while (serrno == SOCEINTR);
-
- if (serrno != 0)
- return 0;
-
- return socket;
- }
-
- The socket handle is just a handle, it cannot be used for anything yet. It
- must first be bound which means that a local address is assigned to it. If the
- specified port is 0, the system allocates a unique port on behalf of the
- caller.
-
- int
- socket = CreateSocket();
-
- sockaddr_in
- in;
-
- in.sin_family = AF_INET;
- in.sin_port = pSocket->port;
- in.sin_addr.s_addr = INADDR_ANY;
-
- ::bind(socket, (sockaddr*) &in,
- sizeof(sockaddr_in));
-
- The operation is not complete yet. We need to have a connection request queue
- for the incoming requests. That is taken care of by listen().
-
- ::listen(socket, SOMAXCONN);
-
- If the socket was created without an explicit port number, we ask the system
- what the actual port number is.
-
- if (pSocket->port == 0)
- {
- int
- size = sizeof(sockaddr_in);
-
- getsockname(socket, (sockaddr*) &in, &size);
- pSocket->port = in.sin_port; // Note, network
- order
- }
-
- pSocket->socket = socket;
-
- The call to AllocConnection() simply allocates an instance of CONNECTION and
- returns it. This completes the implemetation of RmxCreate(). Although not
- necessarily entirely obvious this arrangement allows RmxCreate() to be called
- multiple times using the same port number, yet the actual socket is created
- only once, on subsequent calls only its use-count is increased.
-
- ULONG RmxCreateUnique(ULONG* pulSize,
- PSZ pszName,
- HCONNECTION* phConn);
-
- RmxCreateUnique() is implemented exactly like RmxCreate() except that the port
- number provided is zero. That will then cause the system to allocate some
- unique port.
-
- CreateConnection(0, phConn);
-
- PCONNECTION
- pconn = (PCONNECTION) *phConn;
-
- sprintf(pszPort, "%d", ntohs(pconn->sharedSocket->port));
-
- ULONG RmxOpen(PCSZ pcszHost, PCSZ pcszPort, HCONNECTION*
- hConn);
-
- As the host and port names are expressed textually they must be converted into
- integers before we can use them. The port is simply converted using atoi(),
- the host needs a little bit more attention.
-
- int
- port = atoi(pcszPort);
-
- First we check whether the host has been specified in ordinary dotted decimal
- notation (e.g. 192.26.110.20).
-
- unsigned long
- address = 0;
-
- if (pcszHost)
- {
- address = inet_addr((PSZ) pcszHost);
-
- if (address == (unsigned long) -1)
- {
-
- If it hasn't we attempt to look it up from the hosts file, or from a nameserver
- if present (handled automatically by gethostbyname()).
-
- hostent
- *host = gethostbyname((PSZ) pcszHost);
-
- address = *(unsigned long*) host->h_addr;
- }
- }
-
- When we have the complete address, we can create and connect the socket. First
- we need the actual socket handle.
-
- int
- socket = CreateSocket();
-
- Just like when we were creating the connection this socket cannot be used for
- anything. It has to be connected to a real address. We use the port and
- address we just figured out. Again the port must be converted into network
- format. The address is in network format already when it is returned by
- inet_addr() or gethostbyname().
-
- sockaddr_in
- sin;
-
- sin.sin_family = AF_INET;
- sin.sin_port = htons((unsigned short) port);
- sin.sin_addr.s_addr = address;
-
- ::connect(socket, (sockaddr*) &sin,
- sizeof(sockaddr_in));
-
- The only thing left is to allocate the connection handle.
-
- PCONNECTION
- pconn = AllocConnection();
-
- pconn->privateSocket = socket;
-
- *phConn = pconn;
-
- As the memory returned by AllocConnection() is set to 0, pconn->sharedSocket
- will also be NULL.
-
- ULONG RmxClose(HCONNECTION hConn);
-
- RmxClose() is used by both the client and the server to close a connection.
- The client will never have a shared socket, and the server should not -
- provided it has first (as it ought to) called RmxDisConnect() - have a private
- socket.
-
- PCONNECTION
- pconn = (PCONNECTION) hConn;
-
- if (pconn->sharedSocket)
- ReleaseSOCKET(pconn->sharedSocket);
-
- int
- socket = pconn->privateSocket;
-
- DosFreeMem(pconn);
-
- if (socket)
- ::soclose(socket);
-
- ReleaseSOCKET() decrements the use count of the shared socket, and if it
- reaches 0, it closes the real socket and deletes the SOCKET instance.
-
- ULONG RmxConnect(HCONNECTION hConn);
-
- This function can be directly mapped to the socket call accept(). Accept()
- blocks the caller until a client opens a connection.
-
- PCONNECTION
- pconn = (PCONNECTION) hConn;
-
- int
- socket = ::accept(pconn->sharedSocket->socket, 0, 0);
-
- pconn->privateSocket = socket;
-
- Accept() returns a unique socket handle that can be used for communicating with
- the client that opened the connection
-
- ULONG RmxDisConnect(HCONNECTION hConn);
-
- RmxDisConnect() is called by the server to close a specific client connection.
-
- PCONNECTION
- pconn = (PCONNECTION) hConn;
-
- ::soclose(pconn->privateSocket);
- pconn->privateSocket = 0;
-
- The shared socket is still there, so we can use the same connection handle for
- servicing a new client by calling RmxConnect().
-
- ULONG RmxWrite(HCONNECTION hConn, PCBYTE pbBuffer, ULONG ulBytesToWrite);
- ULONG RmxRead(HCONNECTION hConn, PBYTE pbBuffer, ULONG ulSize, ULONG*
- pulBytesRead);
-
- The socket implementation of RmxRead() and RmxWrite() is almost identical to
- the named pipe implementation. The main difference is that instead of using
- DosRead() and DosWrite() we use recv() and send().
-
- RMX-OS2: An In-Depth View (Part 2) - EDM/2 - Feb 1995 - Volume 3, Issue 2
-
-
- ΓòÉΓòÉΓòÉ 4.8. RMXCOMMS.DLL ΓòÉΓòÉΓòÉ
-
- RMXCOMMS.DLL
-
- If you look at the makefiles of RMXPIPE.DLL and RMXTCPIP.DLL you'll see that I
- generate no import library from the DLLs. That is on purpose. The way I see
- it, these two DLLS are only an implementation of an API. Any program that uses
- the API should be able to use another DLL that implements the same API using
- another communications mechanism.
-
- If an application is linked with an import library generated from RMXPIPE.DLL,
- for example, then that application is forever married to the DLL. If the
- application should use another DLL it must be relinked with an import library
- generated from the other DLL.
-
- If the application is instead linked with RMXCOMMS.LIB which is generated from
- RMXCOMMS.DLL, the application need never be relinked in order to use a
- particular implementation of the RMX communications API.
-
- RMXCOMMS.DLL provides exactly the same interface as RMXPIPE.DLL and
- RMXTCPIP.DLL, and is in fact built using the same header as they are. Its
- implementation is, however, quite different.
-
- First a few types are defined: PPFN is a pointer to a function pointer and
- Function is structure holding an ordinal and a pointer to a function pointer.
-
- typedef PFN* PPFN;
-
- struct Function
- {
- ULONG ulOrdinal;
- PPFN ppfnAddress;
- };
-
- Then a number of module variables. As you notice there is one function pointer
- for each function defined by the RMX communications API. The addresses of the
- function pointers are stored, together with the corresponding ordinal, in an
- array of function structures.
-
- static HMODULE hmodRmxComms;
- static ULONG RMXENTRY (*rmxClose)(HCONNECTION);
- static ULONG RMXENTRY (*rmxConnect)(HCONNECTION);
- static ULONG RMXENTRY (*rmxCreate)(PCSZ, HCONNECTION*);
- static ULONG RMXENTRY (*rmxCreateUnique)(ULONG*, PSZ, HCONNECTION*);
- static ULONG RMXENTRY (*rmxDisConnect)(HCONNECTION);
- static ULONG RMXENTRY (*rmxGetServiceName)(PCSZ, ULONG*, PSZ);
- static ULONG RMXENTRY (*rmxOpen)(PCSZ, PCSZ, HCONNECTION*);
- static ULONG RMXENTRY (*rmxRead)(HCONNECTION, PBYTE, ULONG, ULONG*);
- static ULONG RMXENTRY (*rmxWrite)(HCONNECTION, PCBYTE, ULONG);
- static Function afFunctions[] =
- {
- { ORD_RMXCLOSE, (PPFN) &rmxClose },
- { ORD_RMXCONNECT, (PPFN) &rmxConnect },
- { ORD_RMXCREATE, (PPFN) &rmxCreate },
- { ORD_RMXCREATEUNIQUE, (PPFN) &rmxCreateUnique },
- { ORD_RMXDISCONNECT, (PPFN) &rmxDisConnect },
- { ORD_RMXGETSERVICENAME, (PPFN) &rmxGetServiceName },
- { ORD_RMXOPEN, (PPFN) &rmxOpen },
- { ORD_RMXREAD, (PPFN) &rmxRead },
- { ORD_RMXWRITE, (PPFN) &rmxWrite }
- };
-
- const ULONG cFunctions = sizeof(afFunctions)/sizeof(Function);
-
- Let's then look at the DLL initialization routine of RMXCOMMS.DLL. The first
- thing is to query the value of the environment variable RMXCOMMS.
-
- PCSZ
- pcszRmxComms;
-
- if (DosScanEnv("RMXCOMMS", &pcszRmxComms) != NO_ERROR)
- return 0;
-
- The value of RMXCOMMS is assumed to be the name of the actual communications
- DLL to use.
-
- if (DosLoadModule(0, 0, pcszRmxComms, &hmodRmxComms) != NO_ERROR)
- return 0;
-
- Finally the functions can be resolved.
-
- for (int i = 0; i < cFunctions; i++)
- {
- ULONG
- ulOrdinal = afFunctions[i].ulOrdinal;
- PPFN
- ppfnAddress = afFunctions[i].ppfnAddress;
-
- ULONG
- rc = DosQueryProcAddr(hmodRmxComms, ulOrdinal, 0, ppfnAddress);
-
- if (rc != NO_ERROR)
- return 0;
- }
-
- This means in practice that once the DLL has successfully been loaded, the
- function pointers will point at valid functions. Now each exported function
- can simply be implemented in the following fashion.
-
- ULONG RMXENTRY RmxCreate(PCSZ pszName, HCONNECTION* phConn)
- {
- return rmxCreate(pszName, phConn);
- }
-
- Example
-
- Suppose we have an application CLIENT.EXE that has been linked with
- RMXCOMMS.LIB which was generated from RMXCOMMS.DLL. And suppose that there
- exists the proper communications DLLs RMXPIPE.DLL, RMXTCPIP.DLL and RMXIPX.DLL.
- Then the following three client processes will all use different DLLS.
-
- [C:\]set RMXCOMMS=RMXPIPE
- [C:\]start client.exe
- [C:\]set RMXCOMMS=RMXTCPIP
- [C:\]start client.exe
- [C:\]set RMXCOMMS=RMXIPX
- [C:\]start client.exe
-
- RMX-OS2: An In-Depth View (Part 2) - EDM/2 - Feb 1995 - Volume 3, Issue 2
-
-
- ΓòÉΓòÉΓòÉ 4.9. Sample Programs ΓòÉΓòÉΓòÉ
-
- Sample Programs
-
- Along with this issue of EDM/2 you should get RMXCOMMS.ZIP that contains
- RMXCOMMS.DLL, RMXPIPE.DLL and RMXTCPIP.DLL, and the test/demo programs
- SERVER.EXE, ENGINE.EXE and CLIENT.EXE, along with all the source. In order to
- try them out, you should place the DLLs in some directory along the LIBPATH.
- Remember to place the file RMXPIPE.DAT in the same directory as RMXPIPE.DLL,
- and remember to update the \tcpip\etc\services file if you intend to use
- RMXTCPIP.DLL. SERVER.EXE and ENGINE.EXE should be in the same directory.
-
- If you want to run the server/engine pair and the client in separate computers
- you'll need a network. The DLLs must of course be present in both computers.
-
- Starting the server
-
- Launch a windowed command prompt and go to the directory where SERVER.EXE
- resides.
-
- [C:\RMX\DEMO]set RMXCOMMS=RMXPIPE
- [C:\RMX\DEMO]server
-
- The server prints a notification message when it starts.
-
- Starting the client
-
- The client requires a few command line parameters in order to start. If it is
- started with the wrong parameters it prints:
-
- usage: client [-t #] ([-h host]|-e engine) [-v]
-
- -t # : Number of threads
- -h host : The name of the host
- -e engine : Use this as engine connection
- -v verbose: More chit chat
-
- Note the blank between the flag and the value
-
- example: client -t 5 -h \\ODIN
- client -v
-
- The -t flags specifies how many threads the client should use. As default it
- uses 5. The flag -h specifies the host and -v turns on verbose mode
- (recommended). If you are running locally don't specify a host,
-
- [C:\RMX\DEMO]set RMXCOMMS=RMXPIPE
- [C:\RMX\DEMO]client -v
-
- otherwise the argument of the flag should be the name of the computer where the
- server is running. The host name varies of course depending on which
- communications DLL you are using. In my environment I use:
-
- [C:\RMX\DEMO]set RMXCOMMS=RMXPIPE
- [C:\RMX\DEMO]client -h \\loke -v
-
- with named pipes and
-
- [C:\RMX\DEMO]set RMXCOMMS=RMXTCPIP
- [C:\RMX\DMEO]client -h loke -v
-
- or
-
- [C:\RMX\DEMO]set RMXCOMMS=RMXTCPIP
- [C:\RMX\DMEO]client -h 192.26.110.21 -v
-
- with TCP/IP.
-
- When started the client opens a connection to the server and asks it to start
- an engine, the server starts the engine that creates a connection whose name is
- transferred back to the client. The client opens a new connection to the
- engine and starts sending messages of varying size to the engine that
- subsequently sends them unaltered back. The client then verifies that there
- has been no changes in the message.
-
- RMX-OS2: An In-Depth View (Part 2) - EDM/2 - Feb 1995 - Volume 3, Issue 2
-
-
- ΓòÉΓòÉΓòÉ 4.10. Compiling the source ΓòÉΓòÉΓòÉ
-
- Compiling the source
-
- I've written the code using Borland C++ 1.5 so if you have that compiler you
- should be able - perhaps with a little tinkering - to build the DLLs and the
- applications. If you have another compiler you'll probably have to make more
- modifications. If you do make changes I'd appreciate if you would mail them to
- me so I can include them in the "official" version.
-
- A word of caution
-
- I've been using the RMXPIPE.DLL implementation for quite some time while I've
- been developing RMX and I have not experienced any problems. However, now when
- I tested the programs for this article, the client occasionally received an
- error code (ERROR_REQ_NOT_ACCEP to be exact) when it tried to send data to the
- server. I havn't experienced any problems at all with RMXTCPIP.DLL so I blame
- the named pipe implementation of OS/2. <grin>
-
- RMX-OS2: An In-Depth View (Part 2) - EDM/2 - Feb 1995 - Volume 3, Issue 2
-
-
- ΓòÉΓòÉΓòÉ 4.11. Implementing RPCs ΓòÉΓòÉΓòÉ
-
- Implementing RPCs
-
- Even if this API hides quite a few of the complex details of client-server
- programming, it is still quite tedious to work with. Fortunately it can be
- made easier. This article is long as it is so I won't go into any details but
- I'll show you how WinInitialize is implemented in RMX. I might return to this
- subject in a later article. C++ templates can make the world a better place to
- program in...
-
- Client
-
- HAB APIENTRY WinInitialize(ULONG flOptions)
- {
- ClientConnection
- &c = ClientConnection::connection();
- Packet
- &p = c.freshPacket();
-
- p << RMX_OS2PMWIN;
- p << (ORDINAL) ORD_WIN32INITIALIZE;
- p << flOptions;
-
- c.sendReceive();
-
- HAB
- hab;
-
- p >> hab;
-
- return hab;
- }
-
- Server
-
- void PMWin::winInitialize(Packet& p)
- {
- ULONG
- flOptions;
-
- p >> flOptions;
-
- HAB
- hab = WinInitialize(flOptions);
-
- p.reply() << hab;
- }
-
- RMX-OS2: An In-Depth View (Part 2) - EDM/2 - Feb 1995 - Volume 3, Issue 2
-
-
- ΓòÉΓòÉΓòÉ 4.12. Conclusion ΓòÉΓòÉΓòÉ
-
- Conclusion
-
- So, that was the low level transfer mechanism of RMX. Feel free to use it in
- any way you see fit and don't hesitate to mail me if there is something you
- wonder about. The next article will be about how to mark an application for
- use with RMX.
-
- RMX-OS2: An In-Depth View (Part 2) - EDM/2 - Feb 1995 - Volume 3, Issue 2
-
-
- ΓòÉΓòÉΓòÉ 5. /dev/EDM/BookReview ΓòÉΓòÉΓòÉ
-
-
- ΓòÉΓòÉΓòÉ 5.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.
-
- OS/2 Programming is an introductory book to PM programming. It was released
- just around the same time as OS/2 2.1, so it is slightly out of date, like
- almost all PM books at the moment.
-
- /dev/EDM2/BookReview - EDM/2 - Feb 1995 - Volume 3, Issue 2
-
-
- ΓòÉΓòÉΓòÉ 5.2. Errata ΓòÉΓòÉΓòÉ
-
- Errata
-
- Well, I have now been working on my POVEd program (an editor tailored to
- POV-Ray) for some time, and have picked up a basic proficiency, and a few
- intermediate techniques, so I now consider myself an intermediate PM C
- programmer. I still have loads to learn, but I won't be stumbling over really
- basic concepts any more.
-
- /dev/EDM2/BookReview - EDM/2 - Feb 1995 - Volume 3, Issue 2
-
-
- ΓòÉΓòÉΓòÉ 5.3. OS/2 Programming - Your Fast-Track Guide to OS/2 ΓòÉΓòÉΓòÉ
-
- OS/2 Programming - Your Fast-Track Guide to OS/2
-
- This book is aimed at the intermediate skill level, it says on the back. Does
- that mean intermediate programmers, intermediate C programmers, intermediate
- OS/2 users, or what? A little clarification would be helpful for the buyer.
- In any case, I would rank it as a beginning OS/2 PM C programmer book. Here
- are the chapters:
-
- Part One: Introduction to OS/2 Programming
-
- 1. OS/2: An Overview
- 2. Fundamentals of OS/2 Programming
-
- Part Two: Programming the Presentation Manager
-
- 3. Presentation Manager Programming Overview
- 4. Processing Messages
- 5. Message Boxes and Menus
- 6. Dialog Boxes
- 7. Control Windows
- 8. Icons and Graphics
-
- Part Three: Exploring the API
-
- 9. An Introduction to Multitasking
- 10. Serialization and Inter-Process Communication
- 11. File I/O
- 12. Creating and Using Dynamic Link Libraries
-
- Index
-
- The layout of the book is quite good, although I think that I prefer the
- non-PM stuff at the beginning, not at the end. That way, the sample programs
- can include these features where appropriate. Having said that, this layout
- works too.
-
- Chapters 1 and 2 are fairly straight forward, and there really isn't much to
- say, other than the fact that the introduction is fairly comprehensive in
- explaining what a PC is, what OS/2 is, and a bit about the history of both.
- The OS/2 fundamentals chapter goes through the OS/2 C programming environment
- a bit, and explains how the header files work, what a DEF file is, what C
- prototypes are, how the OS/2 API is broken down, and a few other minor
- background issues. There is no real detail here, just a brief overview.
-
- Section two moves into actual programming, and this is done in the classical
- way of explaining mouse, windows, message queue, and so on, and then giving a
- skeleton program. This book concentrates on CSet++, by the way, so Watcom and
- Borland users are on their own here. Unfortunately, I am not crazy about the
- variable naming style that the authors use (e.g. hand_ab for the
- application's HAB), but that is a question of taste. The skeleton is
- explained, and so it goes. A plus here is the explanation of the DEF file,
- and what it does for you. This explanation is concise and clear, and I now
- know what a DEF file is for <grin> The next chapter deals with messages, and
- the macros are presented for converting MPARAMs. The device context and the
- presentation space are loosely explained, and a few GPI functions and
- constants are introduced to allow colored text to be used. Key presses are
- explained, and how to catch them in the message loop. Mouse messages are
- briefly introduced as well. Finally, a custom message is shown implemented.
- All fairly brief, and well written.
-
- Chapter five concentrates on message boxes and menus. It is nice to see the
- message box covered well, because as you get better, they are excellent for
- quick-and-dirty debugging, and they are also very useful when you are just
- beginning to flesh out the interface of a larger application. At least, if
- you attach message boxes to everything, you know that the gears of the
- interface are working. Menus are, of course, the most used part of the PM
- interface, and deserve a good treatment. They get a good treatment here,
- including how to make accelerator keys work with them.
-
- Chapter 6 introduces dialog boxes. They are presented mainly as an
- information gathering tool, in accordance with the CUA guidelines. Some
- fairly predictable examples are given, but no great amount of writing is spent
- on this.
-
- Control windows are next. Unfortunately, and in accordance with the
- apparently pervasive OS/2 programming book authors' philosophy, only a few are
- shown in detail, and the rest are skipped over lightly. The two that are
- given more than just a cursory example are list boxes and sliders. Whether
- they represent all the others well is up to the reader to decide.
-
- Icons and graphics form the next stop along the bumpy route of learning the PM
- API. How to use the system defined icons and pointers is shown, followed by
- how to define your own, and use them instead. Again, good programs, but a
- little short.
-
- The final section is called "Exploring the API", but perhaps this section
- might be called "Kernel Programming" more accurately, since only non-PM calls
- are demonstrated in this section. Threads and sessions are each given a
- decent introduction, along with how to wait for something the right way.
-
- It is only natural to introduce concurrency issues after waiting has been
- mentioned, and so the next chapter discusses semaphores, and critical
- sections. The explanations are lucid, and good, but again the programs are a
- little on the skinny and functionally challenged side. IPC is also introduced
- in this chapter, with explanations and examples of shared memory and pipes.
-
- File input and output is briefly treated in chapter eleven, along with a brief
- introduction to system information, and how to obtain it.
-
- The last chapter is on DLLs, and as you must expect by now, it is merely an
- introduction. It shows how to put a function into a DLL, and not much more.
-
- /dev/EDM2/BookReview - EDM/2 - Feb 1995 - Volume 3, Issue 2
-
-
- ΓòÉΓòÉΓòÉ 5.4. Summary ΓòÉΓòÉΓòÉ
-
- Summary
-
- This book is quite nice, and a good introduction to PM programming. Its main
- difficulty is the fact that it is slimmer than the competition. There are
- several other good books out there, which cover more, or cover the same in more
- detail, and which cost about the same. If this book were priced around the
- US$20-25 mark, it would be a good bargain. In spite of this, I quite like it.
- The code and commenting style isn't quite what I prefer, but the code examples
- are good, and are built up well. The choice of material is good, and the
- sequence of the material is good. It is a nice, easy introduction to the PM
- API, and has some nice tips and tricks. The motivation of the material is
- better than average.
-
- I would like to see better coverage of the various control windows, but this
- accusation could be leveled at most introductory PM books.
-
- I must say that I was a bit shocked to find that the book had no disk, and that
- the disk would cost US$24.95! For this price, a book should include a disk,
- and most do.
-
- Overall, quite a nice book, but I have a feeling that most people will skip it,
- and pick up one of the other, larger books for about the same price. That is a
- shame, because this book has a lot to offer in the way of explanations. It
- really does need to come with the disk for free, though.
-
- /dev/EDM2/BookReview - EDM/2 - Feb 1995 - Volume 3, Issue 2
-
-
- ΓòÉΓòÉΓòÉ 5.5. 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 - Feb 1995 - Volume 3, Issue 2
-
-
- ΓòÉΓòÉΓòÉ 5.6. 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 - Feb 1995 - Volume 3, Issue 2
-
-
- ΓòÉΓòÉΓòÉ 5.7. 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 looking at 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 Edition, 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.
-
- /dev/EDM2/BookReview - EDM/2 - Feb 1995 - Volume 3, Issue 2
-
-
- ΓòÉΓòÉΓòÉ 6. OOPS Avenue ΓòÉΓòÉΓòÉ
-
-
- ΓòÉΓòÉΓòÉ 6.1. Introduction ΓòÉΓòÉΓòÉ
-
- OOPS Avenue
-
- Written by Gordon Zeglinski
-
- A Look at What's New in SOM 2.1
-
- I finally got the SOM 2.1 toolkit. In this issue, we'll look at its new
- features and then compare its bench mark results with those of SOM 2.0.
-
- Before we move on, let's look at some miscellaneous details. I strongly
- recommend that anyone installing SOM 2.1 over SOM 2.0 edit their CONFIG.SYS
- files and then reboot OS/2 to disable the 2.0 toolkit before installing. This
- will make installation much simpler and helps insure it works the first time.
- The installation is pretty much the same as that of the 2.0 toolkit. Outside
- of a small install manual, the toolkit no longer contains any printed
- documentation. The CD contains a set of PostScript files one can print to make
- manuals, and some FrameView files. This is where I get really annoyed at IBM.
- They only ship a Windows and AIX version of the on-line documentation viewer!
- This means that you have to run WIN-OS/2. Thus, losing all the resources it
- uses while running the memory hungry development tools. This quickly makes 20
- megs of RAM look pretty small.
-
- OOPS Avenue - EDM/2 - Feb 1995 - Volume 3, Issue 2
-
-
- ΓòÉΓòÉΓòÉ 6.2. What's New in SOM 2.1 ΓòÉΓòÉΓòÉ
-
- What's New in SOM 2.1
-
- There are many new features in the 2.1 toolkit. Overall I think a fair summary
- would be saying it's more C++ like but still retains all of the OOP abilities
- from the 2.0 version. Direct To SOM (DTS) C++ compiler support is now included
- along with support for the 32 bit TCP/IP stack available in TCP/IP 2.0 for OS/2
- and the Internet Access Kit in Warp. The use of somInit and somUninit is no
- longer recommended. Instead, a more C++ like method of doing initializers is
- recommended. The methods somDefaultInit, somDefaultConstCopyInit and
- somDestruct have been added. These methods correspond to the default
- constructor, copy constructor and destructor in C++. In addition there's now
- support for an assignment method. The somDefaultConstAssign method provides
- this mechanism and is equivalent to the operator = in C++.
-
- In our previous SOM articles, metaclasses were used to provide class data and
- constructors. Recall that class data is analogous to static data in C++. SOM
- 2.1 now supports staticdata variables. When this is combined with the new
- method of specifying constructors/initializers, metaclasses are rarely needed
- now. The modifiers somallocate and somdeallocate allow user defined memory
- allocation and deallocation functions to be used when creating an instance of
- the object. This is similar to overloading the new and delete operators in
- C++. This concludes our brief look at what's new in SOM 2.1. I haven't
- covered any of the new features in DSOM because we haven't previous looked at
- DSOM.
-
- OOPS Avenue - EDM/2 - Feb 1995 - Volume 3, Issue 2
-
-
- ΓòÉΓòÉΓòÉ 6.3. Benchmarking SOM 2.1 ΓòÉΓòÉΓòÉ
-
- Benchmarking SOM 2.1
-
- The SOM 2.1 documentation claims that SOM 2.1 is faster than SOM 2.0. In this
- section, we'll present the revamped SOM bench mark code and compare the results
- of running the SOM 2.1 benchmark with those previous obtained for SOM 2.0 and
- C++.
-
- Let's start by looking at the new SOMBENCH.IDL file.
-
- #ifndef __SOMBENCH_h__
- #define __SOMBENCH_h__
-
- #include <somobj.idl>
-
- //-----------------------------------
- // SOMfoo
- // Our SOM based test object
- //-----------------------------------
-
-
- interface SOMFoo : SOMObject
- {
-
- attribute long Count; // Stores the Count Data
-
- void IncrementCount(); //procedure
- void OffIncrementCount(); //offset
- void LookIncrementCount(); //lookup
-
-
- #ifdef __SOMIDL__
- implementation
- {
- releaseorder: _get_Count,_set_Count,IncrementCount,\
- OffIncrementCount,LookIncrementCount;
-
- IncrementCount: procedure;
- LookIncrementCount:namelookup;
-
- callstyle=oidl;
- filestem = sombench;
- somDefaultInit: override; // initialize instance
- variables to 0
- somDestruct:override; // just for fun
-
- };
- #endif /* __SOMIDL__ */
- };
-
- #endif /* __SOMBENCH_h__ */
-
- There's no major difference between this version and the previous version of
- sombench. In fact, the only difference is that somInit and somUnitit have been
- replaced by somDefaultInit and somDestruct. Even though the definition isn't
- much different, the bench mark results are different. The following table
- shows the results of the benchmarks.
-
- ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
- ΓöéResolution Type ΓöéC++ ΓöéSOM 2.0 ΓöéSOM 2.1 Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- Γöéprocedure Γöé219 Γöé656 Γöé844 Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- Γöévirtual/offset Γöé281 Γöé719 Γöé1000 Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- Γöéname lookup macro Γöén/a Γöé22031 Γöé1031 Γöé
- Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
- Γöéname lookup explicitΓöén/a Γöé175031 Γöé130062 Γöé
- ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
-
- Table 1. Comparison of call type overhead between C++, SOM 2.0 and SOM 2.1
-
- The times indicated are in milliseconds for 1000000 calls to a given function.
- The results for SOM 2.1 show that there are some speed increases and some
- decreases. In particular, the name lookup resolution method shows dramatic
- speed improvements.
-
- OOPS Avenue - EDM/2 - Feb 1995 - Volume 3, Issue 2
-
-
- ΓòÉΓòÉΓòÉ 6.4. Profile Files Revisited ΓòÉΓòÉΓòÉ
-
- Profile Files Revisited
-
- To explore the new initialization methods, we will once again look at the "ini
- file" manipulation object. The definition for the revised SOM_CPPini file
- follows.
-
- #ifndef __SOMCPP_idl__
- #define __SOMCPP_idl__
-
- #include <somcls.idl>
-
- //-----------------------------------
- // SOMfoo
- // Our SOM based test object
- //-----------------------------------
-
- interface ProfileFile;
-
- interface SOM_CPPini : SOMObject
- {
- typedef unsigned long ULONG;
- typedef sequence<char> buffType;
-
-
- void OpenFile(in string FileName);
- ULONG GetError();
-
- unsigned long GetHandle();
- void SetHandle(in unsigned long H);
-
- void WriteString(in string App,in string Key, in string String);
- // write a string
-
- void WriteData(in string App, in string Key,in buffType Buff,
- in ULONG BLen); // write the Buffer
-
- void WriteInt(in string App,in string Key,in long Num);
- // write an "int"
-
-
-
- ULONG GetDataSize(in string App,in string Key);
- // return the lenght of the data
-
- void GetData(in string App,in string Key,in buffType Buff,
- inout ULONG BufMax); // return the data in Buff
-
- ULONG GetString(in string App,in string Key,in buffType Buff,
- in ULONG BufMax, in string DefArg);
- // return the String in Buff, defaults to DefArg if App/Key not
- // present
-
- ULONG GetStringNoDef(in string App,in string Key, in buffType Buff,
- in LONG BufMax); // return the String in Buff
-
- long GetInt(in string App,in string Key,in long DefArg);
- // return the "int" in App/Key or the DefArg if App/Key
- // not present
-
- long GetIntNoDef(in string App,in string Key);
- // return the "int" in App/Key
-
- void ProfileFileFromName(inout somInitCtrl ctrl, in string name);
- void ProfileFileFromHandle(inout somInitCtrl ctrl, in ULONG H);
-
- #ifdef __SOMIDL__
- implementation
- {
-
- ProfileFile INIFile;
-
- releaseorder: GetHandle,SetHandle,GetError,OpenFile, \
- WriteString,WriteData,WriteInt,GetDataSize,GetData, \
- GetString,GetStringNoDef,GetInt,GetIntNoDef, \
- ProfileFileFromName,ProfileFileFromHandle;
-
- callstyle=oidl;
- filestem = somcpp;
- somDefaultInit: override; // initialize instance variables to 0
- somDestruct:override; // just for fun
- ProfileFileFromHandle:init;
- ProfileFileFromName:init;
-
- passthru C_xh_after= "#define INCL_WINSHELLDATA" \
- "#include <os2.h>" \
- "#include \"iniflobj.h\"";
-
- };
- #endif /* __SOMIDL__ */
- };
-
- #endif /* __SOMBENCH_h__ */
-
- Additions to the interface definition are marked in bold. The revised
- definition has the functions ProfileFileFromName and ProfileFileFromHandle
- declared as initializers. This allows us to use a more C++ like method of
- creating an instance of SOM_CPPini. Following is the revised main routine for
- the object test file.
-
- #include "somcpp.xh"
- #include <stdio.h>
-
- void main(){
- int i;
-
- SOM_CPPini *Prf=new SOM_CPPini("test.ini");
-
- Prf->WriteInt("App","Key",10);
- i=Prf->GetIntNoDef("App","Key");
-
- printf("i= %i\r\n",i);
-
- delete Prf;
- }
-
- The new SOM_CPPini("test.ini") operation will create an instance of the SOM
- object SOM_CPPini and then call the initializer ProfileFileFromName. The SOM
- compiler will map each SOM initializer on to a C++ constructor provided the
- arguments to the SOM methods are different. Basically, this is the same rule
- that is imposed on C++ constructors; no two constructors can have the same
- arguments.
-
- OOPS Avenue - EDM/2 - Feb 1995 - Volume 3, Issue 2
-
-
- ΓòÉΓòÉΓòÉ 6.5. Wrapping Things Up ΓòÉΓòÉΓòÉ
-
- Wrapping Things Up
-
- We now come to the end of our peek at SOM 2.1. The executables, which should
- run under WARP, and source code to these examples is included with this issue.
- We have briefly looked at the new features in SOM 2.1, benchmarked SOM 2.1
- using the simple test code, and then explored the new methods used to
- initialize SOM objects. We have seen that many of the new features allow SOM
- objects to be more C++ like.
-
- In the next issue, I hope to start looking at DSOM. I think DSOM/COBRA have
- the potential to radically alter the way client/server programming is done.
- For this reason, and my on going experience with programming INTERcomm and
- AdeptXBBS, the look at DSOM will be heavily skewed towards client/server type
- scenarios.
-
- OOPS Avenue - EDM/2 - Feb 1995 - Volume 3, Issue 2
-
-
- ΓòÉΓòÉΓòÉ 7. Introduction to PM Programming ΓòÉΓòÉΓòÉ
-
-
- ΓòÉΓòÉΓòÉ 7.1. Introduction ΓòÉΓòÉΓòÉ
-
- Introduction to PM Programming
-
- Written by Larry Salomon, Jr.
-
- Introduction
-
- The purpose of this column is to provide the readers out there who are not
- familiar with PM application development the information necessary to satisfy
- their curiosity, educate themselves, and give them an advantage over the
- documentation supplied by IBM. Of course, much of this stuff could probably be
- found in one of the many books out there, but the problem with books in general
- is that they don't answer the questions you have after you read the book the
- first time through.
-
- I will gladly entertain feedback from the readers about what was "glossed over"
- or what was detailed well, what tangential topics need to be covered and what
- superfluous crap should have been removed. This feedback is essential in
- guaranteeing that you get what you pay for. <grin>
-
- It should be said that you must not depend solely on this column to teach you
- how to develop PM applications; instead, this should be viewed as a supplement
- to your other information storehouses (books, the network conferences, etc.).
- Because this column must take a general approach, there will be some topics
- that you would like to see discussed that really do not belong here. Specific
- questions can be directed to me via email and I will do my best to answer them
- in a timely fashion.
-
- Last Month
-
- Last month, we finished the WC_LISTBOX class and I asked for feedback. I did
- receive some, for which I am very grateful, but the majority of them were
- requests for advanced topics, like the container and the notebook controls. We
- will eventually cover these window classes, but it is essential that we look at
- the rudimentary ones first, so that you can better appreciate what these
- advanced classes do for you.
-
- This month, we will look at the WC_TITLEBAR and WC_STATIC window classes.
- Since both of these classes are not too involving, covering them both should
- not be a problem.
-
- Introduction to PM Programming - EDM/2 - Feb 1995 - Volume 3, Issue 2
-
-
- ΓòÉΓòÉΓòÉ 7.2. The Titlebar ΓòÉΓòÉΓòÉ
-
- The Titlebar
-
- The titlebar class (WC_TITLEBAR) is usually not created directly, but is
- created for you by the frame window through the WinCreateStdWindow() call. It
- has two primary functions: 1) to display the text of the standard window to
- which it belongs, and 2) to act as a shortcut to two primary windowing
- functions, move and maximize/restore.
-
- The titlebar does accept two messages: TBM_SETHILITE and TBM_QUERYHILITE.
-
- TBM_SETHILITE
-
- This message sets the highlight state of the control.
-
- Parameters
-
- SHORT1FROMMP(mpParm1) - the new highlight state of the control.
-
- TRUE Draw the control in highlighted state.
- FALSE Draw the control in normal state.
-
- Returns
-
- SHORT1FROMMR() - success indicator.
-
- TRUE Successful completion.
- FALSE Error occurred.
-
- TBM_QUERYHILITE
-
- This message returns the highlight state of the control.
-
- Returns
-
- SHORT1FROMMR() - highlight state of the control.
-
- TRUE The control is drawn in highlighted state.
- FALSE The control is drawn in normal state.
-
- Highlight state is used to indicate that the window has the focus. Normally,
- you do not need to use these messages, since the frame control will use them
- on your behalf.
-
- To set or query the text displayed in the titlebar, use WinSetWindowText() and
- WinQueryWindowText(). Again, the frame makes this easy by setting or querying
- it for you whenever you set or query the frame window's text.
-
- For reference, it should be noted that, whenever the user attempts to move or
- maximize/restore the window using the titlebar, the titlebar sends its owner
- (the frame usually) a WM_QUERYTRACKINFO message. The owner can fill in this
- structure according to its needs to restrict movement or resizing as
- necessary. We will not discuss this further.
-
- That's all there is to the WC_TITLEBAR class.
-
- Introduction to PM Programming - EDM/2 - Feb 1995 - Volume 3, Issue 2
-
-
- ΓòÉΓòÉΓòÉ 7.3. The Static ΓòÉΓòÉΓòÉ
-
- The Static
-
- The static control is just that: static. It never changes. The point of the
- static control is to display text, rectangles, icons, bitmaps, etc. A table of
- the static styles is shown below:
-
- Style Results in
- SS_AUTOSIZE Automatically size to contain the text, icon, or
- bitmap
- SS_BITMAP Bitmap
- SS_BKGNDFRAME Unfilled rectangle painted with the background
- color
- SS_BKGNDRECT Filled rectangle painted with the background
- color
- SS_FGNDFRAME Unfilled rectangle painted with the foreground
- color
- SS_FGNDRECT Filled rectangle painted with the foreground
- color
- SS_GROUPBOX Groupbox
- SS_HALFTONERECT Filled rectangle painted with a halftoned color
- SS_HALFTONEFRAME Unfilled rectangle painted with a halftoned
- color
- SS_ICON Icon
- SS_SYSICON System icon
- SS_TEXT Text
-
- All but the first style are mutually exclusive. The first style can only be
- used with SS_TEXT, SS_BITMAP, SS_ICON, or SS_SYSICON. Additionally, if
- SS_TEXT is used, you may also or one of the constants from each of the
- following groups to align the text in a specific manner.
-
- Horizontal alignment
-
- Constant Alignment
- DT_LEFT Left aligned
- DT_CENTER Center aligned
- DT_RIGHT Right aligned
-
- Vertical alignment
-
- Constant Alignment
- DT_TOP Top aligned
- DT_VCENTER Center aligned
- DT_BOTTOM Bottom aligned
-
- Window text for SS_TEXT statics can be set and queried using the
- WinSetWindowText() and WinQueryWindowText() functions. They accept no
- messages, nor send any messages.
-
- Using SS_ICON, SS_BITMAP, and SS_SYSICON statics
-
- When creating one of these three types of static controls, the question arises
- about how one specifies the icon, bitmap, or system icon to display. The
- answer is in the window text. For the first two, the text must be in the form
- "#n" where n is the ASCII representation of the resource identifier of the
- icon or bitmap to be displayed. A noteworthy caveat is that the icon or
- bitmap must reside in the .EXE resources; this can create problems if you're
- writing a general purpose DLL that cannot make assumptions about the .EXE
- resources. For the last one, the number represents the ASCII representation
- of the appropriate SPTR_* constant corresponding to the system icon that you
- wish to be displayed.
-
- Opportunities for Mayhem
-
- Because the static control does nothing except "just sit there," there are
- plenty of opportunities to create quick-n- dirty solutions to problems by
- creating a static window and then immediately subclassing it with the intent
- of enhancing its behavior.
-
- Introduction to PM Programming - EDM/2 - Feb 1995 - Volume 3, Issue 2
-
-
- ΓòÉΓòÉΓòÉ 7.4. That's It! ΓòÉΓòÉΓòÉ
-
- That's It!
-
- That's it for this month. Next month, we'll begin looking at the WC_MENU
- window class and we will see how it has become a staple in the bag of tricks
- that every OS/2 programmer has. Again, I will gladly accept any feedback that
- you have regarding this column and its future.
-
- Introduction to PM Programming - EDM/2 - Feb 1995 - Volume 3, Issue 2
-
-
- ΓòÉΓòÉΓòÉ 8. Contributors to this Issue ΓòÉΓòÉΓòÉ
-
- Are You a Potential Author?
-
- We are always looking for (new) authors. If you have a topic about which you
- would like to write, send a brief description of the topic electronically to
- any of the editors, whose addresses are listed below, by the 15th of the month
- before the month in which your article will appear. This alerts us that you
- will be sending an article so that we can plan the issue layout accordingly.
- After you have done this, get the latest copy of the Article Submission
- Guidelines from ftp.cdrom.com in the /pub/os2/2_x/program/newsltr directory.
- (The file is artsub.zip.) The completed text of your article should be sent to
- us no later than five days prior to the last day of the month; any articles
- received after that time may be pushed to the next issue.
-
- The editors can be reached at the following email addresses:
-
- 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):
-
- Marc Mittelmeijer
- Larry Salomon, Jr.
- Eric Slaats
- Carsten Whimster
- Johan Wikman
- Gordon Zeglinski
- Network distributors
-
- Contributors - EDM/2 - Feb 1995 - Volume 3, Issue 2
-
-
- ΓòÉΓòÉΓòÉ 8.1. Marc Mittelmeijer ΓòÉΓòÉΓòÉ
-
- Marc Mittelmeijer
-
- Marc Mittelmeijer studied mathematics at the faculty Mathematics and
- Informatics at the Technical University of Eindhoven. Since 1984 Marc teaches
- mathematics and information technology at the Hogeschool Eindhoven, Faculteit
- Economie Sr Bestuurlijke informatiekunde. Besides teaching he is researching
- the behavior of neural networks in a financial environment.
-
- Marc can be reached at M.Mittelmeijer@fe.hse.nl
-
- Contributors - EDM/2 - Feb 1995 - Volume 3, Issue 2
-
-
- ΓòÉΓòÉΓòÉ 8.2. 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 - Feb 1995 - Volume 3, Issue 2
-
-
- ΓòÉΓòÉΓòÉ 8.3. Eric Slaats ΓòÉΓòÉΓòÉ
-
- Eric Slaats
-
- Eric Slaats teaches information technology at the Hogeschool Eindhoven,
- Faculteit Economie Sr Bestuurlijke Informatiekunde. Besides teaching he is
- researching the behavior of neural networks in a financial environment. He
- started programming OS/2 PM to build an interface for a neural network problem.
-
- Eric can be reached electronically via the internet at E.Slaats@fe.hse.nl
-
- Contributors - EDM/2 - Feb 1995 - Volume 3, Issue 2
-
-
- ΓòÉΓòÉΓòÉ 8.4. 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 - Feb 1995 - Volume 3, Issue 2
-
-
- ΓòÉΓòÉΓòÉ 8.5. 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 - Feb 1995 - Volume 3, Issue 2
-
-
- ΓòÉΓòÉΓòÉ 8.6. Gordon Zeglinski ΓòÉΓòÉΓòÉ
-
- Gordon Zeglinski
-
- Gordon Zeglinski is a freelance programmer/consultant who received his Master's
- degree in Mechanical Engineering with a thesis on C++ sparse matrix objects.
- He has been programming in C++ for 6 years and also has a strong background in
- FORTRAN. He started developing OS/2 applications with version 2.0 .
-
- His current projects include a client/server communications program that
- utilitizes OS/2's features which has entered beta testing. Additionally, he is
- involved in the development of a "real-time" automated vehicle based on OS/2
- and using C++ in which he does device driver development and designs the
- applications that comprise the control logic and user interface.
-
- He can be reached via the Internet at zeglins@cc.umanitoba.ca.
-
- Contributors - EDM/2 - Feb 1995 - Volume 3, Issue 2
-
-
- ΓòÉΓòÉΓòÉ 8.7. Network distributors ΓòÉΓòÉΓòÉ
-
- Network Distributors
-
- These people are part of our distribution system to provide EDM/2 on networks
- other than the Internet. Their help to provide access to this magazine for
- others is voluntary and we appreciate them a lot!
-
- 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 - Feb 1995 - Volume 3, Issue 2
-
-
- ΓòÉΓòÉΓòÉ 9. 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:
- - ftp.cdrom.com in the /pub/os2/2_x/program/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 - Feb 1995 - Volume 3, Issue 2