home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Professional
/
OS2PRO194.ISO
/
os2
/
info
/
prgramer
/
edmi
/
issue_5
/
edmi1_5.inf
(
.txt
)
< prev
next >
Wrap
OS/2 Help File
|
1993-10-06
|
136KB
|
3,632 lines
ΓòÉΓòÉΓòÉ 1. Title Page ΓòÉΓòÉΓòÉ
Welcome to EDM/2 - The Electronic OS/2 Developers Magazine!
Portions copyright (c) by Steve Luzynski, Larry Salomon Jr.
Volume 1, issue 5
Select this to go to the next section
ΓòÉΓòÉΓòÉ 2. Copyright Notice (and other Legal Stuff) ΓòÉΓòÉΓòÉ
The editors of this electronic magazine are Steve Luzynski and Larry Salomon,
Jr.
Portions of EDM/2 are copyrighted by the editors. This publication may be
freely distributed in electronic form provided that all parts are present in
their original unmodified form. A reasonable fee may be charged for the
physical act of distribution; no fee may be charged for the publication itself.
All articles are copyrighted by their authors. No part of any article may be
reproduced without permission from the original author.
Neither this publication nor the editors are affiliated with International
Business Machines Corporation.
OS/2 is a registered trademark of International Business Machines Corporation.
Other trademarks are property of their respective owners. Any mention of a
product in this publication does not constitute an endorsement or affiliation
unless specifically stated in the text.
Select this to go to the next section
ΓòÉΓòÉΓòÉ 3. From the Editors ΓòÉΓòÉΓòÉ
After a rather bumpy release of volume 1, issue 4, we have high hopes that
things will go a bit smoother in the future. Things that are on the board, so
to speak, are:
Subscription service
For the Internet-reachable community, we thought that it wouldn't be too
difficult to write an auto-mailer, especially since the Unix mail program will
accept everything necessary on the command line. Add a list of people to mail
to, bake for 30 minutes, and voila!
Unfortunately, things were not that easy; after a first attempt, a SunOS limit
on the number of processes was reached since mail spawns a copy of sendmail and
then exits. Tweeking the process somewhat (pun intended) yielded a
subscription mailer script that builds the mail header with all of the
addresses in it and calls sendmail once for each part of the file.
Having verified it with a small file, we have decided to test it for the first
time with this issue, so we are asking that you please check the (alphabetical)
list to see if your userid is on it. If it is and you did not receive the
magazine automagically, please send us email as soon as possible. Assuming all
goes well, an article will be posted to comp.os.os2.programmer.misc describing
the subscription process, so be patient. Of course, the magazine will still be
placed on the nets as before, but adding this service will allow us to keep a
more accurate size of our reader base as well as allow us to reach other
networks (like MCIMail) and BBS's with Internet mail links.
New look (somewhat)
Beauty is in the eyes of the beholder, so we've made a few changes to the
magazine format. Firstly, the Questions and Answers column is being revamped,
starting with a name change. It will now be known as the Scratch Patch and
will contain not only Questions and Answers, but also the following items:
o Snippet(s) of the Month
This is for those useful, modular, functions that have been written and found
indispensable by their authors. All submissions are requested (though not
required) to be one function whenever possible and thoroughly commented,
including a prologue which explains the purpose and the parameters.
o Documentation Chop Shop
This is for documentation-related submissions dealing with inaccuracies,
inconsistencies, and vaguries. All potential submitters should remember that
the goal here is to correct and clarify whenever possible; so, while pointing
out the "tar pit" is something to write about, how to grab the vine swinging
overhead is much more helpful.
o Want Ads
As in the past, we always look forward to your comments. Of particular
interest are the requests for specific topics. Since we do not pretend to
know everyone on the Internet, any topics that look appetizing or that have
received a number of requests will be noted in this section in hopes that
someone reading it will be masochistic enough to want to write an article on
one of them. Don't forget to read the Article Submission Guidelines!!!
Any submissions should be received by the editors by no later than the 25th of
the month. And as always, even though it seems redundant, please explicitly
state your permission for us to use what you have sent.
Secondly, for this and all subsequent issues, we will try to include a
hypertext link at the end of each section which will take you to the next
section, so you should only have to repeatedly press ENTER (with an occassional
TAB) to read the entire magazine.
Thirdly, an installation program will be included with each issue, starting
with this one. This Rexx/2 command file searches your entire desktop for a
folder called "EDM/2" and creates one on the desktop if it cannot find an
existing one. After doing this, it creates a shadow object within that folder
that is linked to the filename of the current issue as distributed in the zip
file (so if you change the name, it will not work). Finally, it sets the icon
of the file to a newspaper so that you can spot it in a crowd.
INSTALL.CMD makes the following assumptions:
o C: is the drive containing the desktop folder hierarchy
o The desktop root folder is named "d:\OS!2 2.x Desktop" where "x" is the
version of the operating system. Note that if you reset your desktop using
the Ctrl-Alt-F1 key combination, the system does not overwrite your existing
folder, meaning that the actual desktop folder has another name. To get
around this, you will have to edit the installation file (search for
WP_DESKTOP).
Miscellanea
Last month it seems that we forgot to include the CNR2.ZIP file that was
supposed to accompany the "Programming the Container Control - Part 2" article,
so we are including it in this issue's zip file. We apologize for the
inconvenience.
Also, I have been in contact with Gavin Baker, who was the "Introduction to PM"
columnist and he expressed a desire to continue writing for EDM/2.
Unfortunately, he was pressed for time and could not complete his column for
this issue, so we all will be looking forward to that in the next issue.
Finally, as an update, I have also been contacted by David Singer with whom I
was able to work out the necessary kinks to make EDM/2 available inside of IBM
on a regular basis. He will be placing the issues on his Gopher servers (one
for internal use and one that is reachable via the Internet). We are very
thankful for his "contribution to the cause" and hope that by his offer we can
expand our audience (and thus the number of potential authors). For more
information about reaching the Gopher servers, you can contact him at
singer@almaden.ibm.com.
Enjoy!
The Editors
Select this to go to the next section
ΓòÉΓòÉΓòÉ <hidden> List of Subscription Testers ΓòÉΓòÉΓòÉ
If you are listed below and did not receive the magazine this month via the
subscription mailer, please send mail to os2man@panix.com.
aaa@atl.hp.com menieuwp@cs.vu.nl
abaddon@camelot.bradley.edu mgrice@athena.mit.edu
acmmdj@gsusgi2.gsu.edu mmc@ehabitat.demon.co.uk
bab%se40@se01.wg2.waii.com morio@ma2s2.mathematik.uni-karlsruhe.de
bachww@motcid.rtsg.mot.com mstaedt@va-klaus.va.fh-ulm.de
beaucham@phy.ulaval.ca mullins@magnum.convex.com
benji@lise.unit.no pft@master10.zfe.siemens.de
bhenning@wimsey.com pleitner@cs.curtin.edu.au
bjorn@ludd.luth.se rcs58639@zach.fit.edu
c878109@id.dth.dk rdm@csn.org
chandoni@husc.harvard.edu rickw@umr.edu
coulman@skdad.usask.ca rik@sci.kun.nl
d2henan@dtek.chalmers.se rm3@stc06.ctd.ornl.gov
donsmith@vnet.ibm.com robert.mahoney@f347.n109.z1.fidonet.org
dradhak@unx.ucc.okstate.edu rodrigc@ecf.toronto.edu
duffy@theory.chem.ubc.ca roe2@midway.uchicago.edu
evanc@carbon.isis.org rpr@oce.nl
ghdai@vax1.umkc.edu satish.movva@uic.edu
gilbert@yalevm.ycc.yale.edu schaefer@calle2.e.open.de
gogol@diku.dk schrock@cps.msu.edu
gt7027c@prism.gatech.edu shawnmac@traider.ersys.edmonton.ab.ca
heederik@fwi.uva.nl slumos@peewee.cs.unlv.edu
hepner@gourami.nosc.mil soh3@midway.uchicago.edu
jarlehto@utu.fi spatel@cs.utexas.edu
jgarzik@pantera.atl.ga.us stephen.drye@synapse.org
jjs@iedv6.acd.com timur@seas.gwu.edu
jlauro@umich.edu visser@sci.kun.nl
jofried@fzi.de wayne@stidol.mtv.gtegsc.com
johnh@meaddata.com we44478@is1.bfu.vub.ac.be
kenton+@cmu.edu whitaker@kean.ucs.mun.ca
kevin@elvis.wicat.com wjw@eb.ele.tue.nl
kfischer@hurricane.seas.ucla.edu xtifr@netcom.com
ΓòÉΓòÉΓòÉ 4. This Issue's Features ΓòÉΓòÉΓòÉ
The following articles constitute this issue's features:
o Development of a New Window Class - Part 2
o The Help Manager and Online Documentation
o OS/2 Installable File Systems - Part 2
o Programming the Container Control - Part 3
o A Review of C++ Compilers
ΓòÉΓòÉΓòÉ 4.1. Development of a New Window Class - Part 2 ΓòÉΓòÉΓòÉ
Written by Larry Salomon, Jr.
Select this to go to the next section
ΓòÉΓòÉΓòÉ 4.1.1. Recapitulation and Regurgitation ΓòÉΓòÉΓòÉ
0 to 60 MPH in 1 Paragraph
Last issue we discussed the functional and design considerations for the
development of a loose-leaf paper control. Decided was the control that the
application programmer should be able to exert on our window class through
messages and the notifications that should be sent for different types of
actions. Also, it was determined that window words would be needed to store
instance data for each window of this class.
Is There Anybody Out There?
Aside from defining the interfaces to the users of this control, we can sit in
our room with the door shut and - assuming someone slips a slice of pizza or
two under the door every now and then - eventually we will have something
useable. So where do we begin? Even though entering text is the primary
function of this class, we have to know where to draw the text, so painting the
control will be our first component to implement.
Select this to go to the next section
ΓòÉΓòÉΓòÉ 4.1.2. Painting ΓòÉΓòÉΓòÉ
Painting is easier to implement if you can break it down into distinct
sections; since we defined various components of the paper control last issue,
we can use those as a starting point.
ΓöÇΓö¼ΓöÇΓöÇ Border
Γö£ΓöÇΓöÇ Top margin
Γö£Γö¼ΓöÇ Side margin
ΓöéΓööΓöÇΓöÇΓöÇ Top, middle, and bottom holes
ΓööΓö¼ΓöÇ Paper body
ΓööΓöÇΓöÇΓöÇ One or more text lines
Figure 1. Different parts of the paper control
Above all it most be noted that we cannot make any assumptions about the size
of the control, unless we force the control to follow sizing constraints (we
will not). Our options, therefore, are to paint the control ignoring the size
or to use an abstract coordinate system based on the size when painting begins.
Obviously, the latter is the more desireable so this will be implemented.
The breakdown of the painting as I chose it is as follows:
o Paint the border, if it exists
o Paint the lines, both horizontal and vertical
o Paint the holes
o Paint the title text
o Paint the body text
Paint the border
Since some developers might not want a border, a paper style was added -
PPS_BORDER - to allow them to control this. PM defines its window styles (that
encompass all windows) to be in the upper word of a ULONG, so PPS_BORDER is
defined to be x'0001'.
hpsPaint=WinBeginPaint(hwndWnd,NULLHANDLE,&rclPaint);
WinFillRect(hpsPaint,&rclPaint,CLR_WHITE);
WinQueryWindowRect(hwndWnd,&rclWnd);
//----------------------------------------------------------------
// Paint the border first
//
// +------------+
// +|-----------+|
// || ||
// || ||
// || ||
// White -----> || || <----- Dark gray
// || ||
// |+------------+
// +------------+
//----------------------------------------------------------------
if ((ulStyle & PPS_BORDER)!=0) {
GpiSetColor(hpsPaint,CLR_WHITE);
ptlPoint.x=rclWnd.xLeft+1;
ptlPoint.y=rclWnd.yBottom;
GpiMove(hpsPaint,&ptlPoint);
ptlPoint.x=rclWnd.xRight;
ptlPoint.y=rclWnd.yTop-1;
GpiBox(hpsPaint,DRO_OUTLINE,&ptlPoint,0,0);
GpiSetColor(hpsPaint,CLR_DARKGRAY);
ptlPoint.x=rclWnd.xLeft;
ptlPoint.y=rclWnd.yBottom+1;
GpiMove(hpsPaint,&ptlPoint);
ptlPoint.x=rclWnd.xRight-1;
ptlPoint.y=rclWnd.yTop;
GpiBox(hpsPaint,DRO_OUTLINE,&ptlPoint,0,0);
WinInflateRect(ppidData->habAnchor,&rclWnd,-2,-2);
} /* endif */
Paint the lines
I don't know why, but it just seemed better sense to paint the vertical line
first, which was pink, if memory serves me correctly. This is followed by the
horizontal lines, which were of a cyan tint. Here, we added three new styles -
holes left (PPS_HOLESLEFT x'0000'), holes right (PPS_HOLESRIGHT x'0002'), and
no holes (PPS_HOLESNONE x'0004') - which we check when drawing the vertical
line.
//----------------------------------------------------------------
// Paint the vertical line. Check the window style to see what
// side it is on.
//----------------------------------------------------------------
if ((ulStyle & PPS_HOLESNONE)!=0) {
ptlPoint.x=rclWnd.xLeft+ppidData->fmFont.lAveCharWidth*5;
} else
if ((ulStyle & PPS_HOLESRIGHT)!=0) {
ptlPoint.x=rclWnd.xRight-ppidData->fmFont.lAveCharWidth*5;
} else {
ptlPoint.x=rclWnd.xLeft+ppidData->fmFont.lAveCharWidth*5;
} /* endif */
ptlPoint.y=rclWnd.yBottom;
GpiMove(hpsPaint,&ptlPoint);
ptlPoint.y=rclWnd.yTop;
GpiSetColor(hpsPaint,CLR_PINK);
GpiLine(hpsPaint,&ptlPoint);
//----------------------------------------------------------------
// Paint the horizontal lines. Our strategy is to query each
// line rectangle, and draw a line along the top edge of this
// rectangle. This means the bottom edge of the bottom line
// will not get painted, so explicitly handle this case.
//----------------------------------------------------------------
GpiSetColor(hpsPaint,CLR_DARKCYAN);
for (sIndex=0; sIndex<ppidData->sMaxLines; sIndex++) {
WinSendMsg(hwndWnd,
PPM_QUERYLINERECT,
MPFROMP(&rclLine),
MPFROMSHORT(sIndex));
ptlPoint.x=rclWnd.xLeft;
ptlPoint.y=rclLine.yTop;
GpiMove(hpsPaint,&ptlPoint);
ptlPoint.x=rclWnd.xRight;
GpiLine(hpsPaint,&ptlPoint);
} /* endfor */
ptlPoint.x=rclWnd.xLeft;
ptlPoint.y=rclLine.yBottom-1;
GpiMove(hpsPaint,&ptlPoint);
ptlPoint.x=rclWnd.xRight;
GpiLine(hpsPaint,&ptlPoint);
Paint the holes
In the future, we might want to support four- and seven- hole paper, so we have
to query the number of holes. We then loop, drawing each hole, until done.
//----------------------------------------------------------------
// Note that if PPS_HOLESNONE was specified, 0 is returned
//----------------------------------------------------------------
usMaxHoles=SHORT1FROMMR(WinSendMsg(hwndWnd,PPM_QUERYNUMHOLES,0,0));
for (sIndex=0; sIndex<usMaxHoles; sIndex++) {
WinSendMsg(hwndWnd,
PPM_QUERYHOLERECT,
MPFROMP(&rclHole),
MPFROMSHORT(sIndex));
ptlPoint.x=rclHole.xLeft+(rclHole.xRight-rclHole.xLeft)/2;
ptlPoint.y=rclHole.yBottom+(rclHole.yTop-rclHole.yBottom)/2;
GpiMove(hpsPaint,&ptlPoint);
GpiSetColor(hpsPaint,CLR_PALEGRAY);
GpiFullArc(hpsPaint,
DRO_FILL,
MAKEFIXED(ppidData->fmFont.lAveCharWidth,0));
GpiSetColor(hpsPaint,CLR_DARKGRAY);
GpiFullArc(hpsPaint,
DRO_OUTLINE,
MAKEFIXED(ppidData->fmFont.lAveCharWidth,0));
} /* endfor */
Paint the title text
If there is any title text (set on the WinCreateWindow() call or via
WinSetWindowText()), we draw it here.
if (ppidData->pchTitle!=NULL) {
WinSendMsg(hwndWnd,
PPM_QUERYLINERECT,
MPFROMP(&rclLine),
MPFROMSHORT(0));
rclLine.yBottom=rclLine.yTop+1;
rclLine.yTop=rclLine.yBottom+ppidData->fmFont.lMaxBaselineExt;
WinDrawText(hpsPaint,
-1,
ppidData->pchTitle,
&rclLine,
ppidData->lForeClr,
0,
DT_CENTER);
} /* endif */
Paint the body text
Finally, we paint the body text, using the PPM_CONVERTPOINTS message to convert
the invalid rectangle from world to line coordinates. While this is the only
optimization of the paint process, we could easily extend this to the other
components of the control.
WinSendMsg(hwndWnd,
PPM_CONVERTPOINTS,
MPFROMP(&rclPaint),
MPFROMSHORT(2));
if (rclPaint.yTop<0) {
rclPaint.yTop=0;
} /* endif */
if ((rclPaint.yBottom>ppidData->sMaxLines-1) ||
(rclPaint.yBottom<0)) {
rclPaint.yBottom=ppidData->sMaxLines-1;
} /* endif */
while (rclPaint.yTop<=rclPaint.yBottom) {
WinSendMsg(hwndWnd,
PPM_QUERYLINERECT,
MPFROMP(&rclLine),
MPFROMSHORT(rclPaint.yTop));
if (strlen(ppidData->aachLines[rclPaint.yTop])>0) {
WinDrawText(hpsPaint,
-1,
ppidData->aachLines[rclPaint.yTop],
&rclLine,
ppidData->lBackClr,
0,
DT_LEFT|DT_BOTTOM);
} /* endif */
rclPaint.yTop++;
} /* endwhile */
WinEndPaint(hpsPaint);
Select this to go to the next section
ΓòÉΓòÉΓòÉ 4.1.3. User Input ΓòÉΓòÉΓòÉ
By definition, a window can only receive input when it has the input focus.
Well, this isn't entirely true, since a window receives mouse movement messages
regardless of whom has the focus, with the exception of mouse capture, only
if...All kidding aside, character keystrokes go to the window with the input
focus and the system notifies a window when it receives and loses the focus, so
we use this to implement keystroke processing. Our 1000-foot view shows an
entryfield that belongs to us, created without ES_BORDER, which we position
over the line in which keystrokes are entered. We let the entryfield handle
the keyboard interface and we need only to initialize it with any text
currently on the line, and query it when the line is to change.
First, when can the line number be changed? The answer is ours to define. I
chose to allow first button clicks to set an absolute line and the up and down
arrows to change the line by one in either direction.
Second, what happens when the line number changes? Derived from our 1000-foot
view, we query the entryfield text and update our internal array of line text,
determine the world coordinates of the new line, call WinSetWindowPos() to
change the position of the entryfield to reflect this new position, and finally
initialize the entryfield via WinSetWindowText() with the text of the new line
as stored in our internal array.
These two questions translate to the messages WM_BUTTON1DOWN and WM_CHAR.
case WM_BUTTON1DOWN:
{
RECTL rclWnd;
//----------------------------------------------------------------
// Before we change the line, update the text array from the
// current line
//----------------------------------------------------------------
if (ppidData->sLine>-1) {
WinQueryWindowText(ppidData->hwndText,
sizeof(ppidData->aachLines[ppidData->sLine]),
ppidData->aachLines[ppidData->sLine]);
} /* endif */
//----------------------------------------------------------------
// Query the line clicked on
//----------------------------------------------------------------
rclWnd.xLeft=SHORT1FROMMP(mpParm1);
rclWnd.yBottom=SHORT2FROMMP(mpParm1);
WinSendMsg(hwndWnd,
PPM_CONVERTPOINTS,
MPFROMP(&rclWnd.xLeft),
MPFROMSHORT(1));
//----------------------------------------------------------------
// If the place clicked on is one of the lines, set the new
// entryfield position to that line
//----------------------------------------------------------------
if (rclWnd.yBottom>-1) {
WinSendMsg(hwndWnd,PPM_SETCURRENTLINE,MPFROMP(rclWnd.yBottom),0);
WinShowWindow(ppidData->hwndText,TRUE);
WinSetWindowText(ppidData->hwndText,
ppidData->aachLines[ppidData->sLine]);
} else {
ppidData->sLine=-1;
WinShowWindow(ppidData->hwndText,FALSE);
} /* endif */
WinSetFocus(HWND_DESKTOP,hwndWnd);
}
break;
case WM_CHAR:
if ((CHARMSG(&ulMsg)->fs & (KC_VIRTUALKEY | KC_KEYUP))==
KC_VIRTUALKEY) {
switch (CHARMSG(&ulMsg)->vkey) {
case VK_UP:
{
RECTL rclLine;
//----------------------------------------------------------
// Remember, we can only go up if there is another line
// above us
//----------------------------------------------------------
if (ppidData->sLine>0) {
WinQueryWindowText(ppidData->hwndText,
sizeof(ppidData->aachLines[ppidData->sLine]),
ppidData->aachLines[ppidData->sLine]);
ppidData->sLine--;
WinSendMsg(hwndWnd,
PPM_QUERYLINERECT,
MPFROMP(&rclLine),
MPFROMSHORT(ppidData->sLine));
WinSetWindowPos(ppidData->hwndText,
NULLHANDLE,
rclLine.xLeft,
rclLine.yBottom,
0,
0,
SWP_MOVE);
WinSetWindowText(ppidData->hwndText,
ppidData->aachLines[ppidData->sLine]);
//-------------------------------------------------------
// We only invalidate the line we left because the
// entryfield paints itself
//-------------------------------------------------------
WinSendMsg(hwndWnd,
PPM_QUERYLINERECT,
MPFROMP(&rclLine),
MPFROMSHORT(ppidData->sLine+1));
WinInvalidateRect(hwndWnd,&rclLine,TRUE);
WinUpdateWindow(hwndWnd);
WinSendMsg(ppidData->hwndOwner,
WM_CONTROL,
MPFROM2SHORT(WinQueryWindowUShort(hwndWnd,QWS_ID),
PPN_UP),
MPFROMSHORT(ppidData->sLine));
} else
if (ppidData->sLine==0) {
WinSendMsg(ppidData->hwndOwner,
WM_CONTROL,
MPFROM2SHORT(WinQueryWindowUShort(hwndWnd,QWS_ID),
PPN_BEGINPAGE),
MPFROMSHORT(ppidData->sLine));
} /* endif */
}
break;
//----------------------------------------------------------------
// We treat newline and enter the same as down arrow
//----------------------------------------------------------------
case VK_DOWN:
case VK_NEWLINE:
case VK_ENTER:
{
RECTL rclLine;
//----------------------------------------------------------
// Remember, we can only go down if there is another line
// below us
//----------------------------------------------------------
if ((ppidData->sLine>-1) &&
(ppidData->sLine<ppidData->sMaxLines-1)) {
WinQueryWindowText(ppidData->hwndText,
sizeof(ppidData->aachLines[ppidData->sLine]),
ppidData->aachLines[ppidData->sLine]);
ppidData->sLine++;
WinSendMsg(hwndWnd,
PPM_QUERYLINERECT,
MPFROMP(&rclLine),
MPFROMSHORT(ppidData->sLine));
WinSetWindowPos(ppidData->hwndText,
NULLHANDLE,
rclLine.xLeft,
rclLine.yBottom,
0,
0,
SWP_MOVE);
WinSetWindowText(ppidData->hwndText,
ppidData->aachLines[ppidData->sLine]);
//-------------------------------------------------------
// We only invalidate the line we left because the
// entryfield paints itself
//-------------------------------------------------------
WinSendMsg(hwndWnd,
PPM_QUERYLINERECT,
MPFROMP(&rclLine),
MPFROMSHORT(ppidData->sLine-1));
WinInvalidateRect(hwndWnd,&rclLine,TRUE);
WinUpdateWindow(hwndWnd);
WinSendMsg(ppidData->hwndOwner,
WM_CONTROL,
MPFROM2SHORT(WinQueryWindowUShort(hwndWnd,QWS_ID),
PPN_DOWN),
MPFROMSHORT(ppidData->sLine));
} else
if (ppidData->sLine==ppidData->sMaxLines-1) {
WinSendMsg(ppidData->hwndOwner,
WM_CONTROL,
MPFROM2SHORT(WinQueryWindowUShort(hwndWnd,QWS_ID),
PPN_ENDPAGE),
MPFROMSHORT(ppidData->sLine));
} /* endif */
}
break;
default:
return WinDefWindowProc(hwndWnd,ulMsg,mpParm1,mpParm2);
} /* endswitch */
} else {
return WinDefWindowProc(hwndWnd,ulMsg,mpParm1,mpParm2);
} /* endif */
break;
Notice the notifications PPN_UP, PPN_DOWN, PPN_BEGINPAGE and PPN_ENDPAGE.
Time Out
You will not notice this now, but if you click on the paper control, the line
number is supposed to change. Why do we not get a flashing cursor where the
"invisible" entryfield exists? The answer is in the way the system handles
focus changing.
When the input focus window changes, a number of messages are sent as a result
to both the window losing the focus and to the window receiving the focus.
While this messages are being processed, the system considers no one to have
the focus, so any attempt to change the focus via WinSetFocus() or
WinFocusChange() will have no effect because the system will "overwrite" the
focus change as it completes its processing.
The result of this gibberish is that, if we are clicked on, we want the
entryfield to receive the focus, but since we need to do some processing, we
cannot just call WinSetFocus(HWND_DESKTOP,ppidData->hwndText) since we will
never receive any notification.
The result of that gibberish is that we need to get the focus and then somehow
pass the focus on to the entryfield. Since we cannot change the focus while
the system changes the focus, we need a little hocus-pocus to achieve this.
#define PRVM_SETFOCUS (WM_USER)
case WM_SETFOCUS:
WinPostMsg(hwndWnd,PRVM_SETFOCUS,mpParm1,mpParm2);
break;
case PRVM_SETFOCUS:
if (SHORT1FROMMP(mpParm2)) {
if (ppidData->sLine>-1) {
WinShowWindow(ppidData->hwndText,TRUE);
WinFocusChange(HWND_DESKTOP,
ppidData->hwndText,
FC_NOLOSEACTIVE|FC_NOLOSEFOCUS|FC_NOLOSESELECTION);
} /* endif */
WinSendMsg(ppidData->hwndOwner,
WM_CONTROL,
MPFROM2SHORT(WinQueryWindowUShort(hwndWnd,QWS_ID),
PPN_SETFOCUS),
0);
} else {
//----------------------------------------------------------------
// If we're losing the focus, update the text array, but leave the
// entryfield text alone.
//----------------------------------------------------------------
if (ppidData->sLine>-1) {
WinQueryWindowText(ppidData->hwndText,
sizeof(ppidData->aachLines[ppidData->sLine]),
ppidData->aachLines[ppidData->sLine]);
WinShowWindow(ppidData->hwndText,FALSE);
} /* endif */
WinSendMsg(ppidData->hwndOwner,
WM_CONTROL,
MPFROM2SHORT(WinQueryWindowUShort(hwndWnd,QWS_ID),
PPN_KILLFOCUS),
0);
} /* endif */
break;
In the code above, a couple of things need to be noted:
o The hocus-pocus is in the call to WinPostMsg(). While it is not guaranteed,
it is highly likely that our message will get processed after the system
finished the focus change. This will allow us to do any processing necessary
and then change the focus again to the entryfield.
o The focus change added two new notifications - PPN_SETFOCUS and
PPN_KILLFOCUS. These have the same semantics as the corresponding entryfield
notifications EN_SETFOCUS and EN_KILLFOCUS.
o The call to WinFocusChange() specifies through the use of the FC_* constants
that the window with the focus (that's us) should not receive any
notification that it is losing the focus. This is needed so that we don't
send the PPN_KILLFOCUS notification.
Select this to go to the next section
ΓòÉΓòÉΓòÉ 4.1.4. Owner Notifications ΓòÉΓòÉΓòÉ
Last issue, we defined a list of four events that should result in a
notification to the owner window. The system allows us to define any
notification code as a USHORT that we feel is necessary, since there is no
"standard list" of notifications present for all window classes (versus window
styles, where there is a common set of styles).
The cursor moves up or down
This is implemented in the processing for WM_CHAR. The code consists of the
following call to WinSendMsg().
WinSendMsg(ppidData->hwndOwner,
WM_CONTROL,
MPFROM2SHORT(WinQueryWindowUShort(hwndWnd,QWS_ID),PPN_UP),
MPFROMSHORT(ppidData->sLine));
Substitute PPN_DOWN for PPN_UP as needed.
Note that if the cursor is on line 1/ppidData->sMaxLines and the user presses
the up/down arrow, we instead send a PPN_BEGINPAGE/PPN_ENDPAGE notification to
let the application know that the page boundary was reached.
Any mouse button is clicked or double-clicked
This is implemented in the processing for the various WM_BUTTONxUP and
WM_BUTTONxDBLCLK messages. The code is the same as for the PPN_UP/PPN_DOWN
notifications with the notification code changed as appropriate. Note that we
could use the second half of mpParm2 to include which button was pressed, as a
convenience to the user.
The system-defined sequence for displaying the context menu is pressed
Again, we use the same call to WinSendMsg(), this time from the WM_CONTEXTMENU
message.
Help is requested
And finally one more WinSendMsg() from the WM_HELP message.
That was easy, wasn't it?
Select this to go to the next section
ΓòÉΓòÉΓòÉ 4.1.5. Presentation Parameters ΓòÉΓòÉΓòÉ
Processing these is probably the most interesting, because...well, because the
ability for the user to change the color and font of a window was new with OS/2
2.0. [Get on soapbox] "I remember the days when you didn't have to deal with
these dad-burned paramitization presentations or whatever the hell they're
called."[Get off soapbox]. Because the user has the ability, via the color and
font palettes, to change your presentation parameters, you can no longer avoid
them when developing a new window class.
Changing a presentation parameter is done for you by the system, providing you
use a micro-cache'd presentation space in all of your drawing operations
(obtained through WinBeginPaint() with NULLHANDLE specified as the second
parameter, or through WinGetPS()). If you have your own screen presentation
space which you specify in the call to WinBeginPaint, you will need to
intercept the WM_PRESPARAMCHANGED message. Another time this message is needed
is if you need the size of the current font for other processing or will
unconditionally set the color to some color but want to restore it for other
operations.
Gee, aren't those familiar operations?
We intercept this message and if a color was changed, we query the new color
value and store that in our instance data. If the font changed, we re-query
the FONTMETRICS structure values so that we can base our line size on the
height of the font.
case WM_PRESPARAMCHANGED:
switch (LONGFROMMP(mpParm1)) {
case PP_FOREGROUNDCOLOR:
case PP_FOREGROUNDCOLORINDEX:
{
ULONG ulId;
LONG lColor;
HPS hpsWnd;
SHORT sIndex;
RECTL rclLine;
WinQueryPresParam(hwndWnd,
PP_FOREGROUNDCOLORINDEX,
LONGFROMMP(mpParm1),
&ulId,
sizeof(lColor),
&lColor,
QPF_NOINHERIT);
if (ulId==PP_FOREGROUNDCOLOR) {
hpsWnd=WinGetPS(hwndWnd);
lColor=GpiQueryColorIndex(hpsWnd,0,lColor);
WinReleasePS(hpsWnd);
} /* endif */
ppidData->lForeClr=lColor;
for (sIndex=0; sIndex<ppidData->sMaxLines; sIndex++) {
if (ppidData->aachLines[sIndex][0]!=0) {
WinSendMsg(hwndWnd,
PPM_QUERYLINERECT,
MPFROMP(&rclLine),
MPFROMSHORT(sIndex));
WinInvalidateRect(hwndWnd,NULL,TRUE);
} /* endif */
} /* endfor */
WinUpdateWindow(hwndWnd);
}
break;
case PP_BACKGROUNDCOLOR:
case PP_BACKGROUNDCOLORINDEX:
{
ULONG ulId;
LONG lColor;
HPS hpsWnd;
SHORT sIndex;
RECTL rclLine;
WinQueryPresParam(hwndWnd,
PP_BACKGROUNDCOLORINDEX,
LONGFROMMP(mpParm1),
&ulId,
sizeof(lColor),
&lColor,
QPF_NOINHERIT);
if (ulId==PP_BACKGROUNDCOLOR) {
hpsWnd=WinGetPS(hwndWnd);
lColor=GpiQueryColorIndex(hpsWnd,0,lColor);
WinReleasePS(hpsWnd);
} /* endif */
ppidData->lBackClr=lColor;
for (sIndex=0; sIndex<ppidData->sMaxLines; sIndex++) {
if (ppidData->aachLines[sIndex][0]!=0) {
WinSendMsg(hwndWnd,
PPM_QUERYLINERECT,
MPFROMP(&rclLine),
MPFROMSHORT(sIndex));
WinInvalidateRect(hwndWnd,NULL,TRUE);
} /* endif */
} /* endfor */
WinUpdateWindow(hwndWnd);
}
break;
case PP_FONTNAMESIZE:
case PP_FONTHANDLE:
{
HPS hpsWnd;
RECTL rclWnd;
hpsWnd=WinGetPS(hwndWnd);
GpiQueryFontMetrics(hpsWnd,sizeof(FONTMETRICS),&ppidData->fmFont);
WinReleasePS(hpsWnd);
WinQueryWindowRect(hwndWnd,&rclWnd);
WinSendMsg(hwndWnd,
WM_SIZE,
0,
MPFROM2SHORT((SHORT)rclWnd.xRight,
(SHORT)rclWnd.yTop));
WinInvalidateRect(hwndWnd,NULL,TRUE);
WinUpdateWindow(hwndWnd);
}
break;
default:
return WinDefWindowProc(hwndWnd,ulMsg,mpParm1,mpParm2);
} /* endswitch */
break;
Again, there are other (better) ways of implementing things. Here, we could
query the RGB value of the changed color and use that in our painting to get
the most accuracy. This exercise is left up to the programmer.
While it doesn't really belong here, the window text processing has nowhere
else to go so it is discussed here. The system's interface to a window class
for items like this is through the WM_SETWINDOWPARAMS and WM_QUERYWINDOWPARAMS
messages. The former is sent when a window parameter changes and the latter is
sent to query the data from the window. A window parameter is either the
window text or the control data (which is specific to a window class) and both
are bundled in the WNDPARAMS structure.
typedef struct _WNDPARAMS {
ULONG fsStatus;
ULONG cchText;
PSZ pszText;
ULONG cbPresParams;
PVOID pPresParams;
ULONG cbCtlData;
PVOID pCtlData;
} WNDPARAMS, *PWNDPARAMS;
fsStatus For queries by the system, this specifies (as a combination
of WPM_* constants) the parameters to query.
cchText This specifies the size of the data pointed to by pszText
pszText This points to the window text, terminated by a NULL
character
cbPresParams This specifies the size of the data pointed to by
pPresParams
pPresParams This points to the presentation parameters
cbCtlData This specifies the size of the data pointed to by pCtlData
pCtlData This points to the control data
Since we are interested in the text only, we check the fsStatus field for the
WPM_TEXT constant explicitly and act accordingly. The resulting code is shown
below:
case WM_QUERYWINDOWPARAMS:
{
PWNDPARAMS pwpParms;
pwpParms=(PWNDPARAMS)PVOIDFROMMP(mpParm1);
if ((pwpParms->fsStatus & WPM_TEXT)!=0) {
pwpParms->pszText[0]=0;
strncat(pwpParms->pszText,ppidData->pchTitle,pwpParms->cchText);
WinDefWindowProc(hwndWnd,ulMsg,mpParm1,mpParm2);
return MRFROMSHORT(TRUE);
} /* endif */
return WinDefWindowProc(hwndWnd,ulMsg,mpParm1,mpParm2);
}
case WM_SETWINDOWPARAMS:
{
BOOL bProcessed;
PWNDPARAMS pwpParms;
bProcessed=FALSE;
pwpParms=(PWNDPARAMS)PVOIDFROMMP(mpParm1);
if ((pwpParms->fsStatus & WPM_TEXT)!=0) {
if (ppidData->pchTitle!=NULL) {
free(ppidData->pchTitle);
ppidData->pchTitle=NULL;
} /* endif */
if ((pwpParms->pszText!=NULL) && (strlen(pwpParms->pszText)>0)) {
ppidData->pchTitle=malloc(strlen(pwpParms->pszText)+1);
strcpy(ppidData->pchTitle,pwpParms->pszText);
} /* endif */
bProcessed=TRUE;
} /* endif */
if (bProcessed) {
WinInvalidateRect(hwndWnd,NULL,TRUE);
WinUpdateWindow(hwndWnd);
WinDefWindowProc(hwndWnd,ulMsg,mpParm1,mpParm2);
return MRFROMSHORT(TRUE);
} else {
return WinDefWindowProc(hwndWnd,ulMsg,mpParm1,mpParm2);
} /* endif */
}
Select this to go to the next section
ΓòÉΓòÉΓòÉ 4.1.6. Summary ΓòÉΓòÉΓòÉ
Although a lot of code was presented here, all of it is really only the
extension of our thoughts from last issue when this new class was being
designed. It cannot be stressed enough the importance of a good design; no one
expects you to get it right the first time, but if you think most of it through
the rest becomes significantly easier.
As a final note, all coordinates that we discussed here were (if not already)
mapped to line numbers via the message PPM_CONVERTPOINTS. This allows us to
change the size of each line in one place only. This logic was applied in
other places (PPM_QUERYNUMHOLES) to make our maintenance easier and goes way
back to Computer Science 101 when code modularity was discussed.
Select this to go to the next section
ΓòÉΓòÉΓòÉ 4.2. The Help Manager and Online Documentation ΓòÉΓòÉΓòÉ
Written by Larry Salomon, Jr.
Select this to go to the next section
ΓòÉΓòÉΓòÉ 4.2.1. Introduction ΓòÉΓòÉΓòÉ
More times than I care to remember, I have been asked "How do I add online help
to my applications?" or "How can I write online books?" While the latter takes
less time to answer, I often myself talking for 45 minutes or so on everything
from what an GML language is to the sequence of events that happen when a user
presses the F1 key.
Finally, today I got tired of repeating myself (even as much as I love to talk)
so I decided to write the article that has been promised for so long. Hopefully
after reading this, you should be able to do the following:
o Briefly explain what an GML language is and what markup is
o List the three components needed when adding context-sensitive help to a PM
application and what each component's purpose is
o Describe the sequence of events that occur when a user presses F1
o Detail the minimum markup required to create a valid online help file and to
create a valid online book file
o Be able to write the markup for headings, paragraphs, lists (all five types),
emphasis, hypertext links, and graphics.
o Explain some of the limitations of the IPFC compiler
Select this to go to the next section
ΓòÉΓòÉΓòÉ 4.2.2. The Basics ΓòÉΓòÉΓòÉ
GML is an acronym for generalized markup language and describes a group of
languages which perform operations on blocks of text. Typically, these
languages are used for output formatting but are not limited to this arena.
The language consists of control words (called tags) interspersed within blocks
of text; these tags have the form
ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
:tag [attribute[=value] [attribute[=value] [...]]].[text]
ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
Figure 1. GML tag syntax
The attributes shown above vary from tag to tag and may not exist at all.
Likewise, each attribute may or may not accept a value; consult the
"Information Presentation Facility" reference guide which comes with the 2.x
toolkit for a complete list of the attributes for each tag.
Frequently, a tag is used to mark the beginning of a change in formatting and
has a corresponding end tag to signify the end of that change. The end tag
often is the same as the begin tag prefixed with an "e". Thus, you use :hp2.
to begin emphasis level 2 and :ehp2. to end it.
The term markup is used to describe a combination of tags and text in a GML
file.
Application components
There are three help-related components to any PM application, listed below.
o Source code - calls to and from the Help Manager from your application
o Resources - relationships between focus windows and help panels
o Panel definitions - GML source describing the help panels' appearance
Select this to go to the next section
ΓòÉΓòÉΓòÉ 4.2.3. Source code ΓòÉΓòÉΓòÉ
This consists of the various calls to Help Manager functions from within your
PM application. The bare minimum exists in your main() function and creates an
instance of the Help Manager:
ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
#define INCL_WINHELP
:
INT main(USHORT usArgs,PCHAR apchArgs[])
{
:
HELPINIT hiHelp;
habAnchor=WinInitialize(0);
hmqQueue=WinCreateMsgQueue(habAnchor,0);
:
: /* Create frame window */
:
if (hwndFrame!=NULLHANDLE) {
:
: /* Initialize HELPINIT structure */
:
hwndHelp=WinCreateHelpInstance(habAnchor,&hiHelp);
WinAssociateHelpInstance(hwndHelp,hwndFrame);
:
: /* Message loop in here somewhere */
:
WinDestroyHelpInstance(hwndHelp);
WinDestroyWindow(hwndFrame);
} /* endif */
WinDestroyMsgQueue(hmqQueue);
WinTerminate(habAnchor);
}
ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
Figure 2. Help Manager initialization
The HELPINIT structure is a rather large conglomeration of configurable items.
ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
typedef struct _HELPINIT {
ULONG cb;
ULONG ulReturnCode;
PSZ pszTutorialName;
PHELPTABLE phtHelpTable;
HMODULE hmodHelpTableModule;
HMODULE hmodAccelActionBarModule;
ULONG idAccelTable;
ULONG idActionBar;
PSZ pszHelpWindowTitle;
ULONG fShowPanelId;
PSZ pszHelpLibraryName;
} HELPINIT, *PHELPINIT;
ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
Figure 3. HELPINIT structure
cb Specifies the size of the structure in bytes
ulReturnCode On exit, contains any error codes returned from the Help
Manager
pszTutorialName Points to the string specifying the default tutorial name.
If NULL, there is no default tutorial or it is specified in
the panel definition file.
phtHelpTable Points to the HELPTABLE to use, or specifies the resource
id of the HELPTABLE to be loaded from the resource file.
If this specifies the resource id, it is contained in the
low word and the high word must be 0xFFFF.
hmodHelpTableModule Specifies the handle of the module from whence the
HELPTABLE is to be loaded.
idAccelTable Specifies the resource id of the accelerator table to be
used. A value of 0 specifies that the default is to be
used.
idActionBar Specifies the resource id of the action bar to be used. A
value of 0 specifies that the default is to be used.
hmodAccelActionBarModule Specifies the handle of the module from whence the
accelerator table and action bar are to be loaded.
pszHelpWindowTitle Points to the help window title text.
fShowPanelId Specifies whether or not to show the panel identifier (if
present). Valid values are CMIC_SHOW_PANEL_ID and
CMIC_HIDE_PANEL_ID.
pszHelpLibraryName Points to the filename containing the compiler panel
definitions.
It needs to be noted that even though a valid window handle is returned from
WinCreateHelpInstance(), an error might have occurred whose value is specified
in the ulReturnCode field of the HELPINIT structure.
Messages
There will be times when you will want to send messages to the Help Manager and
when messages will be received. The four most frequent messages sent to the
Help Manager are listed below:
HM_HELP_INDEX Causes the help index to be displayed
HM_EXT_HELP Causes the extended (general) help panel defined for the
active window to be displayed (described more later)
HM_DISPLAY_HELP Causes the panel specified by name (in mpParm1) or resource
id (in mpParm2) to be displayed. Specifying NULL and 0 for
these values cause the "Using Help" panel to be displayed.
HM_KEYS_HELP Causes the keys help panel to be displayed. Since the
active key set is dependent on the current state of the
application, this cannot be statically defined in the
resource tables. Instead, the Help Manager responds by
sending your application an HM_QUERY_KEYS_HELP message to
get the resource id of the keys help panel.
The following three messages sent to your application are probably the most
widely used:
HM_ERROR Sent whenever an error occurs between the time F1 is
pressed and the help operation ends. The error code is
specified in mpParm1
HM_HELPSUBITEM_UNDEFINED Sent whenever help was requested but no entry in the
HELPSUBTABLE was found.
HM_INFORM Sent whenever a link with the inform attribute is
encountered.
Select this to go to the next section
ΓòÉΓòÉΓòÉ 4.2.4. Resources ΓòÉΓòÉΓòÉ
To make the connection between a window and a help panel, two new resource
types were added to PM's resource file definition - HELPTABLEs and
HELPSUBTABLEs. Together, they specify an array of variable length lists that
map a window resource id to a help panel resource id.
ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
HELPTABLE RES_CLIENT
{
HELPITEM RES_CLIENT, SUBHELP_CLIENT, GENHELP_CLIENT
HELPITEM RES_DIALOG1, SUBHELP_DIALOG1, GENHELP_DIALOG1
HELPITEM RES_DIALOG2, SUBHELP_DIALOG2, GENHELP_DIALOG2
:
HELPITEM RES_DIALOGn, SUBHELP_DIALOGn, GENHELP_DIALOGn
}
HELPSUBTABLE SUBHELP_CLIENT
{
HELPSUBITEM WID_WINDOW1, HID_PANEL1
HELPSUBITEM WID_WINDOW2, HID_PANEL2
:
HELPSUBITEM WID_WINDOWn, HID_PANELn
}
HELPSUBTABLE SUBHELP_DIALOG1
{
HELPSUBITEM WID_WINDOW1, HID_PANEL1
HELPSUBITEM WID_WINDOW2, HID_PANEL2
:
HELPSUBITEM WID_WINDOWn, HID_PANELn
}
HELPSUBTABLE SUBHELP_DIALOG2
{
HELPSUBITEM WID_WINDOW1, HID_PANEL1
HELPSUBITEM WID_WINDOW2, HID_PANEL2
:
HELPSUBITEM WID_WINDOWn, HID_PANELn
}
HELPSUBTABLE SUBHELP_DIALOG3
{
HELPSUBITEM WID_WINDOW1, HID_PANEL1
HELPSUBITEM WID_WINDOW2, HID_PANEL2
:
HELPSUBITEM WID_WINDOWn, HID_PANELn
}
ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
Figure 4. Help Manager resource structures
Each HELPITEM specifies the resource id of an active window, the id of the
HELPSUBTABLE associated with this window, and the resource id of the General
Help panel associated with this window.
Each HELPSUBITEM specifies a focus window resource id (WID_*) and a
corresponding help panel resource id (HID_*).
What are the rules that the Help Manager uses to get from F1 to a help panel
id? To answer that question, we need to know the sequence of events that occur
when a user presses F1. Below, we assume that the application is help-enabled.
1. The user presses F1
2. The Help Manager queries the active window resource id (ID1) and the focus
window id (ID2).
3. ID1 is used in a lookup of the HELPTABLE to determine the HELPSUBTABLE to
use.
4. ID2 is used in a lookup of the HELPSUBTABLE to find the resource id of the
help panel to display.
5. The Help Manager displays the panel.
There are two obvious error conditions: 1) there is no HELPSUBTABLE for the
active window and 2) there is no HELPSUBITEM for the focus window. The former
is resolved by examining each window in the parent window chain until a window
that does have a HELPSUBTABLE is found and then the process continues as
normal. If this still yields nothing, the owner window chain is searched and
then if nothing still, an error message is sent to the active window. The
latter is resolved by first sending an HM_HELPSUBITEM_UNDEFINED message to
attempt to alleviate the situation. If the application returns FALSE the
general help panel specified on the HELPITEM statement is displayed.
Select this to go to the next section
ΓòÉΓòÉΓòÉ 4.2.5. Panel definitions ΓòÉΓòÉΓòÉ
This is, by far, the most time-consuming portion of help-enabling. Not only do
you have to write the text to be displayed, but you must also be aware of
formatting options and what effect they have on the output. At a minimum, you
must have the following to create a valid online help file:
ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
:userdoc.
:h1.Heading 1
:p.Paragraph 1
:euserdoc.
ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
Figure 5. Minimum GML markup
The tags used above are described below:
:userdoc. Specifies the beginning of a user document.
:h1. Specifies a heading level 1.
:p. Specifies a new paragraph.
:euserdoc. Specifies the end of the user document.
Online book writers must also specify a :title.Document title after the
:userdoc. tag.
As you can imagine, this file doesn't do much. In fact, it does nothing since
as you can see nowhere are an help panel resource ids specified (even though
you don't know how to specify them). For that matter, what defines a panel?
A panel is a block of formatted text beginning with a heading level and ending
with the beginning of the next panel displayed in the table of contents or the
end of the document body. (the back matter of a document can contain an
index.) Heading levels are specifies after the h and can be in the range 1-9;
by default, only heading levels 1-3 are displayed in the table of contents, but
this is configurable using the :docprof. tag. A heading may also have
attributes; these are the res, id, name, and hide attributes.
ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
:hn [res=value][id=value][name=value][hide].
ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
Figure 6. Heading tag syntax
res=value This specifies a numeric resource id for the panel and is
used in the HELPSUBITEM definitions and can be used to
specify the target of a hypertext link.
id=value This specifies an alphanumeric id that can be used to
specify the target of a hypertext link.
name=value This specifies an alphanumeric id that can be referenced by
an application.
hide This specifies that, regardless of the heading level, the
panel should not show up in the table of contents. This is
useful for hypertext links.
Headings must have data associated with them, i.e. you cannot have an :h1. tag
immediately followed by an :h2. tag. Also, heading levels other than 1 that
are ordinally higher than their predecessors must have the next cardinal value.
Thus, the first example is valid while the second is not:
ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
:h1.Heading 1
:p.Blah blah blah
:h2.Heading 2
:p.Blah blah blah
:h1.Heading 1
:p.Blah blah blah
:h1.Heading 1
:p.Blah blah blah
:h3.Heading 3
:p.Blah blah blah
:h1.Heading 1
:p.Blah blah blah
ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
Figure 7. Heading examples
This does not apply to headings that have a lower value than their
predecessors.
Paragraphs
The most frequently used tag is probably the :p. tag, used to begin a new
paragraph. It should be noted that some constructs implictly begin on a new
paragraph, so this is not needed. A paragraph is denoted by ending the current
line, inserting a blank line, and continuing on the next line. The current
indentation level (modified by lists, etc.) is not changed.
ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
:p.
ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
Figure 8. Paragraph tag syntax
A word here should be mentioned about symbols. What happens when you want to
put a colon (:) in your panels? How is the compiler going to be able to
differentiate between a colon as part of the text or as the beginning of a tag.
GML defines a syntax for symbols such that they begin with an ampersand (&) and
end with a period with a symbol name in the middle. Thus, a colon is &colon.,
and ampersand is &., etc.. Some other commonly used symbols appear below:
&lbrk. Left bracket ([)
&rbrk. Right bracket (])
&lbrc. Left brace ({)
&rbrc. Right brace (})
&vbar. Vertical bar (|)
&apos. Apostrophe (')
&bsl. Backslash (\)
&odq. Open double quote (")
&cdq. Close double quote (")
&osq. Open single quote (`)
&csq. Close single quote (')
You should use the symbol syntax instead of the actual characters whenever
possible to ease the process of translating your IPF source to other languages,
should that happen, since the compiler defines the code point to which each
symbol is mapped according to the codepage in effect.
Lists
IPF (Information Presentation Facility - the language definition) defines five
types of lists - simple, unordered, ordered, definition, and parameter. All
lists consist of a begin tag, one or more list items, and an end tag.
Definition and parameter list items are unique in that they consist of two
parts.
The begin tags/end tags are :sl./:esl., :ul./:eul., :ol./:eol., :dl./:edl., and
:parml./:eparml. for simple, unordered, ordered, definition, and parameter
lists respectively. List items for the first three types are specified using
the :li. tag. Definition list terms are specified using the :dt. tag, and the
corresponding definitions using the :dd. tag. Parameter list items are
specifies using the :pt. tag and - like the definition lists - each item has a
corresponding definition specified using the :pd. tag.
ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
:sl [compact].
:li.text
:esl.
:ul [compact].
:li.text
:eul.
:ol [compact].
:li.text
:eol.
:dl [tsize=value][compact][break={ all | none | fit }].
[:dthd.text]
[:ddhd.text]
:dt.text
:dd.text
:edl.
:parml [tsize=value][compact][break={ all | none | fit }].
:pt.text
:pd.text
:eparml.
ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
Figure 9. List tags syntax
Below are some examples of lists.
ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
:sl.
:li.Simple list item 1
:li.Simple list item 2
:li.Simple list item 3
:esl.
:ul.
:li.Unordered list item 1
:li.Unordered list item 2
:li.Unordered list item 3
:eul.
:ol.
:li.Ordered list item 1
:li.Ordered list item 2
:li.Ordered list item 3
:eol.
:dl.
:dt.Term 1
:dd.Definition 1
:dt.Term 2
:dd.Definition 2
:dt.Term 3
:dd.Definition 3
:edl.
:parml.
:pt.Parameter 1
:pd.Definition 1
:pt.Parameter 2
:pd.Definition 2
:pt.Parameter 3
:pd.Definition 3
:eparml.
ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
Figure 10. Examples of list markup
The above are formatted as:
Simple list item 1
Simple list item 2
Simple list item 3
o Unordered list item 1
o Unordered list item 2
o Unordered list item 3
1. Ordered list item 1
2. Ordered list item 2
3. Ordered list item 3
Term 1 Definition 1
Term 2 Definition 2
Term 3 Definition 3
Parameter 1
Definition 1
Parameter 2
Definition 2
Parameter 3
Definition 3
All lists accept the attribute compact which specifies that items should not be
separated by blank lines. Definition and parameter lists also accept the
attributes tsize=value and break=[all | fit | none]. tsize specifies the width
of the term column in units of the average character width of the current font.
break specifies what is to be done when the term exceeds the column width; the
default is none and starts the definition after the term preceeded by a space.
all specifies that all definitions begin on a new line indented by the term
column width. fit specifies that definitions whose terms exceed the column
width begin on a new line as in all.
Emphasis
Emphasis markups consist of a begin and end tag and have the form :hpn. There
are different emphasis levels, ranging from 1 to 9, which is specified as n.
ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
:hpn.
ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
Figure 11. Emphases tag syntax
Level Meaning
1 Italic
2 Bold
3 Italicized bold
4 Alternate color 1
5 Underlined
6 Underlined italic
7 Underlined bold
8 Alternate color 2
9 Alternate color 3
Emphasis markup has no attributes.
Hypertext
Ever since online documentation became all-the-rage, one of the greatest
advantages that it touted was the elimination of the phrase "For more
information, see page...". Hypertext - as it was termed - is the ability to
jump from one point to another by performing some action (usually a click of
the mouse) on a hot-link; these hot-links are usually a word or phrase that has
more, related information associated with it that the user will supposedly want
to read eventually but without cluttering up the topic already being read.
Hypertext in IPF markup is accomplished using the :link. tag and its
corresponding end tag.
ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
:link reftype={ fn | hd | launch | inform } [res=value][refid=value]
[object='value'][data='value'][x=value y=value cx=value cy=value].
:elink.
ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
Figure 12. Hypertext tag syntax
The type of the link destination is specified by the reftype parameter. It can
have one of the following values:
fn Footnote. The footnote must have an id attribute which is
specified in the refid parameter.
hd Panel. The panel must have either an id or a resource id
specified using the id and res attributes, respectively.
This identifier is specifies in the refid and res
attributes of the link tag.
launch Application. The application, whose full path, name, and
extension must be specified in the object attribute is
executed. Any command line parameters may be specified in
the data attribute.
inform Message. This is applicable to online help only. The
active window is sent an HM_INFORM message with the value
of the res attribute passed in mpParm1.
Hypertext links can be used to allow access to panels displayed elsewhere or
not displayed at all; for example, suppose that you are on heading level 3 and
you have some indirectly related information that you want to avoid cluttering
the panel with. You cannot make it a heading level 4 because it won't show up
in the table of contents. Linking makes a lot of sense here; make the target
panel heading hidden and level 1 (to avoid nonsensical error messages from the
compiler) and then link it to allow the user to read the information only if
desired.
Graphics
Graphics can be included in your documents also; both OS/2 bitmaps and
metafiles are supported. I have found it useful to use a screen capture
program to export a bitmap of my application to a file, annotate it with a
graphical editor, and then include it in the online help to label the items of
interest. The tag that is used is the :artwork.. tag.
ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
:artwork name='filename' [runin][linkfile='filename']
[align={ left | center | right }]
ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
Figure 12. Hypertext tag syntax
name specifies the filename containing the bitmap or metafile. runin specifies
that the graphic does not force an newline before and after the graphic. align
specifies the justification of the graphic. linkfile is for graphical linking
(called hypergraphics) and will not be discussed.
Select this to go to the next section
ΓòÉΓòÉΓòÉ 4.2.6. The Extra Mile ΓòÉΓòÉΓòÉ
Okay, so you've completely enabled your application to use online help...or
have you? Actually, with the exception of rare applications, there is one more
area that needs to be covered and that is message box help.
Message boxes are modal dialogs that contain information for the user - error
messages, usually. However, there is only so much that you can say in a
message box; so, there is a style that you can specify on the call to
WinMessageBox() that says to add a Help pushbutton. Fine, so you have a Help
pushbutton. The user selects it. Nothing happens. What goes on under the
covers?
Hooks
What goes on is that the message box receives the WM_HELP message and, because
it can't determine the window that called WinMessageBox() (for more information
on why this is so, read the documentation for WinLoadDlg() and pay attention to
the part about determining the real owner of a dialog), it sends the message to
the help hook.
What is a help hook? Well, a hook in general is a function that gets called by
an application (or, in this case, the system) when specific types of events
occur; thus, a help hook is a function that receives notification of
help-related events.
ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
BOOL EXPENTRY helpHook(HAB habAnchor,
SHORT sMode,
SHORT sTopic,
SHORT sSubTopic,
PRECTL prclPosition);
ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
Figure 13. Help hook function prototype
habAnchor Specifies the anchor block of the thread to receive the
message
sMode Specifies the context of the help request. The values and
the contexts are:
HLPM_FRAME When the parent of the focus window is an FID_CLIENT
window. sTopic is the frame window id, sSubTopic is the
focus window id, and prclPosition points to the screen
coordinates of the focus window.
HLPM_WINDOW When the parent of the focus window is not an FID_CLIENT
window. sTopic is the focus window's parent id,
sSubTopic and prclPosition have the same meaning as
HLPM_FRAME.
HLPM_MENU When the application is in menu mode. sTopic contains
the active action bar item id, sSubTopic contains the id
of the menu item with the cursor, or -1 if the action
bar has the cursor.
In message boxes, the Help pushbutton is not defined with the BS_NOPOINTERFOCUS
style, so it has the focus after it is selected. According to the rules, the
help hook should get HLPM_WINDOW for the mode and the various identifiers in
the other parameters. However, this is not the case; the help hook does indeed
receive HLPM_WINDOW as the mode, but the sTopic parameter is the number
specified as the fifth parameter to WinMessageBox() (the help button
identifier). sSubTopic always contains the value 1. I have not verified if
prclPosition points to the screen coordinates of the focus window.
Before we can utilitize this information, we need to install the help hook
using the WinSetHook() function. (Don't forget to uninstall it before the
application exits using the WinReleaseHook() function.) Since the Help Manager
works by installing its own help hook, and since WinSetHook() puts the hook at
the beginning of the hook chain, you need to remember two things: 1) install
your hook after calling WinCreateHelpInstance(), and 2) always return FALSE to
insure that the next hook in the chain is called.
As an ending note, someone sent me a while back, detailed instructions on a
short cut which - if I remember correctly - eliminated the need for help hooks
to provide message box help. Unfortunately, I lost these notes.
Select this to go to the next section
ΓòÉΓòÉΓòÉ 4.2.7. Epilogue ΓòÉΓòÉΓòÉ
Now that we have all of the necessary information, we can start developing our
online help and documents. To compile your IPF source, the Developer's Toolkit
contains the IPFC compiler. It takes an IPF source file as input and compiles
it to a HLP binary file in the same directory. For online documents, you need
to specify the /INF switch which instead produces an INF binary file in the
same directory.
You will probably notice some limitations with the compiler.
o There is no symbolic substition a la the C preprocessor
o Numbers can only be expressed in decimal
o All artwork must be in the same directory as the source
The first one got so frustrating that I wrote my own IPFC preprocessor which
processes C-style include files and allows you to substitute the #define macros
in your IPF source as though it were a symbol. This preprocessor is included,
as well as the accompanying User's Guide (in INF format, of course). The
latter two problems could also be resolved by adding functionality to the
preprocessor, but those features were never implemented.
Select this to go to the next section
ΓòÉΓòÉΓòÉ 4.2.8. Summary ΓòÉΓòÉΓòÉ
Whew! A lot of information was presented here. Hopefully, you should be able
to reread the objectives of this article and know the answers to the implied
questions therein. The importance of online help cannot be understressed
because as computer systems and applications become more complex, the more
difficult it becomes to remember every feature that is provided. Online
documentation is also important in that it provides a good vehicle for
information dissemination and saves trees.
Select this to go to the next section
ΓòÉΓòÉΓòÉ 4.3. OS/2 Installable File Systems - Part 2 ΓòÉΓòÉΓòÉ
Written by Andre Asselin
Select this to go to the next section
ΓòÉΓòÉΓòÉ 4.3.1. Introduction ΓòÉΓòÉΓòÉ
Last time I went over some of the background information needed to write an
IFS. In this article, I'll continue on and examine a framework to write a
split ring 0/ring 3 IFS. This month I'm going to limit myself to just the code
that does initialization and communication between ring 0 and ring 3; it's
complicated enough to warrant a full article of it own. The things we will
cover are:
o How an IFS initializes
o How to communicate between ring 0 and ring 3
o How a request to the IFS gets handed up to the control program
This article is going to assume that you're familiar with the concepts of
programming at ring 0. If this seems a little scary, take heart - the next
article will finally get down to implementing the actual FS_* calls, and will
concentrate mostly on ring 3 code.
Select this to go to the next section
ΓòÉΓòÉΓòÉ 4.3.2. Project Layout ΓòÉΓòÉΓòÉ
The source code for the project is divided up into two directories, RING0 and
RING3 (included as IFSR0.ZIP and IFSR3.ZIP - Editor); RING0 holds the ring 0
source, and RING3 hold the ring 3 source. As I mentioned last time, all IFS's
must be 16-bit code, so for the source in the RING0 directory, I'm using
Borland C++ 3.1. The ring 3 side is 32-bit code, however, so for it I'm using
Borland C++ for OS/2. I haven't tried this code on any other compilers, but it
should be easily portable. One thing to note is that I'm compiling the code in
C++ mode and using the C++ extensions for one line comments and anonymous
unions (see for example R0R3SHAR.H). I also use one Borland #pragma for
forcing enumerations to be 16 bits. With a few modifications, this source
should work with any ANSI C compiler.
The contents of the RING0 directory are:
C0.ASM Stripped down Borland C++ startup code
FSD.H FS_* call prototypes and data structures
FSH.H FSH_* call prototypes and data structures
FSHELPER.LIB Import library for file system helpers
OS216.H Header file for 16-bit OS/2 Dos APIs
R0.CFG Borland C++ configuration file
R0.DEF Definition file for the linker
R0.MAK Make file for the IFS
R0COMM.C Routines to communicate with the control program
R0DEVHLP.ASM DevHlp interface routines
R0DEVHLP.H Header file for DevHlp interface routines
R0GLOBAL.C Global variable definitions
R0GLOBAL.H Global variable declarations
R0INC.H Main include file
R0R3SHAR.H Shared data structures between the IFS and control program
R0STRUCT.H IFS private structures
R0STUBS.C FS_* stub routines
The contents of the RING3 directory are:
FSATT.C Sample attach program
FSD.H FS_ call prototypes and data structures
R0R3SHAR.H Shared data structures between the IFS and control program
R3.CFG Borland C++ configuration file
R3.MAK Make file for the control program
R3COMM.C Routines to communicate with the IFS
R3GLOBAL.H Global variable declarations
R3INC.H Main include file
R3STUBS.C FS_* stub routines
The two directories are laid out pretty similarly. Some notes on the files:
o The Rx.MAK file is the make file for the directory, and also generates the
Rx.CFG file, which contains all the compiler switches for the compiler.
o RxGLOBAL.H contains declarations for all of the global variables used. The
IFS side also has R0GLOBAL.C which defines all of the global variables. The
control program side doesn't have a corresponding R3GLOBAL.C because it only
has one global variable that I thought was better put in R3STUBS.C.
o FSD.H and FSH.H are slightly modified versions of the files by the same name
distributed with the IFS documentation. They are modified slightly to
accommodate C++ and some clashes in naming conventions.
o R0R3SHAR.H contains definitions for the data structures that the IFS and
control program share between themselves. This file is a bit tricky to write
because you have to get the same results no matter if you compile with a
16-bit or 32-bit compiler. This means for example, that you have to
explicitly declare all your int's long or short so that both compilers do the
same thing.
o In order to call the DevHlp's from C, we need to write interface functions
for them. Files RING0\R0DEVHLP.H and RING0\R0DEVHLP.ASM contain the
prototypes and assembler functions for the DevHlp's that are used in the
code. The functions use the same parameter ordering as the ones in Steve
Mastrianni's book "Writing OS/2 2.0 Device Drivers in C", although some of
the typedefs and semantics are a bit different. I'd like to thank him for
graciously allowing me to use the same parameter layouts.
Select this to go to the next section
ΓòÉΓòÉΓòÉ 4.3.3. Communicating Between Ring 0 and Ring 3 ΓòÉΓòÉΓòÉ
The easiest and fastest way for ring 0 and ring 3 code to communicate is
through shared memory. The way I implemented it is to have the control program
allocate two buffers when it initializes: one buffer is used to hold all the
parameters for a given operation, and the other serves as a data buffer to hold
data for operations like FS_WRITE. After allocating the buffers, it makes a
special call to the IFS, which sets up a GDT alias for itself (we need to use
GDT selectors because the IFS can be called in the context of any process). In
more detail, what we do is:
When the IFS loads
Call the AllocGDTSelector DevHlp to allocate two GDT selectors. These will be
the selectors used by the IFS to get access to the control program's two
buffers. We allocate them now because GDT selectors can only be allocated at
initialization time.
When the control program loads
o Call DosAllocMem() to allocate memory for the two buffers to communicate
between the IFS and the control program.
o Call the IFS via DosFSCtl() and pass the addresses of the two buffers.
o The IFS calls the VMLock DevHlp to lock the buffers permanently into memory
(we need to do this because the next call requires it).
o Call the LinToGDTSelector DevHlp to map the memory to the GDT selectors we
allocated when the IFS loaded.
This isn't the only way we could've implemented this. We could've had the IFS
allocate the memory, for example, instead of the control program. It really
comes down to personal preference, because either works just as well.
Select this to go to the next section
ΓòÉΓòÉΓòÉ 4.3.4. A Shared Memory Protocol ΓòÉΓòÉΓòÉ
Once the buffers are allocated and accessible to both the ring 0 and ring 3
code, we need to set up some kind of protocol for its use. The control program
needs to know when a valid operation is in the buffers and ready to be
performed. The IFS needs to know when the buffers are in use, and when the
buffers contain the results of a completed operation. Again, there are several
ways to implement this. The method I chose involves using semaphores and
captive threads.
After the control program allocates the buffers and does any other
initialization, it calls the IFS through DosFSCtl(). The IFS sets up the ring
0 GDT aliases for the buffers, and then suspends the control program's thread
by making it wait on a semaphore (thus capturing it). To the control program,
it just looks like it made a system call that is taking a very long time.
When a request comes in to the IFS on another thread, it places the parameters
and data into the two buffers and releases the semaphore that the control
program's thread is blocked on. When that thread starts running again, the IFS
returns from the DosFSCtl() call to the control program, where it executes the
operation and places the results back into the buffer. It then calls the IFS
again, which blocks the control program on the semaphore and starts the whole
process over again.
The advantage of this approach is that whenever the control program is running,
it is guaranteed to have a valid operation in the buffer waiting to be
executed. Thus you never have to worry about semaphores in the control
program. This is especially nice because 16-bit and 32-bit semaphores are
incompatible.
Select this to go to the next section
ΓòÉΓòÉΓòÉ 4.3.5. The Semaphores ΓòÉΓòÉΓòÉ
Even though the control program doesn't have to worry about semaphores, the IFS
certainly does, and in a big way. It has to worry about serializing all the
requests it gets, and handling things like the control program unexpectedly
terminating. To do this, we employ four semaphores:
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
ΓöéName ΓöéMnemonic ΓöéStates Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
ΓöéCPAttached ΓöéControl Program Attached Γöé-1 = never attached, 0 = Γöé
Γöé Γöé Γöénot currently attached, 1Γöé
Γöé Γöé Γöé= attached Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
ΓöéBufLock ΓöéShared buffers are lockedΓöéClear = buffers not Γöé
Γöé Γöé Γöélocked, Set = buffers areΓöé
Γöé Γöé Γöélocked Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
ΓöéCmdReady ΓöéCommand ready to execute ΓöéClear = command ready, Γöé
Γöé Γöé ΓöéSet = command not ready Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
ΓöéCmdComplete ΓöéCommand is complete ΓöéClear = command is Γöé
Γöé Γöé Γöécomplete, Set = command Γöé
Γöé Γöé Γöénot complete Γöé
ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
CPAttached is used to indicate whether the control program is currently
attached to the IFS. A value of -1 indicates that it has never attached to the
IFS, 0 means it currently is not attached, but has been in the past, and 1
means it currently is attached. This semaphore is unique in that it is not a
system semaphore, but an int that is being used as a semaphore. The reason we
need to implement it this way will become clear when we start discussing the
code.
BufLock is used to serialize requests to the IFS. Whenever the IFS gets a
request, the request thread blocks on this semaphore until it's clear, at which
time it knows that its OK to use the shared buffers to initiate the next
operation.
CmdReady is the semaphore used to tell the control program that a request is in
the shared buffers and is ready to execute. The control program thread blocks
on this semaphore; a request thread clears this semaphore when a request is
ready.
CmdComplete indicates to the request thread that the command it initiated is
complete and that the results are in the shared buffers. It is cleared by the
control program thread when it calls back into the IFS after it completes an
operation.
Select this to go to the next section
ΓòÉΓòÉΓòÉ 4.3.6. IFS Initialization ΓòÉΓòÉΓòÉ
When OS/2 is booting and finds an IFS= line in the CONFIG.SYS, it will check
that the file specified is a valid DLL and that it exports all of the required
entry points for IFS's. If it is not a valid IFS, OS/2 will put up a message
and refuse to load it. If the IFS is valid, OS/2 will load it into global
system memory and then initialize it by calling FS_INIT (note that if the IFS
has a LibInit routine, it will be ignored).
RING0\R0COMM.C contains the code for the FS_INIT routine. Just like device
drivers, IFS's get initialized in ring 3. Because of the special state of the
system, an IFS can make calls to a limited set of Dos APIs (see table 1 for a
list of which ones are allowed). It can also call any of the DevHlp routines
that are valid at initialization time, but it cannot call any of the file
system helpers.
FS_INIT gets passed a pointer to the parameters on the IFS= line and a pointer
to the DevHlp entry point. The third parameter is used to communicate between
the IFS and the system's mini-IFS; we can safely ignore it.
The first thing our IFS does is call DosPutMessage() to put up a sign-on
message (it's a good idea to put up a message like this while you are still
debugging the IFS, but you should take it out in release versions). After the
sign-on banner is printed, we call a special routine to initialize the C
runtime environment. This is a stripped down version of the startup code that
comes with Borland C++; all it does is zero the BSS area and call any #pragma
startup routines. Strictly speaking, it is probably not necessary.
Next we save any parameters that were on the IFS= line in a global buffer and
save the address of the DevHlp entry point. Note that contrary to what the IFS
reference says, we have to check the szParm pointer before using it because it
will be NULL if there are no parameters. The reference leads you to believe
that it will point to an empty string, but that isn't true.
Next we allocate a small block of memory in the system portion of the linear
address space with the VMAlloc DevHlp (the system portion is global to all
processes, just like GDT selectors). This memory will be used to hold the two
lock handles that are created by the VMLock DevHlp when we lock down the memory
that is shared between the control program and the IFS. We have to allocate
the lock handles in the linear address range because VMLock can only put its
lock handles there. Since our code is 16-bit, the compiler doesn't know what a
linear address is. We deal with them by creating a new typedef, LINADDR, which
is just an unsigned long.
Next we also allocate two GDT selectors to alias the shared memory on the ring
0 side. This is done here because according to the PDD reference, you can only
allocate GDT selectors at initialization time (in fact, if you do it after
initialization, it still works, but why take the chance, right ?). We then
create pointers out of the GDT selectors and assign them to the two global
variables used to access the shared buffers. Note that at this point, no
memory is allocated! We have our pointers set up, but if we were to try and
access them, we'd get a TRAP D. We must wait for the control program to start
and call the IFS before we can put memory behind those GDT selectors.
After that's done, we set CPAttached to -1, which says that the control
program has never attached to the IFS. We'll see later why its important to
distinguish between when it has never attached, and when it has attached but
then detached.
Select this to go to the next section
ΓòÉΓòÉΓòÉ 4.3.7. Control Program Flow ΓòÉΓòÉΓòÉ
RING3\R3COMM.C contains the code to startup the control program. It first
prints a banner, just like the IFS, and then allocates and commits memory for
the two buffers. Once that is done, it puts the pointers to the two blocks of
memory in the structure that is passed to the IFS for initialization. Before
we call the IFS, though, we make a copy of the file system name in a temporary
buffer. The DosFSCtl() call can use three different methods to figure out
which IFS to call; we want to use the method where we specify the IFS's name.
To do that we have to make a temporary copy of the IFS name because DosFSCtl
could modify the buffer that contains the IFS name.
Once all the preparations are made, the control program calls the IFS to
initialize. To the control program it's really no big deal - just one
DosFSCtl() call. When the DosFSCtl() returns, it will either be because there
was an initialization error, or there was an operation waiting in the shared
buffers to be executed. If an error occurred, we just terminate the control
program (perhaps a more user friendly error message should be printed, but
after all, this is just a framework). If it returned because an operation is
ready, we enter the dispatch loop.
The dispatch loop figures out what operation was requested, and calls that
routine to execute it. Right now we only support the attach routine (which is
actually just a stub that returns NO_ERROR). If it gets a request for an
operation it doesn't understand, it prints an error message and returns
ERROR_NOT_SUPPORTED to the IFS.
Once the operation has been executed, we again copy the IFS name into a
temporary buffer and make a DosFSCtl() call to indicate that this operation is
complete, the results are in the shared buffer, and we're ready for the next
request. When that DosFSCtl() returns, another operation will be waiting in
the shared buffer.
Select this to go to the next section
ΓòÉΓòÉΓòÉ 4.3.8. Ring 0 Side of Control Program Initialization ΓòÉΓòÉΓòÉ
As mentioned above, the ring 3 side of the control program initialization is
very simple. The ring 0 side is a little more complicated, though. FS_FSCTL in
RING0\R0COMM.C contains the code for the initialization. FS_FSCTL is used to
provide an architected way to add IFS specific calls (sort of like the IOCTL
interface for devices). There are three standard calls, which we just ignore
for now. To those we add two new calls, FSCTL_FUNC_INIT and FSCTL_FUNC_NEXT.
FSCTL_FUNC_INIT is called by the control program when it initializes.
FSCTL_FUNC_NEXT is called when the control program has completed an operation
and its ready for the next one.
When FSCTL_FUNC_INIT is called, the first thing we do is check to see if the
control program is already attached. If it is, we return an error code (this
scenario could happen if the user tries to start a second copy of the control
program). If the control program isn't already running, we wait until the
BufLock semaphore is cleared. We do this because theoretically, we could run
into the following situation: a request comes into the IFS and it starts
servicing it. The control program is then detached, and then a new copy is run
and tries to attach. The IFS is still in the middle of trying to service that
request, however, and hasn't yet noticed the control program detached in the
first place. It could be really bad if that ever did happen because the shared
buffers would be corrupted, so we explicitly wait until the BufLock semaphore
is clear, meaning that there are no threads using the shared buffers. We have
to surround this with a check to see if the control program has ever been
attached, because if it hasn't, the BufLock semaphore will not be initialized.
Next we verify that the buffer that was passed to us is the proper size and
that it is addressable. We have to check addressability on everything that is
passed in from a ring 3 program because if it is not addressable, we bring down
the whole entire system.
Once addressability has been verified, we lock down the operation parameter
area, and put the returned lock into the memory we allocated at FS_INIT time.
Once that is done, we map the memory to the GDT selector that we allocated at
FS_INIT time. We then do the same for the data buffer. Once these operations
are complete, the memory can be shared between the IFS and the control program.
Once that is complete, we clear the BufLock semaphore to initialize the
semaphore that indicates that the shared buffer is not being used by anyone.
We then get the process ID of the control program. This is used by the FS_EXIT
routine. FS_EXIT is called whenever any process terminates. We have it check
the process ID of the process that is terminating against the process ID of the
control program, so that if the control program unexpectedly terminates, we
detach it properly.
After all that initialization is completed, CPAttached is set to 1 to indicate
that the control program is attached. We then fall through to FSCTL_FUNC_NEXT.
Since this function will be called every time an operation is completed, we
first ensure that the control program is attached. If it's not, we return an
error code. If it is attached, we first set the CmdReady semaphore to indicate
that a command is no longer in the shared buffers (instead, results are in the
buffers). We then clear CmdComplete to unblock the requesting thread (letting
it know that its results are waiting). We then wait on the CmdReady semaphore,
which will be cleared when a new operation is put into the shared buffers.
At any time, any of the semaphore calls could return ERROR_INTERRUPT if the
user is trying to kill the control program. If that occurs, we detach the
control program before returning the error code.
To detach the control program, we have to first set CPAttached to 0. We have to
do it first to avoid possible deadlocks. We then unlock the shared memory
buffers; if we don't do this, the control program will appear to die, but you
will never be able to get rid of its window. Finally, we clear the CmdComplete
semaphore so that if there is a request in progress, the requesting thread will
unblock.
Select this to go to the next section
ΓòÉΓòÉΓòÉ 4.3.9. An Example Call: Attaching a Drive ΓòÉΓòÉΓòÉ
Before you can use a drive managed by your IFS, you have to attach it. This
creates an association between a drive letter and the IFS. RING3\FSATT.C
contains an example program that attaches a drive. It is basically a front end
to the DosFSAttach() and DosQueryFSAttach() calls. With a little help from the
Control Program Programming Reference, you should be able to figure it out
easily.
The part that needs more explaining is the ring 0 side of the interface. When
you issue a DosFSAttach() or DosQueryFSAttach(), the file system router calls
the IFS's FS_ATTACH entry point (this can be found in RING0\R0STUBS.C). This
code is basically a prototype for all of the FS_* calls that the IFS handles.
It serializes access to the control program, does some preliminary validation
of the parameters, sets up the argument block and passes it to the control
program, waits until the control program executes the operation, and then
returns the results of the operation. Once the details of this call are
understood, all the others can be written pretty easily.
The first thing FS_ATTACH does is check to see if the control program is
attached; if it isn't, it immediately returns an error code. If the control
program is attached, it waits until it can get access to the shared buffers.
It is possible to time out waiting for this access; if we do, we return an
ERROR_NOT_READY return code to the caller.
Once we have access to the shared buffers, we wait until the control program
completes the last operation it started. We have to do this because it is
possible for a thread to give the control program a request to service, and
then time out waiting for it to complete it. We could then have another thread
come along and try to start a new request, but if the control program hasn't
finished the last one yet, the shared buffers will get trashed because the IFS
will be trying to put a new operation in them, and the control program will be
trying to put the results of the last operation in them. Therefore we must
wait until the control program has finished the last operation.
Once those verifications are completed, we check to make sure we can access the
buffer that was passed in. For an attach or detach request, all we have to do
is check for readability, but for the query attach request, we have to check
writability.
We then check that the control program is still attached. This check is
crucial because during any of those semaphore or FSH_PROBEBUF calls we could've
blocked, and the control program could've terminated. If it did, the shared
buffers are no longer valid, and if we try to access them we will trap. It's
for this reason that the CPAttached semaphore is an int and not a system
semaphore - the semaphore calls don't guarantee that they won't block (i.e.
they could block). To make absolutely sure, the only thing we can rely on is a
semaphore implemented as an int (it's probably worthwhile to refresh your
memory here that ring 0 code will never be multitasked - you have to explicitly
give up the CPU).
Once we have verified that the control program is still attached, and thus our
shared buffers are still valid, we setup the shared buffers with the
operation's parameters. You can refer to R0R3SHAR.H (in either RING0 or RING3)
for the data structure used. After that's complete, we clear the CmdReady
semaphore to unblock the control program and indicate to it that a request is
ready to be executed. We then block on CmdComplete waiting for the control
program to execute our request. We specify a time-out to the wait so that we
never get hung up on a faulty control program (if you never want to time out,
you can change the value of MAXCPRESWAIT to -1). If we should time out, we
release our hold on the shared buffer by clearing BufLock, and then return
ERROR_NOT_READY.
After the wait returns and we check for a time out, we also check to make sure
the control program is still attached. It is possible that while the control
program was executing our request that it terminated (maybe we had a bug that
caused it to trap). If so, the shared buffers are no longer accessible, so we
return an error code to the caller. If all went well, we copy the results out
of the result buffers. Note that while we are doing this, we can't do anything
that could cause us to yield because the control program could terminate during
that time.
After we copy the results out, we free up out hold on the shared buffers by
clearing the BufLock semaphore and then return the error code that the control
program told us to return.
Select this to go to the next section
ΓòÉΓòÉΓòÉ 4.3.10. And That's About It ΓòÉΓòÉΓòÉ
That about covers the communications between the ring 0 and ring 3 sides of an
IFS. If you're daring, you now have all the basics to forge ahead and begin
implementing this type of IFS. If this still seems a little scary, don't worry
- in the next article I'll fill in all the rest of the routines to give you a
true skeleton to work with, and start discussing how to implement the FS_*
calls. I will also provide a state diagram that shows all of the various
states the system can be in, along with the states of the semaphores, to show
that no deadlocks will occur in the IFS no matter what happens (this is
actually very important because a deadlock is extremely difficult to track
down, so you're better off investing time up front making sure they will never
occur than beating your head against a wall later trying to track one down).
I'd like to thank everyone who has written to encourage me to continue the
series or with ideas for topics you'd like me to cover. Since the only pay I
receive is your feedback, I hope you'll continue to write.
Select this to go to the next section
ΓòÉΓòÉΓòÉ <hidden> Dos APIs Callable at Initialization Time ΓòÉΓòÉΓòÉ
The following Dos APIs are callable by the IFS at initialization time:
o DosBeep
o DosChgFilePtr
o DosClose
o DosDelete
o DosDevConfig
o DosFindClose
o DosFindFirst
o DosFindNext
o DosGetEnv
o DosGetInfoSeg
o DosGetMessage
o DosOpen
o DosPutMessage
o DosQCurDir
o DosQCurDisk
o DosQFileInfo
o DosQSysInfo
o DosRead
o DosWrite
ΓòÉΓòÉΓòÉ 4.4. Programming the Container Control - Part 3 ΓòÉΓòÉΓòÉ
Written by Larry Salomon, Jr.
Select this to go to the next section
ΓòÉΓòÉΓòÉ 4.4.1. Back at the Batcave ΓòÉΓòÉΓòÉ
Last month we continued our disection of the container control and how to use
it. The tree view was added to our list of conquests, and we started
developing a sample application which we will continue to use. This month, we
will add more meat to the bones of our skeleton by learning about the detail
view and direct editing, among other things.
Select this to go to the next section
ΓòÉΓòÉΓòÉ 4.4.2. Detail View ΓòÉΓòÉΓòÉ
Back in the first installment of this series, the detail view was described in
the following manner.
Each object is represented as a detailed description of the object. The
information displayed is defined by the application.
While I realize that did not say much, it served to illustrate that the detail
view is the most flexible of the views, in terms of what can be displayed. It
should be logical then to assume that this means yet more setup on the part of
the application.
What is the Detail View?
To be precise, the detail view is a matrix view of the contents of a container,
where each row in the matrix is a separate object and each column is an
attribute (called a field) of every object to be displayed.
Since the objects are already added using the CM_ALLOCRECORD/CM_INSERTRECORD
messages, the columns must be added; this is done using the
CM_ALLOCDETAILFIELDINFO/CM_INSERTDETAILFIELDINFO messages. As with its
record-oriented counterpart, the CM_ALLOCDETAILFIELDINFO accepts the number of
fields to allocate memory for and returns a pointer to the first FIELDINFO
structure in the linked-list.
typedef struct _FIELDINFO {
ULONG cb;
ULONG flData;
ULONG flTitle;
PVOID pTitleData;
ULONG offStruct;
PVOID pUserData;
struct _FIELDINFO *pNextFieldInfo;
ULONG cxWidth;
} FIELDINFO, *PFIELDINFO;
Figure 1. The FIELDINFO structure.
cb specifies the size of the structure in bytes.
flData specifies flags (CFA_* constants) for the field, especially
the datatype.
flTitle specifies flags (CFA_* constants) for the column title.
pTitleData points to a string used for the column heading (can be
NULL).
offStruct specifies the offset of the data in the container record to
be formatted according to its datatype. The FIELDOFFSET
macro (defined in the Toolkit) is very helpful in
initializing this field.
When the datatype is CFA_STRING, the field in the container
record is expected to be a pointer to the string and not
the string itself. For example,
typedef struct _MYCNRREC {
MINIRECORDCORE mrcCore;
CHAR achText[256];
ULONG ulNumSold;
float fGrossIncome;
float fNetIncome;
float fTotalCost;
float fNetProfit;
CHAR achProdName[256];
PCHAR pchProdName;
} MYCNRREC, *PMYCNRREC;
we would specify FIELDOFFSET(MYCNRREC,pchProdName) instead
of FIELDOFFSET(MYCNRREC,achProdName). The reason for this
will be clear when we discuss direct editing.
pUserData points to any application-defined data for the field.
pNextFieldInfo points to the next FIELDINFO structure. This is
initialized by the CM_ALLOCDETAILFIELDINFO message.
cxWidth specifies the width in pixels of the field. This is
initialized by the CM_ALLOCDETAILFIELDINFO message to 0,
indicating that the field should be wide enough to show the
widest value. If the default is not used and the data is
too wide to fit, it is truncated when displayed.
The flData field is initialized using one or more CFA_* constants:
Data type
CFA_BITMAPORICON offStruct "points" to the handle of a bitmap or icon,
depending on whether or not CA_DRAWICON or CA_DRAWBITMAP
was specified in the flWindowAttr field in the
CM_SETCNRINFO message (CA_DRAWICON is the default if not
explicitly changed by the application).
CFA_STRING offStruct "points" to a pointer to the string to be
displayed. Only data of this type can be directly edited.
CFA_ULONG offStruct "points" to an unsigned long integer.
CFA_DATE offStruct "points" to a CDATE structure.
CFA_TIME offStruct "points" to a CTIME structure.
For the latter three, NLS support is provided automatically by the container.
You should note that there is no support for short integers, since they map
directly to long integers with no loss in precision, nor is there support for
floating point (none of PM uses floating point, so why should they start now).
The latter means that you have to also have a string representation of the
number (and creates all kinds of headaches if you will allow editing of the
value).
Alignment
CFA_LEFT Align the data to the left
CFA_CENTER Horizontally center the data
CFA_RIGHT Align the data to the right
CFA_TOP Align the data to the top
CFA_VCENTER Vertically center the data
CFA_BOTTOM Align the data to the bottom
Miscellaneous
CFA_SEPARATOR Displays a vertical separator to the right of the field
CFA_HORZSEPARATOR Displays a horizontal separator underneath the column
heading
CFA_OWNER Enables ownerdraw for this field
CFA_INVISIBLE Prevents display of this column
CFA_FIREADONLY Prevents direct editing of the data if CFA_STRING is the
datatype
The flTitle field is initialized using one or more of the alignment fields
and/or one or both of the following
Miscellaneous
CFA_BITMAPORICON pTitleData is the handle of a bitmap or icon, depending on
whether or not CA_DRAWICON or CA_DRAWBITMAP was specified
in the flWindowAttr field in the CM_SETCNRINFO message
(CA_DRAWICON is the default if not explicitly changed by
the application). If this is not specified, pTitleData is
expected to point to character data.
CFA_FITITLEREADONLY the text of the title is not directly editable.
What's Next?
Once you have initialized all of the FIELDINFO structures, you can "insert"
them into the container using the CM_INSERTDETAILFIELDINFO message. Again
using the parallel of the CM_INSERTRECORD message, it expects a pointer to the
first FIELDINFO structure as well as a pointer to a FIELDINFOINSERT structure.
typedef struct _FIELDINFOINSERT {
ULONG cb;
PFIELDINFO pFieldInfoOrder;
ULONG fInvalidateFieldInfo;
ULONG cFieldInfoInsert;
} FIELDINFOINSERT, *PFIELDINFOINSERT;
Figure 2. The FIELDINFOINSERT structure.
cb specifies the size of the structure in bytes.
pFieldInfoOrder specifies the FIELDINFO structure to be linked after, or
CMA_FIRST or CMA_LAST to specify that these FIELDINFO
structures should go to the head/tail of the list,
respectively.
fInvalidateFieldInfo specifies whether or not the fields are to be invalidated.
cFieldInfoInsert specifies the number of FIELDINFO structures being
inserted.
Finally, changing the view to detail view is as simple as - you guessed it -
sending the control a CM_SETCNRINFO message.
CNRINFO ciInfo;
ciInfo.flWindowAttr=CV_DETAIL;
WinSendMsg(hwndCnr,
CM_SETCNRINFO,
MPFROMP(&ciInfo),
MPFROMLONG(CMA_FLWINDOWATTR));
Figure 3. Changing to the detail view.
Note that, even if you initialize the pTitleData field of the FIELDINFO
structure to point to the column heading, the titles are not displayed unless
you specify CA_DETAILSVIEWTITLES in the flWindowAttr field.
Select this to go to the next section
ΓòÉΓòÉΓòÉ 4.4.3. Direct Editing ΓòÉΓòÉΓòÉ
Direct editing is accomplished by pressing the proper combination of keys
and/or mouse buttons as defined in the "Mappings" page of the "Mouse"
settings (in the "OS/2 System"/"System Setup" folder) while the mouse is over a
directly-editable region. When this is done, a multi-line edit control appears
and is initialized with the current text, in which you can make your changes;
the enter key acts as a newline, while the pad enter key completes the editing
operation and (normally) applies the changes.
From a programming perspective, three notifications are sent to the application
whenever direct-editing is requested by the user when over a non-read-only
field ("field" is used here to mean any text string and not as it was defined
in the discussion of the detail view) - CN_BEGINEDIT, CN_REALLOCPSZ, and
CN_ENDEDIT (in that order). For all three, mpParm2 points to a CNREDITDATA
structure which describes the state of the record being edited. The purpose of
CN_BEGINEDIT and CN_ENDEDIT is to notify the user that editing is about to
begin/end. However, only the CN_REALLOCPSZ is important, since the former two
can be ignored while the latter cannot.
typedef struct _CNREDITDATA {
ULONG cb;
HWND hwndCnr;
PRECORDCORE pRecord;
PFIELDINFO pFieldInfo;
PSZ *ppszText;
ULONG cbText;
ULONG id;
} CNREDITDATA;
Figure 4. The CNREDITDATA structure.
cb specifies the size of the structure in bytes.
hwndCnr specifies the handle of the container.
pRecord points to the record being edited.
pFieldInfo points to the FIELDINFO structure describing the field
being edited.
ppszText points to the pointer to the text being edited.
cbText specifies the size of the text.
id specifies which part of the container contains the text to
be edited and can be one of the following:
CID_CNRTITLEWND, CID_LEFTDVWND, CID_RIGHTDVWND,
CID_LEFTCOLTITLEWND, or CID_RIGHTCOLTITLEWND.
The CN_REALLOCPSZ indicates that editing is about to end and that the
application should allocate a new block of memory to contain the text.
ppszText double-points to the old text and cbText specifies the length of the
new text. If a new memory block is allocated, the pointer to the new memory
block must be stored in ppszText. Returning TRUE from this notification
indicates that ppszText points to a memory block sufficiently large enough to
hold cbText bytes and that the container should copy the new text to this
buffer. (I am not sure if cbText includes the null terminator - `\0')
Returning FALSE indicates that the changes should not be copied and should be
discarded.
Select this to go to the next section
ΓòÉΓòÉΓòÉ 4.4.4. Altered States ΓòÉΓòÉΓòÉ
As defined by CUA '91 (I think), an object in general can be in one or more of
five states (or none at all) - source, target, in-use, cursored, and selected.
A container record stores information on its current state in the flRecordAttr
(in both the RECORDCORE and MINIRECORDCORE structures) in the form of CRA_*
constants. Setting the state, however, is not a simple matter of setting this
field, since the container will have no way of knowing that you've changed the
field. Instead, you send the container a CM_SETRECORDEMPHASIS message which
updates this field in the record and updates the display of that record on the
screen.
Those who are "on the ball" will notice that there is no CRA_SOURCE constant
defined in the 2.0 Toolkit. This was inadvertently left out and should be
defined to be 0x00004000L in pmstddlg.h.
So what do all of these states mean?
CRA_CURSORED the record has the input focus.
CRA_INUSE the record (and thus the object) is in use by the
application.
CRA_SELECTED the record is selected for later manipulation.
CRA_SOURCE the record is a source for a direct-manipulation operation.
CRA_TARGET the record is a target for a direct-manipulation operation.
When you want to query all records with a particular emphasis type, you use the
CM_QUERYRECORDEMPHASIS message. This returns the next record that has the
specifies emphasis (or NULL if none exists).
Popup Menus
If you take a close look at the Workplace Shell, you will see all of these
states used in one way or another. A more interesting use is in conjunction
with popup menus; if the record underneath the mouse is not selected, it alone
is given source emphasis. If it is selected, all records that are selected are
given source emphasis. If no record is underneath the mouse, the container
itself is given source emphasis. After the appropriate record states have been
changed, WinPopupMenu() is called. Finally, the WM_MENUEND message is
intercepted to "un-source" the records that were changed. Broken down into
pseudo-code, this becomes:
1. Determine if the mouse is over a container record
o If so, check the selection state
- If the record is selected, add source emphasis to all selected records
- If the record is not selected, give it source emphasis only
o If not, select the enter container
2. Call WinPopupMenu()
3. Undo source emphasis changes
While determining if the mouse is over a record is easy when processing the
WM_CONTROL message, it is a bit more difficult when in the WM_CONTEXTMENU menu.
The solution, it would appear from looking at our arsenal of messages that we
can send to the container, would be to send the container a
CM_QUERYRECORDFROMRECT message, specifying the mouse position as the rectangle
to query. Looking a bit closer at the documentation reveals that the rectangle
has to be specified in virtual coordinates. What???
Virtual Coordinates
Okay, okay, everyone has probably heard of and is vaguely familiar with virtual
coordinates, or else you would not be in PM programming to begin with. The
container's notion of the origin in its coordinate system is somewhat awry,
unfortunately, and this confuses things; the origin is defined to be the screen
coordinate of the lower left corner of the container at the time the last
CM_ARRANGE message was sent.
So, you either have to keep track of when you send the container a CM_ARRANGE
message and perform all sorts of hocus pocus to remember where the origin is
supposed to be, or you can finish reading this sentence and discover that the
documentation for CM_QUERYRECORDFROMRECT is flat-out wrong. The rectangle
specified in this message is in window coordinates. Whew! That greatly
simplifies things, except that when in detail view the record returned is the
one above the one the mouse is over. Oh boy. Fortunately, we can calculate
the height of a record using the CM_QUERYRECORDRECT message, which we use to
adjust the mouse position before calling CM_QUERYRECORDFROMRECT.
Now that we have the record underneath the mouse, we can check its selection
state by examining the flRecordAttr field. If the record is selected, it is
probably more efficient to use the CM_QUERYRECORDEMPHASIS message to get all
selected records, but we already have this exquisite recursive search function,
so I used that instead. Another example of poor documentation is in
CM_SETRECORDEMPHASIS where it does not tell you that you can set the
container's source emphasis by specifying NULL for the record.
Finally, we call WinPopupMenu() and undo the source emphasis and - voila! -
we're done.
Select this to go to the next section
ΓòÉΓòÉΓòÉ 4.4.5. CNR3 - A Sample Application Revisited ΓòÉΓòÉΓòÉ
CNR3 builds upon CNR2 by adding detail view support, direct editing support,
and "proper" popup menu support. As with part II, landmarks have been added to
CNR3.C which are to point out things of interest. These landmarks are
described below.
Landmark 1
This is to point out the additions of the last four fields to the MYCNREDIT
structure and the addition of the pmcrMenu field to the CLIENTDATA structure.
Landmark 2
This points out the allocation, initialization, and insertion of the FIELDINFO
structures.
Landmark 3
This points out the new procedure for handling the context menu.
Landmark 4
This points out the correction for the bug in the CM_QUERYRECORDFROMRECT
message when in details view as described above.
Landmark 5
This points out the processing of the CN_REALLOCPSZ notification.
Landmark 6
This points out the addition of the detail view menu item.
Select this to go to the next section
ΓòÉΓòÉΓòÉ 4.4.6. Summary ΓòÉΓòÉΓòÉ
This month we learned a lot of things, namely how to setup the details view,
how direct editing is performed and what the container expects from the
application with regards to this, and how selection states are set, queried,
and used. We also saw how inadequate the documentation is when it contains so
many examples of incorrect or incomplete information.
Now you have enough information to use the container well. However, we're not
done yet; next month, I will try to figure out some of the more advanced
capabilities of the container such as record sharing and deltas. Stay tuned,
same Bat-time, same Bat-channel!
Select this to go to the next section
ΓòÉΓòÉΓòÉ 4.5. A Review of C++ Compilers ΓòÉΓòÉΓòÉ
Written by Gordon W. Zeglinski
Select this to go to the next section
ΓòÉΓòÉΓòÉ 4.5.1. Introduction ΓòÉΓòÉΓòÉ
This article, examines various real-world aspects of the primary C++ compilers
available for OS/2. The compilers included in this review are:
Borland C++ for OS/2 version 1.0
Watcom C/C++ version 9.5
IBM Cset++ version 2.0
EMX/gcc version 0.8g
I have omitted Zoretch C++ from this list because I do not have access to this
compiler. The last time I looked at this compiler, it did not include an OS/2
debugger.
These compilers will be compared on the bases of:
o Time to compile the test code
o Execution time of the test code
o Size of the resultant .EXE file
o Quality of bundled tools
o Bugs found
About the Tests
The test code consists of a series of matrix objects developed by the author.
These objects rely on both floating point operations and list manipulations
(integer math, pointer dereferencing, etc.). The object hierarchy makes
extensive use of virtual, pure virtual, and inline "functions". "Functions" in
this case refer to both operators and functions.
Select this to go to the next section
ΓòÉΓòÉΓòÉ 4.5.2. Compiler Overview ΓòÉΓòÉΓòÉ
In this section, I will examine the non-quantitative aspects of the various
compilers, i.e. how easy they are to install, ease of use, and quality of the
support tools. Given this, one should keep in mind that the following
discussion is subjective and, as such, is the opinion of the author.
Borland C++ for OS/2 version 1.0
This compiler is very similar to its windows counter part. Its IDE uses the
same basic design and the windows version. The package comes with both a
command line and PM-based compiler, a debugger, and documents on OS/2
programming in .INF format. Also, various on-line documents are provided to
help use the the tools.
I have found several bugs in the compiler. The following list is by no means
extensive.
1. The editor in the IDE has a tendency to drop line feed characters every so
often this leads to the occasional hard to find syntax error.
2. None of the PM functions are accessible using the <CTRL><F1> help hotkeys
from within the IDE.
3. The compiler has problems filling in virtual function tables under certain
instances. It leaves them as NULL. The results of calling a function in
this state is a protection violation error.
4. The debugger is next to unusable in my opinion. When debugging C++
programs (especially PM programs), one can expect to reboot their systems
very often. This is due to OS/2's single threaded message queue and the
inability to kill the debugger via repeated <CTRL><ESC>'s.
5. TLINK is unable to properly handle segments with IOPL permission, resulting
is a protection violation. There was a work around for this problem, but I
have not tried it because bug #3 prevents me from using this compiler in
the application that needs IOPL priviledges.
The Resource Workshop that ships with this version is similar to its Windows
counterpart but lack much of its nice features. For instance, when editing
menus one is simply typing a resource script into a large entry field control.
The only resources that are edited visually are the dialog boxes, bitmaps and
icons; however, the icons no longer work under OS/2 2.1 and the bitmap editor
has some bugs in it that cause stray pixels to appear. The dialog editor is
great, though, and is far better than the one IBM supplies with CSet++.
This version lacks OWL and the profiler which ships with its DOS/Windows
counterpart. On the positive side, this is a fast compiler. It compiles
source files faster than any other compiler tested. Also, it seems that there
has been some bugs fixed in its optimizer because code that would bomb under
the DOS compiler when optimized now works under the OS/2 compiler.
Tech support from Borland is available through Compuserve. I have heard that
they no longer provide tech support to Internet mail addresses but cannot say
if this is true. Past experience with them was a little disappointing. If you
report a bug that has been fixed, they will tell you about a patch if it
exists. Bug fixes integrated into the product, though, are usually done in a
future version, which you have to purchase to get these fixes.
Watcom C/C++ version 9.5
The Watcom compiler ships with a modified version of the OS/2 Toolkit version
2.0. It generates code for 32 bit DOS, Windows (with a DOS extender), NT and
OS/2. If multi-platform code generation is a must for you, then this is the
compiler you want. It includes a text mode debugger and profiler. Another plus
for this package is that it is capable of optimizing for the Pentinum
processor. Also included in the package is quite the hefty stack of
documentation. However, in order to program in any of the above environments,
one still has to buy the appropriate programming guides for the target
operating systems. Shipped documentation includes:
o WATCOM C/C++32. Optimizing User's Guide
o WATCOM C/C++ Tools User's Guide
o WATCOM VIDEO User's Guide
o WATCOM VIDEO User's Guide Addendum
o WATCOM Linker User's Guide
o Supplement to WATCOM Linker User's Guide
o WATCOM C Library Reference
o WATCOM C++ Class Library Reference
o WATCOM C++ Container Class Library Reference
o WATCOM C Graphics Library Reference
o The C++ Programming Language
o WATCOM C Language Reference
o WATCOM C/C++32 Commonly Asked Questions & Answers
Although being a VIO mode app, the debugger is quite powerful. In addition to
the typical features associated with a source-level debugger, it also allows
debugger instructions to be executed at break points. However, there does not
seem to be any C++ specific functionality, e.g. class browsers.
The profiler included is also a VIO mode application and is divided into two
separate parts: the sampler and sample displayer. The sampler uses the system
clock to periodically interrupt the executing program and see where it is. It
also allows the programmer to set "profile points" or "marks" within the
source code, allowing it to be used as either an intrusive or non-intrusive
profiler. Because the profiler does not use any device drivers, it is likely
that under OS/2 the sample rate is only approximately 32 Hz, due to the fact
that the profiler can only use the standard API calls to gain access to the
timer interrupts.
At any rate, the non-intrusiveness of the profiler is a negative point when it
comes to profiling existing C++ code since in any OOP language, it is common to
have many small functions that are frequently called. The only way to
determine the impact of these functions is to place marks around them. For
large amounts of code, this is undesirable because it has to be done manually.
The sample displayer is pretty basic. It only gathers and displays time and
frequency related data.
One of the things which bothers me the most about the product is that the
Watcom linker does not use standard .DEF files.
In the few days that I have tested this package, the following bugs were found:
1. The compiler has problems dealing with the construct virtual operator=. I
was able to work around this bug so that I could perform the benchmarks.
2. I could not get it to work properly with the Workframe version 1.1. This
is probably due to the fact that I'm using the beta version of the
Workframe from the last PDK until my copy of CSet++ arrives.
Having found what I consider to be a serious bug (#1 above), I decided to give
their tech support a try, provided through email. My first bug report was
answered within 2 hours and subsequent email were all answered the same day.
One day after sending them the code, I got a reply. Unfortunately for me,
there is a grey area in the C++ "standards" that revolve around the use of
virtual operator=(). Watcom follows the approach taken in MSC 7.0 which is
different than just about every other C++ compiler. Watcom will be bringing
this area to the attention of the ANSI C++ standards committee.
IBM CSet++ version 2.0
Now that my copy of the GA version of CSet++ has arrived, I can compare it to
the release version of the others and to the beta version some of you may have.
The package includes the OS/2 toolkit, IBM's Workframe, and the CSet++
compiler. I'll concentrate on the compiler and it's support tools.
The comparison of the GA to the BETA version of this compiler can be summarized
in one sentence: the GA version compiles code about 2-3 times faster and it's
tools are far more stable.
One of the most important things (for some of my applications) about this
compiler and the accompanying linker is that it is capable of interfacing with
IOPL code segments. I have not tested Watcom's ability in this area. I also
believe that the EMX package has input/output functions. Like Watcom, this
compiler is compatible with version 3.0 of the C++ specifications, meaning that
they support exceptions among other things. The Borland compiler does not and
I'm not sure about the EMX package.
The debugger is PM-based and has object browsing abilities built into it. It
also has features which are specific to debugging PM-based programs (ie.
monitoring message queues and such). My biggest problem with any PM-based
debugger is that, if it is buggy, it can hang both the system queue and itself
such that a reset is required. It does not appear to have the ability to
program actions at breakpoints like the Watcom compiler does.
After much use, the debugger performed almost flawlessly. I have found that it
sometimes kills itself when the program monitor (a variable browser) is packed
with variables. The best feature it has is its ability to save all of the
settings and restore them when you resume debugging. These settings include
breakpoints, user defined messages, etc. I have found a minor bug in its user
defined messages - it did not properly display the message parameters after
restarting the debugger although it did remember the messages I defined.
The profiler is excellent, feature wise, and is PM-based. It is an intrusive
analyzer that requires one to compile the code with both debugging info and
profiling hooks turned on. The programmer can also insert profiling points
within the code. The profiler is a full featured execution analyzer capable of
measuring time, the number of times a profiler hook was called, and displaying
a call tree. The time and frequency data can be displayed in a number of
formats. The call tree displays which functions called who and the number of
times these calls were made. It has problems, however; I have found that one
of its display options will not function with a trace file I generated. Even
worse is that some of its display modules do not support long filenames, which
is unacceptable.
The object browser is also quite useful; it allows a graphical and textual
exploration of the classes used in the program. It displays the relationship
between these objects by showing inheritance, who calls who, and class members.
I have not used the PM object library that comes with this compiler because I
am creating my own library. Others have complained that there is a lot of
overhead in using this library, though, and that it takes along time to compile
code that uses it.
My biggest complaint about this product is its so-called documentation. I
bought the 3.5" format package hoping to find tons of hard copy manuals. To my
surprise, the hard copy documentation is very similar in size to the
documentation that came with OS/2 2.0 GA. In several places, the hard copy
documentation refers the reader to the on-line help. The only hard copy
manuals with some thickness to them are:
o Class Libraries Reference Summary
o User Interface Class Library User's Guide (an IBM Red Book)
o Programming Guide
The rest of the manuals seem to have trivial content; if you were really stuck,
you would typically be referred to the on-line help for the product or some
other on-line file. There are also a few typo's in the manuals.
Latest Bug Fixes
My complaints about the lack of long filename support in EXTRA (the profiler)
have been solved by applying CSD level 0001 to the package and then running the
following little REXX program, which sets a flag in the EXTRA executables that
allow them to see files with long names.
Note: This REXX file assumes that the program EXEHDR.EXE is on the path (it is
included with the toolkit).
/* This exec addes the "newfiles" flag to the header of each of the
Extra executables. This allows the user to use long names for trace
files */
"EXEHDR /NEWFILES IXTRA.EXE"
"EXEHDR /NEWFILES ITIME.EXE"
"EXEHDR /NEWFILES IEXCDENS.EXE"
"EXEHDR /NEWFILES ICALNEST.EXE"
"EXEHDR /NEWFILES ISTATS.EXE"
"EXEHDR /NEWFILES IDCGRAPH.EXE"
EMX/gcc version 0.8g
The EMX/GNU compiler package is a very impressive freeware compiler. The
package includes the GNU gcc version 2.4.5 compiler and a debugger. A bunch of
other Unix-like tools are also included. The debugger is a VIO program which
is command line driven. I haven't used it for PM programming or interfacing
with OS/2's API. The docs for the package are available via ftp from the
primary OS/2 FTP sites. The reader can get these documents for themselves if
they so desire.
Select this to go to the next section
ΓòÉΓòÉΓòÉ 4.5.3. Summary of Features ΓòÉΓòÉΓòÉ
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé ΓöéBorland C++ forΓöéWatcom C/C++ ΓöéIBM CSet++ ΓöéEMX/gcc Γöé
Γöé ΓöéOS/2 Γöé Γöé Γöé Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
ΓöéMulti-Platform ΓöéNo Γöé32 bit OS/2, ΓöéNo ΓöéOS/2, DOS Γöé
Γöé Γöé ΓöéDOS, Windows, Γöé Γöé Γöé
Γöé Γöé ΓöéNT Γöé Γöé Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
ΓöéDebugger ΓöéPM ΓöéVIO ΓöéPM ΓöéVIO Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
ΓöéProfiler ΓöéNo ΓöéVIO ΓöéPM ΓöéNo Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
ΓöéC++ Level Γöé2.1 Γöé3.0 Γöé3.0 Γöé2.1(?) Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
ΓöéPrecompiled Headers ΓöéYes ΓöéNo ΓöéYes ΓöéNo Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
ΓöéTech Support Γöécompuserve ΓöéInternet email ΓöéInternet email ΓöéNo official Γöé
Γöé Γöé Γöé Γöé Γöésupport Γöé
ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
Select this to go to the next section
ΓòÉΓòÉΓòÉ 4.5.4. On with the Benchmarks ΓòÉΓòÉΓòÉ
The benchmarking was performed on a 486 DX/33-based machine with 16M of RAM.
The test consist of two programs both are related to my work on object oriented
matrix classes. One test (the full matrix) is mostly floating point while the
other (the sparse matrix) is a combination of list manipulation and floating
point. Listed are the times it took to compile the two first test both with
and without optimizations enabled is measured, the times it took to execute the
each test program, and the size of the resulting executables.
Note: in the following charts, (opt) means that optimization was turned on
Compile Times
These times are measured with a stopwatch and represent the time it took NMAKE
or MAKE (in the case of EMX) to create the executable. The source code
consists of approximately 11 files for each test.
Note: For each table below, all times are in seconds and all sizes are in
bytes.
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé ΓöéBorland ΓöéWatcom ΓöéIBM ΓöéEMX/gcc ΓöéBorland(opt)ΓöéWatcom(opt)ΓöéIBM(opt) ΓöéEMX/gcc(opt)Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
ΓöéFull Matrix Γöé106 Γöé240 Γöé161 Γöé270 Γöé122 Γöé259 Γöé204 Γöé288 Γöé
ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
Executable size
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé ΓöéBorland ΓöéWatcom ΓöéIBM ΓöéEMX/gcc ΓöéBorland(opt)ΓöéWatcom(opt)ΓöéIBM(opt) ΓöéEMX/gcc(opt)Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
ΓöéFull Matrix Γöé86549 Γöé137532 Γöé147328 Γöé176132 Γöé83477 Γöé145622 Γöé125552 Γöé118788 Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
ΓöéSparse Matrix Γöé113173 Γöé156712 Γöé171344 Γöé229380 Γöé107541 Γöé164775 Γöé147104 Γöé139628 Γöé
ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
Execution Time
The full test measures the time to LU decompose a 200 x 200 matrix. The sparse
test measures the time to LU decompose and solve a 800 x 800 sparse matrix.
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé ΓöéBorland ΓöéWatcom ΓöéIBM ΓöéEMX/gcc ΓöéBorland(opt)ΓöéWatcom(opt)ΓöéIBM(opt) ΓöéEMX/gcc(opt)Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
ΓöéFull Matrix Γöé8 Γöé8.33 Γöé11 Γöé11.67 Γöé6.33 Γöé6.00 Γöé7.67 Γöé5.33 Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
ΓöéSparse Matrix Γöé75.5 Γöé51.5 Γöé102.5 Γöé109 Γöé67.5 Γöé48.5 Γöé50.5 Γöé50.0 Γöé
ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
I've noticed that while running the sparse test, the Watcom compiler required
about 8M of RAM. All the other compilers produced executables that only
required about 2M of RAM when executed, which requires further investigation.
All of the compilers except Borland performed equally well in the sparse test.
Select this to go to the next section
ΓòÉΓòÉΓòÉ 4.5.5. Closing Remarks ΓòÉΓòÉΓòÉ
When I began this series of tests, I was hoping that there would be a clear
"winner." However, I do not think that one exists; each of the products have
their own unique qualities which will appeal to different users. I strongly
feel that for people looking simply for an OS/2 C++ compiler with excellent
tools, CSet++ is the right choice. For others, I hope the guidelines below
will help.
As a side note, I am very impressed with the EMX/GCC package. It is as good as
any of the others and costs nothing. If I didn't need a profiler and want
precompiled header files, I'd definetly save some cash and use it.
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
ΓöéDesired Usage ΓöéCompiler(s) Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
ΓöéInterfacing with ΓöéCSet++, Watcom C/C++ or EMX/GCC(?) Γöé
ΓöéIOPL code Γöé Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
ΓöéFloating Point ΓöéEMX/GCC, Watcom C/C++, or CSet++ Γöé
Γöéintensive code Γöé Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
ΓöéPM programming ΓöéCSet++ or Borland (because of precompiled Γöé
Γöé Γöéheader files). Any of the 4 are quite able inΓöé
Γöé Γöéthis area. Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
ΓöéC++ code with Γöé EMX/GCC or CSet++ (Watcom follows the MSC Γöé
Γöéworking virtual Γöéway of handling the operator=() ) Γöé
Γöéfunctions Γöé Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
ΓöéProfiler ΓöéCSet++ or Watcom C/C++ Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
ΓöéEnvironment like BC ΓöéBorland C++ for OS/2 Γöé
Γöé3.1 Γöé Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé Really fast Γöé Borland C++ or CSet++ (CSet++ is slower but Γöé
Γöécompilation Γöéit's debugger is better) Γöé
ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
Select this to go to the next section
ΓòÉΓòÉΓòÉ 4.5.6. Editor's Notes ΓòÉΓòÉΓòÉ
After mentioning in comp.os.os2.programmer.misc that this issue would contain a
comparison of C++ compilers, I received a request from Tim Francis in the
C-Set++ compiler group to review the article pre-press. I gladly sent him a
copy with the stipulation that the article would not be modified based on any
comments he made, to which he agreed. However, I feel it necessary to put
these comments here, as a side-note, with the intent of demonstrating the
apparent customer-driven attitude of this group. As a disclaimer, while I do
know some people on the compiler development team, I have never had any
business dealings with them and am not doing this as a favor to them or because
of any bias I have.
Tim Francis writes:
I didn't find anything really wrong in the compiler review. I thought C-Set++
came out looking quite good, actually. I would be interested in seeing the
actual code used in the execution benchmarks - we were last in the opt/full
matrix test, and 2nd last in the opt/sparse matrix test. I'm certainly not
trying to dispute these numbers, but our benchmarks tend to place us a little
higher in the competition than that. Our performance evaluation guy would
really like a look at what's happening, to see if we can improve anything.
The only other comment I have is in the summary of features, Tech support
column. As documented in the C-Set++ package, we offer the following support:
o Non-defect support, and informal (best effort) defect support
- Compuserve
- Internet
- Talklink
o Formal defect support
- 1-800 number, available 24hrs/day, 7 days/week.
Obviously we feel that the support we offer is a key component of the product,
so if you mention the above somewhere in EDMI I'd appreciate it.
For the purposes of completeness, I am also including the following from Ian
Ameline:
If possible, could you have those floating point tests run with /Gf+ turned on
- it will result in *much* faster FP code by relaxing our strict interpretation
of the IEEE standard. The other compilers all use the more relaxed
interpretation - and this places us at a bit of a performance disadvantage
compared to them, but we do produce results that are the same as any other IEEE
64 bit FP processor (Of course the Intel one is uses 80 bits of precision
naturally, and if we try to conform to the 64 bit standards, we have to
truncate the numbers each time they're stored to a variable. This truncation
is expensive)
Also, I'll make sure the long filename bug in Extra is fixed for CSD 2.
Ian's comment about the compiler's strict interpretation of IEEE standards
prompted me to request that the author use the /Gf+ option for the optimized
part of the benchmarks (he was using /Gf); he did so and reported that /Gf and
/Gf+ resulted in no difference in time (it appears that /Gf invokes the default
which is /Gf+), while /Gf- resulted in an time increase of 1.5 seconds.
Select this to go to the next section
Select this to go to the next section
ΓòÉΓòÉΓòÉ 5. Columns ΓòÉΓòÉΓòÉ
The following columns can be found in this issue:
o Scratch Patch
ΓòÉΓòÉΓòÉ 5.1. Scratch Patch ΓòÉΓòÉΓòÉ
Welcome to this month's "Scratch Patch"! Each month, I collect various items
that fit into this column sent to me via email. The ones that I feel contribute
the most to developers, whether in terms of information or as a nifty trick to
tuck into your cap, get published in this column.
To submit an item, send it via email to my address - os2man@panix.com - and be
sure to grant permission to publish it (those that forget will not be
considered for publication). This month, we have the following:
o Questions and Answers
o Snippet(s) of the Month
o Documentation Chop Shop
o Want Ads
ΓòÉΓòÉΓòÉ 5.1.1. Questions and Answers ΓòÉΓòÉΓòÉ
Brian Stark (stark@saturn.sdsu.edu) writes:
While building entry fields I noticed the style option ES_AUTOTAB, which moves
the cursor to the next "control window" automatically when the maximum number
of characters are entered (Ref. OS/2 2.0 Technical Library, Programming Guide
Volume II). My assumption is that this is done by sending a WM_CONTROL message
to the current control windows owner. However I was unable to verify this in
the document, or any other document. Currently I am only able to establish
control of an entry field using the mouse, I would like to be able to use the
arrow keys and the tab to move from field to field. Is the application
responsible for this? If so, is there a document available that gives a clear
description of this process, or am I just doing something wrong when I create
the fields.
After checking the toolkit header files (mainly pmwin.h) and not finding any
notifications that would indicate what you are hoping (EN_* constants), a quick
test application yielded that no WM_CONTROL messages are indeed sent that
indicate the auto-tab feature has been invoked. However, the EN_KILLFOCUS and
EN_SETFOCUS notifications are sent to the entryfield losing the focus and to
the one receiving the focus, respectively.
While these are not sent only when the auto-tab takes place (a mouse click in
another entryfield will generate the same two notifications), a little thought
and hocus-pocus will help you figure out how to do what you want to do.
Regarding arrow keys and tabs, the system treats the keys in the following
ways:
Tab/Down arrow Moves the focus to the next control ("next" is defined
using Z-order) with the WS_TABSTOP style.
Backtab/Up arrow Moves the focus to the previous control ("previous" is
defined using Z-order) with the WS_TABSTOP style.
In entryfields, the left/right arrows maneuver the cursor within the control.
Dominique Beauchamp (beaucham@phy.ulaval.ca) writes:
What is the difference between a "string" and a "message" in a resource file?
Each can be loaded with WinLoadString and WinLoadMessage but afterwards it
seems we can use them the same way. If I want to program an error message box,
should I use "string" or "message" to do it? (No, it's not obvious!)
To be honest, often times I have asked the same question. Within PM, there
seem to be a few items whose usefulness are questionable (this being one of
them). To my knowledge, there is no difference between the two; one of the two
might exist for historical reasons (SAA comes to mind), or there might be other
logic at work here. In any case, I personally prefer WinLoadString since its
name implies that it is used for more than just messages; whatever you choose,
be consistent in your coding.
ΓòÉΓòÉΓòÉ 5.1.2. Snippet(s) of the Month ΓòÉΓòÉΓòÉ
Since I announced this new section this month, I would be expecting a lot if I
wanted to put user-submissions here. Thus, here are some of my favorite
subroutines:
SHORT winDisplayMessage(HWND hwndParent,
HWND hwndOwner,
ULONG ulStyle,
HMODULE hmDll,
USHORT usId,
USHORT usHelpId,...)
//-------------------------------------------------------------------------
// This function puts a message to the screen in PM mode (using WinMessageBox).
// The title of the message box is assumed to have the message id usId|0x8000.
//
// Input: hwndParent - handle of the parent window
// hwndOwner - handle of the owning window
// ulStyle - specifies the WinMessageBox styles
// hmDll - handle of the DLL containing the message
// usId - specifies the id of the message to load
// usHelpId - specifies the id of the corresponding help panel
// Returns: TRUE if successful, FALSE otherwise
//-------------------------------------------------------------------------
{
CHAR achMsg[1024];
CHAR achTitle[256];
va_list vlArgs;
CHAR achBuf[2048];
ULONG ulRc;
if (ulStyle==0L) {
ulStyle=MB_INFORMATION | MB_OK;
} /* endif */
if ((ulStyle & MB_SYSTEMMODAL)==0) {
ulStyle|=MB_APPLMODAL;
} /* endif */
if (usHelpId!=0) {
ulStyle|=MB_HELP;
} /* endif */
ulStyle|=MB_MOVEABLE;
//----------------------------------------------------------------------
// Load the message box text and title
//----------------------------------------------------------------------
if (WinLoadString(NULLHANDLE,
hmDll,
usId,
sizeof(achMsg),
achMsg)==0) {
return MBID_ERROR;
} /* endif */
if (WinLoadString(NULLHANDLE,
hmDll,
usId | 0x8000,
sizeof(achTitle),
achTitle)==0) {
return MBID_ERROR;
} /* endif */
//----------------------------------------------------------------------
// Format the message and display it
//----------------------------------------------------------------------
va_start(vlArgs,usHelpId);
vsprintf(achBuf,achMsg,vlArgs);
va_end(vlArgs);
ulRc=WinMessageBox(hwndParent,
hwndOwner,
achBuf,
achTitle,
usHelpId,
ulStyle);
return ulRc;
}
VOID winCenterWindow(HWND hwndCenter)
//-------------------------------------------------------------------------
// This function centers the window within its parent
//
// Input: hwndCenter - handle of the window to center
//-------------------------------------------------------------------------
{
SWP swpCenter;
RECTL rclParent;
WinQueryWindowPos(hwndCenter,&swpCenter);
WinQueryWindowRect(WinQueryWindow(hwndCenter,QW_PARENT),&rclParent);
swpCenter.x=(rclParent.xRight-swpCenter.cx)/2;
swpCenter.y=(rclParent.yTop-swpCenter.cy)/2;
WinSetWindowPos(hwndCenter,NULLHANDLE,swpCenter.x,swpCenter.y,0,0,SWP_MOVE);
}
ΓòÉΓòÉΓòÉ 5.1.3. Documentation Chop Shop ΓòÉΓòÉΓòÉ
Problem with BN_PAINT
I have a confession to make: I have yet to upgrade my machine at work to OS/2
2.1, so the following problem might have been fixed in 2.1. I will try to
remember to check at home, but if anyone else knows the answer already I would
appreciate email. The problem is with buttons created with the style
BS_USERBUTTON; the documentation states that, when the button needs to be
repainted, you will receive a BN_PAINT notification and mpParm2 will point to a
USERBUTTON structure which contains four fields:
hwnd handle of the button window
hps presentation space in which drawing should be performed
fsState the current state of the button
fsStateOld the previous state of the button
According to the documentation, the fields fsState and fsStateOld can be one of
three values - BDS_DEFAULT, BDS_HILITED, or BDS_DISABLED. When creating a
32-bit application utilizing "ownerdraw" buttons, this did not seem to work, so
I added a few calls to fprintf() and below is what I got (the labelling of the
events were added later):
Upon window creation
--------------------
Button state = 0x00000000
Button state (old) = 0x00040010
First down
----------
Button state = 0x00000100
Button state (old) = 0xD0DF032B
First up
--------
Button state = 0x01000000
Button state (old) = 0x01000100
Second down
-----------
Button state = 0x00000100
Button state (old) = 0xD0DF032B
Second up
---------
Button state = 0x01000000
Button state (old) = 0x01000100
If you will accept the notion that my code is correct, you can see that the
documentation appears to be completely wrong. I tried to reinterpret the
values but still I ran into problems. Several calls to printf() later, a
pattern emerged. I quickly followed my hunch and all of my problems went away.
IBM defined the USERBUTTON structure incorrectly! The fsState and fsStateOld
fields which are defined as ULONG's should be USHORT's instead. That
simplified the problem to having to undefine BDS_DEFAULT (0x0400) and
redefining it as 0x0000.
Workaround
The workaround should be obvious - either change your toolkit header files or
define your own structure and redefine the BDS_DEFAULT constant. The former is
preferred since you will not have to "kludge" every program that utilizes
user-buttons to get this to work.
ΓòÉΓòÉΓòÉ 5.1.4. Want Ads ΓòÉΓòÉΓòÉ
My apologies for all of you who have sent requests for other topics that I did
not place below. My memory is getting fragile in my old age. (*grin*) These
seem to be good topics to write on; I have tried to assign some weighting on
the "hotness" ("heat" just doesn't convey the idea properly, so I made up a new
word) of the topic, so feel free to write on the really hot ones.
Anything on Rexx/2 (hot) - many people have requested more articles on Rexx/2.
I, for one, would like to see how to write external functions encased in DLL's,
but other topics include interfacing Rexx/2 with C (as in a macro language),
writing "Enhanced Editor" macros in Rexx/2, and interfacing with the Workplace
Shell from Rexx/2.
Using Input Hooks (hot) - this is a complicated topic which is brought up
frequently in the comp.os.os2.programmer.misc newsgroup.
Hit testing (warm) - one reader noted that the Jigsaw sample in both the IBM
and Borland toolkits (are they not the same?) perform there own correlation and
wondered why? Charles Petzold, in his OS/2 book "Programming the OS/2
Presentation Manager" briefly describes correlation and hit-testing, but does
not go into any detail nor does it describe the Gpi functions used for this
purpose.
Animation (warm) - a few readers expressed an interest in the various animation
techniques that can be applied to PM applications. The ultimate article, in my
opinion, would be one that develops a sprite library a la the Commodore 64's
(and Amiga's?) built-in routines, since this is probably the hardest component
of any good animation sequence.
Client/Server (warm) - using either named pipes (with or without a network) or
sockets, client/server programming is all-the-rage these days. Some time ago,
I started development on a post-office and a named-pipe implementation of FTP;
maybe I will get time to finish them and will write articles on them.
Multiple Threads in a PM application (warm) - this is another complicated topic
which is brought up from time to time in the comp.os.os2.programmer.misc
newsgroup. While various solutions to the dilemma of communication without
global variables have been discussed, it would be nice to see (one or more of)
them in a more permanent place than a news server.
Select this to go to the next section
ΓòÉΓòÉΓòÉ 6. Future Attractions ΓòÉΓòÉΓòÉ
As always, we are always looking for (new) authors. If you have a topic about
which you would like to write, send a brief description of the topic
electronically to any of the editors, whose addresses are listed below, by the
15th of the month in which your article will appear. This alerts us that you
will be sending an article so that we can plan the issue layout accordingly.
After you have done this, get the latest copy of the Article Submission
Guidelines from ftp.cdrom.com in the /pub/os2/2_x/program/newsltr directory.
(the file is artsub.zip) The completed text of your article should be sent to
us no later than the last day of the month; any articles received after that
time may be pushed to the next issue.
The editor's can be reached at the following email addresses:
o Steve Luzynski - sal8@po.cwru.edu (Internet), 72677,2140 (Compuserve).
o Larry Salomon - os2man@panix.com (Internet).
Since Steve is incapacitated at the moment, Larry is the preferred contact at
this time.
Select this to go to the next section
ΓòÉΓòÉΓòÉ 7. Contributors to this Issue ΓòÉΓòÉΓòÉ
The following people contributed to this issue in one form or another (in
alphabetical order):
o Andre Asselin
o Larry Salomon, Jr.
o Gordon Zeglinski
o Network distributors
ΓòÉΓòÉΓòÉ 7.1. Andre Asselin ΓòÉΓòÉΓòÉ
Andre Asselin recently graduated Cum Laude from Rensselaer Polytechnic
Institute with a Bachelor of Science degree in Computer Science. He has worked
with OS/2 since version 1.3, and also has extensive experience with MS-DOS and
Microsoft Windows. He currently works in IBM's OS/2 TCP/IP Development group
in Raleigh NC, where his responsibilities include the NFS client, a remote file
system implemented as an IFS.
Andre is also a member of Alpha Sigma Phi Fraternity, and enjoys hockey,
soccer, and a good science fiction novel. He can be reached via email at
asselin@vnet.ibm.com or on CompuServe at 71075,133.
ΓòÉΓòÉΓòÉ 7.2. Larry Salomon, Jr. ΓòÉΓòÉΓòÉ
Larry Salomon wrote his first Presentation Manager application for OS/2 version
1.1 in 1989. Since that time, he has written numerous VIO and PM applications,
including the Scramble applet included with OS/2 and the I-Brow/Magnify/Screen
Capture trio included with the IBM Professional Developers Kit CD-ROM currently
being distributed by IBM. Currently, he works for International Masters
Publishers in Stamford, Connecticut and resides in Bellerose, New York with his
wife Lisa.
Larry can be reached electronically via the Internet at os2man@panix.com.
ΓòÉΓòÉΓòÉ 7.3. 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 and is soon to enter 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.
ΓòÉΓòÉΓòÉ 7.4. Network distributors ΓòÉΓòÉΓòÉ
These people are part of our distribution system to provide EDM/2 on networks
other than the Internet. Their desire to help provide others access to this
magazine is voluntary and we appreciate them a lot!
o Paul Hethman (hethman@cs.utk.edu) - Compuserve
o David Singer (singer@almaden.ibm.com) - IBM Internal
ΓòÉΓòÉΓòÉ <hidden> ΓòÉΓòÉΓòÉ
Precompiled headers are very useful when including OS2.H. Precompiled header
files can greatly decrease the amount of time necessary to compile files that
include many and/or large header files. However, not all compilers are equal in
this respect. Borland compiles all headers into one large file. This probably
make it a bit faster to access than multiple files. But, when one header
changes, all headers must be recompiled and stored in the large file. IBM's
CSet++ on the other hand uses one precompiled file for each header. Thus, when
you change one of your header files, only that file has to be recompiled. Also,
the method employed by CSet++ allows the same precompiled headers to be used by
multiple projects, unlike the method used by Borland.
ΓòÉΓòÉΓòÉ <hidden> ΓòÉΓòÉΓòÉ
The following command line options were used with Borland's compiler:
-I.. -If:\bcos2\include -ff -G -4 -O2it -vi -c -D_USE_POST_FIX_
ΓòÉΓòÉΓòÉ <hidden> ΓòÉΓòÉΓòÉ
The following command line options were used with IBM's compiler:
/I.. /Tdp /J- /Si- /O+ /Oi+ /Os+ /W1 /Gf /Gi /G4 /Gx+ /C /d_USE_POST_FIX_
ΓòÉΓòÉΓòÉ <hidden> ΓòÉΓòÉΓòÉ
The following command line options were used with Watcom's compiler:
/i=.. /i=. /i=f:\watcom\H /i=f:\TOOLKIT\C\OS2H /mf /4r /bt=os2 /sg /d_USE_POST_FIX_ /oneatx /zp4
ΓòÉΓòÉΓòÉ <hidden> ΓòÉΓòÉΓòÉ
The following command line options were used with GNU's EMX compiler:
-c -O2 -m486 -I..
ΓòÉΓòÉΓòÉ <hidden> ΓòÉΓòÉΓòÉ
The following command line options were used with Borland's compiler:
-I.. -If:\bcos2\include -ff -G -4 -vi -c -D_USE_POST_FIX_
ΓòÉΓòÉΓòÉ <hidden> ΓòÉΓòÉΓòÉ
The following command line options were used with IBM's compiler:
/I.. /Tdp /J- /Si- /W1 /Gf /Gi /G4 /Gx+ /C /d_USE_POST_FIX_
ΓòÉΓòÉΓòÉ <hidden> ΓòÉΓòÉΓòÉ
The following command line options were used with Watcom's compiler:
/i=.. /i=. /i=f:\watcom\H /i=f:\TOOLKIT\C\OS2H /mf /4r /bt=os2 /sg /d_USE_POST_FIX_ /zp4
ΓòÉΓòÉΓòÉ <hidden> ΓòÉΓòÉΓòÉ
The following command line options were used with GNU's EMX compiler:
-c -m486 -I..