home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 18 REXX
/
18-REXX.zip
/
rxdlg11.zip
/
rxcapp.INF
(
.txt
)
< prev
next >
Wrap
OS/2 Help File
|
1995-03-03
|
34KB
|
980 lines
ΓòÉΓòÉΓòÉ 1. Application Usage ΓòÉΓòÉΓòÉ
Rexx Dialog allows REXX scripts to present an enduser with a PM interface,
allowing him to use the mouse and keyboard to manipulate scrolling lists of
text, menus, sliders, buttons, etc, as well as being able to arrange all of
these in multiple windows that also can be manipulated with the mouse and
keyboard.
RX.EXE is an application that launches a REXX script written for Rexx Dialog.
RXDLG.DLL is the REXX function library that makes the PM interface possible.
It's also possible to utilize RXDLG.DLL in your own C applications (ie, just
like RX.EXE does). By doing so, this makes it very easy to add a REXX
interface to your application. In fact, it's easier than if you tried to add
such an interface from scratch because RXDLG has functions your C app can call
which you'd probably have to duplicate if you wrote your own REXX interface.
When your app uses RXDLG.DLL, it inherits all of the Rexx Dialog commands, and
therefore any REXX script that your app launches can open PM windows with PM
controls and do message loops, etc. But, RXDLG.DLL also easily allows your app
to add new REXX commands which your app has its own functions to handle.
ΓòÉΓòÉΓòÉ 1.1. App requirements ΓòÉΓòÉΓòÉ
There are a few mandatory things that your app must be and do. They are:
1. Your app must be a Presentation Manager app. RXDLG.DLL can't be used by a
windowed or full-screen app.
2. The thread that is going to launch the REXX script must call PM's
WinInitialize() to get an anchor block, and WinCreateMsgQueue() to get a
message queue. The handle of the anchor block must be stored in the Hab
field of your app's REXXSPEC. A REXXSPEC is a special structure defined by
Rexx Dialog (in RX.H) which RXDLG.DLL automatically creates and makes
available to your app.
3. The thread must open a PM standard (ie, frame and client) window and place
the handle of the client in the REXXSPEC's Hwnd field. The ID of this
client, and the IDs of any direct children of it (ie, not children of
children) should be less than 7680. This then becomes the "Main Window"
into which any windows opened by a REXX script open. Alternately, if you
want the REXX script to create its own Main Window (ie, on the Desktop),
then instead of setting the REXXSPEC's Hwnd field, you must supply an icon
for the Main Window's CLOSE ICON in your executable's resource (ie, .RC
file). This icon must have an ID of 256.
4. If your app wishes to add new REXX commands (ie, in addition to the Rexx
Dialog commands), you must fill in the REXXSPEC's Cmds, Prefix, and
Handlers fields with a string containing the new commands, and an array of
pointers to the functions in your app which handle those commands.
5. The thread must call RXDLG's RexxSet() once to initialize the REXX
environment. Afterward, the app can launch REXX scripts, and use various
RXDLG.DLL functions.
6. Before ending, the thread should call RexxFree() to free up any REXX
resources. Also the message queue created in step 1 should be freed with
WinDestroyMsgQueue() and PM resources freed with WinTerminate().
In order to avoid name conflicts with RXDLG.DLL, it's recommended that you not
name any of your functions or variables starting with the letters Rexx or Dlg.
RXDLG.DLL utilizes window message IDs of WM_USER to WM_USER+49. Therefore, if
your app defines message IDs of its own, it should start those IDs at
WM_USER+50.
ΓòÉΓòÉΓòÉ 1.2. RX.EXE ΓòÉΓòÉΓòÉ
RX.EXE is an example of an app that does the minimum required setup. It then
calls the RXDLG function RexxRunScript() which launches and runs a REXX script.
RexxRunScript is passed the null-terminated name of the REXX script to execute.
(You supply the desired script name as an argument when running RX.EXE from the
command line. Remember, you can run PM apps from an OS/2 command line too).
When RexxRunScript() returns, the REXX script will have been completely
executed. RX.EXE simply ends after the script has been executed once (doing
the necessary cleanup).
RX.EXE doesn't open a Main Window on behalf of the REXX script. So, it has an
icon resource (RX.ICO) with an ID of 256 bound to its executable (ie, via
RX.RC).
RX.EXE doesn't add any REXX commands of its own, so it doesn't set the REXXSPEC
Cmds, Prefix, and Handlers fields.
RX.EXE also uses the RXDLG function DlgErrMsg() to display an error message for
any error number returned by RexxSet, and DlgMsgStr() to display any error
messages that RX.EXE creates and wants to present to the user. These 2
functions simply pop up a PM message box to display the error message.
Examine the source code RX.C to see an example of an app that uses RXDLG.DLL to
launch a Rexx Dialog REXX script, and use it to launch any of the example REXX
scripts included in this archive.
ΓòÉΓòÉΓòÉ 1.3. Supplying a Main Window ΓòÉΓòÉΓòÉ
You may wish to supply a Main Window into which all of the REXX script's
windows open. For example, your app will most likely have its own window
anyway, and you may want to keep all your app's windows in one place, including
ones opened by REXX scripts that your app launches, so that minimizing your
app's window also minimizes all of those child windows. Furthermore, a MODAL
window opened by the script will disable your app window and its children.
Note that your app can open windows, but still choose to not supply a Main
Window on behalf of the REXX scripts. The scripts will simply open their own
Main Window on the Desktop. But, if your app windows are opened by the thread
that calls RexxRunScript(), then you should follow the guidelines in App Window
Procedure.
RX2.C adds code to RX.C in order to demonstrate supplying a Main Window.
In order to properly support the script opening MODAL windows, the Main Window
must have the class style CS_HITTEST (and may have other styles of your
choosing) and its window procedure must call the RXDLG function DlgCheckModal()
when receiving a WM_HITTEST message.
Furthermore, for proper updating of the display, the WM_PAINT handling for the
Main Window should clear the invalid portion of its window using its desired
background color. (This eliminates a need to handle WM_ERASEBACKGROUND).
After this, you may then do any other painting that is desired in your Main
Window.
RX2.C launches the REXX script whenever you double-click the mouse 1 button.
This is done in the Main Window's window procedure myClientWndProc(), for the
WM_BUTTON1DBLCLK case. Since it's possible for the window procedure to be
called while the REXX script is executing (ie, because the user may have
manipulated our Main Window while the script was doing user interaction with
RXDLG), and we don't want to launch another Rexx Dialog script while one is
already running, we first check that the REXXSPEC's EXECUTING Flag is not set.
RexxRunScript() sets this Flag when a script is running, and clears this after
a script is done executing.
It's also possible to subsequently open a Main Window on behalf of the script
after you've already called RexxSet() to setup the REXX interface. You could
open the Main Window before calling RexxRunScript(), storing the client handle
in RexxSpec.Hwnd, and after RexxRunScript() returns, close the Main Window and
clear (ie, set to 0) RexxSpec.Hwnd.
ΓòÉΓòÉΓòÉ 1.4. App Window Procedure ΓòÉΓòÉΓòÉ
Even if you don't supply a Main Window, but if the thread that creates the
message queue and calls RexxRunScript opens other application windows, then
it's possible that your application window procedure could be called while a
REXX script is executing. The scenario would be as follows -- the REXX script
creates one or more windows and then calls RXDLG to do user interaction (ie,
Operation 0 or 1). The user then manipulates one of your application windows
while the script is "sleeping" in RXDLG. Your app window procedure gets called
with the appropriate message for the user's action (ie, if he double-clicks the
mouse 1 button, then your window procedure receives a WM_BUTTON1DBLCLK
message).
If it's important that your window procedure know if there's a REXX script
being executed, check the EXECUTING Flag of REXXSPEC's Flags field. This will
be set if there's a REXX script executing.
One thing that you shouldn't do when there's a REXX script executing is call
RexxRunScript() to launch another REXX script. Doing so may result in the
second REXX script closing any windows opened by the first script, when the
second script ends, and vice versa. Your app should only execute one REXX
script at a time.
Another caveat is receiving a WM_CLOSE message, while a REXX script is running,
for the Main Window that you supply to the script. You could ignore this
message by returning 0 if you want to allow the script to end of its own
accord. Or if you want to close up your Main Window, abort the REXX script,
and allow any WinGetMsg()/WinDispatchMsg() loop that you do in your app to drop
out (due to WM_QUIT), you can simply let the OS/2 WinDefWindowProc() handle the
WM_CLOSE. If the script is written to EXIT when RXDLG returns RXID = -99 (or
any negative value), then the script should properly abort, and your app will
get its WM_QUIT. If you close any Main Window that you've supplied to the
script, then you should clear (ie, set to 0) REXXSPEC's Hwnd. (If your app is
going to terminate now, this isn't really necessary). You should clear this
field when processing the WM_DESTROY message of your window procedure for that
Main Window.
ΓòÉΓòÉΓòÉ 1.5. App REXX Commands ΓòÉΓòÉΓòÉ
You can add your own REXX commands to the set of standard REXX commands (ie,
PARSE, LEFT, etc) as well as Rexx Dialog commands (ie, RXDLG, RXSET, etc). For
each new REXX command that you add, you must specify a function in your app to
be called for that command. Whenever RXDLG.DLL detects one of these commands
being issued in the REXX script, it calls your app function associated with
that command, passing any arguments (ie, additional text supplied by the script
after the command).
In setting up a REXX interface for your app, you need to decide if you'd like a
prefix on your command set. For example, you'll note that all of the Rexx
Dialog commands begin with the 2 letters RX. This makes it easy to distinguish
Rexx Dialog commands from standard REXX commands as well as labels in the REXX
script. (ie, When writing a script, it's easier to avoid using any label that
starts with RX, rather than having to remember every Rexx Dialog command so
that you don't inadvertently use one of those for a label. This would cause
the REXX interpreter to misinterpret what may be intended to be a call to a
Rexx Dialog function). It's recommended that you choose a prefix for your
command set. We'll arbitrarily choose the prefix APP for our example command
set. You need to place a pointer to this null-terminated string in REXXSPEC's
Prefix field. The string must be in upper case. For example:
RexxSpec.Prefix = "APP";
If you don't want any prefix on your commands, leave the Prefix field cleared.
Next, you need to decide what particular commands you'd like to create. For
example, let's say that we want a REXX command called APPADD which will add 2
numbers supplied by the script, and return the sum to the script. Here's how
we want the script to call APPADD to add 5 and 384.
APPADD 5 384
Let's say that we also want a REXX command called APPSUBTRACT which will
subtract 2 numbers supplied by the script, and return the difference to the
script. Here's how we want the script to call APPSUBTRACT to subtract 5 from
384.
APPSUBTRACT 384 5
Now that we have our new command set (consisting of 2 commands), we need to set
the REXXSPEC's Cmds field to point to a UCHAR array containing these commands.
The first UCHAR of the array will be a count of how many characters are in the
first command name, followed by that first command name. In other words, the
strings are stored in Pascal style. Then, the second command's length, and
name follows. After the last command, there must be a 0 UCHAR to mark the end
of the commands in the array. The command names must be in upper case. They
are not null-terminated. Do not include the prefix on the commands (ie, the
first command is ADD, not APPADD). Here's how we setup the array for our
command set.
UCHAR cmdArray[] = {3, 'A', 'D', 'D', 8, 'S', 'U', 'B', 'T', 'R', 'A', 'C',
'T', 0 };
RexxSpec.Cmds = &cmdArray[0];
Finally we need to setup an array of pointers to our functions for those
commands. Let's say that we have a function called Add2() to handle the APPADD
command, and a function called Subtract2() to handle the APPSUBTRACT command.
The functions must be listed in the same order as the commands are specified in
the Cmds array (ie, since we specified APPADD first, then Add2() is first in
the functions array). Here's how we setup the array for our functions.
PFUNC cmdFuncs[] = { Add2, Subtract2 };
RexxSpec.Handlers = &cmdFuncs[0];
Now let's talk about those functions. When a function is called, REXXSPEC's
FromStr points to any text that was supplied by the script. Using our above
example of the script calling APPADD, when Add2() is called, RexxSpec.FromStr
points to the null-terminated string "5 384". REXX does not supply numeric
variables in binary form. Rather, REXX always supplies args the way that any
argv[] args are supplied to your main() function; as ascii strings. Of course,
you get all of the args that the REXX script supplies, as one long string that
you have to parse. For example, to add the two numbers, we have to parse the
string into its separate elements of "5" and "384", and convert those two
strings to binary (as you might using atoi) to perform the addition. When we
return the sum to the REXX script, we have to convert that binary value back
into an ascii string (as you might using itoa).
RXDLG.DLL has several functions for parsing the arg string, and functions for
converting ascii strings to binary and vice versa. For example,
DlgAsciiToNum() parses an ascii string for the next numeric value, converts it
to binary, and optionally returns an updated pointer to the parsed string (ie,
points to after the numeric value which has been converted). This makes it
very trivial to parse and convert the arg string passed to Add2().
DlgAsciiToNum() takes 3 args; the pointer to the string to parse (in this case
RexxSpec.FromStr), a handle where to place the updated pointer (or 0 if we
don't want that updated pointer returned), and a format char which is 'H' if
the value is expressed as hexidecimal, 'B' if the value is expressed as binary,
or anything else means that the value is expressed in decimal. Since we expect
the script to supply decimal values, we'll pass a format of 0. Because we have
to parse 2 numbers from the string, we'll ask for the updated pointer to be
returned when we parse the first number, and we'll have it returned in
RexxSpec.FromStr.
Finally, we need to return the value to the script. RXDLG.DLL has a function
called RexxReturnNum() which converts a binary value to an ascii string, and
indicates that we want that returned to the script in the special variable RC.
So, here's our Add2() function.
VOID EXPENTRY Add2(VOID)
{
LONG num1, num2;
/* Get first number */
num1 = DlgAsciiToNum(RexxSpec.FromStr, &RexxSpec.FromStr, 0);
/* Get second number */
num2 = DlgAsciiToNum(RexxSpec.FromStr, 0, 0);
/* Add numbers and return it to the script */
RexxReturnNum(num1+num2);
}
The Subtract2() function will be the same except that we'll return num1-num2.
The REXX script gets its return in the special REXX variable RC as so:
APPADD 5 384
RXSAY '5 + 384 =' RC
Suppose you wanted to return the value in the REXX variable SUM instead of RC.
The function RexxSetNumVar() will convert the binary value to an ascii string,
and set the value of a REXX variable to that. The variable name that you
supply must be in upper case, and a legal REXX variable name (including stem
and compound variable names). So, let's substitute RexxSetNumVar() for
RexxReturnNum().
VOID EXPENTRY Add2(VOID)
{
LONG num1, num2;
/* Get first number */
num1 = DlgAsciiToNum(RexxSpec.FromStr, &RexxSpec.FromStr, 0);
/* Get second number */
num2 = DlgAsciiToNum(RexxSpec.FromStr, 0, 0);
/* Add numbers and return it to the script */
RexxSetNumVar("SUM", num1+num2);
}
Now the REXX script gets its return in the REXX variable SUM (case is
irrelevant) as so:
APPADD 5 384
RXSAY '5 + 384 =' Sum
By not returning the value in RC, you can then use RC to indicate an error
condition. For example, let's say that the Subtract2() function wants to
indicate an error if the second number is larger than the first (ie, a negative
result would be flagged as an error). DlgMsgStr() allows your function to
return an error message or number (for RC, which is otherwise a null string or
0 for success) to the script. It also displays that error message in a PM
message box if the display level that you specify is lower than the severity
level set by RXERR. Finally it raises a FAILURE in the REXX script.
VOID EXPENTRY Subtract2(VOID)
{
LONG num1, num2;
/* Get first number */
num1 = DlgAsciiToNum(RexxSpec.FromStr, &RexxSpec.FromStr, 0);
/* Get second number */
num2 = DlgAsciiToNum(RexxSpec.FromStr, 0, 0);
/* Check if num1 < num2. If so, then return an error. We arbitrarily choose
an error number of ERRAPP+DISPLAYLEVEL, and a display level of DISPLAYLEVEL
(defined in RX.H) */
if (num1 < num2)
{
DlgMsgStr("Second number smaller than first", ERRAPP+DISPLAYLEVEL,
DISPLAYLEVEL);
return;
}
/* Subtract numbers and return it to the script in the variable "ANSWER.0" */
RexxSetNumVar("ANSWER.0", num1-num2);
}
Now the REXX script can check for an error. (Assume the script wants error
messages instead of numbers -- ie, it hasn't set RXERR to return numbers, and
therefore RC = "" for success rather than 0).
APPSUBTRACT 384 5
IF RC <> "" THEN RXSAY RC
ELSE RXSAY '384 - 5 =' Answer.0
Note: Case is irrelevant when the REXX script calls one of your app's REXX
functions. For example,
AppAdd 384 5
would achieve the same result as APPADD.
RX3.C adds code to RX.C to demonstrate adding the above 2 REXX commands.
TESTRX3.CMD is a REXX script that tests those commands. It makes one call to
APPADD and one call to APPSUBTRACT, printing out the results of those calls.
Then, if you press the ENTER key, another call to APPSUBTRACT is made, with
args that will cause Subtract2() to return its error message.
The following sections detail the RXDLG.DLL functions that your script can
utilize in fetching and setting the values of REXX variables, parsing args,
converting strings to binary values and vice versa, setting/displaying error
messages and numbers, etc.
ΓòÉΓòÉΓòÉ 1.5.1. RexxSkipSpaces ΓòÉΓòÉΓòÉ
Template RexxSkipSpaces();
Description Skips any blank spaces in the string that RexxSpec.FromStr
points to, updating RexxSpec.FromStr to point to the next
non-blank character (could be the terminating null byte). This
is useful when parsing the arg string from a REXX script into
separate arguments where spaces may be separating the arguments.
ΓòÉΓòÉΓòÉ 1.5.2. RexxParseArg ΓòÉΓòÉΓòÉ
Template length = RexxParseArg();
Description Extracts the next arg in the string pointed to by
RexxSpec.FromStr, up to a | character or the terminating null
byte, and copies that arg to the buffer pointed to by
RexxSpec.InStr. Leading and trailing blanks are trimmed off of
the extracted arg. Updates RexxSpec.FromStr to point past the
extracted arg. Returns the length of the extracted arg (ie,
will be 0 if there was no arg to extract). This can be used to
extract args where a | character is used as a separator, such as
the way that RXSAY separates the Message arg from the optional
Type and Heading args.
Note: Since FromStr and InStr point to the same buffer, and may
overlap, once you've extracted an arg and then wish to
save it beyond another call to RexxParseArg(), you should
copy the null-terminated arg that InStr points to, to
some other buffer. You can use OutStr as that buffer if
you don't need to call RexxParseArg2() or RexxSetVar()
before you're done with the arg.
Returns UCHAR length
ΓòÉΓòÉΓòÉ 1.5.3. RexxParseArg2 ΓòÉΓòÉΓòÉ
Template length = RexxParseArg2();
Description Extracts the next arg in the string pointed to by
RexxSpec.FromStr, up to a space or the terminating null byte,
and copies that arg to the buffer pointed to by RexxSpec.OutStr.
Leading and trailing blanks are trimmed off of the extracted
arg. Updates RexxSpec.FromStr to point past the extracted arg.
Returns the length of the extracted arg (ie, will be 0 if there
was no arg to extract). This can be used to extract args where
one or more spaces are used as a separator, such as extracting a
numeric value.
Returns UCHAR length
ΓòÉΓòÉΓòÉ 1.5.4. RexxParseArg3 ΓòÉΓòÉΓòÉ
Template length = RexxParseArg3();
Description Extracts the next arg in the string pointed to by
RexxSpec.FromStr, up to a space or the terminating null byte,
and copies that arg to the buffer pointed to by RexxSpec.InStr.
Leading and trailing blanks are trimmed off of the extracted
arg. If the arg is enclosed in quotes, then everything in
between the quotes is extracted (ie, the arg can have embedded
as well as leading and trailing, spaces). Updates
RexxSpec.FromStr to point past the extracted arg. Returns the
length of the extracted arg (ie, will be 0 if there was no arg
to extract). This can be used to extract args where either one
or more spaces are used as a separator, such as extracting a
numeric value, or args where the script wants to allow embedded
spaces (and therefore the arg is enclosed in a set of quotes)
such as how the WindowTitle arg is specified in RXDLG.
Note: When REXX passes an arg to an app, it "eats" the outer
set of quotes. Therefore, it's necessary to use a second
set of quotes around the arg. For example, in order for
your app to receive the string "hello there" (ie, with
the quotes), then the script must specify '"hello
there"'. RexxParseArg3 will recognize double or single
quotes (ie, so "'hello there'" would achieve the same result).
Note: Since FromStr and InStr point to the same buffer, and may
overlap, once you've extracted an arg and then wish to
save it beyond another call to RexxParseArg(), you should
copy the null-terminated arg that InStr points to, to
some other buffer. You can use OutStr as that buffer if
you don't need to call RexxParseArg2() or RexxSetVar()
before you're done with the arg.
Returns UCHAR length
ΓòÉΓòÉΓòÉ 1.5.5. RexxReturnNum ΓòÉΓòÉΓòÉ
Template RexxReturnNum(number);
Description Takes the binary number, converts it to an ascii string, and
returns it to the script in the special variable RC, without
causing a FAILURE in the REXX script. This may be used by a
function which wishes to return a numeric value, and the
function doesn't need to reserve RC to return an error number
(ie, the function doesn't do anything that would necessitate
reporting an error to the script).
This function clears RexxSpec.InStr, which indicates to
RXDLG.DLL that the RC value is to be set to the string that
RexxSpec.ErrStr points to. So, you should not call any function
that references RexxSpec.InStr such as RexxParseArg(),
RexxParseArg3(), RexxFetchVar(), or RexxFetchStemVar(), nor
should your function reference RexxSpec.InStr. It's best to
call RexxReturnNum() only when your function is prepared to
return immediately after.
Args LONG number
ΓòÉΓòÉΓòÉ 1.5.6. RexxSetNumVar ΓòÉΓòÉΓòÉ
Template error = RexxSetNumVar(var_name, value);
Description Converts the binary value to an ascii (numeric) string, and sets
the specified REXX variable to that value. This is used to set
a REXX variable in the script to some numeric value.
Note: This may only be called by the same thread that launched
the script with RexxRunScript().
Args UCHAR * var_name -- a pointer to the null-terminated name of the
REXX variable. Must be upper case. Can be a stem or compound
variable name.
LONG val -- the signed value to set the REXX variable to.
Returns UCHAR error -- 0=success, non-zero is an error in setting the
REXX variable.
Note: It's not necessary to display an error message, nor setup
an error return to the script. RXDLG.DLL takes care of
that. Your function merely needs to check for a non-zero
return, and if encountered, abort whatever it's doing and return.
ΓòÉΓòÉΓòÉ 1.5.7. RexxSetVar ΓòÉΓòÉΓòÉ
Template error = RexxSetVar(var_name);
Description Sets the specified REXX variable to the string contained in the
buffer that RexxSpec.OutStr points to. This is used to set a
REXX variable in the script to some value (numeric or
otherwise). Before calling RexxSetVar(), you must copy the
desired, null-terminated string to the buffer pointed to
RexxSpec.OutStr. (Setting the first byte of RexxSpec.OutStr to
0, ie, nulling the buffer, causes the REXX variable to be set to
a null string). You must not copy more than 255 bytes to the
buffer.
Note: This may only be called by the same thread that launched
the script with RexxRunScript().
Args UCHAR * var_name -- a pointer to the null-terminated name of the
REXX variable. Must be upper case. Can be a stem or compound
variable name.
Returns UCHAR error -- 0=success, non-zero is an error in setting the
REXX variable.
Note: It's not necessary to display an error message, nor setup
an error return to the script. RXDLG.DLL takes care of
that. Your function merely needs to check for a non-zero
return, and if encountered, abort whatever it's doing and return.
ΓòÉΓòÉΓòÉ 1.5.8. RexxFetchVar ΓòÉΓòÉΓòÉ
Template error = RexxFetchVar(var_name);
Description Fetches the value of the specified REXX variable (ie, whatever
value the REXX script has set that variable to), and copies that
value string into the buffer pointed to by RexxSpec.InStr.
Resets RexxSpec.FromStr to RexxSpec.InStr (ie, resets it to
point to the fetched value string so that string can be parsed
with RexxParseArg(), RexxParseArg2(), or RexxParseArg3()).
Note: Fetching a variable's value overwrites the original arg
string that was passed to your function from the script.
It's best to parse the original arg string before
fetching any variable values. You can use
RexxSpec.OutStr as a 256 byte temporary buffer to hold
any strings you wish to save while fetching values.
Note: This may only be called by the same thread that launched
the script with RexxRunScript().
Args UCHAR * var_name -- a pointer to the null-terminated name of the
REXX variable. Must be upper case. Can be a stem or compound
variable name.
Returns UCHAR error -- 0=success, non-zero is an error in fetching the
REXX variable. If the REXX script has failed to initialize the
variable to some value, then this is flagged as an error.
Note: It's not necessary to display an error message, nor setup
an error return to the script. RXDLG.DLL takes care of
that. Your function merely needs to check for a non-zero
return, and if encountered, abort whatever it's doing and return.
ΓòÉΓòÉΓòÉ 1.5.9. RexxFetchStemVar ΓòÉΓòÉΓòÉ
Template error = RexxFetchStemVar(var_name, stem);
Description Fetches the value of the specified REXX variable (ie, whatever
value the REXX script has set that variable to), and copies that
value string into the buffer pointed to by RexxSpec.InStr.
Resets RexxSpec.FromStr to RexxSpec.InStr (ie, resets it to
point to the fetched value string so that string can be parsed
with RexxParseArg(), RexxParseArg2(), or RexxParseArg3()). The
difference from RexxFetchVar() is that the binary stem arg is
appended to the var_name in order to create a stem variable.
So, this function is used to specifically fetch a stem
variable's value. For example, assume that you wish to fetch
the value of the variable MyVar.30:
error = RexxFetchStemVar("MYVAR", 30);
The variable name should be limited to 22 characters.
It's also possible to fetch a compound variable, if var_name
already has one stem on it. For example, assume that you wish
to fetch the value of the variable MyVar.0.30:
error = RexxFetchStemVar("MYVAR.0", 30);
Note: Fetching a variable's value overwrites the original arg
string that was passed to your function from the script.
It's best to parse the original arg string before
fetching any variable values. You can use
RexxSpec.OutStr as a 256 byte temporary buffer to hold
any strings you wish to save while fetching values.
Note: This may only be called by the same thread that launched
the script with RexxRunScript().
Args UCHAR * var_name -- a pointer to the null-terminated name of the
REXX variable. Must be upper case.
ULONG stem -- the stem that is appended to the variable name.
Returns UCHAR error -- 0=success, non-zero is an error in fetching the
REXX variable. If the REXX script has failed to initialize the
variable to some value, then this is flagged as an error.
Note: It's not necessary to display an error message, nor setup
an error return to the script. RXDLG.DLL takes care of
that. Your function merely needs to check for a non-zero
return, and if encountered, abort whatever it's doing and return.
ΓòÉΓòÉΓòÉ 1.5.10. RexxAppendStem ΓòÉΓòÉΓòÉ
Template ptr = RexxAppendStem(var_name, buffer, stem);
Description Copies the var_name to the buffer, adding the stem. The buffer
should be large enough to hold the final, null-terminated
variable name. This may be used to create a variable name that
is to be passed to RexxFetchVar(), RexxSetVar(), or
RexxSetNumVar(). For example, to create the variable name
MYVAR.0 in a buffer:
UCHAR buffer[8];
RexxAppendStem("MYVAR", &buffer[0], 0);
A pointer to the end of the new variable name (ie, the address
of its null byte) is returned. This can be used to find the
length of the string by subtracting it from the buffer arg
passed to RexxAppendStem.
Args UCHAR * var_name -- a pointer to the null-terminated name of the
REXX variable.
UCHAR * buffer -- a pointer to the buffer where the new variable
name is created.
ULONG stem -- the stem that is appended to the new variable
name.
Returns UCHAR * ptr -- a pointer to the null byte of the new variable
name.
ΓòÉΓòÉΓòÉ 1.5.11. DlgAsciiToNum ΓòÉΓòÉΓòÉ
Template number = DlgAsciiToNum(source, &updatedPtr, format);
Description Converts the source string (ie, points to a numeric string) to a
32-bit binary value. (This is essentially a 32-bit version of
atoi). Skips leading spaces on the source. If updatedPtr is
not 0 (ie, it's a handle to UCHAR *), then the updated pointer
(ie, points to after the converted numeric string) is returned.
This is useful for making multiple calls to DlgAsciiToNum in
order to parse several numeric args from one arg string. The
format arg is 'H' if the source string was specified in
hexidecimal (for example, "3FF0"). Format is 'B' if the source
string was expressed in binary "bits" (for example,
"0011111111110000" is the binary equivalent of hex "3FF0"). Any
other value for format means that the source string is expressed
in decimal.
Args UCHAR * source -- points to the numeric string to be converted.
UCHAR ** updatedPtr -- a handle to where the updated UCHAR * is
returned. If 0, then the updated pointer isn't returned.
UCHAR format -- 'H' is hexidecimal, 'B' is binary, any other is
decimal.
Returns LONG number
ΓòÉΓòÉΓòÉ 1.5.12. DlgNumToAscii ΓòÉΓòÉΓòÉ
Template ptr = DlgNumToAscii(number, buffer, flag);
Description Converts the binary number into a null-terminated ascii numeric
string, storing it in the buffer. (This is a 32-bit version of
itoa). The buffer must be large enough to hold the converted
string. Flag is a 1 if commas are desired after every third
digit (ie, the way a human might write the number). Flag is 0
if no commas are desired. (Commas should not be used if the
resulting string is going to be used to set the value of a REXX
variable which will be used in some mathematical way. REXX
doesn't like commas in numeric values that it must perform math
upon). A pointer to the end of the converted string (ie, the
address of its null byte) is returned. This can be used to find
the length of the string by subtracting it from the buffer arg
passed to DlgNumToAscii.
Args LONG number -- the signed binary value to be converted.
UCHAR * source -- points to the buffer where the numeric string
is created.
UCHAR flag -- 1 if commas are desired, 0 otherwise.
Returns UCHAR * ptr -- a pointer to the null byte of the converted
string.
ΓòÉΓòÉΓòÉ 1.5.13. RexxSet ΓòÉΓòÉΓòÉ
Template error = RexxSet();
Description This must be called once when setting up RXDLG.DLL for use by
the app. RexxSpec.Hab must first be set to a PM anchor block,
and if your app adds its own REXX commands, it's best to setup
the Cmds, Handlers, and Prefix fields of the REXXSPEC also prior
to calling RexxSet.
Returns ULONG error -- 0 if success, non-zero if an error. If there's
an error, it is up to your script to display an error message to
the enduser. You can call DlgErrMsg() to have the DLL post a PM
message box containing an appropriate error message. The errors
that RexxSet() returns are "Window registration failed." (3) or
"Can't install REXX." (4). These are described in the Errors
section of the Rexx Dialog 1.0 manual, under the discussion of
errors that aren't related to Rexx Dialog commands (ie, the
bottom of the page).
ΓòÉΓòÉΓòÉ 1.5.14. RexxFree() ΓòÉΓòÉΓòÉ
Template RexxFree();
Description Closes down the REXX interface. This will also cause any
currently executing REXX script to abort. This must be called
once before the app ends.
ΓòÉΓòÉΓòÉ 1.5.15. RexxRunScript ΓòÉΓòÉΓòÉ
Template error = RexxRunScript(scriptname);
Description This executes a REXX script, where scriptname points to the
null-terminated name of the script to execute. It sets the
EXECUTING Flag of REXXSPEC while the script is executing, and
clears that Flag when the script is done. It then returns an
error indication. The error number is also returned in
RexxSpec.ErrNum.
RexxRunScript() may end up passing on messages to any app window
procedure, for windows opened by the same thread that called
RexxRunScript(), while the REXX script is executing. It may
also call any REXX command functions that your app added,
whenever those commands are issued by the script.
Args UCHAR * scriptname -- the null-terminated name of the script to
run. It should be fully qualified (ie, with the drive and path)
if the script is not in the current directory.
Returns ULONG error -- 0 if the script successfully executed, non-zero
otherwise.
Note: It's not necessary to display an error message.
RXDLG.DLL takes care of that.
ΓòÉΓòÉΓòÉ 1.5.16. DlgErrMsg ΓòÉΓòÉΓòÉ
Template DlgErrMsg(msgNum, errNum);
Description Displays an appropriate error message for any error number
returned by RexxSet(), and sets RexxSpec.ErrNum to the specified
errNum.
Args ULONG msgNum -- the error number returned by RexxSet().
ULONG errNum -- the error number that you want RexxSpec.ErrNum
to be set to. It is recommended that you add the constant
ERRAPP to msgNum. That will result in error numbers that are
unique from anything else returned by RXDLG.DLL functions such
as RexxRunScript().
ΓòÉΓòÉΓòÉ 1.5.17. DlgMsgStr ΓòÉΓòÉΓòÉ
Template DlgMsgStr(errMsg, errNum, displayLevel);
Description Returns the specified error message or error number (depending
upon whether the script has indicated that it wants messages or
numbers returned for errors) to the script (in the special
variable RC). It also displays the error message if the script
has set the display level at least as high as the specified
displayLevel. It also causes a FAILURE in the REXX script.
The error number should be 20000 (ie, ERRAPP) or greater since
RXDLG.DLL reserves lower error numbers, which it may return to
the script. In this way, a script can always distinguish a
problem with your app (ie, calling RexxSet or RexxRunScript, or
an error returned by your own REXX functions) versus an error
returned by one of the Rexx Dialog functions. Furthermore, it's
recommended that you set any error numbers generated by your
REXX functions to be greater than ERRAPP+DISPLAYLEVEL. Each
error returned by your functions should be a unique number, as
this number gets passed on to the REXX script (if it asks for
error numbers returned).
Your app can also call DlgMsgStr whenever a REXX script is not
executing (ie, to display error messages for functions not
related to REXX), in which case, a message box with the error
message is always presented.
Args UCHAR * errMsg -- the null-terminated error message to return to
the script.
ULONG msgNum -- the error number to return to the script.
ULONG displayLevel -- the display level for this error. A
message box will be displayed if the script has set the display
level to at least this amount (using RXERR). Otherwise, no
message box is displayed. (ie, It will up to the REXX script to
post an error indication to the enduser, as the script will
receive notice of this error).
ΓòÉΓòÉΓòÉ 1.5.18. DlgCheckModal ΓòÉΓòÉΓòÉ
Template value = DlgCheckModal(hwnd, mp1);
Description This is only called during the WM_HITTEST processing in a window
procedure that services windows opened by the same thread that
has called RexxRunScript(). It allows the script to properly
utilize MODAL windows. The app windows should have the class
style CS_HITTEST.
Args HWND hwnd -- the window handle arg passed to the app's window
procedure from OS/2.
MPARAM mp1 -- the message parameter 1 arg passed to the app's
window procedure from OS/2.
Returns MRESULT value -- the value that your window procedure's
WM_HITTEST case returns (to OS/2).
ΓòÉΓòÉΓòÉ 1.6. Returning RC values to the REXX script ΓòÉΓòÉΓòÉ
The special RC REXX variable is usually used to return error messages or
numbers to the REXX script. (The script uses RXERR to set whether it wants
messages or numbers returned for errors). If a function executes successfully,
it sets RC to either a null string (ie, "") if messages are to be returned, or
0 if numbers are to be returned. Non-null strings or non-zero values for RC
indicate that the function encountered some error. In this case, RC is either
an error messsage or an error (non-zero) number. The script can then check the
RC value after each call to a Rexx Dialog or your app's own REXX commands to
determine whether an error occurred or not.
Your app must use DlgMsgStr() to display error messages that your app creates
and wishes to return to the script. (DlgErrMsg() may be optionally used to
display messages corresponding to error numbers returned by RexxSet()). This
function properly sets up the error return for RC, and it also causes the
FAILURE flag to be set in the REXX script (so that the script can use SIGNAL ON
FAILURE to automatically jump to error handling whenever your app returns an
error). Also, DlgMsgStr() takes care of automatically displaying the error
message in a PM message box if the script desires that (ie, the script uses
RXERR to set the display level to determine which error messages if any it
wants automatically displayed).
Note: Your REXX function need do nothing if it wants to return a successful RC
value. RXDLG.DLL defaults to returning that, until such time as your
app specifically calls DlgMsgStr() to set an error return.
If your REXX function always succeeds (ie, there's no scenario in which an
error condition may arise), then you can instead use the RC variable to return
a value from your function. Use the RexxReturnNum() function to return a
numeric value to the script.
Note: RexxReturnNum() is passed a signed long, so if you're passing a ULONG
instead, you're limited to a range of 0 to 2,147,483,647.
RexxReturnNum() clears the RexxSpec.InStr pointer, (which indicates to
RXDLG.DLL that the RC value is to be set to the string that RexxSpec.ErrStr
points to). So, after calling RexxReturnNum(), you should not call any
function that references RexxSpec.InStr such as RexxParseArg(),
RexxParseArg3(), RexxFetchVar(), RexxFetchStemVar(), DlgMsgStr(), or
DlgErrMsg(), nor should your function reference RexxSpec.InStr. It's best to
call RexxReturnNum() only when your function is prepared to return immediately
after.
RexxReturnNum() does not raise the FAILURE flag in the script.
If your function wishes to return a string (rather than a numeric value) to a
script (without causing a FAILURE in the script), simply set RexxSpec.ErrStr to
point to the desired string, clear RexxSpec.InStr, and return. For example,
here is a function that simply returns the string "Hello" to the script.
VOID EXPENTRY ReturnHello(VOID)
{
/* Set ErrStr to point to the desired string. Obviously, it can't be in a
local buffer (ie, on the stack) because we need to return it */
RexxSpec.ErrStr = "Hello";
/* Clear InStr to let RXDLG.DLL know that we want to return ErrStr in the RC
variable */
RexxSpec.InStr = 0;
/* Return control back to the script */
return;
}
For example, if your app associated ReturnHello() with a APPHELLO command, the
script would do:
APPHELLO
/* Print the returned "Hello" */
RXSAY RC
ΓòÉΓòÉΓòÉ 1.7. InStr and OutStr ΓòÉΓòÉΓòÉ
The RexxSpec pointers InStr and OutStr each point to a (different) 256 byte
buffer. These pointers are valid ONLY while RXDLG.DLL is calling one of your
app's REXX functions. You can determine that this is the case by checking the
OutStr pointer. If it's not zero, then InStr and OutStr are valid. Initially,
InStr points to the arg string that was received by the script. You can use
these buffers for any purpose that you desire, although some of the RXDLG.DLL
functions also use these buffers. RexxParseArg(), RexxParseArg3(),
RexxFetchVar(), and RexxFetchStemVar() use InStr (and its buffer), and
RexxParseArg2() and RexxSetVar() use OutStr (and its buffer). It's best to not
alter these 2 pointers if you're going to be calling the preceding RXDLG.DLL
functions.
ΓòÉΓòÉΓòÉ 1.8. Documentation ΓòÉΓòÉΓòÉ
If you want to provide an online book (ie, .INF file, like this one) detailing
your app's REXX interface, the best approach is to create an INF file that
lists only those new REXX commands that your app implements. For example,
RX3.IPF is example IPFC source code that documents the 2 commands APPADD and
APPSUBTRACT that we added to RX3.C. It has a heading page, followed by a page
for each REXX command. The layout is similiar to that used for the online book
for Rexx Dialog (ie, RX.INF).
Then, create a desktop object that invokes OS/2's VIEW.EXE utility. For the
Parameters field of the object, add RX.INF and the name of the INF file
describing your new functions. For example:
RX+RX3
The result is a book that combines the 2 INF files into one.