home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 32 Periodic
/
32-Periodic.zip
/
edmi2-6.zip
/
EDMI2-6.INF
(
.txt
)
< prev
next >
Wrap
OS/2 Help File
|
1994-06-06
|
278KB
|
4,711 lines
ΓòÉΓòÉΓòÉ 1. June 1994 Title Page ΓòÉΓòÉΓòÉ
Welcome to EDM/2 - The Electronic OS/2 Developer's Magazine!
Portions copyright (c) by Larry Salomon Jr.
Volume 2, issue 6
Copyright Notice and Other Stuff
The Editor-in-Chief of this electronic magazine is Larry Salomon, Jr.
Portions of EDM/2 are copyrighted by the editors. This publication may be
freely distributed in electronic form provided that all parts are present in
their original unmodified form. A reasonable fee may be charged for the
physical act of distribution; no fee may be charged for the publication itself.
All articles are copyrighted by their authors. No part of any article may be
reproduced without permission from the original author.
Neither this publication nor the editors are affiliated with International
Business Machines Corporation.
OS/2 is a registered trademark of International Business Machines Corporation.
Other trademarks are property of their respective owners. Any mention of a
product in this publication does not constitute an endorsement or affiliation
unless specifically stated in the text.
Administrivia
This month has been another hectic one. After a couple of months, when
releasing a new issue meant around the 10th or so, I decided to get this one
out on time.
You can see how well I did.
At least one good thing has happened this past month: we now have cable here
at our house, so my wife watches the Chinese channel (she's from the mainland)
instead of griping about how much time I spend on the computer.
Seriously, there is a lot of stuff in this issue. The Scratch Patch is teeming
with goodies for you to snatch up, and there is part 2 of the sprites series as
well as how to interpret and decompile resources from EXE and DLL files. Great
stuff!
Format Change
In the quest to make EDM/2 easier to concatenate, Carsten suggested adding a
header to the beginning of each article and column, to show the title of the
article or column you are reading. This inspired me to also add a footer at
the bottom of each section with the article/column/panel name, the date, and
the volume and issue number. These have been done this issue; contact us to
let us know what you think about them.
While I'm talking about him, let me publicly acknowledge the work
above-and-beyond the call of duty that Carsten has put into each issue. The
quality, in my not-so-humble opinion, has gone up considerably since he started
catching my mistakes and those of the authors. Thanks a lot for your hard
work!
Conferences
PC Expo is just around the corner. If you plan to be in New York City, I'm
interested in meeting you. Send me email. Speaking of which, become more than
a Team OS/2 member; become more than an OS/2 Evangelist; become an EDM/2
Promoter. If you go to a SIG that either has OS/2 as its reason for existence,
or is multi-platform oriented with development being the primary focus, let me
know; for the IBM PSP Conference, I drew some fliers that I handed out
throughout the week that I was there, and I can easily send you a PostScript
version of the flyer so that you can tell everyone about EDM/2 at your next
gathering.
Title Page - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ 2. Features for June 1994 ΓòÉΓòÉΓòÉ
The following articles constitute this issue's features:
o Sprites and Animation - Part 2
o Resources and Decompiling Them
o Visual REXX Faceoff
Features - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ 2.1. Sprites and Animation - Part 2 ΓòÉΓòÉΓòÉ
ΓòÉΓòÉΓòÉ 2.1.1. Introduction ΓòÉΓòÉΓòÉ
Sprites and Animation - Part 2
Written by Larry Salomon, Jr.
Introduction
Last month, we took a good look at the beginnings of a sprite library that is
well-suited for simple animation. We looked at:
o how a sprite is drawn
o how the drawing of sprites requires a mask, which adds an extra burden on the
management of the sprite data structure by the library
o how a sprite is moved in a manner that minimizes flicker
But even discussing these things left some holes, namely the background and the
workarea that enables the flicker-free movement. This month, we will wrap up
our discussion on the design of the library and will begin delving into the
code for the sprite library itself. Next month's conclusion will look into the
intricacies of animating a set of sprites using the i495.exe sample as a
starting point.
Ch-Ch-Ch-Changes
Ah, there's nothing like quintessential Bowie...
Since last month, I have integrated the sprite library into the Common/2
library that was on hobbes (if this new version isn't there already, wait for a
week and remind me if I still have forgotten). However, I have kept the
original version and have modified it to provide semaphore exclusion to the
data structures (actually, I surgically cut-n-pasted the related functions from
Common/2 <grin>), and it is this version that we will present in this series.
It should be noted, though, that the version in Common/2 does come complete
with an online reference.
Sprites and Animation (Part 2) - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ 2.1.2. Monkey Bars and the Like ΓòÉΓòÉΓòÉ
Monkey Bars and the Like
A distinguishing characteristic between a sprite and a bitmap is that the
former has a transparency color which means that whatever is below the sprite
shows through it. If the background were simply a solid color, this could be
done through sleight-of-hand by drawing the bitmap with the background color in
the areas that are to be see-through. Movement would be quite simple, reduced
to a few WinFillRect() calls and a single GpiWCBitBlt().
Unfortunately, a sprite can make no assumptions about the underlying surface:
it could be a solid color or it could be a complex drawing or it even could be
another sprite. Worse yet, we cannot simply take what is already on the
screen, for what shall we restore there if - for example - the sprite has its
visibility state set to FALSE?
Because of this (and other reasons), we need to maintain yet another data
structure which will, among other things, manage a static bitmap to be used for
the background; we will call it a playground since it is the area in which we
will let the sprites play. Conceptually, a playground is the "blackboard"
while each sprite is just a chalk drawing on the blackboard, so all drawing
related functions are considered to be associated with a playground instead of
the sprites that occupy a playground.
The playground support must provide the following functions:
o Create a playground
o Destroy a playground
o Add a sprite to the playground
o Remove a sprite from the playground
o Draw the entire playground
Adding and removing sprites from the playground is needed so that we can access
data structures in the playground given the sprite handle. For example, only
one work area is needed per playground since one thread at most can access an
HPS, so we put the work area in the playground, but need to access this work
area from each sprite.
Drawing a playground involves drawing the background and then looping through
its membership list to draw each sprite at its current position. This is
intended to provide a simple method of processing the WM_PAINT message within
an application.
ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
If all of these design issues get confusing, remember that I have the power of
hindsight and you do not, so do not get dismayed or (worse yet) think that I
have some divine capability to think of these things before writing a line of
code; I had the same trial-n-error development process that you would have were
our places exchanged.
Sprites and Animation (Part 2) - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ 2.1.3. Final Design Considerations ΓòÉΓòÉΓòÉ
Final Design Considerations
In even the most elementary computer programming classes, the concept of code
reuse is hammered into the brains of the students; here, this concept is
stressed as well. There are many code blocks that are needed in various places
throughout the library, so these code blocks were turned into functions.
Mutually exclusive access to the data structures is also an important part of
any good library, and it is implemented here through the use of a mutex
sempahore and a status flag; the status flag indicates if access has already
been granted to a function, i.e. if a function calls another function, the
called function will deadlock if it requests the semaphore. The
access-granting function is accessSem(), and it would be worth the time to
understand how this is implemented to avoid confusion later.
Sprites and Animation (Part 2) - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ 2.1.4. Where's Your Library Card? ΓòÉΓòÉΓòÉ
Where's Your Library Card?
Since utilitizing the library involves far more complex coding than the code
for the library itself (did I ever say that animation was easy?), we will look
at the library itself. If you have not already done so, unzip the source.zip
file and take a look at the files within.
MAKEFILE the makefile for the library
SPRITE.C the source file
SPRITE.HPP the .H preprocessor file. This is fed to HEADER.EXE which
generates an implementor's .H file and a user's .H file.
You should change the first two lines of this file so that
the paths make sense.
//@HEADER OUTFILE INT D:\SOURCE\SPRITE\SPRITE.H
//@HEADER OUTFILE EXT D:\MYINC\SPRITE.H
The INT line defines the file name of the "internal" use .H
file, while the EXT line defines the file name of the
"external" use .H file.
SPRITE.H Generated by the command "HEADER -tINT SPRITE.HPP"
SPRITE.OBJ Object file
SPRITE.LIB Library file
HEADER.EXE Header file preprocessor. I think I have placed this on
hobbes, possibly under the name HPREP. If not, this is an
older version, but it will do. (I keep forgetting to bring
the newer version in to work.)
Before we can begin to understand the code, we must first understand the data
structures on which the code operates, so we first look at the playground:
typedef struct _PLAYGROUND {
ULONG ulSig;
ULONG ulStatus;
HMTX hsmAccess;
HAB habAnchor;
HDC hdcWork;
HPS hpsWork;
HBITMAP hbmWork;
HBITMAP hbmBack;
BITMAPINFOHEADER2 bmihBack;
LONG lBackColor;
BOOL bUpdate;
HSPRITE ahsSprites[MAX_SPRITES];
ULONG ulNumMembers;
} PLAYGROUND, *HPLAYGROUND;
typedef HPLAYGROUND *PHPLAYGROUND;
ulSig 4-byte signature for the data structure, used for parameter
validation.
ulStatus 32-bit status flags used for semaphore access at this time.
hsmAccess mutex (mutual exclusion) semaphore handle.
habAnchor anchor block handle of the calling thread.
hdcWork OD_MEMORY device context handle for the work area.
hpsWork presentation space handle associated with hdcWork.
hbmWork bitmap handle set into hpsWork.
hbmBack bitmap handle for the background bitmap. If NULLHANDLE,
the playground has a background color instead (lBackColor).
bmihBack bitmap information header for hbmBack. If hbmBack is
NULLHANDLE, the cx and cy fields indicate the size of the
playground; otherwise the size of the playground is
specified by the size of the background bitmap.
lBackColor the background color of the playground, if hbmBack is
NULLHANDLE.
bUpdate update flag. If FALSE, no drawing is actually performed.
This is useful for changing sprites in place.
ahsSprites array of sprite handles comprising the membership list.
ulNumMembers current number of members.
We will see how these fields are used when we look at the code. But now, we
will look at the sprite structure:
typedef struct _SPRITE {
ULONG ulSig;
ULONG ulStatus;
HMTX hsmAccess;
HAB habAnchor;
HBITMAP hbmBitmap;
HBITMAP hbmMask;
BITMAPINFOHEADER2 bmihBitmap;
BITMAPINFOHEADER2 bmihMask;
struct _PLAYGROUND *hpgPlay;
POINTL ptlPos;
BOOL bVisible;
} SPRITE, *HSPRITE;
typedef HSPRITE *PHSPRITE;
ulSig 4-byte signature for the data structure, used for parameter
validation.
ulStatus 32-bit status flags used for semaphore access at this time.
hsmAccess mutex (mutual exclusion) semaphore handle.
habAnchor anchor block handle of the calling thread.
hbmBitmap bitmap handle which defines the sprite.
hbmMask bitmap handle which defines the mask.
bmihBitmap bitmap information header for hbmBitmap.
bmihMask bitmap information header for hbmMask.
hpgPlay playground handle of which the sprite is a member.
ptlPos current position.
bVisible current visibility state.
A comment on the list of exposed functions below: notice the symmetry of the
function names. For each create, there is a destroy; querys have corresponding
sets when appropriate; the add has a remove. While much is often said about
intuitiveness of the user-interface, the same concepts along with the
advantages gained can be applied to the "programmer-interface". The
non-exposed (internal) functions, obviously, do not need to follow this
guideline, although it does help if more than one person is developing and/or
maintaining the code.
Each function below is a hypertext link to its code and the explanation of the
code; feel free to explore the functions in any order.
Internal Functions
o accessSem
o clipBltPoints
o drawSpriteAt
o drawBackAt
o getMemHps
o queryHandleType
External Functions
o SprAddSprite
o SprCreatePlayground
o SprCreateSprite
o SprDestroyPlayground
o SprDestroySprite
o SprDrawPlayground
o SprDrawSprite
o SprQueryPlaygroundBack
o SprQueryPlaygroundColor
o SprQueryPlaygroundSize
o SprQuerySpritePosition
o SprQuerySpriteRect
o SprQuerySpriteSize
o SprQuerySpriteVisibility
o SprQueryUpdateFlag
o SprRemoveSprite
o SprSetPlaygroundBack
o SprSetPlaygroundColor
o SprSetPlaygroundSize
o SprSetSpritePosition
o SprSetSpriteVisibility
o SprSetUpdateFlag
Sprites and Animation (Part 2) - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ 2.1.5. Summary ΓòÉΓòÉΓòÉ
Summary
This month, we finished our discussion of the design of the sprite library by
describing the need for a master data structure called the playground, and the
code to implement the library routines was presented to illustrate the concepts
that we have already learned in our discussions up to this point. Next month,
we will take these underpinings and will apply them to the i495.exe application
to see how they can be utililized to perform rudimentary animation.
All comments, suggestions, bugs, etc. are welcome c/o the author.
Sprites and Animation (Part 2) - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ <hidden> queryHandleType ΓòÉΓòÉΓòÉ
queryHandleType
The purpose of this function is parameter validation, and uses the ulSig field
of both data structures. Note that for non-accessible memory pointers, this
function will still cause the application to trap (although it is possible to
check for NULL).
static ULONG queryHandleType(PVOID pvHandle)
//-------------------------------------------------------------------------
// This function returns a QH_* constant specifying the handle type. It
// will be replaced by CmnQueryHandle() when this subsystem is integrated
// into Common/2.
//
// Input: pvHandle - points the the handle to query
// Returns: QH_ERROR if error, QH_* constant otherwise
//-------------------------------------------------------------------------
{
if (pvHandle==NULL) {
return QH_ERROR;
} /* endif */
switch (((PHEADER)pvHandle)->ulSig) {
case SIG_HSPRITE:
return QH_HSPRITE;
case SIG_HPLAYGROUND:
return QH_HPLAYGROUND;
default:
return QH_ERROR;
} /* endswitch */
}
Sprites and Animation (Part 2) - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ <hidden> accessSem ΓòÉΓòÉΓòÉ
accessSem
The purpose of this function is to provide mutually exclusive access to the
data structures used by the library. By requiring that all data structures
have a common set of fields defined first (semaphore handle, etc.), we can cast
the data structures to the type PHANDLE to allow us access to the common
fields.
static USHORT accessSem(PHEADER phHandle,USHORT usAction)
//-------------------------------------------------------------------------
// This function provides semaphore access for mutual exclusion of private
// data access.
//
// Input: phHandle - points to the handle header
// usAction - specifies the action to perform:
// ACCSEM_SET - requests access to the handle
// ACCSEM_CLEAR - relinquishes access to the handle
// ACCSEM_ALREADYSET - not used
// ACCSEM_NOTSET - not used
// Returns: ACCSEM_ERROR if an error occurred, else the action to take
// on the next call to this function
//-------------------------------------------------------------------------
{
switch (usAction) {
case ACCSEM_SET:
if ((phHandle->ulStatus & HSTATUS_INLIBRARY)!=0) {
return ACCSEM_ALREADYSET;
} /* endif */
DosRequestMutexSem(phHandle->hsmAccess,SEM_INDEFINITE_WAIT);
phHandle->ulStatus|=HSTATUS_INLIBRARY;
return ACCSEM_CLEAR;
case ACCSEM_CLEAR:
if ((phHandle->ulStatus & HSTATUS_INLIBRARY)==0) {
return ACCSEM_NOTSET;
} /* endif */
DosReleaseMutexSem(phHandle->hsmAccess);
phHandle->ulStatus&=~HSTATUS_INLIBRARY;
return ACCSEM_SET;
case ACCSEM_ALREADYSET:
return ACCSEM_NOTSET;
case ACCSEM_NOTSET:
return ACCSEM_ALREADYSET;
default:
return ACCSEM_ERROR;
} /* endswitch */
}
Sprites and Animation (Part 2) - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ <hidden> getMemHps ΓòÉΓòÉΓòÉ
getMemHps
There is nothing special about this function that needs to be noted.
static HPS getMemHps(HAB habAnchor)
//-------------------------------------------------------------------------
// This function creates an HPS associated with a memory HDC. The
// HDC handle can be retrieved using the GpiQueryDevice() function.
//
// Input: habAnchor - anchor block of the calling thread.
// Returns: HPS handle if successful, NULLHANDLE otherwise
//-------------------------------------------------------------------------
{
HDC hdcMem;
SIZEL szlHps;
HPS hpsMem;
hdcMem=DevOpenDC(habAnchor,OD_MEMORY,"*",0,NULL,NULLHANDLE);
if (hdcMem==NULLHANDLE) {
return NULLHANDLE;
} /* endif */
szlHps.cx=0;
szlHps.cy=0;
hpsMem=GpiCreatePS(habAnchor,
hdcMem,
&szlHps,
PU_PELS|GPIT_MICRO|GPIA_ASSOC);
if (hpsMem==NULLHANDLE) {
DevCloseDC(hdcMem);
} /* endif */
return hpsMem;
}
Sprites and Animation (Part 2) - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ <hidden> clipBltPoints ΓòÉΓòÉΓòÉ
clipBltPoints
The library clips all drawing to the playground size. (A future enhancement to
the library would be to allow the application to specify where in a window the
lower-left corner of the playground should be.) This function clips a pair of
rectangles (which are defined by a pair of points, meaning that we have four
points total) to a rectangle defined to be (0,0)-(pszlPlay->cx,pszlPlay->cy).
You can see the clipping effect by enlarging the size of the i495 window.
static BOOL clipBltPoints(HAB habAnchor,PPOINTL pptlArray,PSIZEL pszlPlay)
//-------------------------------------------------------------------------
// This function clips the first two points in pptlArray to a rectangle
// of size pszlPlay. The last two points in pptlArray are then adjusted
// by the amount clipped.
//
// It is assumed that the first two points refer to a coordinate space
// of size pszlPlay and that the two rectangles formed by the first and
// last pair of points in pptlArray are of the same size.
//
// Input: habAnchor - anchor block of the calling thread.
// pptlArray - points to array of 4 points for GpiBitBlt()
// pszlPlay - points to the size of the playground to clip to
// Output: pptlArray - points to adjusted array
// Returns: TRUE if at least one pel was *not* clipped, FALSE if all
// points fell outside of the clipping region.
//-------------------------------------------------------------------------
{
RECTL rclPlay;
RECTL rclDest;
RECTL rclInter;
RECTL rclDelta;
rclPlay.xLeft=0;
rclPlay.yBottom=0;
rclPlay.xRight=pszlPlay->cx-1;
rclPlay.yTop=pszlPlay->cy-1;
rclDest.xLeft=pptlArray[0].x;
rclDest.yBottom=pptlArray[0].y;
rclDest.xRight=pptlArray[1].x;
rclDest.yTop=pptlArray[1].y;
WinIntersectRect(habAnchor,&rclInter,&rclPlay,&rclDest);
//----------------------------------------------------------------------
// If the result is an empty rectangle, return FALSE to indicate so.
//----------------------------------------------------------------------
if (WinIsRectEmpty(habAnchor,&rclInter)) {
return FALSE;
} /* endif */
rclDelta.xLeft=rclDest.xLeft-rclInter.xLeft;
rclDelta.yBottom=rclDest.yBottom-rclInter.yBottom;
rclDelta.xRight=rclDest.xRight-rclInter.xRight;
rclDelta.yTop=rclDest.yTop-rclInter.yTop;
pptlArray[0].x-=rclDelta.xLeft;
pptlArray[0].y-=rclDelta.yBottom;
pptlArray[1].x-=rclDelta.xRight;
pptlArray[1].y-=rclDelta.yTop;
pptlArray[2].x-=rclDelta.xLeft;
pptlArray[2].y-=rclDelta.yBottom;
pptlArray[3].x-=rclDelta.xRight;
pptlArray[3].y-=rclDelta.yTop;
return TRUE;
}
Sprites and Animation (Part 2) - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ <hidden> drawSpriteAt ΓòÉΓòÉΓòÉ
drawSpriteAt
This function draws a sprite at the specified position; it calls
clipBltPoints() to insure that all necessary clipping is performed. An
advantage of having this as a function is that the transition to z-order
implementation is eased somewhat. It is because of this that the background is
not drawn in this function, but is removed to its own function - drawBackAt().
static BOOL drawSpriteAt(HPS hpsDraw,
HSPRITE hsSprite,
PSIZEL pszlSize,
PPOINTL pptlPos)
//-------------------------------------------------------------------------
// This function draws the sprite at the specified position. It is assumed
// that the background has already been drawn into hpsDraw before this
// function is called.
//
// Input: hpsDraw - handle of the presentation space to draw in
// hsSprite - handle of the sprite to draw
// pszlSize - points to the size of hpsDraw. If NULL, the size
// of the playground is used.
// pptlPos - points to the point specifying the position. If
// NULL, the sprite's current position is used.
// Returns: TRUE if successful, FALSE otherwise
//-------------------------------------------------------------------------
{
POINTL ptlUse;
SIZEL szlUse;
POINTL aptlPoints[4];
if (!hsSprite->hpgPlay->bUpdate) {
return TRUE;
} /* endif */
//----------------------------------------------------------------------
// Initialize the local variables with either what was passed in or
// the defaults as noted above in the function prologue
//----------------------------------------------------------------------
if (pptlPos==NULL) {
ptlUse=hsSprite->ptlPos;
} else {
ptlUse=*pptlPos;
} /* endif */
if (pszlSize==NULL) {
SprQueryPlaygroundSize(hsSprite->hpgPlay,&szlUse);
} else {
szlUse=*pszlSize;
} /* endif */
aptlPoints[0].x=ptlUse.x;
aptlPoints[0].y=ptlUse.y;
aptlPoints[1].x=aptlPoints[0].x+hsSprite->bmihMask.cx-1;
aptlPoints[1].y=aptlPoints[0].y+hsSprite->bmihMask.cy-1;
aptlPoints[2].x=0;
aptlPoints[2].y=0;
aptlPoints[3].x=aptlPoints[2].x+hsSprite->bmihMask.cx;
aptlPoints[3].y=aptlPoints[2].y+hsSprite->bmihMask.cy;
if (clipBltPoints(hsSprite->habAnchor,aptlPoints,&szlUse)) {
//-------------------------------------------------------------------
// Blit the mask and then the bitmap
//-------------------------------------------------------------------
GpiWCBitBlt(hpsDraw,
hsSprite->hbmMask,
4,
aptlPoints,
ROP_SRCAND,
BBO_IGNORE);
GpiWCBitBlt(hpsDraw,
hsSprite->hbmBitmap,
4,
aptlPoints,
ROP_SRCPAINT,
BBO_IGNORE);
} /* endif */
return TRUE;
}
Sprites and Animation (Part 2) - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ <hidden> drawBackAt ΓòÉΓòÉΓòÉ
drawBackAt
This function draws the background at the specified position with the specified
size. This is used to copy the background onto the display as well as into the
work area as needed. Notice that this is where background coloring is done if
a background bitmap was not specified.
static BOOL drawBackAt(HPS hpsDraw,
HPLAYGROUND hpgPlay,
PRECTL prclDest,
PSIZEL pszlDest,
PRECTL prclSrc)
//-------------------------------------------------------------------------
// This function draws the background in the specified presentation space.
//
// Input: hpsDraw - handle of the presentation space to draw in
// hpgPlay - handle of the playground containing the background
// prclDest - points to the destination rectangle. If NULL, the
// value of prclSrc is used.
// pszlDest - points to the size of hpsDraw. If NULL, the size of
// the playground is used.
// prclSrc - points to the source rectangle. If NULL, the entire
// background is painted.
// Returns: TRUE if successful, FALSE otherwise
//-------------------------------------------------------------------------
{
RECTL rclUseSrc;
RECTL rclUseDest;
SIZEL szlUse;
POINTL aptlPoints[4];
if (!hpgPlay->bUpdate) {
return TRUE;
} /* endif */
if (prclSrc==NULL) {
rclUseSrc.xLeft=0;
rclUseSrc.yBottom=0;
rclUseSrc.xRight=hpgPlay->bmihBack.cx;
rclUseSrc.yTop=hpgPlay->bmihBack.cy;
} else {
rclUseSrc=*prclSrc;
} /* endif */
if (prclDest==NULL) {
rclUseDest=rclUseSrc;
rclUseDest.xRight--;
rclUseDest.yTop--;
} else {
rclUseDest=*prclDest;
} /* endif */
if (pszlDest==NULL) {
szlUse.cx=hpgPlay->bmihBack.cx;
szlUse.cy=hpgPlay->bmihBack.cy;
} else {
szlUse=*pszlDest;
} /* endif */
aptlPoints[0].x=rclUseDest.xLeft;
aptlPoints[0].y=rclUseDest.yBottom;
aptlPoints[1].x=rclUseDest.xRight;
aptlPoints[1].y=rclUseDest.yTop;
aptlPoints[2].x=rclUseSrc.xLeft;
aptlPoints[2].y=rclUseSrc.yBottom;
aptlPoints[3].x=rclUseSrc.xRight;
aptlPoints[3].y=rclUseSrc.yTop;
if (clipBltPoints(hpgPlay->habAnchor,aptlPoints,&szlUse)) {
//-------------------------------------------------------------------
// If there is a background bitmap, blit it, otherwise black out the
// area.
//-------------------------------------------------------------------
if (hpgPlay->hbmBack!=NULLHANDLE) {
GpiWCBitBlt(hpsDraw,
hpgPlay->hbmBack,
4,
aptlPoints,
ROP_SRCCOPY,
BBO_IGNORE);
} else {
//----------------------------------------------------------------
// WinFillRect() excludes the top and right of the rectangle
//----------------------------------------------------------------
rclUseDest.xRight++;
rclUseDest.yTop++;
WinFillRect(hpsDraw,&rclUseDest,hpgPlay->lBackColor);
} /* endif */
} /* endif */
return TRUE;
}
Sprites and Animation (Part 2) - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ <hidden> SprAddSprite ΓòÉΓòÉΓòÉ
SprAddSprite
This function adds a sprite handle to the end of the ahsSprites array within
the HPLAYGROUND structure. By insuring - in SprRemoveSprite() - that the array
is always compact, we save a test for NULL in the SprDrawPlayground() loop.
Note that there are no functions for modifying the order of the sprite within
the array, because as of now there is no need. However, once z-ordering is
added to the library, another function - SprSetLayer() - will be added to set
the sprite's position to the top, bottom, previous, or next position in the
z-order stack.
SPRERROR EXPENTRY SprAddSprite(HPLAYGROUND hpgPlay,HSPRITE hsSprite)
//-------------------------------------------------------------------------
// This function labels a sprite as a "member" of the specified playground.
// Doing so allows the application to control the sprite's position,
// visibility, etc. on a drawing surface.
//
// Input: hpgPlay - handle to the playground
// hsSprite - handle to the sprite to add
// Returns: SPR_ERR_NOERROR if successful, SPR_ERR_* constant otherwise
//-------------------------------------------------------------------------
{
USHORT usAction;
if (queryHandleType(hpgPlay)!=QH_HPLAYGROUND) {
return SPR_ERR_BADHANDLE;
} else
if (queryHandleType(hsSprite)!=QH_HSPRITE) {
return SPR_ERR_BADHANDLE;
} /* endif */
usAction=accessSem((PHEADER)hpgPlay,ACCSEM_SET);
if (hsSprite->hpgPlay!=NULL) {
accessSem((PHEADER)hpgPlay,usAction);
return SPR_ERR_HASPLAYGROUND;
} else
if (hpgPlay->ulNumMembers==MAX_SPRITES) {
accessSem((PHEADER)hpgPlay,usAction);
return SPR_ERR_PLAYGROUNDFULL;
} /* endif */
hpgPlay->ahsSprites[hpgPlay->ulNumMembers]=hsSprite;
hpgPlay->ulNumMembers++;
hsSprite->hpgPlay=hpgPlay;
accessSem((PHEADER)hpgPlay,usAction);
return SPR_ERR_NOERROR;
}
Sprites and Animation (Part 2) - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ <hidden> SprCreatePlayground ΓòÉΓòÉΓòÉ
SprCreatePlayground
Note the use of calloc(). Using any of the C runtime is not a good thing
because it is implemented (usually) using suballocation, meaning an application
can accidentally increment hpgPlay by 2 bytes (for example) and would not get a
trap notification by OS/2; instead unpredictable behavior would occur, which
misleads the developer into thinking a stack error is occuring. It would be
better to use DosAllocMem() instead.
SPRERROR EXPENTRY SprCreatePlayground(HAB habAnchor,PHPLAYGROUND phpgPlay)
//-------------------------------------------------------------------------
// This function creates a playground to which sprites can be added.
//
// Input: habAnchor - anchor block of the calling thread.
// Output: phpgPlay - points to the variable with the HPLAYGROUND handle
// Returns: SPR_ERR_NOERROR if successful, SPR_ERR_* constant otherwise
//-------------------------------------------------------------------------
{
BITMAPINFOHEADER2 bmihInfo;
LONG lValue;
*phpgPlay=calloc(1,sizeof(PLAYGROUND));
if (*phpgPlay==NULL) {
*phpgPlay=NULL;
return SPR_ERR_NOMEMORY;
} /* endif */
(*phpgPlay)->ulSig=SIG_HPLAYGROUND;
(*phpgPlay)->ulStatus=0;
if (DosCreateMutexSem(NULL,&(*phpgPlay)->hsmAccess,0,FALSE)) {
free(*phpgPlay);
return SPR_ERR_RESOURCE;
} /* endif */
(*phpgPlay)->habAnchor=habAnchor;
(*phpgPlay)->hpsWork=getMemHps(habAnchor);
if ((*phpgPlay)->hpsWork==NULLHANDLE) {
free(*phpgPlay);
*phpgPlay=NULL;
return SPR_ERR_RESOURCE;
} /* endif */
(*phpgPlay)->hdcWork=GpiQueryDevice((*phpgPlay)->hpsWork);
Since the workarea is used for moving sprites with position overlap, the size
of the bitmap to be set in the HPS is (MAX_SPRITE_CX*2,MAX_SPRITE_CY*2).
bmihInfo.cbFix=16;
bmihInfo.cx=MAX_SPRITE_CX*2;
bmihInfo.cy=MAX_SPRITE_CY*2;
bmihInfo.cPlanes=1;
DevQueryCaps((*phpgPlay)->hdcWork,CAPS_COLOR_BITCOUNT,1,&lValue);
bmihInfo.cBitCount=lValue;
(*phpgPlay)->hbmWork=GpiCreateBitmap((*phpgPlay)->hpsWork,
&bmihInfo,
0,
NULL,
NULL);
if ((*phpgPlay)->hbmWork==NULLHANDLE) {
GpiDestroyPS((*phpgPlay)->hpsWork);
DevCloseDC((*phpgPlay)->hdcWork);
free(*phpgPlay);
*phpgPlay=NULL;
return SPR_ERR_RESOURCE;
} /* endif */
GpiSetBitmap((*phpgPlay)->hpsWork,(*phpgPlay)->hbmWork);
(*phpgPlay)->lBackColor=CLR_BLACK;
(*phpgPlay)->bUpdate=TRUE;
(*phpgPlay)->hbmBack=NULLHANDLE;
(*phpgPlay)->ulNumMembers=0;
return SPR_ERR_NOERROR;
}
Sprites and Animation (Part 2) - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ <hidden> SprCreateSprite ΓòÉΓòÉΓòÉ
SprCreateSprite
See SprCreatePlayground() for notes on using calloc().
SPRERROR EXPENTRY SprCreateSprite(HAB habAnchor,
HBITMAP hbmBitmap,
PHSPRITE phsSprite)
//-------------------------------------------------------------------------
// This function creates a sprite from the specified bitmap. The sprite
// cannot be moved, shown, etc., however, until it is associated with a
// playground.
//
// The color black is used as the transparency color. If you need to use
// black in the bitmap without it becoming transparent, use the next
// closest color. <grin>
//
// New sprites are initialized as being at position (0,0) and hidden.
//
// Note that, once this function is called, the bitmap is managed by
// the sprite subsystem. The bitmap should *NOT* be deleted by the
// application or else unpredictable results will occur.
//
// Input: habAnchor - anchor block of the calling thread.
// hbmBitmap - handle to the bitmap
// Output: phsSprite - points to the sprite handle
// Returns: SPR_ERR_NOERROR if successful, SPR_ERR_* constant otherwise
//-------------------------------------------------------------------------
{
HPS hpsMem;
HDC hdcMem;
POINTL aptlPoints[4];
*phsSprite=calloc(1,sizeof(SPRITE));
if (*phsSprite==NULL) {
*phsSprite=NULL;
return SPR_ERR_NOMEMORY;
} /* endif */
(*phsSprite)->ulSig=SIG_HSPRITE;
(*phsSprite)->ulStatus=0;
if (DosCreateMutexSem(NULL,&(*phsSprite)->hsmAccess,0,FALSE)) {
free(*phsSprite);
return SPR_ERR_RESOURCE;
} /* endif */
(*phsSprite)->habAnchor=habAnchor;
(*phsSprite)->hbmBitmap=hbmBitmap;
(*phsSprite)->ptlPos.x=0;
(*phsSprite)->ptlPos.y=0;
(*phsSprite)->bVisible=FALSE;
(*phsSprite)->bmihBitmap.cbFix=16;
GpiQueryBitmapInfoHeader((*phsSprite)->hbmBitmap,&(*phsSprite)->bmihBitmap);
//----------------------------------------------------------------------
// Get an OD_MEMORY HDC and HPS to create the mask in. Since we will
// save the bitmap handle, but don't give a $%#@ about the HDC/HPS, they
// can be local variables.
//----------------------------------------------------------------------
hpsMem=getMemHps(habAnchor);
if (hpsMem==NULLHANDLE) {
free(*phsSprite);
*phsSprite=NULL;
return SPR_ERR_RESOURCE;
} /* endif */
hdcMem=GpiQueryDevice(hpsMem);
(*phsSprite)->bmihMask=(*phsSprite)->bmihBitmap;
(*phsSprite)->bmihMask.cPlanes=1;
(*phsSprite)->bmihMask.cBitCount=1;
(*phsSprite)->hbmMask=GpiCreateBitmap(hpsMem,
&(*phsSprite)->bmihMask,
0,
NULL,
NULL);
if ((*phsSprite)->hbmMask==NULLHANDLE) {
GpiDestroyPS(hpsMem);
DevCloseDC(hdcMem);
free(*phsSprite);
*phsSprite=NULL;
return SPR_ERR_RESOURCE;
} /* endif */
GpiSetBitmap(hpsMem,(*phsSprite)->hbmMask);
aptlPoints[0].x=0;
aptlPoints[0].y=0;
aptlPoints[1].x=aptlPoints[0].x+(*phsSprite)->bmihMask.cx-1;
aptlPoints[1].y=aptlPoints[0].y+(*phsSprite)->bmihMask.cy-1;
aptlPoints[2].x=0;
aptlPoints[2].y=0;
aptlPoints[3].x=aptlPoints[2].x+(*phsSprite)->bmihBitmap.cx;
aptlPoints[3].y=aptlPoints[2].y+(*phsSprite)->bmihBitmap.cy;
//----------------------------------------------------------------------
// Set the foreground to white and the background to black so that this
// works. The resulting behavior in the GpiWCBitBlt() call is
// inconsistent with the docs, so I don't know what to think.
//----------------------------------------------------------------------
GpiSetColor(hpsMem,CLR_WHITE);
GpiSetBackColor(hpsMem,CLR_BLACK);
GpiWCBitBlt(hpsMem,
(*phsSprite)->hbmBitmap,
4,
aptlPoints,
ROP_SRCCOPY,
BBO_IGNORE);
GpiSetBitmap(hpsMem,NULLHANDLE);
GpiDestroyPS(hpsMem);
DevCloseDC(hdcMem);
return SPR_ERR_NOERROR;
}
Sprites and Animation (Part 2) - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ <hidden> SprDestroyPlayground ΓòÉΓòÉΓòÉ
SprDestroyPlayground
This function destroys the sprites in the playground before destroying the
playground itself. However, if you look in SprDestroySprite() you'll see a
check to see if the sprite is still a member of a playground (and fails if it
is); thus, we have to call SprRemoveSprite() first.
SPRERROR EXPENTRY SprDestroyPlayground(HPLAYGROUND hpgPlay)
//-------------------------------------------------------------------------
// This function destroys the playground including any sprites that are
// still members of it. All resources consumed by the playground,
// including the back bitmap, are returned to the system.
//
// Input: hpgPlay - handle to the playground
// Returns: SPR_ERR_NOERROR if successful, SPR_ERR_* constant otherwise
//-------------------------------------------------------------------------
{
USHORT usAction;
ULONG ulIndex;
HSPRITE hsSprite;
if (queryHandleType(hpgPlay)!=QH_HPLAYGROUND) {
return SPR_ERR_BADHANDLE;
} /* endif */
usAction=accessSem((PHEADER)hpgPlay,ACCSEM_SET);
if (hpgPlay->hbmBack!=NULLHANDLE) {
GpiDeleteBitmap(hpgPlay->hbmBack);
} /* endif */
for (ulIndex=0; ulIndex<hpgPlay->ulNumMembers; ulIndex++) {
hsSprite=hpgPlay->ahsSprites[ulIndex];
SprRemoveSprite(hpgPlay,hsSprite);
SprDestroySprite(hsSprite);
} /* endfor */
GpiSetBitmap(hpgPlay->hpsWork,NULLHANDLE);
if (hpgPlay->hbmBack!=NULLHANDLE) {
GpiDeleteBitmap(hpgPlay->hbmBack);
} /* endif */
GpiDestroyPS(hpgPlay->hpsWork);
DevCloseDC(hpgPlay->hdcWork);
accessSem((PHEADER)hpgPlay,usAction);
free(hpgPlay);
return SPR_ERR_NOERROR;
}
Sprites and Animation (Part 2) - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ <hidden> SprDestroySprite ΓòÉΓòÉΓòÉ
SprDestroySprite
There is nothing special about this function that needs to be noted.
SPRERROR EXPENTRY SprDestroySprite(HSPRITE hsSprite)
//-------------------------------------------------------------------------
// This function destroys the sprite and returns all resources to the
// system.
//
// Input: hsSprite - handle to the sprite
// Returns: SPR_ERR_NOERROR if successful, SPR_ERR_* constant otherwise
//-------------------------------------------------------------------------
{
USHORT usAction;
if (queryHandleType(hsSprite)!=QH_HSPRITE) {
return SPR_ERR_BADHANDLE;
} /* endif */
usAction=accessSem((PHEADER)hsSprite,ACCSEM_SET);
if (hsSprite->hpgPlay!=NULL) {
accessSem((PHEADER)hsSprite,usAction);
return SPR_ERR_HASPLAYGROUND;
} /* endif */
GpiDeleteBitmap(hsSprite->hbmBitmap);
GpiDeleteBitmap(hsSprite->hbmMask);
accessSem((PHEADER)hsSprite,usAction);
free(hsSprite);
return SPR_ERR_NOERROR;
}
Sprites and Animation (Part 2) - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ <hidden> SprDrawPlayground ΓòÉΓòÉΓòÉ
SprDrawPlayground
See SprAddSprite() for notes about ahsSprites being a compact array for
performance reasons.
SPRERROR EXPENTRY SprDrawPlayground(HPS hpsDraw,HPLAYGROUND hpgPlay)
//-------------------------------------------------------------------------
// This function redraws the playground and all sprites belonging to the
// playground.
//
// Input: hpsDraw - handle to the HPS to draw the playground in
// hpgPlay - handle to the playground
// Returns: SPR_ERR_NOERROR if successful, SPR_ERR_* constant otherwise
//-------------------------------------------------------------------------
{
USHORT usAction;
ULONG ulIndex;
if (queryHandleType(hpgPlay)!=QH_HPLAYGROUND) {
return SPR_ERR_BADHANDLE;
} /* endif */
usAction=accessSem((PHEADER)hpgPlay,ACCSEM_SET);
if (hpgPlay->bUpdate) {
drawBackAt(hpsDraw,hpgPlay,NULL,NULL,NULL);
for (ulIndex=0; ulIndex<hpgPlay->ulNumMembers; ulIndex++) {
SprDrawSprite(hpsDraw,hpgPlay->ahsSprites[ulIndex]);
} /* endfor */
} /* endif */
accessSem((PHEADER)hpgPlay,usAction);
return SPR_ERR_NOERROR;
}
Sprites and Animation (Part 2) - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ <hidden> SprDrawSprite ΓòÉΓòÉΓòÉ
SprDrawSprite
Given the drawSpriteAt() and drawBackAt() functions, the implementation of this
function is fairly trivial.
SPRERROR EXPENTRY SprDrawSprite(HPS hpsDraw,HSPRITE hsSprite)
//-------------------------------------------------------------------------
// This function draws a sprite
//
// Input: hpsDraw - handle to the HPS to draw the sprite in
// hsSprite - handle to the sprite
// Returns: SPR_ERR_NOERROR if successful, SPR_ERR_* constant otherwise
//-------------------------------------------------------------------------
{
USHORT usAction;
RECTL rclSprite;
if (queryHandleType(hsSprite)!=QH_HSPRITE) {
return SPR_ERR_BADHANDLE;
} /* endif */
usAction=accessSem((PHEADER)hsSprite,ACCSEM_SET);
if (hsSprite->hpgPlay==NULL) {
accessSem((PHEADER)hsSprite,usAction);
return SPR_ERR_HASNOPLAYGROUND;
} /* endif */
if ((!hsSprite->bVisible) || (!hsSprite->hpgPlay->bUpdate)) {
accessSem((PHEADER)hsSprite,usAction);
return SPR_ERR_NOERROR;
} /* endif */
rclSprite.xLeft=hsSprite->ptlPos.x;
rclSprite.yBottom=hsSprite->ptlPos.y;
rclSprite.xRight=rclSprite.xLeft+hsSprite->bmihMask.cx;
rclSprite.yTop=rclSprite.yBottom+hsSprite->bmihMask.cy;
drawBackAt(hpsDraw,hsSprite->hpgPlay,NULL,NULL,&rclSprite);
drawSpriteAt(hpsDraw,hsSprite,NULL,NULL);
accessSem((PHEADER)hsSprite,usAction);
return SPR_ERR_NOERROR;
}
Sprites and Animation (Part 2) - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ <hidden> SprQueryPlaygroundBack ΓòÉΓòÉΓòÉ
SprQueryPlaygroundBack
There is nothing special about this function that needs to be noted.
SPRERROR EXPENTRY SprQueryPlaygroundBack(HPLAYGROUND hpgPlay,
HBITMAP *phbmBack)
//-------------------------------------------------------------------------
// This function returns the handle of the background bitmap currently in
// use.
//
// Input: hpgPlay - handle to the playground
// Output: phbmBack - points to the handle to the background bitmap.
// Returns: SPR_ERR_NOERROR if successful, SPR_ERR_* constant otherwise
//-------------------------------------------------------------------------
{
USHORT usAction;
if (queryHandleType(hpgPlay)!=QH_HPLAYGROUND) {
return SPR_ERR_BADHANDLE;
} /* endif */
usAction=accessSem((PHEADER)hpgPlay,ACCSEM_SET);
*phbmBack=hpgPlay->hbmBack;
accessSem((PHEADER)hpgPlay,usAction);
return SPR_ERR_NOERROR;
}
Sprites and Animation (Part 2) - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ <hidden> SprQueryPlaygroundColor ΓòÉΓòÉΓòÉ
SprQueryPlaygroundColor
There is nothing special about this function that needs to be noted.
SPRERROR EXPENTRY SprQueryPlaygroundColor(HPLAYGROUND hpgPlay,
PLONG plBackColor)
//-------------------------------------------------------------------------
// This function returns the background color of the playground and is
// only valid if the playground doesn't have a bitmap.
//
// Input: hpgPlay - handle to the playground
// plBackColor - points to the variable to receive the background
// color
// Returns: SPR_ERR_NOERROR if successful, SPR_ERR_* constant otherwise
//-------------------------------------------------------------------------
{
USHORT usAction;
if (queryHandleType(hpgPlay)!=QH_HPLAYGROUND) {
return SPR_ERR_BADHANDLE;
} /* endif */
usAction=accessSem((PHEADER)hpgPlay,ACCSEM_SET);
if (hpgPlay->hbmBack!=NULLHANDLE) {
accessSem((PHEADER)hpgPlay,usAction);
return SPR_ERR_HASBACKGROUND;
} /* endif */
*plBackColor=hpgPlay->lBackColor;
accessSem((PHEADER)hpgPlay,usAction);
return SPR_ERR_NOERROR;
}
Sprites and Animation (Part 2) - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ <hidden> SprQueryPlaygroundSize ΓòÉΓòÉΓòÉ
SprQueryPlaygroundSize
There is nothing special about this function that needs to be noted.
SPRERROR EXPENTRY SprQueryPlaygroundSize(HPLAYGROUND hpgPlay,PSIZEL pszlSize)
//-------------------------------------------------------------------------
// This function returns the size of the playground. For playgrounds with
// bitmaps set as the background, the returned value is the size of the
// bitmap. Otherwise, the returned value is that which was specified on
// the last call to SprSetPlaygroundSize().
//
// Input: hpgPlay - handle to the playground
// pszlSize - points to the variable to receive the size of the
// playground
// Returns: SPR_ERR_NOERROR if successful, SPR_ERR_* constant otherwise
//-------------------------------------------------------------------------
{
USHORT usAction;
if (queryHandleType(hpgPlay)!=QH_HPLAYGROUND) {
return SPR_ERR_BADHANDLE;
} /* endif */
usAction=accessSem((PHEADER)hpgPlay,ACCSEM_SET);
pszlSize->cx=hpgPlay->bmihBack.cx;
pszlSize->cy=hpgPlay->bmihBack.cy;
accessSem((PHEADER)hpgPlay,usAction);
return SPR_ERR_NOERROR;
}
Sprites and Animation (Part 2) - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ <hidden> SprQuerySpritePosition ΓòÉΓòÉΓòÉ
SprQuerySpritePosition
There is nothing special about this function that needs to be noted.
SPRERROR EXPENTRY SprQuerySpritePosition(HSPRITE hsSprite,PPOINTL pptlPos)
//-------------------------------------------------------------------------
// This function returns the current position of the sprite. Note that
// a sprite has a current position even if it is hidden.
//
// Input: hsSprite - handle to the sprite
// Output: pptlPos - points to the current position
// Returns: SPR_ERR_NOERROR if successful, SPR_ERR_* constant otherwise
//-------------------------------------------------------------------------
{
USHORT usAction;
if (queryHandleType(hsSprite)!=QH_HSPRITE) {
return SPR_ERR_BADHANDLE;
} /* endif */
usAction=accessSem((PHEADER)hsSprite,ACCSEM_SET);
if (hsSprite->hpgPlay==NULL) {
accessSem((PHEADER)hsSprite,usAction);
return SPR_ERR_HASNOPLAYGROUND;
} /* endif */
*pptlPos=hsSprite->ptlPos;
accessSem((PHEADER)hsSprite,usAction);
return SPR_ERR_NOERROR;
}
Sprites and Animation (Part 2) - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ <hidden> SprQuerySpriteRect ΓòÉΓòÉΓòÉ
SprQuerySpriteRect
Since the bounding rectangle of a sprite varies with its position, and since a
sprite cannot have a position until it is a member of a playground, this
function checks for membership and fails if there is none. Note that the
rectangle returned is all-inclusive, i.e. the upper and right edge of the
rectangle is part of the sprite.
SPRERROR EXPENTRY SprQuerySpriteRect(HSPRITE hsSprite,PRECTL prclRect)
//-------------------------------------------------------------------------
// This function returns the bounding rectangle of the sprite at its
// current position.
//
// Input: hsSprite - handle to the sprite
// Output: prclRect - points to the current bounding rectangle
// Returns: SPR_ERR_NOERROR if successful, SPR_ERR_* constant otherwise
//-------------------------------------------------------------------------
{
USHORT usAction;
if (queryHandleType(hsSprite)!=QH_HSPRITE) {
return SPR_ERR_BADHANDLE;
} /* endif */
usAction=accessSem((PHEADER)hsSprite,ACCSEM_SET);
if (hsSprite->hpgPlay==NULL) {
accessSem((PHEADER)hsSprite,usAction);
return SPR_ERR_HASNOPLAYGROUND;
} /* endif */
prclRect->xLeft=hsSprite->ptlPos.x;
prclRect->yBottom=hsSprite->ptlPos.y;
prclRect->xRight=prclRect->xLeft+hsSprite->bmihBitmap.cx-1;
prclRect->yTop=prclRect->yBottom+hsSprite->bmihBitmap.cy-1;
accessSem((PHEADER)hsSprite,usAction);
return SPR_ERR_NOERROR;
}
Sprites and Animation (Part 2) - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ <hidden> SprQuerySpriteSize ΓòÉΓòÉΓòÉ
SprQuerySpriteSize
Unlike SprQuerySpritePosition() and SprQuerySpriteRect(), SprQuerySpriteSize()
is not variant, but is fixed at the time of creation, so we do not need to
check for membership in a playground.
SPRERROR EXPENTRY SprQuerySpriteSize(HSPRITE hsSprite,PSIZEL pszlSize)
//-------------------------------------------------------------------------
// This function returns the current size of the sprite.
//
// Input: hsSprite - handle to the sprite
// Output: pszlSize - points to the current size
// Returns: SPR_ERR_NOERROR if successful, SPR_ERR_* constant otherwise
//-------------------------------------------------------------------------
{
USHORT usAction;
if (queryHandleType(hsSprite)!=QH_HSPRITE) {
return SPR_ERR_BADHANDLE;
} /* endif */
usAction=accessSem((PHEADER)hsSprite,ACCSEM_SET);
pszlSize->cx=hsSprite->bmihBitmap.cx;
pszlSize->cy=hsSprite->bmihBitmap.cy;
accessSem((PHEADER)hsSprite,usAction);
return SPR_ERR_NOERROR;
}
Sprites and Animation (Part 2) - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ <hidden> SprQuerySpriteVisibility ΓòÉΓòÉΓòÉ
SprQuerySpriteVisibility
There is nothing special about this function that needs to be noted.
SPRERROR EXPENTRY SprQuerySpriteVisibility(HSPRITE hsSprite,PBOOL pbVisible)
//-------------------------------------------------------------------------
// This function returns the visibility state of the sprite
//
// Input: hsSprite - handle to the sprite
// Output: pbVisible - points to the visibility state
// Returns: SPR_ERR_NOERROR if successful, SPR_ERR_* constant otherwise
//-------------------------------------------------------------------------
{
USHORT usAction;
if (queryHandleType(hsSprite)!=QH_HSPRITE) {
return SPR_ERR_BADHANDLE;
} /* endif */
usAction=accessSem((PHEADER)hsSprite,ACCSEM_SET);
if (hsSprite->hpgPlay==NULL) {
accessSem((PHEADER)hsSprite,usAction);
return SPR_ERR_HASNOPLAYGROUND;
} /* endif */
*pbVisible=hsSprite->bVisible;
accessSem((PHEADER)hsSprite,usAction);
return SPR_ERR_NOERROR;
}
Sprites and Animation (Part 2) - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ <hidden> SprQueryUpdateFlag ΓòÉΓòÉΓòÉ
SprQueryUpdateFlag
There is nothing special about this function that needs to be noted.
SPRERROR EXPENTRY SprQueryUpdateFlag(HPLAYGROUND hpgPlay,PBOOL pbUpdate)
//-------------------------------------------------------------------------
// This function returns the setting of the update flag. See the notes
// for SprSetUpdateFlag() for more information about this setting.
//
// Input: hpgPlay - handle to the playground
// pbUpdate - points to the variable to receive the update flag
// Returns: SPR_ERR_NOERROR if successful, SPR_ERR_* constant otherwise
//-------------------------------------------------------------------------
{
USHORT usAction;
if (queryHandleType(hpgPlay)!=QH_HPLAYGROUND) {
return SPR_ERR_BADHANDLE;
} /* endif */
usAction=accessSem((PHEADER)hpgPlay,ACCSEM_SET);
*pbUpdate=hpgPlay->bUpdate;
accessSem((PHEADER)hpgPlay,usAction);
return SPR_ERR_NOERROR;
}
Sprites and Animation (Part 2) - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ <hidden> SprRemoveSprite ΓòÉΓòÉΓòÉ
SprRemoveSprite
This function removes a sprite from the ahsSprites field of the HPLAYGROUND
structure and moves all members after the sprite down one slot in the array.
SPRERROR EXPENTRY SprRemoveSprite(HPLAYGROUND hpgPlay,HSPRITE hsSprite)
//-------------------------------------------------------------------------
// This function removes the sprite from the membership list of the
// specified playground. The sprite can then be added to another
// playground, or this one at a later time.
//
// Since there is a limited number of sprites that can be members of
// a playground, this function can be used to temporarily remove unused
// sprites from a playground so that others can be used.
//
// Input: hpgPlay - handle to the playground
// hsSprite - handle to the sprite to remove
// Returns: SPR_ERR_NOERROR if successful, SPR_ERR_* constant otherwise
//-------------------------------------------------------------------------
{
USHORT usAction;
ULONG ulIndex;
if (queryHandleType(hpgPlay)!=QH_HPLAYGROUND) {
return SPR_ERR_BADHANDLE;
} else
if (queryHandleType(hsSprite)!=QH_HSPRITE) {
return SPR_ERR_BADHANDLE;
} /* endif */
usAction=accessSem((PHEADER)hpgPlay,ACCSEM_SET);
for (ulIndex=0; ulIndex<hpgPlay->ulNumMembers; ulIndex++) {
if (hpgPlay->ahsSprites[ulIndex]==hsSprite) {
break;
} /* endif */
} /* endfor */
if (ulIndex==hpgPlay->ulNumMembers) {
accessSem((PHEADER)hpgPlay,usAction);
return SPR_ERR_HASNOPLAYGROUND;
} /* endif */
//----------------------------------------------------------------------
// Adjust the member array by moving all of the sprites after the one
// being removed to the slot just before there current position. Then,
// decrement the number of members and we're done.
//----------------------------------------------------------------------
hpgPlay->ulNumMembers--;
while (ulIndex<hpgPlay->ulNumMembers) {
hpgPlay->ahsSprites[ulIndex]=hpgPlay->ahsSprites[ulIndex+1];
ulIndex++;
} /* endwhile */
hpgPlay->ahsSprites[ulIndex]=NULL;
hsSprite->hpgPlay=NULL;
accessSem((PHEADER)hpgPlay,usAction);
return SPR_ERR_NOERROR;
}
Sprites and Animation (Part 2) - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ <hidden> SprSetPlaygroundBack ΓòÉΓòÉΓòÉ
SprSetPlaygroundBack
There is nothing special about this function that needs to be noted.
SPRERROR EXPENTRY SprSetPlaygroundBack(HPLAYGROUND hpgPlay,
HBITMAP hbmNew,
HBITMAP *phbmOld)
//-------------------------------------------------------------------------
// This function sets the background bitmap of the playground.
//
// Note that, once this function is called, the bitmap is managed by
// the sprite subsystem. The bitmap should *NOT* be deleted by the
// application unless the bitmap is "unset" from the playground (by
// calling this function again with a different handle).
//
// Input: hpgPlay - handle to the playground
// hbmNew - handle to the new bitmap to used as the background
// Output: phbmOld - points to the handle to the old background bitmap.
// This can be NULL, meaning that the application isn't interested
// in receiving this value.
// Returns: SPR_ERR_NOERROR if successful, SPR_ERR_* constant otherwise
//-------------------------------------------------------------------------
{
USHORT usAction;
if (queryHandleType(hpgPlay)!=QH_HPLAYGROUND) {
return SPR_ERR_BADHANDLE;
} /* endif */
usAction=accessSem((PHEADER)hpgPlay,ACCSEM_SET);
if (phbmOld!=NULL) {
*phbmOld=hpgPlay->hbmBack;
} /* endif */
hpgPlay->hbmBack=hbmNew;
//----------------------------------------------------------------------
// We're only interested in the cx and cy fields
//----------------------------------------------------------------------
hpgPlay->bmihBack.cbFix=16;
GpiQueryBitmapInfoHeader(hpgPlay->hbmBack,&hpgPlay->bmihBack);
accessSem((PHEADER)hpgPlay,usAction);
return SPR_ERR_NOERROR;
}
Sprites and Animation (Part 2) - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ <hidden> SprSetPlaygroundColor ΓòÉΓòÉΓòÉ
SprSetPlaygroundColor
Note that we do not force a repaint, since I didn't want to deal with passing
in an HPS on too many functions. It is conceivable that the background color
can be set immediately after creation of the playground and let it get
displayed the next time WM_PAINT is processed.
SPRERROR EXPENTRY SprSetPlaygroundColor(HPLAYGROUND hpgPlay,LONG lBackColor)
//-------------------------------------------------------------------------
// This function sets the new background color of the playground and is
// only valid if the playground doesn't have a bitmap.
//
// Input: hpgPlay - handle to the playground
// lBackColor - specifies the new background color
// Returns: SPR_ERR_NOERROR if successful, SPR_ERR_* constant otherwise
//-------------------------------------------------------------------------
{
USHORT usAction;
if (queryHandleType(hpgPlay)!=QH_HPLAYGROUND) {
return SPR_ERR_BADHANDLE;
} /* endif */
usAction=accessSem((PHEADER)hpgPlay,ACCSEM_SET);
if (hpgPlay->hbmBack!=NULLHANDLE) {
accessSem((PHEADER)hpgPlay,usAction);
return SPR_ERR_HASBACKGROUND;
} /* endif */
hpgPlay->lBackColor=lBackColor;
accessSem((PHEADER)hpgPlay,usAction);
return SPR_ERR_NOERROR;
}
Sprites and Animation (Part 2) - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ <hidden> SprSetPlaygroundSize ΓòÉΓòÉΓòÉ
SprSetPlaygroundSize
This function is allowed only if there is no background bitmap.
SPRERROR EXPENTRY SprSetPlaygroundSize(HPLAYGROUND hpgPlay,PSIZEL pszlSize)
//-------------------------------------------------------------------------
// This function sets the playground size for playgrounds that do not have
// a bitmap set as the background.
//
// Input: hpgPlay - handle to the playground
// pszlSize - points to the size of the playground
// Returns: SPR_ERR_NOERROR if successful, SPR_ERR_* constant otherwise
//-------------------------------------------------------------------------
{
USHORT usAction;
if (queryHandleType(hpgPlay)!=QH_HPLAYGROUND) {
return SPR_ERR_BADHANDLE;
} /* endif */
usAction=accessSem((PHEADER)hpgPlay,ACCSEM_SET);
if (hpgPlay->hbmBack!=NULLHANDLE) {
accessSem((PHEADER)hpgPlay,usAction);
return SPR_ERR_HASBACKGROUND;
} /* endif */
hpgPlay->bmihBack.cx=pszlSize->cx;
hpgPlay->bmihBack.cy=pszlSize->cy;
accessSem((PHEADER)hpgPlay,usAction);
return SPR_ERR_NOERROR;
}
Sprites and Animation (Part 2) - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ <hidden> SprSetSpritePosition ΓòÉΓòÉΓòÉ
SprSetSpritePosition
SPRERROR EXPENTRY SprSetSpritePosition(HPS hpsDraw,
HSPRITE hsSprite,
PPOINTL pptlNew)
//-------------------------------------------------------------------------
// This function changes the position of the sprite. This function is
// optimized so that, if the rectangle bounding the sprite at the new
// position overlaps the old, only one "bit blit" to the specified HPS
// is done, eliminating flicker.
//
// Input: hpsDraw - handle to the HPS to draw the sprite in once it is
// moved
// hsSprite - handle to the sprite
// Returns: SPR_ERR_NOERROR if successful, SPR_ERR_* constant otherwise
//-------------------------------------------------------------------------
{
USHORT usAction;
SIZEL szlPlay;
SIZEL szlWork;
RECTL rclOld;
RECTL rclNew;
RECTL rclUnion;
RECTL rclSrc;
RECTL rclDest;
POINTL ptlWork;
POINTL aptlPoints[4];
if (queryHandleType(hsSprite)!=QH_HSPRITE) {
return SPR_ERR_BADHANDLE;
} /* endif */
usAction=accessSem((PHEADER)hsSprite,ACCSEM_SET);
if (hsSprite->hpgPlay==NULL) {
accessSem((PHEADER)hsSprite,usAction);
return SPR_ERR_HASNOPLAYGROUND;
} /* endif */
if ((hsSprite->bVisible) && (hsSprite->hpgPlay->bUpdate)) {
szlWork.cx=MAX_SPRITE_CX*2;
szlWork.cy=MAX_SPRITE_CY*2;
SprQueryPlaygroundSize(hsSprite->hpgPlay,&szlPlay);
Note how we query the sprite rectangle before and after the position changes
and then call WinUnionRect() to check for overlap. This determines our course
of action. If there is no overlap, call drawBackAt() and SprDrawSprite() to
erase the sprite at its old position and redraw at its new position.
Otherwise, perform the delta processing (see below).
SprQuerySpriteRect(hsSprite,&rclOld);
hsSprite->ptlPos=*pptlNew;
SprQuerySpriteRect(hsSprite,&rclNew);
WinUnionRect(hsSprite->habAnchor,&rclUnion,&rclOld,&rclNew);
if ((rclUnion.xRight-rclUnion.xLeft>MAX_SPRITE_CX*2) ||
(rclUnion.yTop-rclUnion.yBottom>MAX_SPRITE_CY*2)) {
rclSrc.xLeft=rclOld.xLeft;
rclSrc.yBottom=rclOld.yBottom;
rclSrc.xRight=rclSrc.xLeft+hsSprite->bmihBitmap.cx;
rclSrc.yTop=rclSrc.yBottom+hsSprite->bmihBitmap.cy;
drawBackAt(hpsDraw,hsSprite->hpgPlay,NULL,NULL,&rclSrc);
SprDrawSprite(hpsDraw,hsSprite);
} else {
rclUnion contains the bounding rectangle of the old and new positions, so first
transfer this rectangle from the background to the workarea (offset by
(-rclUnion.xLeft,-rclUnion.yBottom)).
rclSrc=rclUnion;
rclSrc.xRight++;
rclSrc.yTop++;
rclDest.xLeft=0;
rclDest.yBottom=0;
rclDest.xRight=rclUnion.xRight-rclUnion.xLeft;
rclDest.yTop=rclUnion.yTop-rclUnion.yBottom;
drawBackAt(hsSprite->hpgPlay->hpsWork,
hsSprite->hpgPlay,
&rclDest,
&szlWork,
&rclSrc);
Once the background has been drawn, call drawSpriteAt() with a position also
offset by (-rclUnion.xLeft,-rclUnion.yBottom). This completes our drawing in
the workarea; now we simply need to remove the offset, clip to the playground,
and call GpiBitBlt() to transfer the entire rclUnion-sized rectangle from the
workarea to the screen.
ptlWork.x=hsSprite->ptlPos.x-rclUnion.xLeft;
ptlWork.y=hsSprite->ptlPos.y-rclUnion.yBottom;
drawSpriteAt(hsSprite->hpgPlay->hpsWork,hsSprite,&szlWork,&ptlWork);
//----------------------------------------------------------------
// GpiBitBlt is non-inclusive on source AND target
//----------------------------------------------------------------
aptlPoints[0].x=rclUnion.xLeft;
aptlPoints[0].y=rclUnion.yBottom;
aptlPoints[1].x=rclUnion.xRight+1;
aptlPoints[1].y=rclUnion.yTop+1;
aptlPoints[2].x=0;
aptlPoints[2].y=0;
aptlPoints[3].x=rclUnion.xRight-rclUnion.xLeft+1;
aptlPoints[3].y=rclUnion.yTop-rclUnion.yBottom+1;
if (clipBltPoints(hsSprite->habAnchor,aptlPoints,&szlPlay)) {
GpiBitBlt(hpsDraw,
hsSprite->hpgPlay->hpsWork,
4,
aptlPoints,
ROP_SRCCOPY,
BBO_IGNORE);
} /* endif */
} /* endif */
} else {
hsSprite->ptlPos=*pptlNew;
} /* endif */
accessSem((PHEADER)hsSprite,usAction);
return SPR_ERR_NOERROR;
}
Sprites and Animation (Part 2) - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ <hidden> SprSetSpriteVisibility ΓòÉΓòÉΓòÉ
SprSetSpriteVisibility
There is nothing special about this function that needs to be noted.
SPRERROR EXPENTRY SprSetSpriteVisibility(HPS hpsDraw,
HSPRITE hsSprite,
BOOL bVisible)
//-------------------------------------------------------------------------
// This function shows or hides a sprite.
//
// Input: hpsDraw - handle to the HPS to draw in once the sprite is
// shown or hidden
// hsSprite - handle to the sprite
// bVisible - new visibility state
// Returns: SPR_ERR_NOERROR if successful, SPR_ERR_* constant otherwise
//-------------------------------------------------------------------------
{
USHORT usAction;
RECTL rclSprite;
if (queryHandleType(hsSprite)!=QH_HSPRITE) {
return SPR_ERR_BADHANDLE;
} /* endif */
usAction=accessSem((PHEADER)hsSprite,ACCSEM_SET);
if (hsSprite->hpgPlay==NULL) {
accessSem((PHEADER)hsSprite,usAction);
return SPR_ERR_HASNOPLAYGROUND;
} /* endif */
if (hsSprite->bVisible!=bVisible) {
hsSprite->bVisible=bVisible;
if (hsSprite->hpgPlay->bUpdate) {
if (hsSprite->bVisible) {
SprDrawSprite(hpsDraw,hsSprite);
} else {
rclSprite.xLeft=hsSprite->ptlPos.x;
rclSprite.yBottom=hsSprite->ptlPos.y;
rclSprite.xRight=rclSprite.xLeft+hsSprite->bmihMask.cx;
rclSprite.yTop=rclSprite.yBottom+hsSprite->bmihMask.cy;
drawBackAt(hpsDraw,hsSprite->hpgPlay,NULL,NULL,&rclSprite);
} /* endif */
} /* endif */
} /* endif */
accessSem((PHEADER)hsSprite,usAction);
return SPR_ERR_NOERROR;
}
Sprites and Animation (Part 2) - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ <hidden> SprSetUpdateFlag ΓòÉΓòÉΓòÉ
SprSetUpdateFlag
There is nothing special about this function that needs to be noted.
SPRERROR EXPENTRY SprSetUpdateFlag(HPLAYGROUND hpgPlay,BOOL bUpdate)
//-------------------------------------------------------------------------
// This function sets the update flag for the playground. If FALSE, no
// drawing actually takes place in any of the functions requiring an HPS,
// and the value of the HPS handle may be NULLHANDLE. If TRUE, updating
// is reenabled, but you should still call SprDrawPlayground() to refresh
// the screen with the current contents.
//
// Input: hpgPlay - handle to the playground
// bUpdate - specifies the new update flag
// Returns: SPR_ERR_NOERROR if successful, SPR_ERR_* constant otherwise
//-------------------------------------------------------------------------
{
USHORT usAction;
if (queryHandleType(hpgPlay)!=QH_HPLAYGROUND) {
return SPR_ERR_BADHANDLE;
} /* endif */
usAction=accessSem((PHEADER)hpgPlay,ACCSEM_SET);
hpgPlay->bUpdate=bUpdate;
accessSem((PHEADER)hpgPlay,usAction);
return SPR_ERR_NOERROR;
}
Sprites and Animation (Part 2) - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ 2.2. Resources and Decompiling Them ΓòÉΓòÉΓòÉ
ΓòÉΓòÉΓòÉ 2.2.1. Introduction ΓòÉΓòÉΓòÉ
Resources and Decompiling Them
Written by Martin Lafaix
Introduction
What's that?
OS/2 comes with a resource compiler, RC, which allows us to put resources in an
executable file; but, it would sometimes be useful to do just the opposite,
namely, extracting resources from an executable (or from a DLL).
Why?
If we were able to extract resources, it would help us adapting this lovely
little tool, which unfortunately has all its messages and menus in, say,
Chinese... :-) and which is no longer supported by its author. Or, it would
allow us to correct those lovely typographical errors in the base OS/2 system,
too. (At least, the French version includes some boring typos, in menu items
and shortcuts :-( ) Or, it would even allow us to grab some lovely dialog box
and include it in our wonderful projects.
Contents
This article contains four parts. The first one describes the general
executable file structure, the second one describes the 16-bit EXE structure,
the third describes the 32-bit EXE structure, and the fourth describes the RES
to RC translation. Sample code will be given in REXX, which will use many
user-defined functions, such as readw() or readl().
A (short) bibliography can be found at the end of this document.
Resources and Decompiling Them - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ 2.2.2. A Voyage to OMF ΓòÉΓòÉΓòÉ
A Voyage to OMF
In the following sections, "EXE" will be used as a generic term for .EXE or
.DLL files.
The Old DOS Header
Each EXE starts with an old DOS header. Two fields interest us, namely e_magic
and e_lfanew. The first one contains "MZ" and allowed us to recognize the EXE
header, and the second one contains the offset of the new EXE header, which is
where the fun begins. The remaining fields are used for a DOS 'stub', that is,
a program which displays a message like:
This program cannot be run in a DOS session.
This message is displayed whenever the program is run from vanilla DOS.
struct exe_hdr /* DOS 1, 2, 3 .EXE header */
{
unsigned short e_magic; /* Magic number */
unsigned short e_cblp; /* Bytes on last page of file */
unsigned short e_cp; /* Pages in file */
unsigned short e_crlc; /* Relocations */
unsigned short e_cparhdr; /* Size of header in paragraphs */
unsigned short e_minalloc; /* Minimum extra paragraphs needed */
unsigned short e_maxalloc; /* Maximum extra paragraphs needed */
unsigned short e_ss; /* Initial (relative) SS value */
unsigned short e_sp; /* Initial SP value */
unsigned short e_csum; /* Checksum */
unsigned short e_ip; /* Initial IP value */
unsigned short e_cs; /* Initial (relative) CS value */
unsigned short e_lfarlc; /* File address of relocation table */
unsigned short e_ovno; /* Overlay number */
unsigned short e_res[ERES1WDS];/* Reserved words */
unsigned short e_oemid; /* OEM identifier (for e_oeminfo) */
unsigned short e_oeminfo; /* OEM information; e_oemid specific */
unsigned short e_res2[ERES2WDS];/* Reserved words */
long e_lfanew; /* File address of new exe header */
};
Figure 1. The DOS 1, 2, 3 .EXE header.
Recognizing it from REXX
In the following code, infile contains the EXE filename. base will then contain
the new header offset.
if charin(infile,,2) = 'MZ' then base = 1+l2d(charin(infile,61,4))
Resources and Decompiling Them - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ 2.2.3. Exploring 16-Bit Headers ΓòÉΓòÉΓòÉ
Exploring 16-Bit Headers
Recognizing a 16-bit EXE header
A 16-bit EXE header starts with the 'NE' magic number; a 16-bit EXE structure
starts with this magic number:
struct new_exe /* New .EXE header */
{
unsigned short ne_magic; /* Magic number NE_MAGIC */
unsigned char ne_ver; /* Version number */
unsigned char ne_rev; /* Revision number */
unsigned short ne_enttab; /* Offset of Entry Table */
unsigned short ne_cbenttab; /* Number of bytes in Entry Table */
long ne_crc; /* Checksum of whole file */
unsigned short ne_flags; /* Flag word */
unsigned short ne_autodata; /* Automatic data segment number */
unsigned short ne_heap; /* Initial heap allocation */
unsigned short ne_stack; /* Initial stack allocation */
long ne_csip; /* Initial CS:IP setting */
long ne_sssp; /* Initial SS:SP setting */
unsigned short ne_cseg; /* Count of file segments */
unsigned short ne_cmod; /* Entries in Module Reference Table */
unsigned short ne_cbnrestab; /* Size of non-resident name table */
unsigned short ne_segtab; /* Offset of Segment Table */
unsigned short ne_rsrctab; /* Offset of Resource Table */
unsigned short ne_restab; /* Offset of resident name table */
unsigned short ne_modtab; /* Offset of Module Reference Table */
unsigned short ne_imptab; /* Offset of Imported Names Table */
long ne_nrestab; /* Offset of Non-resident Names Table */
unsigned short ne_cmovent; /* Count of movable entries */
unsigned short ne_align; /* Segment alignment shift count */
unsigned short ne_cres; /* Count of resource entries */
unsigned char ne_exetyp; /* Target operating system */
unsigned char ne_flagsothers; /* Other .EXE flags */
char ne_res[NERESBYTES];
/* Pad structure to 64 bytes */
};
Figure 2. The OS/2 286 .EXE header.
The following fields interest us:
Field Contents
ne_cseg is the number of segments in the EXE. Segments containing
resources are at the end of the segment table.
ne_segtab is the offset of the segment table. Each entry in this
table contains the following:
ssector (WORD) is the segment's beginning sector, from the beginning of
the EXE. See ne_align below for more explanations on how
to compute the segment's effective position in EXE.
cb (WORD) is the segment's size in bytes.
sflags (WORD) is the segment's flags. Interesting bits are:
NSMOVE 0x0010 Moveable segment flag
NSSHARED 0x0020 Shared segment flag
NSPRELOAD 0x0040 Preload segment flag
NSDISCARD 0x1000 Segment is discardable
smin (WORD) is the minimum allocation in bytes. This field's value
is not used with segments containing resources.
Note: The segment table offset is from the beginning of
the 286 EXE header, not from the beginning of the EXE.
ne_rsrctab is the offset of the resource table. Each entry in this
table contains two fields:
etype (WORD) is the resource type.
ename (WORD) is the resource name (well, for OS/2, it's a number).
Note: The resource table offset is from the beginning of
the 286 EXE header, not from the beginning of the EXE.
ne_align is the segment alignment shift count. It's the number of
bits we should shift the segment's beginning sector value
to find the segment's position in EXE.
For example, if ne_align is 4, segments will be aligned on
16-byte boundaries (that is, the EXE will be composed of
16-byte 'sectors').
ne_cres is the number of resources in the EXE. Each resource uses
a segment.
More information on the 16-bit EXE header can be found in NEWEXE.H, which comes
with the Developer's toolkit. Unfortunately, it isn't very informative.
Extracting a Resource From The EXE
The process of extracting resources from an EXE to a RES file is quite simple.
We walk through the resource table (rsrctab), and, for each entry, we find and
emit the corresponding segment. (We have to twiddle the segment flag and
create a small header for the resource, but that's not a big deal.)
o First, we have to find the corresponding resource table entry (resource
number cnt):
call charin infile,base+rsrctab+cnt*4,0
o Then, we have to read the entry's content:
etype = readw()
ename = readw()
o Then, we have to find and read the corresponding segment table entry:
call segin cseg-rsrccnt+1+cnt
o Then we...(I'm using a procedure here for readability.)
segin:
call charin infile,base+segtab+(arg(1)-1)*8,0
ssector = readw()
cb = readw()
sflags = readw()
smin = readw()
o We then calculate the resource offset:
pos = 1+(2**segshift)*ssector
o And we translate the segment flag (from NSMMOVE, etc. to MOVEABLE, etc.):
flags = 0
if bit(sflags,10) then flags = flags+64
if bit(sflags,12) then flags = flags+16
if bit(sflags,4) then flags = flags+4096
if \ bit(sflags,11) then flags = flags+32
o We are now ready to write the resource header to a RES file:
call emit 'FF'x||d2w(etype)'FF'x||d2w(ename)d2w(flags)d2l(cb)
o And, last but not least, we have to write the resource data, too:
call emit charin(infile,pos,cb)
Note: The RES format is explained in the section The RES File Format
Resources and Decompiling Them - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ 2.2.4. Exploring 32-Bit Headers ΓòÉΓòÉΓòÉ
Exploring 32-Bit Headers
Recognizing a 32-bit EXE header
A 32-bit EXE header starts with the 'LX' magic number; a 32-bit structure
starts with this magic number:
struct e32_exe /* New 32-bit .EXE header */
{
unsigned char e32_magic[2]; /* Magic number E32_MAGIC */
unsigned char e32_border; /* The byte ordering for the .EXE */
unsigned char e32_worder; /* The word ordering for the .EXE */
unsigned long e32_level; /* The EXE format level for now = 0 */
unsigned short e32_cpu; /* The CPU type */
unsigned short e32_os; /* The OS type */
unsigned long e32_ver; /* Module version */
unsigned long e32_mflags; /* Module flags */
unsigned long e32_mpages; /* Module # pages */
unsigned long e32_startobj; /* Object # for instruction pointer */
unsigned long e32_eip; /* Extended instruction pointer */
unsigned long e32_stackobj; /* Object # for stack pointer */
unsigned long e32_esp; /* Extended stack pointer */
unsigned long e32_pagesize; /* .EXE page size */
unsigned long e32_pageshift; /* Page alignment shift in .EXE */
unsigned long e32_fixupsize; /* Fixup section size */
unsigned long e32_fixupsum; /* Fixup section checksum */
unsigned long e32_ldrsize; /* Loader section size */
unsigned long e32_ldrsum; /* Loader section checksum */
unsigned long e32_objtab; /* Object table offset */
unsigned long e32_objcnt; /* Number of objects in module */
unsigned long e32_objmap; /* Object page map offset */
unsigned long e32_itermap; /* Object iterated data map offset */
unsigned long e32_rsrctab; /* Offset of Resource Table */
unsigned long e32_rsrccnt; /* Number of resource entries */
unsigned long e32_restab; /* Offset of resident name table */
unsigned long e32_enttab; /* Offset of Entry Table */
unsigned long e32_dirtab; /* Offset of Module Directive Table */
unsigned long e32_dircnt; /* Number of module directives */
unsigned long e32_fpagetab; /* Offset of Fixup Page Table */
unsigned long e32_frectab; /* Offset of Fixup Record Table */
unsigned long e32_impmod; /* Offset of Import Module Name Table */
unsigned long e32_impmodcnt; /* Number of entries in Import Module Name Table */
unsigned long e32_impproc; /* Offset of Import Procedure Name Table
unsigned long e32_pagesum; /* Offset of Per-Page Checksum Table */
unsigned long e32_datapage; /* Offset of Enumerated Data Pages */
unsigned long e32_preload; /* Number of preload pages */
unsigned long e32_nrestab; /* Offset of Non-resident Names Table */
unsigned long e32_cbnrestab; /* Size of Non-resident Name Table */
unsigned long e32_nressum; /* Non-resident Name Table Checksum */
unsigned long e32_autodata; /* Object # for automatic data object */
unsigned long e32_debuginfo; /* Offset of the debugging information */
unsigned long e32_debuglen; /* The length of the debugging info. in bytes */
unsigned long e32_instpreload;/* Number of instance pages in preload section of .EXE file */
unsigned long e32_instdemand; /* Number of instance pages in demand load section of .EXE file */
unsigned long e32_heapsize; /* Size of heap - for 16-bit apps */
unsigned long e32_stacksize; /* Size of stack */
unsigned char e32_res3[E32RESBYTES3];
/* Pad structure to 196 bytes */
};
Figure 3. The OS/2 386 .EXE header.
The following fields interest us:
Field Contents
e32_pageshift is the page alignment shift in the EXE. It's the number of
bits we should shift the page offset value to find the
page's position in EXE.
For example, if e32_pageshift is 4, pages will be aligned
on 16-byte boundaries.
e32_objtab is the object table offset. Each entry contains the
following fields:
32 1 32 1
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé size Γöé base Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé flags Γöé pagemap Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé mapsize Γöé reserved Γöé
ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
o size is the object virtual size.
o base is the object base virtual address.
o flags is the object attribute flags.
o pagemap is the object page map index.
o mapsize is the number of entry in the object's page map.
o reserved is, well, reserved :-)
We're only interested by flags and pagemap. That is, flags
contains the resource flags (MOVEABLE, LOADONCALL, and so
on), while pagemap allows us to find the object's pages
within the EXE. (See the "Tables and Maps Relations"
section below, for more explanations on pagemap, the object
table, and other tables.)
flags' interesting bits are:
OBJWRITE 0x0002L Writeable Object
OBJDISCARD 0x0010L Object is Discardable
OBJSHARED 0x0020L Object is Shared
OBJPRELOAD 0x0040L Object has preload pages
Note: The object table offset is from the beginning of the
386 EXE header, not from the beginning of the EXE.
e32_objmap is the object page map offset. Each entry contains the
following fields:
32 1 16 1 16 1
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé pagedataoffset Γöé pagesize Γöé pageflags Γöé
ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
o pagedataoffset is the file offset of page.
o pagesize is the number of bytes of page data.
o pageflags is per-page attributes.
Note: The object page map offset is from the beginning of
the 386 EXE header, not from the beginning of the EXE.
e32_rsrctab is the offset of the resource table. Each entry contains
the following fields:
16 1 16 1 32 1
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé type Γöé name Γöé cb Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
Γöé obj Γöé offset Γöé
ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
16 1 32 1
o type is the resource type.
o name is the resource name.
o cb is the resource size, in bytes.
o obj is the number of the object containing the resource.
o offset is the resource's offset within object. Resource will be in object
obj, starting at the specified offset:
0 offset offset+cb
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
objΓöé Γöé<- resource ->Γöé ...
ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
Note: The resource table offset is from the beginning of
the 386 EXE header, not from the beginning of the EXE.
e32_rsrccnt is the number of resources in the EXE.
e32_datapage is the offset of Enumerated data page. It's the position
of the first data page in the EXE.
Warning: This offset is from the beginning of the EXE, not
from the beginning of the 386 EXE header. It's THE
exception :-/
More information on the 32-bit EXE header can be found on exe386.h, which comes
with the Developer's toolkit. It's not that informative, though, and OMF.INF
is much better. I highly recommend it.
Tables and Maps Relations
In this section, we will view the various relations between tables and maps.
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
file Γöé Γöé Γöé Γöé Γöé ...
ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
object's page 1Γöé Γöé object's page 2
ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ Γöé
Γöé Γöé
Γöé Γöé
: Γöé Γöé
Γöé Γöé Γöé Γöé : Γöé Γöé Γöé Γöé
r-1Γöé Γöé Γöé Γöé ΓöîΓöÇΓöÇΓöÇΓöÇΓö£ΓöÇΓöÇΓöÇΓöÇΓöñ Γöé Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöñ : Γöé Γöé Γöé p Γöé Γöé ΓöÇΓöÿ Γöé
r Γöé Γöé ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ Γöé Γöé Γöé Γö£ΓöÇΓöÇΓöÇΓöÇΓöñ Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöñ Γöé o-1Γöé Γöé Γöé p+1Γöé Γöé ΓöÇΓöÇΓöÇΓöÿ
r+1Γöé Γöé ΓööΓöÇΓöÇΓöÇΓöÇΓö£ΓöÇΓöÇΓöÇΓöÇΓöñ Γöé Γö£ΓöÇΓöÇΓöÇΓöÇΓöñ
Γöé Γöé o Γöé Γöé ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ : Γöé Γöé
: Γöé Γöé Γö£ΓöÇΓöÇΓöÇΓöÇΓöñ Γöé Γöé
Γöé Γöé o+1Γöé Γöé Γöé Γöé
resource object page map
table table
Figure 4. Tables and maps relations.
To find the data of a resource r, we first have to read the corresponding entry
in the resource table. The obj field of this entry allows us to find the
object which contains the data. The pagemap field of the object table entry
then allows us to locate the object's pages in the EXE, via the page map. To
find the resource data, we then just have to read cb bytes from object,
starting at offset.
Extracting a Resource From The EXE
The process of extracting resources from a 32-bit EXE to a RES file is similar
to the 16-bit EXE to RES conversion. We walk through the resource table
(rsrctab), and, for each entry, we find and emit the corresponding resource.
(We have to twiddle the object flag and create a small header for the resource,
but that's not a big deal.)
Note: In the following code, we'll assume that objects span over consecutive
pages. That is, we will not handle the case where the object's pages are
arranged discontinuously in the EXE.
o First, we have to find the corresponding resource table entry (resource
number cnt):
call charin infile,base+rsrctab+cnt*14,0
o Then, we have to read the entry's content:
etype = readw() /* resource type */
ename = readw() /* resource name */
cb = readl() /* resource size */
eobj = readw() /* object containing resource */
eoffset = readl() /* resource's offset in eobj */
call objin eobj
(I'm using a procedure here for readability.)
objin:
call charin infile,base+objtab+(arg(1)-1)*24,8
oflags = readl() /* object attributes */
opagemap = readl() /* object page map index */
omapsize = readl() /* -- not used -- */
opagedataoffset = l2d(charin(infile,base+objmap+(opagemap-1)*8,4))
o We then calculate the resource offset:
pos = 1+datapage+eoffset+(2**pageshift)*opagedataoffset
o And we translate the object flag (from OBJPRELOAD, ... to LOADONCALL, ...):
flags = 0
if bit(oflags,10) then flags = flags+64
if bit(oflags,11) then flags = flags+16
if bit(oflags,12) then flags = flags+4096
if \ bit(oflags,15) then flags = flags+32
o We are now ready to write the resource header to a RES file:
call emit 'FF'x||d2w(etype)'FF'x||d2w(ename)d2w(flags)d2l(cb)
o And, last but not least, we have to write the resource data, too:
call emit charin(infile,pos,cb)
Note: The RES format is explained in the next section (The RES file format).
Resources and Decompiling Them - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ 2.2.5. The RES to RC Translation ΓòÉΓòÉΓòÉ
The RES to RC Translation
And now the last part. First, we will describe the RES format, and we will
then describe some resources data. We will focus our interest on the
human-readable resources, such as menus, stringtables and so on.
Note: An important exception will be the dialog templates, for the following
two reasons:
o The Dialog editor already does this.
o It's not that different from the others resources, and it would necessitated
an even more fastidious enumeration.
For these reasons, our resource decompiler will not extract dialog templates
from RES to RC.
And we will finally describe briefly the included resource decompiler, rdc.
The RES format
The RES file is an aggregate of resources. Each resource is composed of a
header, followed by the resource data.
8 1 16 1 8 1 16 1 16 1
ΓöîΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
Γöé0xFFΓöé Type Γöé0xFFΓöé Id Γöé Flags Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
Γöé cb Γöé
ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
32 1
Figure 5. The resource header.
Type is the resource type (see below).
Id is the resource name/identifier.
Flags is the resource attributes (MOVEABLE, LOADONCALL, ...).
cb is the resource size.
The following types may appear:
RT_POINTER 1 /* mouse pointer shape */
RT_BITMAP 2 /* bitmap */
RT_MENU 3 /* menu template */
RT_DIALOG 4 /* dialog template */
RT_STRING 5 /* string tables */
RT_FONTDIR 6 /* font directory */
RT_FONT 7 /* font */
RT_ACCELTABLE 8 /* accelerator tables */
RT_RCDATA 9 /* binary data */
RT_MESSAGE 10 /* error msg tables */
RT_DLGINCLUDE 11 /* dialog include file name */
RT_VKEYTBL 12 /* key to vkey tables */
RT_KEYTBL 13 /* key to UGL tables */
RT_CHARTBL 14 /* glyph to character tables */
RT_DISPLAYINFO 15 /* screen display information */
RT_FKASHORT 16 /* function key area short form */
RT_FKALONG 17 /* function key area long form */
RT_HELPTABLE 18 /* Help table for Cary Help manager */
RT_HELPSUBTABLE 19 /* Help subtable for Cary Help manager */
RT_FDDIR 20 /* DBCS uniq/font driver directory */
RT_FD 21 /* DBCS uniq/font driver */
Other values for type denote user-defined resources.
Resource data format
We first have to read the resource header:
res2rc: /* convert .RES format to .RC */
call skip 1 /* skipping the 'FF'x */
rt = readw() /* the resource type */
call skip 1 /* skipping the 'FF'x */
id = readw() /* the resource ID/name */
opt = readw() /* the resource flag */
cb = readl() /* the resource data size */
Then, according to the resource type, we'll have to do specific operations:
select
when rt = 1 then call emit 'POINTER 'id' 'option()' 'file('ptr')nl
when rt = 2 then call emit 'BITMAP 'id' 'option()' 'file('bmp')nl
when rt = 7 then call emit 'FONT 'id' 'option()' 'file('fon')nl
If the resource is a pointer, a bitmap, a font or an icon, the resource data is
the corresponding pointer, bitmap, font or icon. We then just have to recreate
a file containing this data.
when rt = 3 then do; call emit 'MENU 'id' 'option()nl'BEGIN'nl; call emit menuout(' ')nl'END'nl; end
If the resource is a menu, it's not that simple :-) The resource data is the
corresponding menu structure:
16 1 16 1 16 1 16 1 16 1
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
Γöé cb Γöé type Γöé cp Γöé offs Γöé countΓöé ...
ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
cb is the size of the menu data.
type is the menu type (Only 0 and 1 are valid).
cp is the menu code page (850 by default).
offs is the starting offset of the menu data, from the start of the structure.
count is the number of item composing the menu.
If the menu type is 1, count is followed by another 16-bit field, ppoffs
(presentation parameter offset, from the start of the structure). But we won't
handle type 1 menus, so...
Every item has the following format:
16 1 16 1 16 1
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
Γöéstyle ΓöéattribΓöé Id Γöé [Optional Data]
ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
style is the item style (MIS_*).
attrib is the item attributes (MIA_*).
Id is the item identifier.
If the item contains data (that is, if item is a submenu, or has the
MIS_BITMAP, MIS_STRING or ... style), then the previous structure is followed
by the corresponding data:
MIS_SUBMENU Data is a menu structure, as previously defined.
MIS_STRING Data is a null-terminated string.
MIS_BITMAP Data can be any of the following:
'FF'x, followed by a 16-bit word, representing the resource
identifier.
'00'x. No resource identifier provided.
"#", and subsequent characters make up the decimal representation
of the resource identifier.
So, if the resource is a menu, we will have to emit each item, recursively (as
a menu can contain a submenu, ...).
when rt = 5 then call emit 'STRINGTABLE 'option()nl'BEGIN'strout()'END'nl
when rt = 10 then call emit 'MESSAGETABLE 'option()nl'BEGIN'strout()'END'nl
If the resource is a stringtable or a messagetable, then we have to emit the
corresponding table. Each string/messagetable contains up to 16 strings. (In a
RC file, you can have more than one stringtable, with more than 16 strings, but
rc does not preserve your ordering -- string IDs are maintained, though.)
In the RES file, STRINGTABLE data looks like the following:
16 1 8 1 1 len+1 8 1
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
Γöé dummyΓöélenΓöéstring1 0ΓöélenΓöéstring2 ...
ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
Each string is zero-terminated. If len is zero, the string does not exists.
when rt = 8 then do; call emit 'ACCELTABLE 'id' 'option()nl'BEGIN'nl||keyout()'END'nl; end
If the resource is an acceltable, then we have to emit the corresponding table.
ACCELTABLE resource data looks like the following:
16 1 16 1 16 1 16 1 16 1
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
ΓöécountΓöé cp Γöétype1Γöékey1 Γöécmd1 Γöétype2Γöé... ...
ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
count is the number of keys in the acceltable.
cp is the acceltable codepage.
And the type/key/cmd triplets describe the accel-keys :
type is the key's type (VIRTUALKEY, shifted, ...).
key is the key's value (VK_F1, "a", ...).
cmd is the accel command.
when rt = 11 then do; call emit 'DLGINCLUDE 'id' 'charin(infile,,cb)nl; cb = 0; end
If the resource is a dlginclude statement, then the resource data will contain
the included file name.
Note: This information is of little value if you don't have the included
file...
when rt = 18 then call emit 'HELPTABLE 'id||nl'BEGIN'htout()'END'nl
If the resource is a helptable, then the resource data will contain the
following:
16 1 16 1 16 1 16 1
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
Γöéwnd 1Γöésub 1Γöé-----Γöéext 1Γöéwnd 2Γöésub 2Γöé-----Γöéext 2Γöé ...
ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
< helpitem 1 > < helpitem 2 >
wnd n is the application window ID.
sub n is the help subtable ID.
ext n is the extended help panel ID.
when rt = 19 then call emit 'HELPSUBTABLE 'id||hstout()nl
If the resource is a HELPSUBTABLE, then the resource data will contain the
following:
16 1 16 1 16 1
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
Γöésize Γöéwnd 1Γöéhelp1Γöéwnd 2Γöéhelp2Γöé ...
ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
<subitem 1> <subitem 2>
Each subitem contains size items (the default size value is 2):
wnd n is the child window ID.
help n is the help panel ID.
And, if size is more than 2, the remaining integers have an application-defined
meaning.
otherwise
call emit 'RESOURCE 'rt' 'id' 'option()' 'file('dat')
If the resource is of any other type, then we emit the 'RESOURCE' generic
statement, and we put the resource data in a .DAT file. The rc compiler will
handle that gracefully. :-)
end /* select */
The Resource decompiler
The interesting part, at last! A resource decompiler (named rdc.cmd) is
provided in rdc.zip. It's usage is as follow:
Usage: rdc [<options>] <.EXE input file> [<.RC output file>]
-r - Extract .res file
-h - Access Help
Figure 6. The resource decompiler usage.
Note: Please note the following:
o It's not highly polished.
o The RES to RC translation part is known to be buggy.
o You can't directly obtain a RC file from an EXE. You have to take a two-step
process (first, rdc -r xxx.exe, and then rdc xxx.res xxx.rc).
o If files named res_*.* are present in the current directory, you'll get a
strange result. (And it may trash these files.)
o The default RC file extension is '.RC2'. It'll protect you from bad
surprises.
o And, as stated earlier, it does not extract dialog templates from RES to RC
files (but it does extract them from EXE to RES).
But, to be optimistic, it works just fine most of the time :-)
Resources and Decompiling Them - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ 2.2.6. Summary ΓòÉΓòÉΓòÉ
Summary
In the previous parts, we have seen how to extract resources from an executable
(a .EXE or a .DLL), and how to extract some resources from a .RES file, as we
have focused our interest on the 'human-readable' resources.
While I realize there are still many obscure points, I hope you will find the
included information useful. And I'll try my best to answer all questions on
it.
Resources and Decompiling Them - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ 2.2.7. Bibliography ΓòÉΓòÉΓòÉ
Bibliography
On EXE format
OMF.INF describes the new 32-bit file format. It's a good
discussion on OMF (Object Module Format) and the 386 EXE
header. It's available on ftp-os2.cdrom.com.
EXE.H, EXE386.H and NEWEXE.H These C header files contain the various EXE
header structures. But it's not really a good place to
start with.
On RC/RES format
Control Program Guide and Reference This on-line manual describes the
DosGetResource/DosFreeResource APIs. It's part of the
Developer's toolkit.
PM Reference This on-line manual contains much useful information on the
resource data format (In Related Information/Resource
File). It's part of the Developer's toolkit.
Tools Reference This on-line manual contains the Resource Compiler
reference. It describes all RC statements/directives.
It's part of the Developer's toolkit.
Resources and Decompiling Them - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ <hidden> outname ΓòÉΓòÉΓòÉ
outname
This function, which requires two parameters, returns an output filename (if
one does not already exists).
outname: /* return name made from infile and extension */
if outfile = '' then
if lastpos('.',arg(1)) > lastpos('\',arg(1)) then
outfile = left(arg(1),lastpos('.',arg(1)))arg(2)
else
outfile = arg(1)'.'arg(2)
return outfile
Resources and Decompiling Them - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ <hidden> readw ΓòÉΓòÉΓòÉ
readw
This function reads one word (two bytes) from current file position. The
file's position is updated.
readw: /* read one word from infile */
return w2d(charin(infile,,2))
Resources and Decompiling Them - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ <hidden> readl ΓòÉΓòÉΓòÉ
readl
This function reads one long word (four bytes) from current file position. The
file's position is updated.
readl: /* read one long from infile */
return l2d(charin(infile,,4))
Resources and Decompiling Them - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ <hidden> skip ΓòÉΓòÉΓòÉ
skip
This function skips arg(1) chars in current file. The file's position is
updated.
skip: /* skip arg(1) chars */
return charin(infile,,arg(1))
Resources and Decompiling Them - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ <hidden> bit ΓòÉΓòÉΓòÉ
bit
This function returns bit arg(2) of arg(1). arg(1) can contain up to 32 bits.
Note: bits are numbered from left to right.
bit: /* return bit arg(2) of arg(1) */
return substr(x2b(d2x(arg(1),4)), arg(2),1)
Resources and Decompiling Them - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ <hidden> w2d ΓòÉΓòÉΓòÉ
w2d
This function translates a little-endian word to a REXX integer.
w2d: /* little-endian word to decimal */
w = c2x(arg(1))
return x2d(substr(w,3,2)substr(w,1,2))
Resources and Decompiling Them - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ <hidden> d2w ΓòÉΓòÉΓòÉ
d2w
This function translates a REXX integer to a little-endian word.
d2w: /* decimal to little-endian word */
w = d2x(arg(1),4)
return x2c(substr(w,3,2)substr(w,1,2))
Resources and Decompiling Them - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ <hidden> l2d ΓòÉΓòÉΓòÉ
l2d
This function translates a little-endian long word to a REXX integer.
l2d: /* little-endian long to decimal */
l = c2x(arg(1))
return x2d(substr(l,7,2)substr(l,5,2)substr(l,3,2)substr(l,1,2))
Resources and Decompiling Them - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ <hidden> d2l ΓòÉΓòÉΓòÉ
d2l
This function translates a REXX integer to a little-endian long word.
d2l: /* decimal to little-endian long */
l = d2x(arg(1),8)
return x2c(substr(l,7,2)substr(l,5,2)substr(l,3,2)substr(l,1,2))
Resources and Decompiling Them - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ <hidden> emit ΓòÉΓòÉΓòÉ
emit
This function writes arg(1) to output file.
emit: /* write data to output file */
return charout(outfile,arg(1))
Resources and Decompiling Them - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ <hidden> option ΓòÉΓòÉΓòÉ
option
This function translates the option's attributes into a RC string.
option: /* convert flags to option string */
if bit(opt,10) then r = 'PRELOAD'; else r = 'LOADONCALL'
if bit(opt,12) then r = r' MOVEABLE'
if bit(opt, 4) then r = r' DISCARDABLE'
if \ (bit(opt,4) | bit(opt,12)) then r = r' FIXED'
if r = 'LOADONCALL MOVEABLE DISCARDABLE' then r = ''
return r
Resources and Decompiling Them - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ <hidden> file ΓòÉΓòÉΓòÉ
file
This function creates a new file, with extension arg(1), and fill it with cb
bytes of infile.
file: /* write cb bytes to res_xxx.arg(1) */
r = 'res_'right(fnum,4,'0')'.'arg(1)
call charout r,charin(infile,,cb)
fnum = fnum+1; cb = 0
call stream r,'c','close'
return r
Resources and Decompiling Them - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ <hidden> strout ΓòÉΓòÉΓòÉ
strout
This function extracts a string/messagetable definitions, and returns a string
containing the table.
strout: /* extract strings definitions */
call skip 2
id = (id-1)*16; cb = cb-2; r = nl
do while cb > 0
len = x2d(c2x(charin(infile,,1)))
if len > 1 then r = r' 'left(id,8)'"'charin(infile,,len-1)'"'nl
call skip 1
id = id+1; cb = cb-len-1
end /* do */
return r
Resources and Decompiling Them - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ <hidden> keyout ΓòÉΓòÉΓòÉ
keyout
This functions extracts an acceltable definition, and returns a string
containing the acceltable.
keyout: /* extract acceltable definitions */
procedure expose nl cb infile outfile
cnt = readw()
cp = readw()
cb = cb-4
if cp \= 850 then call emit arg(1)'CODEPAGE 'cp||nl
do cnt
typ = readw()
key = readw()
if \ bit(typ,15) & key >= 32 & key <= 255 then key = '"'d2c(key)'"'; else key = '0x'd2x(key)
cmd = readw()
cb = cb-6; t = ''
if bit(typ,16) then t = t', CHAR'
if bit(typ,15) then t = t', VIRTUALKEY'
if bit(typ,14) then t = t', SCANCODE'
if bit(typ,13) then t = t', SHIFT'
if bit(typ,12) then t = t', CONTROL'
if bit(typ,11) then t = t', ALT'
if bit(typ,10) then t = t', LONEKEY'
if bit(typ, 8) then t = t', SYSCOMMAND'
if bit(typ, 7) then t = t', HELP'
call emit ' 'left(key',',8)left(cmd',',8)substr(t,3)nl
end /* do */
return ''
Resources and Decompiling Them - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ <hidden> htout ΓòÉΓòÉΓòÉ
htout
This function returns a string containing the HELPTABLE definition.
htout: /* extract helptable definitions */
r = nl
i = readw()
do while i \= 0
r = r' HELPITEM 'i', 'readw()
call skip 2
r = r', 'readw()nl; cb = cb-8
i = readw()
end /* do */
cb = cb-2
return r
Resources and Decompiling Them - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ <hidden> hstout ΓòÉΓòÉΓòÉ
hstout
This function returns a string containing the HELPSUBTABLE definition.
hstout: /* extract helpsubtable definitions */
sis = readw()
if sis \= 2 then r = nl'SUBITEMSIZE 'sis; else r = ''
r = r||nl'BEGIN'nl; cb = cb-2
i = readw()
do while i \= 0
r = r||' HELPSUBITEM 'i
do sis-1; r = r', 'readw(); end
cb = cb-2*sis; r = r||nl
i = readw();
end /* do */
cb = cb-2
return r'END'
Resources and Decompiling Them - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ <hidden> itemout ΓòÉΓòÉΓòÉ
itemout
This functions emits the current menu item.
itemout: /* extract menu item definition */
procedure expose nl cb infile outfile
cb = cb-6; s = ''; a = ''; r = arg(1)'MENUITEM "'; x = '| MIS_'; y = '| MIA_'
sty = readw()
att = readw()
iid = readw()
if \ (bit(sty,13) | bit(sty,14)) then
do
c = charin(infile); cb = cb-1
if c = 'FF'x & bit(sty,15) then do; r = r'#'readw(); cb = cb-2; end
else do while c \= '00'x; r = r||c; c = charin(infile); cb = cb-1; end
end
if bit(sty,15) then s = s x'BITMAP'
if bit(sty,14) then s = s x'SEPARATOR'
if bit(sty,13) then s = s x'OWNERDRAW'
if bit(sty,12) then s = s x'SUBMENU'
if bit(sty,11) then s = s x'MULTMENU'
if bit(sty,10) then s = s x'SYSCOMMAND'
if bit(sty, 9) then s = s x'HELP'
if bit(sty, 8) then s = s x'STATIC'
if bit(sty, 7) then s = s x'BUTTONSEPARATOR'
if bit(sty, 6) then s = s x'BREAK'
if bit(sty, 5) then s = s x'BREAKSEPARATOR'
if bit(sty, 4) then s = s x'GROUP'
if bit(sty, 3) then s = s x'SINGLE'
if bit(att,11) then a = a y'NODISMISS'
if bit(att, 4) then a = a y'FRAMED'
if bit(att, 3) then a = a y'CHECKED'
if bit(att, 2) then a = a y'DISABLED'
if bit(att, 1) then a = a y'HILITED'
if a \= '' then a = ','substr(a,3)
if s \= '' then s = ','substr(s,3); else if a \= '' then s = ','
call emit r'", 'iid||s||a||nl
if bit(sty,12) then do; call emit arg(1)'BEGIN'nl; call emit menuout(arg(1)' ','')arg(1)'END'nl; end
return
Resources and Decompiling Them - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ <hidden> menuout ΓòÉΓòÉΓòÉ
menuout
This functions emit the current menu or submenu.
menuout: /* extract menus definitions */
procedure expose nl cb infile outfile
cb = cb-10;
cbs = readw()
typ = readw()
cp = readw()
off = readw()
cnt = readw()
if arg(2) \= '' then
do
if cp \= 850 then call emit 'CODEPAGE 'cp||nl
call emit arg(2)
end /* do */
do cnt; call itemout arg(1); end
return ''
Resources and Decompiling Them - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ 2.3. Visual REXX Faceoff ΓòÉΓòÉΓòÉ
ΓòÉΓòÉΓòÉ 2.3.1. Introduction ΓòÉΓòÉΓòÉ
Visual Rexx Faceoff
Written by Gordon Zeglinski
Introduction
This issue sees the first part of the Visual Rexx Faceoff. We start by looking
at VX-REXX.
The Watcom VX-REXX package includes two 3.5" HD floppies and a 700+ page
manual. The manual is nicely written, but the reference section is slightly
hard to use. (Fortunately, one doesn't have to use it!) The first few
chapters are instructions on how to do things, the rest of the manual is all
reference. There are plenty of sample programs included.
Visual Rexx Faceoff - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ 2.3.2. Installation ΓòÉΓòÉΓòÉ
To install VX-REXX, you need about 5-6 megs of free disk space. The install
program is pretty simple and non-intrusive. It fits in nicely with the OS/2
environment. There's nothing I hate more than these "ego-maniac" install
programs that go out of their way to make you sit and watch them install.
Fortunately, this package doesn't have one of those. After answering a few
questions, popping in the two disks, and rebooting, we have the following
folder on our desktop.
Figure 1. Installation Folder
Visual Rexx Faceoff - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ 2.3.3. Look and Feel ΓòÉΓòÉΓòÉ
The interface is neat and easy to use. It consists of the window you are
designing and a tool palette, as shown in figure 2.
Figure 2. User Interface
VX-REXX uses SOM to implement its "tools". Each of the objects in the Tools
window (except for the pointer) is a SOM object. Note, just because VX-REXX is
based in SOM does not mean it's a WPS app, which is not a bad thing. VX-REXX
does mimic the feel of the WPS. Each object has a pop-up menu that can be used
to change the various properties of the object you have clicked on. Figure 2
shows the popup for the main window; other objects have similar menus. Figure
3 shows the properties notebook for a static text object.
Figure 3. Properties Notebook
Overall, if you are used to the WPS, you will quickly learn how to configure
and create objects. We now get to the best part of the interface.
I found the reference section of the manual a bit difficult to use. It's hard
to find answers to questions like "How do I put text into the damn listbox?!".
Fortunately, you don't have to look in the manual for this. VX has a code
insertion ability. To use this ability, you bring up a context menu, select
"Code Insert", then select the action you want the code to perform. The
inserted code will usually require some editing. In addition to the popup menu
method of code insertion, the user can drag a control from the window they are
designing and drop it on the code window. VX-REXX will then prompt the user
for the action to perform, after which the code necessary to perform the action
is inserted.
Visual Rexx Faceoff - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ 2.3.4. Wrapping Things Up ΓòÉΓòÉΓòÉ
I have found one annoying thing/bug in VX, but before I comment on it, I will
wait for a response from Watcom tech support. Next issue, we'll look at VisPro
REXX (from HockWare) and see how VX-REXX compares to it.
Visual Rexx Faceoff - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ 3. Columns ΓòÉΓòÉΓòÉ
The following columns can be found in this issue:
o /dev/EDM/BookReview
o C++ Corner
o Introduction to PM Programming
o Scratch Patch
Columns - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ 3.1. /dev/EDM/BookReview ΓòÉΓòÉΓòÉ
ΓòÉΓòÉΓòÉ 3.1.1. Introduction ΓòÉΓòÉΓòÉ
/dev/EDM2/BookReview
Written by Carsten Whimster
Introduction
/dev/EDM2/BookReview is a monthly column which focuses on development oriented
books and materials. The column is from a beginning PM programmer's eyes,
because that's what I am. Try to pick up whichever book strikes your fancy,
and join the growing group of people following our introductory PM programming
columns. I will review books aimed at beginners for a while, and then move on
from there.
Please send me your comments and thoughts so that I can make this column as
effective as possible. After all, this is our magazine, and it will be most
effective with reader feedback.
This book is the last of my current collection, and even though it was written
for OS/2 1.3, and thus only covers the 16-bit functions, it still manages to
give decent coverage to concepts which are 16/32 bit insensitive.
/dev/EDM/BookReview - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ 3.1.2. Errata ΓòÉΓòÉΓòÉ
Errata
There are three things on my agenda this month.
First of all, I got mail from a couple of people who disagreed with my rosy
evaluation of Writing OS/2 2.1 Device Drivers in C, 2nd Edition, Mastrianni. I
guess I should have made a couple of things a little clearer. If you are to use
the book, you need Mastrianni's library, unless you write your own DevHlp
functions, and the library costs around $150 US. Secondly, the reason I feel
strongly about his book is that Mastrianni has done a lot of work abstracting
device driver writing away from naked assembler, giving the opportunity to many
more people to write device drivers than otherwise would have attempted it.
This is the main accomplishment, I think. In any case, you must judge for
yourself whether it is worth it. If you do decide to write a device driver,
you'll most likely need the device driver kit from IBM, but from what I have
heard, it is in dire need of cleaning up. It is supposedly messy, expensive,
and incomplete. IBM would really do us all a huge favour by putting out a
better kit, and in fact, they may be headed in that direction. Supposedly
Mastrianni has been hired by IBM, and I presume he'll be working on their kit
development team. If anyone has any factual information on this development,
I'd love to hear it. One can always hope. In any case, the book will help you
get off to a faster, surer start than if you were to try it on your own.
Secondly, I have had a report of an inaccuracy in Real World Programming for
OS/2 2.1, Blain, Delimon, and English. Here is the deal, courtesy of Gordon
Zeglinski: on page 440, the following occurs:
!DosSubSet(pHeap,DOSSUB_SPARSE_OBJ|DOSSUB_SERIALIZE))
If we look in the header file however, we find
#define DosSubSet DosSubSetMem
#define DOSSUBSET DosSubSetMem
APIRET APIENTRY DosSubSetMem(PVOID pbBase,
ULONG flag,
ULONG cb);
So the code fragment does not even have the correct number of arguments. It
will not compile without a warning in C, and will not compile at all under C++.
Look at Gordon's C++ Queue Object column for the proper way to use this API.
Finally, from what I have heard, the new Watcom C/C++ 10.0 is imminent, and
from reviews of the beta, it is something. It will have an IDE, and an
assembler (yes!), on top of the usual stuff, and of course will include the new
2.1 toolkit. If you are about to buy a C/C++ compiler, wait until this comes
out before you decide which one to spend your hard-earned money on. It looks
like IBM may finally get a run for their money in the high-end
compiler-department.
As a foot-note, I am working (with a little help from my friends here at EDM/2)
on a tabular content format for what each book covers, and what it does not
cover. Exiting stuff, and this may be just what you have been waiting for to
be able to decide which book has what you need. Thanks to Gordon (again) for
the great idea.
/dev/EDM/BookReview - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ 3.1.3. OS/2 Presentation Manager GPI ΓòÉΓòÉΓòÉ
OS/2 Presentation Manager GPI
Sample code fans will like the layout of this book. In some ways, it is quite
similar to Real World Programming for OS/2 2.1. Each chapter starts out with a
small motivating section, followed by detail on how to actually apply the
concepts being presented. The sample code follows at the end of the chapter,
and consists of complete snippets of code, with some meaningful function,
although whole programs are not included. This is in keeping with the author's
stated objective of assisting the intermediate to advanced PM programmer in
becoming a proficient GPI programmer. With its 318 pages, the book is fairly
substantial, considering that it only covers one major topic.
Here are the chapter headings:
1. Introduction: Simple Text and Graphics Output
2. Presentation Spaces and Device Contexts
3. Drawing Primitives and Attributes
4. Fonts
5. Bitmaps
6. Color Tables
7. Coordinate Spaces and Transformation
8. Clipping and Regions
9. Orders, Elements, and Segments
10. Correlation and Boundary Data Accumulation
11. MetaFiles
12. Printing
13. The OS/2 2.0 32-Bit Operating System
14. Appendix 1: DevOpenDC Parameters
15. Appendix 2: PMPRINT Queue Processor Parameters
16. Appendix 3: Introduction to Transforms and Matrices
17. Appendix 4: Sample MetaFile Internals
18. Appendix 5: Sample Orders
19. Appendix 6: GPI Functions Supported only by a Normal-PS
Before using this book, you should already know how to set up a skeleton
program, with message queue, window procedure, and all the rest of the
trimmings. This book ONLY talks about the GPI and a few Dev functions.
The first chapter introduces the methodology and intent of the GPI APIs. Brief
explanations of device contexts, presentation spaces, and so on are presented.
Personally, I would have liked a section explaining the design choices made in
the GPI APIs, but that doesn't hurt the coverage of the book. One thing I did
miss, though, was a little humour. Like so many programming books, humour is
conspicuous by its absense. I am not calling for a Monty Python-esque
treatment of the material, just a little light-hearted jesting. This would make
it more enjoyable, and less dry. Oh well, it's not the first time I have
missed that, and it won't be the last.
The general feeling of the subsequent chapters is one of a programmer who knows
his field very well, but has a lot to cover. Each sentence has terse
information to present, and it never lets up. This is not a book you sit down
and read in a couple of days! Every sentence has to be thought through after
reading it! You probably have to try the code from each chapter in your own
applications before really understanding what's going on.
Chapter two lays the ground-work for all GPI programming. Device contexts and
presentation spaces are explained again in much greater detail than the first
time. There are limitations to each choice of PS, and the correct type of DC
must be used. This seems logical at first sight, but can be complicated when
your requirements are not clearly delineated. Unfortunately, the diagrams used
in the book look like something the original word-processors might have
produced, not very professional. Not good for a book on presentation graphics
and printing.
The various drawing primitives and attribute groups, characters and text, lines
and curves, filled areas or patterns, markers, and image and bitblt pixel
operations are introduced in depth, and their relationship to each other are
explained. Non-bundle attributes are explained next. Many functions and their
related possibilities are explained.
Each of chapters four to eleven introduces its own area, and finally in chapter
twelve, the particular problems of printing are demonstrated. I did feel that
chapter five on bitmaps was short, given the disproportionate amount of
interest this particular topic usually has.
There are a lot of gotcha's in GPI programming, and a lot of little rules you
have to be aware of. Most people will not really use that much of the GPI,
except to output a text-string here and there, and for these people, this book
is overkill. It is not intended as a reference book, there are other books for
that purpose. This book is intended, in the author's own words "...to provide
answers (illustrated by programming examples) to any questions that Application
Developers may have concerning the GPI and Dev functions. Rather than providing
a tutorial description, I have concentrated on making the description of each
topic as comprehensive as possible." The end result is a book which is similar
to a reference in coverage, but more like a tutorial in presentation.
My final impression is a good one, even if the book is a little dry. It
concentrates on presenting OS/2's way of programming with graphics, not on
teaching the un-initiated about graphics programming. The one major
short-coming is that it was written for 16-bit OS/2 1.3. I find it strange
that the book has not been re-written for OS/2 2.x, but perhaps the sales of
this book were disappointing because of the slowness with which 1.3 caught on.
Now is a different story, though. With over 5 million copies of OS/2 sold at
last count, we need an updated version of this book. My final evaluation of
this book reflects this.
/dev/EDM/BookReview - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ 3.1.4. Summary and Ratings ΓòÉΓòÉΓòÉ
Summary and Ratings
This book ought to be updated. There aren't many books on the market
specifically about the GPI, and with the current boom going on with OS/2 2.1,
2.11, warp/personal OS/2, and so on, it would be perfect timing to have a new
edition now. The book is terse, well-written and accurate, but needs
descriptions of the 32-bit functions, the new functions, and how to interface
the two with 16-bit functions.
ΓöîΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
ΓöéBOOK ΓöéAUDIENCE ΓöéMARKΓöéCOMMENTS Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
ΓöéReal World Programming for OS/2 2.1,ΓöéIntermediateΓöéB+ ΓöéLots of good code examples, but sometimes it is Γöé
ΓöéBlain, Delimon, and English, SAMS Γöéto Advanced Γöé Γöétoo complex for novices. Accurate. Well Γöé
ΓöéPublishing. ISBN 0-672-30300-0. ΓöéPM C Γöé Γöéorganized. The index needs a little beefing up. Γöé
ΓöéUS$40, CAN$50. Γöéprogrammers Γöé ΓöéGood, but not entirely complete how-to reference.Γöé
Γöé Γöé Γöé ΓöéGood purchase. Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
ΓöéLearning to Program OS/2 2.0 ΓöéBeginning PMΓöéB- ΓöéThis book can be both frustrating and very Γöé
ΓöéPresentation Manager by Example, ΓöéC Γöé Γöérewarding. It is not very large, and a bit Γöé
ΓöéKnight, Van Nostrand Reinhold. ISBN ΓöéProgrammers Γöé Γöépricey, but has some excellent chapters on Γöé
Γöé0-442-01292-6. US$40, CAN$50. Γöé Γöé Γöébeginning topics, such as messages, resources, Γöé
Γöé Γöé Γöé ΓöéIPF, and dialog boxes. Strictly for beginners. Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
ΓöéWriting OS/2 2.1 Device Drivers in ΓöéAdvanced C ΓöéA- ΓöéThe only thing a device driver programmer would Γöé
ΓöéC, 2nd Edition, Mastrianni, Van ΓöéProgrammers,Γöé Γöénot find in here is how to write SCSI, ADD, and Γöé
ΓöéNostrand Reinhold. ISBN Γöéfamiliar Γöé ΓöéIFS drivers. Most everything else is in here, Γöé
Γöé0-442-01729-4. US$35, CAN$45. Γöéwith Γöé Γöéalong with skeleton examples. An optional DevHlpΓöé
Γöé Γöéhardware Γöé Γöélibrary of C-callable functions can be purchased Γöé
Γöé Γöéprogramming Γöé Γöéby those who don't have time to write their own. Γöé
Γö£ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
ΓöéOS/2 Presentation Manager GPI, Winn,ΓöéIntermediateΓöéC+ ΓöéThis book needs updating for OS/2 2.x. It is a Γöé
ΓöéVan Nostrand Reinhold. ISBN Γöéto advanced Γöé Γöéwell-written in-depth coverage of the OS/2 way ofΓöé
Γöé0-442-00739-6. US$35, CAN$45. ΓöéPM C Γöé Γöéprogramming for graphics. It is not an Γöé
Γöé Γöéprogrammers Γöé Γöéintroductory PM or graphics programming book. Γöé
Γöé Γöé Γöé ΓöéYou should know how to create windows etc. Γöé
ΓööΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
This table contains all books I have reviewed, so that you can find what you
are looking for at a glance. I will be careful to rate books fairly relative
to each other. If I feel a need to adjust ratings, I will adjust all of them
at the same time, and write a note explaining why I felt this necessary.
Please note that books aimed at different audiences should only be compared
with great care, if at all. I intend to concentrate on the strong points of
the books I review, but I will point out any weaknesses in a constructive
manner. Read the reviews carefully.
BOOK: The name of the book, author(s), publishing company, ISBN, and
approximate price.
AUDIENCE: This is a description of the audience I think the book targets best.
This is not intended as gospel, just a guideline for people not familiar with
the book.
MARK: My opinion of the success of the book's presentation, and how well it
targets its audience. Technical content, accuracy, organization, readability,
and quality of index all weigh heavily here, but the single most important item
is how well the book covers what it says it covers. I don't expect to see any
book score less than C, but the scale is there if necessary.
ΓöîΓöÇΓöÇΓöÇΓö¼ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÉ
ΓöéA+ ΓöéGround-breaking, all-around outstanding book Γöé
Γö£ΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
ΓöéA ΓöéExcellent book. This is what I want to see happen a lot Γöé
Γö£ΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
ΓöéA- ΓöéExcellent book with minor flaws Γöé
Γö£ΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
ΓöéB+ ΓöéVery good book with minor flaws or omissions Γöé
Γö£ΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
ΓöéB ΓöéGood book with some flaws and omissions Γöé
Γö£ΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
ΓöéB- ΓöéGood book, but in need of improvement Γöé
Γö£ΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
ΓöéC+ ΓöéMediocre book with some potential, but in need of some updating Γöé
Γö£ΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
ΓöéC ΓöéMediocre book with some good sections, but badly in need of fixing Γöé
Γö£ΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
ΓöéC- ΓöéMediocre book, little good material, desperately in need of an overhaul Γöé
Γö£ΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
ΓöéD ΓöéDon't buy this book unless you need it, and nothing else exists Γöé
Γö£ΓöÇΓöÇΓöÇΓö╝ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöñ
ΓöéF ΓöéDon't buy this book. Period Γöé
ΓööΓöÇΓöÇΓöÇΓö┤ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÿ
COMMENTS: This is a summary of the review proper, although in a very brief
format.
/dev/EDM/BookReview - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ 3.1.5. Coming Up ΓòÉΓòÉΓòÉ
Coming Up
Next month I will be looking at The Art of OS/2 C Programming, Panov, Salomon
and Panov, if it gets here in time (sounds very familiar :). Otherwise I will
review some REXX book. The books I intend to review are (not necessarily in
this order):
o The Art of OS/2 C Programming, Panov, Salomon and Panov
o OS/2 Presentation Manager Programming, Petzold - 1994 - not yet published :(
o The Design of OS/2, 2nd Edititon, Kogan and Deitel - 1994 - not published
yet? I will review the old version if someone sends me one, but if I have to
buy it I will wait for the new edition.
This list is not set in stone, but they are books I am interested in. I am
considering reviewing the IBM OS/2 Redbooks, since they are readily and cheaply
available, and look like good introductory reference. I am also considering
reviewing OS/2 Unleashed, but it is not strictly speaking a development book,
so I'm going to wait until the list of real development books has diminished a
bit. By the way, does anyone know why the special edition of OS/2 Unleashed
has completely different authors? And what is different about the Special
Edition? Finally, I am considering reviewing Designing OS/2 Applications,
Reich, mostly because it promises to present a different angle on OS/2
programming, namely that of how to design OS/2 applications, rather than how
to program OS/2 applications.
If anyone has a book they want to see reviewed, I will be happy to oblige as
long as I can afford it. Of course, requests can be satisfied quicker when
accompanied by a book. :) Publishers can send me books at the address on my
personal page at the end of the magazine, and I will review all OS/2
development-related books I receive.
/dev/EDM/BookReview - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ 3.2. C++ Corner ΓòÉΓòÉΓòÉ
ΓòÉΓòÉΓòÉ 3.2.1. Introduction ΓòÉΓòÉΓòÉ
C++ Corner
Written by Gordon Zeglinski
Introduction
This issue's C++ column is in response to a reader's email. Things are
extremely hectic, so sometimes email doesn't get answered as promptly as I
would like it to be. Keep the mail coming, but please be patient; I'll answer
it as soon as I can.
In this issue, we will look at multi-threading using the IThread class. The
IThread class is part of the ICLUI shipped with C-Set++ 2.0 and 2.1; however,
we will be building upon the drag and drop example of last issue, which
requires C-Set++ 2.1.
C++ Corner - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ 3.2.2. The IThread Class ΓòÉΓòÉΓòÉ
The IThread Class
The IThread class is used to manipulate threads in the ICLUI. An instance of
IThread and the thread it represents are not bound together. If the thread
terminates, the instance is not destroyed. If the instance is destroyed, the
thread continues executing.
Starting a Thread with IThread
There are two methods that can be used to start a thread. The first, is to use
the one of the constructors that take a function address or a reference to an
instance of IThreadFn as an argument. The second is to use the "start member"
function. We will look at both methods here.
The following code snippet illustrates both methods of starting a thread. In
this example, we use a member function of the class foo as the threads starting
function.
class foo{
public:
foo();
void fooThread();
};
void main(){
//Create an instance of the foo class
foo fooInst;
// create the member thread dispatch object
IThreadMemberFn<foo> ThreadFnc(fooInst,&foo::fooThread);
// create a reference to the member thread dispatch object
IReference<IThreadFn> ThreadFncRef(&ThreadFnc);
// create a thread object and start the thread
IThread ImmediateDispatch(ThreadFncRef);
// create a thread object
IThread DelayedDispatch;
/*
Do some work here
*/
DelayedDispatch.start(ThreadFncRef);
}
The class IThreadMemberFn<class T> holds both an instance of a class and a
pointer to a member function of that class. These two pieces of information
are need to start the thread using the correct member function and instance.
Note, that since IThreadMemberFn is a template class, it can be used with any
class.
In this example, we only looked at starting threads on member functions. The
method of starting threads on non-member functions is almost identical to the
one outlined above. Thus, it is left up to the reader to explore this further.
C++ Corner - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ 3.2.3. Building a Simple Test Application ΓòÉΓòÉΓòÉ
Building a Simple Test Application
This section should look familiar to those who have read the last issue. We
will take our application from last issue, and move the file loading routine
into a separate thread. In this section, we will look at only the
modifications made to last issues code that are needed to accomplish our goal
here.
The class definition for MyFrame has been expanded to allow multi-threaded
loading of files into the MLE.
class MyFrame:public IFrameWindow{
public:
MyFrame(const char *Title);
void StartLoadThread(); //Added to start thread
void SetFileName(IString &S){FileName=S;} //Added to set the filename
protected:
void LoadFile(); //Added: load thread start point
// New members to support multi-threaded loading
IReference<IThreadFn> ThreadFncRef;
IThreadMemberFn<MyFrame> *ExecuteThreadFnc;
IThread ExecuteThread;
IString FileName;
IMultiLineEdit EditWin;
AFileProvider FileProvider;
};
Added to the version of MyFrame shown above, are several several new member
functions that handle the multi-threaded file loading. The constructor is
modified as follows to initialize the new data members.
MyFrame::MyFrame(const char *Title):
IFrameWindow(Title,IResourceId(1), //-------------------
IFrameWindow::titleBar| //
IFrameWindow::sizingBorder| //
IFrameWindow::minimizeButton| // Create the Frame
IFrameWindow::systemMenu| // Window
IFrameWindow::shellPosition| //
IFrameWindow::minimizeButton| //
IFrameWindow::windowList| //
IFrameWindow::maximizeButton), //--------------------
EditWin(10,this,this){ // Create the Edit Window
// ID=10, use this frame
// window as the parent and
//owner
//---------------------
setIcon(IResourceId(1));
setClient(&EditWin);
//enable default drag and drop handler
IDMHandler::enableDragDropFor(&EditWin);
//attach the provider
EditWin.setItemProvider(&FileProvider);
//Create the thread support members.
ExecuteThreadFnc=new IThreadMemberFn<MyFrame>(*this,
&MyFrame::LoadFile);
ThreadFncRef=IReference<IThreadFn>(ExecuteThreadFnc);
show();
}
The targetDrop routine is modified so that it uses the member functions we
added to MyFrame to set the file name and start the thread which will read in
the file. In the following code, we use the fact that the parent of the MLE is
an instance of MyFrame to get the instance of the MyFrame window and call the
thread creation function.
Boolean AFileItem::targetDrop( IDMTargetDropEvent &Event){
IMultiLineEdit *DropWin=(IMultiLineEdit *)this->targetOperation()->targetWindow();
IString
fname = this->containerName() + this->sourceName();
MyFrame *FrameWin=(MyFrame*) DropWin->parent();
FrameWin->SetFileName(fname);
FrameWin->StartLoadThread();
return true;
}
Now that we've seen how the old code was modified, we look at the two new
member functions of MyFrame. These functions don't do anything that we haven't
already seen.
void MyFrame::StartLoadThread(){
ExecuteThread.start(ThreadFncRef);
}
void MyFrame::LoadFile(){
//erase the edit window
EditWin.removeAll();
//load the file into the edit window
EditWin.importFromFile(FileName);
}
The source code to this issue is packaged the same way as the source code in
the previous issue.
C++ Corner - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ 3.2.4. Summary ΓòÉΓòÉΓòÉ
Summary
In this issue, we have seen how to use IThread and its supporting classes to
create a multi-threaded PM application by building upon our drag and drop
example from last issue.
C++ Corner - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ 3.3. Introduction to PM Programming ΓòÉΓòÉΓòÉ
ΓòÉΓòÉΓòÉ 3.3.1. Introduction ΓòÉΓòÉΓòÉ
Introduction to PM Programming
Written by Larry Salomon, Jr.
Introduction
The purpose of this column is to provide the readers out there who are not
familiar with PM application development the information necessary to satisfy
their curiousity, educate themselves, and give them an advantage over the
documentation supplied by IBM. Of course, much of this stuff could probably be
found in one of the many books out there, but the problem with books in general
is that they don't answer the questions you have after you read the book the
first time through.
I will gladly entertain feedback from the readers about what was "glossed over"
or what was detailed well, what tangential topics need to be covered and what
superfluous crap should have been removed. This feedback is essential in
guaranteeing that you get what you pay for. :)
It should be said that you must not depend solely on this column to teach you
how to develop PM applications; instead, this should be viewed as a supplement
to your other information storehouses (books, the network conferences, etc.).
Because this column must take a general approach, there will be some topics
that you would like to see discussed that really do not belong here. Specific
questions can be directed to the Scratch Patch, where an attempt to answer them
will be made.
Last Month
Last month, we explored more of the PM APIs and began looking into the
WC_ENTRYFIELD window class. This month, we will continue where we left off,
and will continue in directions unknown; unknown, that is, unless you continue
reading.
Introduction to PM Programming - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ 3.3.2. More Messages ΓòÉΓòÉΓòÉ
More Messages
Let us first begin by looking at the remaining entryfield messages.
EM_CLEAR
This message is sent to delete the selected text in the entryfield.
Parameters
param1
ulReserved (ULONG)
Reserved, 0.
param2
ulReserved (ULONG)
Reserved, 0.
Returns
reply
bSuccess (BOOL)
TRUE successful completion
FALSE an error occurred.
EM_COPY
This message is sent to copy the selected text to the clipboard.
Parameters
param1
ulReserved (ULONG)
Reserved, 0.
param2
ulReserved (ULONG)
Reserved, 0.
Returns
reply
bSuccess (BOOL)
TRUE successful completion
FALSE an error occurred.
EM_CUT
This message is sent to copy the selected text to the clipboard and then delete
it. This is equivalent to sending an EM_COPY message followed by an EM_CLEAR
message.
Parameters
param1
ulReserved (ULONG)
Reserved, 0.
param2
ulReserved (ULONG)
Reserved, 0.
Returns
reply
bSuccess (BOOL)
TRUE successful completion
FALSE an error occurred.
EM_PASTE
This message is sent to paste text from the clipboard into the entryfield. If
there is selected text, it is replaced with the pasted contents; otherwise, the
text is inserted at the current cursor position.
Parameters
param1
ulReserved (ULONG)
Reserved, 0.
param2
ulReserved (ULONG)
Reserved, 0.
Returns
reply
bSuccess (BOOL)
TRUE successful completion
FALSE an error occurred.
EM_SETFIRSTCHAR
This message is sent to set the zero-based index of the first character visible
in the entryfield.
Parameters
param1
sFirstChar (SHORT)
0-based index of the first visible character
param2
ulReserved (ULONG)
Reserved, 0.
Returns
reply
bSuccess (BOOL)
TRUE successful completion
FALSE an error occurred.
EM_SETINSERTMODE
This message is sent to set the insert mode of the entryfield.
Parameters
param1
bInsert (BOOL)
TRUE insert mode
FALSE overwrite mode
param2
ulReserved (ULONG)
Reserved, 0.
Returns
reply
bOldState (BOOL)
TRUE the entryfield was previously in insert mode
FALSE the entryfield was previously in overwrite mode
EM_SETREADONLY
This message is sent to set the read-only state of the entryfield.
Parameters
param1
bReadOnly (BOOL)
TRUE the entryfield should be read-only
FALSE the entryfield should be editable
param2
ulReserved (ULONG)
Reserved, 0.
Returns
reply
bOldState (BOOL)
TRUE the entryfield was previously read-only
FALSE the entryfield was previously editable
EM_SETSEL
This message is sent to set the current selection of the entryfield.
Parameters
param1
sMinSel (SHORT)
The first 0-based point of selection
sMaxSel (SHORT)
The last 0-based point of selection
param2
ulReserved (ULONG)
Reserved, 0.
Returns
reply
bSuccess (BOOL)
TRUE successful completion
FALSE an error occurred.
EM_SETTEXTLIMIT
This message is sent to set the maximum number of characters the entryfield can
contain.
Parameters
param1
usLimit (USHORT)
maximum number of characters allowed
param2
ulReserved (ULONG)
Reserved, 0.
Returns
reply
bSuccess (BOOL)
TRUE successful completion
FALSE an error occurred.
Introduction to PM Programming - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ 3.3.3. Conceptually Speaking ΓòÉΓòÉΓòÉ
Conceptually Speaking
Of Selections, Anchor Points, and Cursor Points
What is a selection? A selection is an area that is defined by the user. Any
"objects" (defined by the context of the selection) that are contained within
the selected area are defined to be selected. Any selection consists of two
things: an anchor point, which is defined to be the point where the selection
was started and is fixed, and a cursor point, which is defined to the be place
where the cursor/pointer currently is and moves with the cursor/pointer. A
selection is performed, according to CUA guidelines, using either the mouse or
the keyboard. Using the mouse requires the user to press one of the buttons
(usually the first) and hold the button while moving the mouse. When the
selection has been made, the button is released. Using the keyboard, the user
presses the Shift key while moving the cursor with the arrow keys. When the
Shift key is released, the selection is completed.
There are other semantics associated with selections when using the mouse, but
we will not discuss them here. The point of these definitions is to allow you
to understand the purpose of the EM_SETSEL message. Its two parameters are
slightly different that what was discussed above; if the anchor point is always
before the cursor point ("before" is used, since the entryfield can be
considered as being a one-dimensional stream of characters), then sMinSel is
the anchor point and sMaxSel is the cursor point, otherwise the two are
reversed.
Clipboard
For those of you who have no GUI experience, the clipboard is a temporary
storage place for placing items to be used later in the same or other
applications. Items of predefined, or application-defined formats, may be
placed on the clipboard.
Its capabilities and interfaces are beyond the scope of this discussion, but it
is important that you know what it is.
Text Limits
When you create an entryfield, by default it allows up to 32 characters maximum
to be entered. If, however, you need more (or less) than this amount, you must
first send it an EM_SETTEXTLIMIT message, which tells the entryfield to
allocate more or less memory for its use.
Introduction to PM Programming - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ 3.3.4. Notifications ΓòÉΓòÉΓòÉ
Notifications
The entryfield also sends notifications to its owner. These notifications are
listed below.
o EN_CHANGE - this is sent whenever the entryfield's contents have changed.
o EN_KILLFOCUS - this is sent whenever the entryfield is losing the input
focus.
o EN_MEMERROR - this is sent whenever the entryfield cannot allocate memory in
response to an EM_SETTEXTLIMIT message.
o EN_OVERFLOW - this is sent whenever an attempt to insert or paste more text
than is allowed by the text limit is made.
o EN_SCROLL - this is sent whenever the entryfield has scrolled from a call to
WinScrollWindow(), the cursor moving beyond the visible area, or when the
text has changed.
o EN_SETFOCUS - this is sent whenever the entryfield is gaining the input
focus.
The notifications you will use the most often are the EN_CHANGE, EN_SETFOCUS,
and EN_KILLFOCUS ones, although the others have their uses.
Introduction to PM Programming - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ 3.3.5. Summary ΓòÉΓòÉΓòÉ
Summary
This month, we finished looking at the entryfield messages, the notifications
sent by the entryfield, and the concepts associated with the new messages.
Next month, we will begin looking at the WC_LISTBOX class, and will continue
with nameDlgProc().
Introduction to PM Programming - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ 3.4. Scratch Patch ΓòÉΓòÉΓòÉ
ΓòÉΓòÉΓòÉ 3.4.1. Introduction ΓòÉΓòÉΓòÉ
Scratch Patch
Written by Larry Salomon, Jr.
Introduction
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).
Scratch Patch - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ 3.4.2. Corrections ΓòÉΓòÉΓòÉ
Corrections
If you remember, last month I stated that APAR number PJ13781 was created
against the Palette Manager; well, it has been closed. The reason is, if
you'll look in the source code (ugpm.zip), you'll see that a call to WinGetPS()
is made without 1) creating a message queue first and 2) without releasing it
later. This is causing the instabilities that were reported.
My apologies for not figuring this out before, but I never looked at the
source.
Scratch Patch - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ 3.4.3. Gotcha Notes! ΓòÉΓòÉΓòÉ
Gotcha Notes!
When using the IBM User Interface Class Libraries, if you use the new operator
to create an instance of your main window in the main() function, you must use
the delete operator after the IApplication::current().run() call or else the
application will not close after the main window has been destroyed.
Scratch Patch - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ 3.4.4. Snippet(s) of the Month ΓòÉΓòÉΓòÉ
Snippet(s) of the Month
"When it rains, it pours."
Eberhard Mattes (mattes@azu.informatik.uni-stuttgart.de) submitted the
following item:
This is the template for a dialog box that contains a system icon. Note that
DLGEDIT doesn't know how to handle SS_SYSICON.
DLGTEMPLATE 3141
BEGIN
DIALOG "A System Icon", 3141, 20, 20, 200, 64, , FCF_TITLEBAR | FCF_SYSMENU
BEGIN
CONTROL SPTR_ICONQUESTION, 0, 90, 28, 0, 0, WC_STATIC, WS_VISIBLE | SS_SYSICON
DEFPUSHBUTTON "OK", 1, 8, 8, 32, 16
END
END
ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
Jeff Garzik (jgarzik@pantera.atl.ga.us) submitted the following item:
/*
* DosStartSession() sample code for a DOS session
* by Jeff Garzik (jgarzik@pantera.atl.ga.us)
*
* It has been tested under BC++ 1.0 and emx+gcc 0.8h.
*/
#define INCL_DOS /* we're lazy */
#include <os2.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <conio.h>
int main (int argc, char *argv[])
{
STARTDATA sd; /* DosStartSession parm structure */
PID pid = 0, pid2 = 0; /* unused but necessary */
ULONG SessID; /* "" */
RESULTCODES rcResults; /* "" */
APIRET rc; /* OS/2 API return code */
/* the program we want to run */
char s[128] = "c:\\os2\\mdos\\qbasic.exe",
/* the parms of the program to be exec'd */
s1[128] = "/EDITOR c:\\autoexec.bat",
/* MS-DOS settings of program */
DosSettings[200] = "DOS_BACKGROUND_EXECUTION=0\0"
"DPMI_MEMORY_LIMIT=0\0"
"EMS_MEMORY_LIMIT=0\0"
"HW_TIMER=1\0"
"IDLE_SECONDS=10\0"
"IDLE_SENSITIVITY=20\0"
"XMS_MEMORY_LIMIT=0\0"
"\0";
/* fill in data for DosStartSession() */
memset((void *)&sd, 0, sizeof(sd));
sd.Length = sizeof(STARTDATA);
sd.Related = SSF_RELATED_CHILD;
sd.FgBg = SSF_FGBG_FORE;
sd.TraceOpt = SSF_TRACEOPT_NONE;
sd.PgmTitle = "My First DOS Sub-Process"; /* not really necessary */
sd.PgmName = s;
sd.PgmInputs = s1;
sd.TermQ = NULL;
/*
* The two following settings are somewhat different for
* DOS sessions. You CANNOT set the environment of a
* DOS session. The Environment field in STARTDATA is
* instead used for the DOS settings. InheritOpt is really
* irrelevant because DOS sessions get their environment
* from the settings in DOS_AUTOEXEC (defaults to C:\AUTOEXEC.BAT)
* exclusively
*/
sd.Environment = DosSettings; /* or 0 for no settings */
sd.InheritOpt = SSF_INHERTOPT_SHELL;
/* set to SFF_TYPE_WINDOWEDVDM for windowed session */
sd.SessionType = SSF_TYPE_VDM;
sd.IconFile = 0;
sd.PgmHandle = 0;
sd.PgmControl = 0;
sd.InitXPos = 0; /* unused for fullscreen session */
sd.InitYPos = 0; /* unused for fullscreen session */
sd.InitXSize = 0; /* unused for fullscreen session */
sd.InitYSize = 0; /* unused for fullscreen session */
sd.Reserved = 0;
sd.ObjectBuffer = NULL;
sd.ObjectBuffLen = 0;
if ((rc = DosStartSession(&sd, &SessID, &pid)) != 0)
printf("\nDosStartSession error %ld\n\n", rc);
else
printf("Successfully executed DOS Editor on AUTOEXEC.BAT.\n");
DosWaitChild(DCWA_PROCESS, DCWW_WAIT, &rcResults, &pid2, pid);
printf("\nPress any key to continue...");
getch();
putchar('\n');
return 0;
}
ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
Someone sent me the following in a uuencoded format, and I forgot to save their
name and email address! Sounds familiar? :)
#define INCL_DOS
#define INCL_DOSDEVIOCTL
#define INCL_ERRORS
#include <os2.h>
#include <stdio.h>
/*+-------------------------------------------------------------------+
|Function QueryCommPorts |
| |
|Purpose : |
| Determine nr. of commports available on the PC. |
| Try to figure out which port is used by the mouse. |
|Inputparameters : |
| *cCommPorts : pointer to a byte to place the number of available|
| comm ports |
| bVerbose : bool that indicates whether or not a status report|
| must be displayed |
|Outputparameters : |
| cMousePort : byte that contains the nr of the comm port used by |
| the mouse |
+-------------------------------------------------------------------+*/
BYTE QueryNrCommPorts( BYTE *cCommPorts, BOOL bVerbose )
{
BYTE cMousePort = -1;
BOOL bMouse = FALSE;
HFILE hfMouse;
ULONG ulAction;
APIRET rc;
struct
{
USHORT DeviceID;
USHORT CommPort;
USHORT SecDeviceID;
} DataWords;
ULONG ulDataLength = sizeof( DataWords );
CHAR szDeviceID[6][30] = { "Unknown",
"Bus mouse",
"Serial Mouse",
"Inport mouse",
"PS/2*-style pointing device",
"IBM* 8516 Touch Display"
};
/*+----------------------------------------------+
| Determine the number of comm ports on the PC |
+----------------------------------------------+*/
DosDevConfig( cCommPorts, DEVINFO_RS232 );
/*+---------------------------------------------+
| Determine nr of comm port used by the mouse |
+---------------------------------------------+*/
if ( DosOpen( "MOUSE$",
&hfMouse,
&ulAction,
0,
FILE_SYSTEM,
OPEN_ACTION_OPEN_IF_EXISTS,
OPEN_ACCESS_READONLY | OPEN_SHARE_DENYNONE,
0 ) == NO_ERROR )
{
rc = DosDevIOCtl( hfMouse,
IOCTL_POINTINGDEVICE,
0x6B, /* Query Pointing Device ID */
NULL,
0L,
NULL,
(PVOID) &DataWords,
ulDataLength,
&ulDataLength );
if ( rc != NO_ERROR )
{
printf( "\nError while querying mouse info.\n" );
goto end;
}
else
{
bMouse = TRUE;
cMousePort = DataWords.CommPort;
}
}
/*+-------------------+
|Print status report|
+-------------------+*/
if ( bVerbose )
{
printf( "\nSTATUS REPORT COMM PORTS\n\n" );
printf( "Comm ports available : %d\n", *cCommPorts );
if ( bMouse )
{
printf( "Mouse detected.\n" );
printf( "\tType : %s\n", szDeviceID[ DataWords.DeviceID ] );
printf( "\tComm port : %d\n", DataWords.CommPort );
}
}
end:
return ( cMousePort );
}
ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
Finally, I came up with the following function.
typedef VOID (* _Optlink PFNDRVENUM)(CHAR,PVOID);
VOID enumDrives(PFNDRVENUM pfnEnumFn,PVOID pvData)
//--------------------------------------------------
// This function enumerates all of the drives at the
// time the function is called, and for each local
// drive calls the callback specified.
//
// Input: pfnEnumFn - callback function
// pvData - pointer to user-defined data to
// pass to callback
//
// Callback has the following form:
//
// BOOL _Optlink callBack(CHAR chDrive,PVOID pvData)
//
// Input: chDrive - uppercase drive letter
// pvData - as was passed to enumDrives()
//--------------------------------------------------
{
BYTE abBuffer[1024];
ULONG ulCurrent;
ULONG ulDriveMap;
CHAR chDrive;
CHAR achDrive[3];
PFSQBUFFER2 pfsqbBuffer;
ULONG ulSzBuf;
APIRET arReturn;
//-----------------------------------------------
// Disable errors so that we don't get any "device
// is locked" popups.
//-----------------------------------------------
DosError(FERR_DISABLEHARDERR | FERR_DISABLEEXCEPTION);
DosQueryCurrentDisk(&ulCurrent,&ulDriveMap);
ulDriveMap>>=2;
chDrive='C';
memset(achDrive,0,sizeof(achDrive));
achDrive[1]=':';
pfsqbBuffer=(PFSQBUFFER2)abBuffer;
while (chDrive<='Z') {
if ((ulDriveMap % 2)==1) {
achDrive[0]=chDrive;
ulSzBuf=sizeof(abBuffer);
memset(pfsqbBuffer,0,ulSzBuf);
arReturn=DosQueryFSAttach(achDrive,
0,
FSAIL_QUERYNAME,
pfsqbBuffer,
&ulSzBuf);
if (arReturn==0) {
switch (pfsqbBuffer->iType) {
case FSAT_LOCALDRV:
(*pfnEnumFn)(chDrive,pvData);
break;
default:
break;
} /* endswitch */
} /* endif */
} /* endif */
ulDriveMap>>=1;
chDrive++;
} /* endwhile */
DosError(FERR_ENABLEHARDERR | FERR_ENABLEEXCEPTION);
}
Scratch Patch - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ 3.4.5. Documentation Chop Shop ΓòÉΓòÉΓòÉ
Documentation Chop Shop
In the File Systems section of the Control Program Guide and Reference, there
are a couple of problems.
1. The API DosQueryFileInfo() is referenced when DosQueryFSInfo() is intended.
2. The sample to retrieve file system information via DosQueryFSAttach() is
incorrect. Declaring a variable of type FSQBUFFER2 and passing it to
DosQueryFSAttach() will return ERROR_BUFFER_OVERFLOW. You must pass a
pointer to a buffer (stack-based or dynamically allocated) that has
sufficient space.
ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ
_wpPopulate is listed in the online reference as having a third parameter of
WPFolder * and its value is the "real name of the folder to populate". In
actuality, it is a PSZ which is the path of the folder to populate.
Scratch Patch - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ 3.4.6. Want Ads ΓòÉΓòÉΓòÉ
Want Ads
Below are the hot topics as of this issue's writing. Feel free to write on any
of these.
Workplace Shell Programming (hot) - this is still quite the "black magic"
topic, about which we have seen only one article. There are many out there who
do WPS programming for a living, but they have not been sufficiently
enlightened about our need. :)
Client/Server (hot) - using either named pipes (with or without a network) or
sockets, client/server programming is all the rage these days. On a related
note, some people have also expressed an interest in learning about interfacing
with the various protocol drivers (e.g. NDIS, IPX/SPX, etc.). Any articles in
this area are most welcome.
Multimedia (warm) - we recently had two articles on this topic. However, they
both dealt with sound, which we all know is not the only alternative media
type. Articles on anything else - MIDI, video, etc. - are needed.
Scratch Patch - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ 4. How Do I Get EDM/2? ΓòÉΓòÉΓòÉ
How Do I Get EDM/2?
EDM/2 can be obtained in any of the following ways:
On the Internet
o All back issues are available via anonymous FTP from the following sites:
- ftp.cdrom.com in the /pub/os2/2_x/program/newsltr directory.
- ftp.luth.se in the /pub/os2/programming/newsletter directory.
- generalhq.pc.cc.cmu.edu
o The EDM/2 mailing list. Send an empty message to edm2-info@knex.mind.org to
receive a file containing (among other things) instructions for subscribing
to EDM/2. This is a UUCP connection, so be patient please.
o IBM's external gopher/WWW server in Almaden. The address is
index.almaden.ibm.com and it is in the "Non-IBM-Originated" submenu of the
"OS/2 Information" menu; the URL is
"gopher://index.almaden.ibm.com/1nonibm/os2nonib.70".
On Compuserve
All back issues are available in the OS/2 Developers Forum 2.
IBM Internal
o IBM's internal gopher/WWW server in Almaden. The address is
n6tfx.almaden.ibm.com and it is in the "Non-IBM-Originated Files" menu; the
URL is "gopher://n6tfx.almaden.ibm.com/1!!nonibm/nonibm.70".
o IBM's REQUEST command on all internal VM systems. Enter the VM command
REQUEST LIST FROM ASSELIN AT RALVM12 and a list of the requestable packages
will be sent to you; in this list are the names of the packages containing
the EDM/2 issues.
How do I Get EDM/2? - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ 5. Contributors to this Issue ΓòÉΓòÉΓòÉ
Are You a Potential Author?
We are always looking for (new) authors. If you have a topic about which you
would like to write, send a brief description of the topic electronically to
any of the editors, whose addresses are listed below, by the 15th of the month
before the month in which your article will appear. This alerts us that you
will be sending an article so that we can plan the issue layout accordingly.
After you have done this, get the latest copy of the Article Submission
Guidelines from ftp.cdrom.com in the /pub/os2/2_x/program/newsltr directory.
(the file is artsub.zip) The completed text of your article should be sent to
us no later than five days prior to the last day of the month; any articles
received after that time may be pushed to the next issue.
The editors can be reached at the following email addresses:
o Larry Salomon - os2man@panix.com (Internet).
o Carsten Whimster - bcrwhims@undergrad.math.uwaterloo.ca (Internet).
The following people contributed to this issue in one form or another (in
alphabetical order):
o Martin Lafaix
o Larry Salomon, Jr.
o Carsten Whimster
o Gordon Zeglinski
o Network distributors
Contributors - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ 5.1. Martin Lafaix ΓòÉΓòÉΓòÉ
Martin Lafaix
Martin Lafaix is a computer science student at the UniversitВ de Nice-Sophia
Antipolis. He currently works on his PhD thesis (key areas: functional
language, type as value, programming in the large, ...).
He can be reached at the following address: lafaix@sophia.inria.fr
Contributors - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ 5.2. Larry Salomon, Jr. ΓòÉΓòÉΓòÉ
Larry Salomon, Jr.
Larry Salomon, Jr. wrote his first Presentation Manager application for OS/2
version 1.1 in 1989. Since that time, he has written numerous VIO and PM
applications, including the Scramble applet included with OS/2 and the
I-Brow/Magnify/Screen Capture trio being distributed by IBM with the
Professional Developers Kit CD-ROM. Currently, he works for Cheyenne Software
in Roslyn, New York and resides in Bellerose, New York with his wife Lisa.
Larry can be reached electronically via the Internet at os2man@panix.com.
Contributors - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ 5.3. Carsten Whimster ΓòÉΓòÉΓòÉ
Carsten Whimster
I am an undergraduate Computer Science student at the University of Waterloo,
and an OS/2 enthusiast as of OS/2 2.0. I am currently in my third year, taking
mainly operating system, language, and compiler courses as much as possible.
This is not too difficult obviously, since this covers most of what they try to
teach us in any case :). This summer and fall I am working at the University
as a tutor in CS241, an introductory course to compilers. I am a TEAM-OS/2
member, and try to keep up with several OS/2 groups on the Internet.
I am a beginning OS/2 PM programmer with a few projects on the go, and many
more in my head. I use EMX/GCC 2.5.8, Watcom C/C++ 9.5, and Watcom VX-REXX
2.0, and I am anxiously awaiting Watcom C/C++ 10.0's impending release.
You may reach me...
...via email:
bcrwhims@undergrad.math.uwaterloo.ca - Internet
gopher://descartes.math.uwaterloo.ca:70/h0/mathSOC/.csc/.www/.bcrwhimster/homepage.html
- Mosaic homepage
...via snail mail:
Carsten Whimster
319 Erb Street West, 3rd floor
Waterloo, Ontario
Canada
N2L 1W4
Contributors - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ 5.4. Gordon Zeglinski ΓòÉΓòÉΓòÉ
Gordon Zeglinski
Gordon Zeglinski is a freelance programmer/consultant who received his Master's
degree in Mechanical Engineering with a thesis on C++ sparse matrix objects.
He has been programming in C++ for 6 years and also has a strong background in
FORTRAN. He started developing OS/2 applications with version 2.0 .
His current projects include a client/server communications program that
utilitizes OS/2's features which has entered beta testing. Additionally, he is
involved in the development of a "real-time" automated vehicle based on OS/2
and using C++ in which he does device driver development and designs the
applications that comprise the control logic and user interface.
He can be reached via the Internet at zeglins@cc.umanitoba.ca.
Contributors - EDM/2 - June 1994 - Volume 2, Issue 6
ΓòÉΓòÉΓòÉ 5.5. Network distributors ΓòÉΓòÉΓòÉ
Network Distributors
These people are part of our distribution system to provide EDM/2 on networks
other than the Internet. Their help to provide access to this magazine for
others is voluntary and we appreciate them a lot!
o Paul Hethmon (hethmon@apac.ag.utk.edu) - Compuserve
o Gess Shankar (gess@knex.mind.org) - Internet
o David Singer (singer@almaden.ibm.com) - IBM Internal
o Andre Asselin (ASSELIN AT RALVM12) - IBM Internal
If you would like to become a "network distributor", be sure to contact the
editors so that we can give you the credit you deserve!
Contributors - EDM/2 - June 1994 - Volume 2, Issue 6