But, most DLLs are not written this way. Typically, operating system DLLs are not written to directly support REXX. A REXX script can't use RXFUNCADD() or a LIBRARY statement to use such a DLL. Therefore, the script can't directly call any operating system function such as Windows' CreateWindow() or VirtualAlloc(), nor the functions in many other DLLs not specially designed for REXX.
To circumvent this limitation, the Reginald REXX interpreter offers a way to register a function in any DLL so that it can be directly called by a REXX script. You first use the built-in function called FUNCDEF() to register this function. Then, you can call the function just like any other function. So what is the difference between a LIBRARY statement and FUNCDEF()? With FUNCDEF(), you have to register each function in DLL, and you have to provide a lot more information about the type of arguments that get passed to a function, as well as what type of value the function returns. You may have to define "structures" which are used by that function, so that Reginald can translate these structures into REXX compound variables, and vice versa. In other words, although Reginald allows you to register and directly call a DLL function not specially written for REXX, you do have to supply a lot more information about the function, and calling it may be more involved than using a DLL specially designed for Reginald.
Registering a function with FUNCDEF()
The FUNCDEF() function is as follows:
err = FUNCDEF(REXXname, Definition, DLLname, FUNCTIONname, Errors, CallType)
REXXname is the desired name that you would like to use when you call the function in your script. The name you use to call the function does not need to be the same as the real name of the function. For our examples, let's assume that we're always going to be registering a function named "MyFunc".
DLLname is the name of the DLL that contains the function. You do not need to put any .dll extension upon the name. For our examples, let's assume that we're always going to be registering a function in a DLL named "MyDLL".
Note: If you omit DLLname, then FUNCDEF() can be used to register a struct (without also registering a function). That is discussed later. When registering a struct, then REXXname is instead the struct's name.
FUNCTIONname is the real name of the function. If omitted, then REXXname is used as-is. For our examples, let's assume that the real name of the function is indeed "MyFunc" with exactly that case, so we will always omit this argument.
Definition is a string containing numerous items of information, each separated by a comma. The first item is the type of return from the function. It must be one of the types listed below, or blank if the function does not return a value. The next item is the type for the first argument passed to the function (if there is such an argument). What follows will be the types for the remaining arguments (if any more). You'll have to consult the programming documentation with the DLL to determine how many args it accepts (if any), what it returns if anything, and the type of those args/return. (Warning: Your script may crash, or perform other unexpected/dangerous behavior, if you specify the wrong number/type of args/return).
Errors is applicable only if the return type of the function is an array (ie, [] is used), str, or a struct. It is a list of all of the return values which indicate an error. If omitted, then the default is to check only for a NUL (0) pointer.
CallType is the method used to pass arguments to the function, and must be "api", "c", "pascal", or "pc". For our examples, let's assume that we wish to register a function in an operating system DLL. Most all operating system DLLs use "api" calling method. If we omit this argument, it defaults to "api", so we will omit this for our examples.
If FUNCDEF is successful, it will return 0. We can then start making numerous calls to the registered function, just like with any other built-in function.
The Definition string
The Definition string contains numerous items of information, each separated by a comma. Here is the synopsis of the Definition string:
return_type , arg1_type , arg2_type , etc...
Types of arguments/return:
Value Meaning '8' An 8-bit (char) whole number. '16' A 16-bit (short) whole number. '32' A 32-bit (long) whole number. '8u' An unsigned 8-bit (unsigned char) whole number. '16u' An unsigned 16-bit (unsigned short) whole number. '32u' An unsigned 32-bit (unsigned long) whole number. A returned (ie, from the function) pointer or handle to something that your REXX script is not supposed to directly manipulate would be declared as this type. '8[X]' Pointer to an array of 8-bit (char) whole numbers. X is the number of items in the array. '16[X]' Pointer to an array of 16-bit (short) whole numbers. X is the number of items in the array. '32[X]' Pointer to an array of 32-bit (long) whole numbers. X is the number of items in the array. '8u[X]' Pointer to an array of unsigned 8-bit (unsigned char) whole numbers. X is the number of items in the array. '16u[X]' Pointer to an array of unsigned 16-bit (unsigned short) whole numbers. X is the number of items in the array. '32u[X]' Pointer to an array of unsigned 32-bit (unsigned long) whole numbers. X is the number of items in the array. 'char' A single (8-bit) ascii character. 'char[X]' Pointer to an array of ascii characters. X is the number of characters in the array. 'str[X]' Pointer to an array of nul-terminated ascii characters (ie, a C-style string). X is the minimum number of characters in the array. If X is omitted, then the array is however many characters it is initialized to be. 'struct name' A 'structure' where name is the name of the REXX variable that defines the type of each field in that struct. The value of that variable is just like the definition string above, except that there is no return type first. There are only the type for each field, each separated by a comma. 'struct name[X]' Pointer to an array of 'structure' where name is the name of the REXX variable that defines the type of each field in that struct. X is the number of structures in the array. 'func name' Pointer to a callback (ie, some subroutine in your script that is called by a non-REXX-function) where name is the name of the variable that contains the Definition string for your subroutine. The 'func' type is applicable only to an argument passed to a function or some field of a structure. It can't be specified as a return.
'void' As the return type, 'void' means that the function is returning a handle (ie, a special numeric value) to some array or struct that your script does not intend to directly access. For an argument passed to a function, or a field of a struct, 'void' indicates one of these handles (ie, special numeric value) that was returned by some function.
If a function doesn't return a value, then place nothing before the first comma.
When calling the function that you've registered, REXX converts the args that you pass to the function to their appropriate data types (as you specified in the Definition string). For the function's return value, it is converted and stored in the REXX variable to which you assign the return. For arrays or a struct, the values will be stored in REXX compound variables.
How you specify the args when calling the function:
Type Specify '8', '16', or '32' You supply a literal string consisting of a whole number in base 10 (decimal), or the name of a variable whose value is a number, or some expression whose value is numeric. This is converted to a char, short, or long number and passed to the function. '8u', '16u', or '32u' You supply a literal string consisting of a whole number in base 10 (decimal), or the name of a variable whose value is a number, or some expression whose value is numeric. This is converted to an unsigned char, unsigned short, or unsigned long number and passed to the function. '8[X]', '16[X]', or '32[X]' You supply the name of a stem variable where name.1 contains the first number, name.2 contains the second, etc. This is converted to a char, short, or long array, and a pointer to this array is passed to the function. '8u[X]', '16u[X]', or '32u[X]' You supply the name of a stem variable where name.1 contains the first number, name.2 contains the second, etc. This is converted to an unsigned char, unsigned short, or unsigned long array, and a pointer to this array is passed to the function. 'char' You supply a literal string consisting of a single character, or the name of a variable whose value is a character, or some expression that evaluates to a single character. This character is passed to the function. 'char[X]' You supply a literal string, or the name of a variable whose value is that string, or some expression. A pointer to that string is passed to the function. X is the minimum size (in characters) of the array. It must be at least 1. NOTE: If 'stor' is specified in arg's type, then you must specify a variable name (instead of a literal string or expression).
'str[X]' You supply a literal string, or the name of a variable whose value is that string, or some expression. A pointer to a nul-terminated copy of that string is passed to the function. If X was specified in the arg's type, then the minimum size of the array is that many characters. NOTE: If 'stor' is specified in arg's type, then you must specify a variable name (instead of a literal string or expression).
'struct name' You supply the name of a REXX stem variable which is setup to hold the values for the structure's fields where name.1 to name.X will be the values. A pointer to the structure is passed to the function. 'struct name[X]' You supply the name of a REXX stem variable which is setup to hold the values for each structure's fields where name.[1 to X].1 to name.[1 to X].X will be the values. A pointer to the array is passed to the function. 'func name' You supply the name of some REXX subroutine/function within your script (that will be called by the DLL). A pointer to REXX's Generic Dll Interface is passed on the stack, which the DLL may use to call your REXX function. REXX will manage the data conversions between the DLL and your REXX script, just as it does when you call the DLL's function. 'void' 'void' is the same as if the arg is declared as '32u' (on a 32-bit OS). Use this for any handle or pointer to some void type that some function returned to you. You should have stored that handle/pointer in some variable when it was returned from a function. When you pass it back to another function, simply pass that variable's value.
Basic examples
Let's assume that MyFunc takes no arguments and has no return. In the C language, you may find it defined in some .H file as so:
void MyFunc(void);To register this with FUNCDEF(), you supply an empty Definition string. After all, there are no args nor return to declare.
err = FUNCDEF('MyFunc', '', 'MyDll') IF err \= 0 THEN SAY "ERROR: MyFunc didn't register!"And you call MyFunc as so:
CALL MyFunc /* Reginald allows CALL MyFunc() or simply * MyFunc() if you use ADDRESS NULL or OPTIONS 'C_CALL' */Now let's assume that MyFunc takes 1 argument which is a (32-bit) long and has no return. In the C language, you may find it defined in some .H file as so:
void MyFunc(long);To register this with FUNCDEF(), you supply a Definition string that has nothing before the first comma (because there is no defined return), and one argument of type "32":
err = FUNCDEF('MyFunc', ', 32', 'MyDll')And you call MyFunc as so, passing it a value of 100:
CALL MyFunc(100)Note that you can also supply a variable name containing the numeric value, just like you would with any other function:
MyVariable = 100 CALL MyFunc(MyVariable)Of course, you do need to be aware of the nature of data types. A data type of long implies a numeric value. Therefore you can't pass a non-numeric value as so:
CALL MyFunc("This is text")The above would result in a SYNTAX condition. But other mistakes you make in specifying the correct format for arguments may be passed on to the function and could result in it crashing your script, or doing unintended things. So exercise care.
So too, a data type of long cannot have a decimal portion. Therefore you can't pass a numeric value such as so:
CALL MyFunc(100.3)In C parlance, such a numeric value would need to be a datatype of float or double.
Now let's assume that MyFunc takes 2 arguments both of which are (32-bit) longs and it returns one long. In the C language, you may find it defined in some .H file as so:
long MyFunc(long, long);To register this with FUNCDEF(), you supply a Definition string that has a return type of "32", and two arguments of type "32":
err = FUNCDEF('MyFunc', '32, 32, 32', 'MyDll')And you call MyFunc as so, passing it values of 100 and 200 and assigning its return value to the variable named MyVariable:
MyVariable = MyFunc(100, 200)
Now let's assume that MyFunc takes 1 argument that is a nul-terminated string (of any length) and it returns one long. In the C language, you may find it defined in some .H file as so:
long MyFunc(char *);To register this with FUNCDEF(), you supply a Definition string that has a return type of "32", and one argument of type str:
err = FUNCDEF('MyFunc', '32, str', 'MyDll')And you call MyFunc as so, passing it a string of "This is text" and assigning its return value to the variable named MyVariable:
MyVariable = MyFunc("This is text")Note that you can also supply a variable name containing the string, just like you would with any other function:
MyVariable = "This is text" CALL MyFunc(MyVariable)
Numeric arrays
Sometimes, a function may require you to pass it an array of numbers. Let's assume that MyFunc takes 1 argument which is an array of 4 (32-bit) longs and it returns one long. In the C language, you may find it defined in some .H file as so:
long MyFunc(long *);To register this with FUNCDEF(), you supply a Definition string that has a return type of "32", and an argument of type "32[4]":
err = FUNCDEF('MyFunc', '32, 32[4]', 'MyDll')When you specify an argument that is an array type (ie, you use []), then you must first initialize some compound variables with the values you want in the array, and then you pass the name of the stem variable to the function.
For example, let's say that we want the array to have the values -1, -2, -3, and -4. Let's also assume that we'll use the stem variable "MyStem." for this array. First, we initialize the array's 4 values as so:
MyStem.1 = -1 /* First value of the array */ MyStem.2 = -2 /* Second value */ MyStem.3 = -3 /* Third value */ MyStem.4 = -4 /* Fourth value */Notice how the first field of the array is stored in a variable where you append 1 to the stem name (ie, MyStem.1). The second field is stored in a variable where you append 2 to the stem name (ie, MyStem.2). The third and fourth values of the array are stored in MyStem.3 and MyStem.4 respectively. Hopefully this makes sense to you. If not, study it some more.
Now that the array is initialized with the desired values, we can call MyFunc as so, passing it the stem variable's name (and note that we don't end it with a dot, nor do we need to quote it although it would be acceptable to do the latter):
MyVariable = MyFunc(MyStem)If you wish to initialize an entire array to the same value, then you should set the stem variable itself to that value. In this way, you do not have to individually and explicitly set every compound variable for the array. For example, here we set all values to 0:
MyStem. = 0 MyVariable = MyFunc(MyStem)Note: If any of the array values have previously been set, then you should first do a DROP on the stem before you set its value.
For any value which you don't want to be the default value, then you can set that particular array element. For example, here we set only the first value.
MyStem. = 0 MyStem.1 = 100 MyVariable = MyFunc(MyStem)Note: You must initialize the non-default values [u]after[/u] you set the stem's value.
Note: Do not trap the NOVALUE condition prior to the call to the function, or you will get NOVALUE raised for any array element you have not explicitly set.
Structures
Some functions require you to pass something known as a struct in C. This is an entity that can have many fields of various datatypes. (Think of it as an array, but where each field can be a different data type). In order to use a struct, you must first register it with REXX. You register the struct at the same time you register the function with FUNCDEF(). And you will use a second Definition string to specify the data types of each of the struct's fields. It will look exactly like the Definition string for the function itself, except there is no return type for a struct's Definition string. The first item in the struct's Definition string is the data type for the first field.
Let's assume that MyFunc takes 1 argument which is a pointer to a struct called a "Something" and it returns one long. In the C language, you may find that structure defined in some .H file as so:
struct Something { long firstField; short secondField; };And somewhere in the .H file is the definition for MyFunc as so:
long MyFunc(struct Something *);Note that the Something struct has two fields. The first field is a long data type. We already know that as "32" in a Definition string. The second field is a type of short, which is specified as "16" in a Definition string. So, the Definition string for this struct will be:
'32, 16'We need to assign this Definition string to a REXX variable with the same name as the struct, which is "Something". So here's what we put in our script before our call to FUNCDEF():
Something = '32, 16'Now, MyFunc()'s Definition string needs to specify that the first arg's type is a "struct Something". FUNCDEF() will therefore register both MyFunc() as well as the Something struct. Here's how we do that:
/* Assign a Definition string for a Something * struct in a variable of the same name. */ Something = '32, 16' /* Register MyFunc. Note that the first arg is of type 'struct Something' */ err = FUNCDEF('MyFunc', '32, struct Something', 'MyDll')After the FUNCDEF, you can DROP the Something variable, or reuse it for some other purpose.
Note:
A struct name should be limited to 128 characters maximum.Now when we wish to call MyFunc, we must first initialize some compound variables with the values we want in the struct, and then you pass the name of the stem variable to the function. For example, let's say that we want the first field of the struct to have the value 100 and the second field of the struct to have the value 200. Let's also assume that we'll use the stem variable "MyStem." for this struct. First, we initialize the struct's 2 fields as so:
MyStem.1 = 100 /* First field of the struct */ MyStem.2 = 200 /* Second field */Now that the struct is initialized with the desired values, we can call MyFunc as so, passing it the stem variable's name (and note that we don't end it with a dot, nor do we need to quote it although it would be acceptable to do the latter):
MyVariable = MyFunc(MyStem)Notice that this is exactly like we did with the numeric array.
Let's take another example. Let's say that the Something struct instead looks like this is C:
struct Something { long firstField; char secondField[32]; short thirdField; };We've got an extra field, whose type is an array of chars (ie, a string which is limited to a maximum of 32 characters for this particular field). Our Definition string for the struct will instead be:
Something = '32, char[32], 16'And before we actually call the function, we need to initialize the second field with some characters (ie, text):
MyStem.1 = 100 /* First field of the struct */ MyStem.2 = "Some text" /* Second field */ MyStem.3 = 200 /* Third field */But structures can be complicated. Besides datatypes of 8, 16, 32, 8u, 16u, 32u, str, and char, a struct may contain a numeric array which will slightly complicate the way you need to initialize the struct. Let's say that the Something struct instead looks like this in C:
struct Something { long firstField[4]; short secondField; };Note that Something's first field actually has 4 fields inside of it. (It's as if Something has a total of 5 fields to initialize). Our Definition string for Something now is this:
Something = '32[4], 16'And before we call the function, we need to initialize the Something struct. Since its first field is an array, then we append further tails to it in order to initialize the actual fields of that array:
MyStem.1.1 = -1 /* First field of Something, first field of the array */ MyStem.1.2 = -2 /* First field of Something, second field of the array */ MyStem.1.3 = -3 /* First field of Something, third field of the array */ MyStem.1.4 = -4 /* First field of Something, fourth field of the array */ MyStem.2 = 200 /* Second field of Something */ /* Call MyFunc, passing the initialized Something struct within MyStem */ MyVariable = MyFunc(MyStem)Note: A structure's name is case-sensitive. So for example, if you register a function as being passed a 'struct MYNAME', and another function as being passed a 'struct MyName', then these are two different structures (although both would use the REXX variable named "MYNAME" to provide the Definition string).
And things can be even more complicated. Structures can actually contain other structures (which may also contain further structs and/or arrays). Let's say that the Something struct instead looks like this in C, and it contains a second struct called an Another:
struct Another { long firstField[4]; short secondField; }; struct Something { struct Another firstField; short secondField; };We need Definition strings for each of those structs. And the Definition string for the Something struct is going to specify another struct.
/* Assign a Definition string for a Something * struct in a variable of the same name. Note * that the first field is a type of 'struct * Another'. */ Something = 'struct Another, 16' /* Assign a Definition string for an Another * struct in a variable of the same name. */ Another = '32[4], 16' /* Register MyFunc. Note that the first arg is of type 'struct Something' */ err = FUNCDEF('MyFunc', '32, struct Something', 'MyDll')Now before we call the function, we need to initialize the Something struct. Since its first field is itself a struct, then we append further tails to it to initialize the actual fields of Another:
MyStem.1.1.1 = -1 /* First field of Something, first field of Another, first field of array */ MyStem.1.1.2 = -2 /* First field of Something, first field of Another, second field of array */ MyStem.1.1.3 = -3 /* First field of Something, first field of Another, third field of array */ MyStem.1.1.4 = -4 /* First field of Something, first field of Another, fourth field of array */ MyStem.1.2 = 200 /* First field of Something, second field of Another */ MyStem.2 = 300 /* Second field of Something */ /* Call MyFunc, passing the initialized Something struct within MyStem */ MyVariable = MyFunc(MyStem)
Note: Once a struct is registered, you no longer have to initialize a variable containing its Definition string if you happen to reference that struct again. Consider the following example:
/* Assign a Definition string for a Something * struct in a variable of the same name. */ Something = '32, 16' /* Register MyFunc. Note that the first arg is of type 'struct Something' */ err = FUNCDEF('MyFunc', '32, struct Something', 'MyDll') /* Now both MyFunc() and the Something struct are registered. We * can DROP the variable we used for the Something struct */ DROP Something /* Even though we DROP'ed the Something variable above, the * Something struct is still registered. And since it is * registered, we can reference it again without needing * to set that Something variable. Here we register AnotherFunc * which returns a Something struct */ /* Register AnotherFunc. Note that its return type is 'struct Something' */ err = FUNCDEF('AnotherFunc', 'struct Something', 'MyDll')If you wish to initialize all the fields in the entire structure to the same value, then you should set the stem variable itself to that value. In this way, you do not have to individually and explicitly set every compound variable for the struct. For example, here we set all fields of the Something struct to 0 before passing it to MyFunc:
MyStem. = 0 MyVariable = MyFunc(MyStem)Note: If any of the fields have previously been set, then you should first do a DROP on the stem before you set its value.
For any field which you don't want to be the default value, then you can set that particular field. For example, here we set only the first field.
MyStem. = 0 MyStem.1 = 100 MyVariable = MyFunc(MyStem)Note: You must initialize the non-default fields [u]after[/u] you set the stem's value.
Note: One caveat with a 'str *' field is that, if you do not explicitly set its value, then Reginald will pass a 0 in the pointer field. With a REXX callback (ie, 'func') field, if you do not explicitly set the field to the name of your subroutine, then Reginald also passes a 0 for the function address.
Note: Do not trap the NOVALUE condition prior to the call to the function, or you will get NOVALUE raised for any struct field you have not explicitly set.
Registering only a structure
When you register a function where one of the arg types references a struct, then that struct is automatically registered along with the function.
But, you can also register a struct by itself. To do so, simply omit the DLLname passed to FUNCDEF. And for the REXXname, specify the name of the struct (without the word "struct" before it). The Definition string you pass is for the struct, so you do not need to set a similiarly-named variable.
Here we register the Something struct:
/* Register a Something struct */ err = FUNCDEF('Something', '32, 16')Of course, if the Something struct were to reference another struct, then you would need to setup a variable with that other struct's Definition string (unless that other struct was already registered too).
/* Assign a Definition string for an Another * struct in a variable of the same name. */ Another = '32[4], 16' /* Register a Something struct that references an Another struct */ err = FUNCDEF('Something', '32, struct Another')
Pointer (*) qualifier
Sometimes, a struct will not contain an array, string, other struct, etc, but rather, will have a "reference" to it. In C parlance, this is known as a "pointer". Let's say that we have a Something struct which contains a pointer to a nul-terminated string. It may be declared as so in C:
struct Something { long firstField; char * secondField; short thirdField; };Note that the second field is a pointer to a string. When you have a pointer, you must qualify the argument type with a '*'. Here is the Definition string for the Something struct, and an example of MyFunc defined as being passed this struct:
/* Assign a Definition string for a Something * struct in a variable of the same name. Note * that the second field is a str *. */ Something = '32, str *, 16' /* Register MyFunc. Note that the first arg is of type 'struct Something' */ err = FUNCDEF('MyFunc', '32, struct Something', 'MyDll')Now before we call the function, we need to initialize the Something struct. We initialize it exactly as we would without the pointer.
/* Initialize a Something struct using the variable MyStem */ MyStem.1 = 100 /* First field of Something */ MyStem.2 = 'This is some text' /* Second field of Something. It's our string */ MyStem.3 = 200 /* Third field of Something */ /* Call MyFunc passing our initialized Something struct */ err = MyFunc(MyStem)As another example, let's say that Something contains a pointer to an array of 4 longs. Here is the C definition:
struct Something { long firstField; long * secondField; short thirdField; };Here is the Definition string for the Something struct, and an example of MyFunc defined as being passed this struct:
/* Assign a Definition string for a Something * struct in a variable of the same name. Note * that the second field is a 32[4] *. */ Something = '32, 32[4] *, 16' /* Register MyFunc. Note that the first arg is of type 'struct Something' */ err = FUNCDEF('MyFunc', '32, struct Something', 'MyDll')Now before we call the function, we need to initialize the Something struct. Since its second field is a pointer, then we need to specify a variable name for the second field, and then set that variable to our desired string.
/* Initialize a Something struct using the variable MyStem */ MyStem.1 = 100 /* First field of Something */ MyStem.2.1 = -1 /* Second field of Something, first array value */ MyStem.2.2 = -2 /* Second field of Something, second array value */ MyStem.2.3 = -3 /* Second field of Something, third array value */ MyStem.2.4 = -4 /* Second field of Something, fourth array value */ MyStem.3 = 200 /* Third field of Something */ /* Call MyFunc passing our initialized Something struct */ err = MyFunc(MyStem)The pointer qualifier doesn't really change the way that you setup your REXX variables for a structure. But it does affect the way that the struct gets internally formatted when it is passed to the function. Make sure that if you have some field in a struct that is supposed to be a pointer, you use the pointer qualifier in your Definition string. (When you're looking at the C Include files for the Windows operating system, you may see a lot of "qualifiers" such as LPTSTR, LPHANDLE, etc. Usually the LP stands for "long pointer" and means that the field should be defined with the pointer qualifier).
'stor' qualifier
Sometimes, a function will want your script to pass (as one argument) an array, struct, string, or other item where the function will store (ie, return) some values. Your script will not be expected to initialize the array/struct/string/etc before the call, but will nevertheless expect you to supply some variable where the function can store its values. In other words, the function will not directly return some value, but rather, will return some values by setting some variables in your script. When a function expects one of its arguments to be of this nature, you should append the word 'stor' for that arg's data type (in the Definition string). Note that stor must appear as the last thing before the comma.
When you qualify an argument as 'stor', then you must pass the name of a variable where the function may store the value(s) associated with that argument.
Let's assume that MyFunc takes 1 argument which is a place to store a long (numeric value) and has no return. In the C language, you may find it defined in some .H file as so:
void MyFunc(long *);To register this with FUNCDEF(), you supply a Definition string that has nothing before the first comma (because there is no defined return), and one argument of type "32 stor":
err = FUNCDEF('MyFunc', ', 32 stor', 'MyDll')When you call MyFunc, you must pass the name of a variable where the value may be stored. Let's assume we'll use a variable named "MyVariable". Note that we don't need to quote the name, although it would be acceptable to do so. Also, we do not need to initialize MyVariable before calling MyFunc (and the NOVALUE condition will not be raised even if we have not).
CALL MyFunc(MyVariable)After the function is called, MyVariable will contain the numeric value stored by the function.
If the argument is an array, then the function will store the values in the same way that you would normally pass them to the function. In other words, it will use compound variables. You must pass the name of the stem variable you desire to be used.
Let's assume that MyFunc takes 1 argument which is a place to store 4 long values and has no return. To register this with FUNCDEF(), you supply a Definition string that has nothing before the first comma, and one argument of type "32[4] stor":
err = FUNCDEF('MyFunc', ', 32[4] stor', 'MyDll')When you call MyFunc, you must pass the name of a stem variable where the values may be stored. Let's assume we'll use a stem variable named "MyVariable.". Note that we don't put a dot at the end of the stem name, nor need to quote the name. Again, we do not need to initialize MyVariable before calling MyFunc.
CALL MyFunc(MyVariable)After the function is called, it will have stored the first numeric value (of the array) in MyVariable.1, the second value in MyVariable.2, the third value in MyVariable.3, and the fourth value in MyVariable.4.
It is also possible to declare a struct as 'stor'. Again, you must pass the name of a stem variable which the function will use to return the struct's values. Let's assume that MyFunc is passed our Another struct shown earlier. Here's how we FUNCDEF() and call the function:
/* Assign a Definition string for an Another * struct in a variable of the same name. */ Another = '32[4], 16' /* Register MyFunc. Note that the first arg is of type 'struct Another stor' */ err = FUNCDEF('MyFunc', ', struct Another stor', 'MyDll') /* Call MyFunc and let it store the struct's values in MyVariable */ CALL MyFunc(MyVariable) /* Print out the stored values */ SAY "First array value in Another's first field" MyVariable.1.1 SAY "Second array value in Another's first field" MyVariable.1.2 SAY "Third array value in Another's first field" MyVariable.1.3 SAY "Fourth array value in Another's first field" MyVariable.1.4 SAY "Another's second field" MyVariable.2
Note: When declaring a struct itself as 'stor', do not also declare any of its fields as 'stor'. If a struct has pointers in it for which you must supply an area to store some value, then do not declare the struct itself as 'stor'. Instead, when you specify the struct's Definition string, qualify each one of its individual fields as 'stor'.
Note: If you declare a struct field as a 'str *' type, and also qualify it with 'stor', then you must supply a minimum length for the string. Choose a value that is as large the largest string you expect the function will ever return. For example, if the field is for storage of a Windows filename, you could declare it as 'str[260] *'. The only time that you can specify a struct's field as simply 'str *' is if you don't qualify it with 'stor'. Otherwise, any 'str' field in a struct must have a specific size.
'dual' qualifier
Sometimes, a function will want your script to pass (as one argument) an array, struct, string, or other item where the function will store (ie, return) some values. But unlike with 'stor', the function will also require you to initialize the argument first. For such an argument, you must declare it as 'dual' (rather than 'stor'). Your script will supply some variable that is initialized to the desired values, and when the function returns, new values will be stored in that variable. Note that dual must appear as the last thing before the comma, and it can not be used in conjunction with stor.
Let's assume that MyFunc takes 1 argument which is a array of 4 longs (numeric values) and has no return. To register this with FUNCDEF(), you supply a Definition string that has nothing before the first comma (because there is no defined return), and one argument of type "32 dual":
err = FUNCDEF('MyFunc', ', 32 dual', 'MyDll')Before calling MyFunc, you must initialize a variable with the value. Then, you pass the name of that variable. Let's assume we'll use a variable named "MyVariable". After the function is called, MyVariable will contain the new value(s) stored by the function. Note that we don't need to quote the variable name, although it would be acceptable to do so.
/* Initialize the 4 values of the array */ MyVariable.1 = 100 MyVariable.1 = 200 MyVariable.1 = 300 MyVariable.1 = 400 /* Call MyFunc with the initialized array */ CALL MyFunc(MyVariable) /* Print the new values that MyFunc stored */ SAY MyVariable.1 SAY MyVariable.2 SAY MyVariable.3 SAY MyVariable.4
'void' type
Sometimes, a function will return a struct that is not intended for your script to be able to directly access. (ie, You're not allowed to directly read nor change any of the fields in this struct). All that the function returns to you is the "address" of that struct. The "address" is simply a special numeric value associated with the struct. For example, Windows' FindWindowA() function will return a special numeric value associated with a particular window.
So what is the purpose of having this address if you can't do anything directly with it? Well, many other Windows' functions accept (and require) this address in order to perform operations upon that particular window. So, your purpose for getting this address from some function is so that you can later pass it to other functions.
When a function returns such an address, you should declare its type as 'void'. (In C, a void return type means "no return", but in Reginald's FUNCDEF implementation, 'void' means "a return type for some struct whose fields my script cannot directly access via REXX variables").
Let's examine the C language definition of FindWindowA(). You'll see it in some .H file as so:
HWND FindWindowA(LPCTSTR, LPCTSTR);An LPCTSTR is really just a nul-terminated string. So, FindWindowA is passed a window class name (string) and/or a window title (string). It looks for a window with either that class and/or that title, and if found, it returns an HWND. So what is an HWND? Nobody except the people at Microsoft really know. All that any software is supposed to do with an HWND is pass it as an argument to other functions which require that. So, we specify its type as 'void'. Here then is our FUNCDEF for FindWindowA() which happens to be in the DLL named "user32":
err = FUNCDEF('FindWindowA', 'void, str, str', 'user32')Now assume we want to see if there is a window whose class name is "TOOLS" and whose window title is "Tool Window". We could call FindWindowA as so, and store that address in the variable MyVariable:
MyVariable = FindWindowA('TOOLS', 'Tool Window')FindWindowA will return that special numeric value associated with the window. If this happens to be 0, then the window we requested does not exist. So one of the useful things we can do with FindWindowA's return (besides pass it to other functions) is to simply check if the window exists as so:
MyVariable = FindWindowA('TOOLS', 'Tool Window') IF MyVariable = 0 THEN SAY "Tool Window is not open" ELSE SAY "Tool Window is open and its address is" MyVariableThere is another function called DestroyWindow(). This can close a particular window. It takes one argument which is, you guessed it, that HWND from FindWindowA(). So we declare this arg as type 'void'. DestroyWindow() returns something called BOOL which is just an unsigned long (ie, "32u") whose value is either 1 or 0 (depending upon whether DestroyWindow succeeded or failed, respectively). So here is how we locate that Tool Window and close it:
/* Register FindWindowA(). Note a void return */ err = FUNCDEF('FindWindowA', 'void, str, str', 'user32') /* Register DestroyWindow(). Note that the first argument has a void type */ err = FUNCDEF('DestroyWindow', '32u, void', 'user32') /* Call FindWindowA() to locate our Tool Window */ MyVariable = FindWindowA('TOOLS', 'Tool Window') /* Did we find it? */ IF MyVariable \= 0 THEN DO /* Yes. Call DestroyWindow() to close it */ err = DestroyWindow(MyVariable) /* Did it close ok? */ IF err = 0 THEN SAY "Tool Window did not close." END
Error returns
Often, a function will return a particular value to indicate that the function failed (ie, had some problem doing whatever it was supposed to do).
If the function is defined as returning an array (ie, [] is used), str, or a struct, then the function is returning a pointer type of value. Some functions will return a NUL pointer to indicate an error. Reginald automatically checks for a NUL pointer and will raise a SYNTAX condition. In this case, the function does not return a value and therefore no REXX variables will get set as a result of the call. (No 'stor' args will get set either). So, you can trap SYNTAX condition to handle such error returns.
Let's assume that MyFunc returns an array of 4 longs. (Its return type is '32[4]'). But let's also assume that if there's an error, MyFunc returns a NUL pointer. Here is how we would handle errors.
/* Register MyFunc to return an array 32[4] */ err = FUNCDEF('MyFunc', '32[4]', 'MyDll') DO /* Call MyFunc. Store the 4 values in MyVariable. If MyFunc returns * an error, we'll do the CATCH SYNTAX instruction */ MyVariable = MyFunc() CATCH SYNTAX SAY "MyFunc returned an error:" CONDITION('D') ENDCONDITION('E') will return error/sub-error 44.1.
If the function you're calling also returns other error values, then you can specify those with the Errors string passed to FUNCDEF(). For example, let's assume that if there's an error, MyFunc may also return the values -1 or -2. Here is how we would define the NUL, -1, and -2 returns to be errors, and raise the SYNTAX condition if encountered:
/* Note an Errors string of "-1 -2" */ err = FUNCDEF('MyFunc', '32[4]', 'MyDll', , '-1 -2') DO /* Call MyFunc. If it returns a NUL or "-1" or "-2" pointer, * we'll do the CATCH SYNTAX */ MyVariable = MyFunc() CATCH SYNTAX SAY "MyFunc returned an error:" CONDITION('D') ENDThe Errors string may be used with functions that return any type. (It is not restricted to just pointer types such as arrays and structures). For example, assume that you have a function that returns a type of '32'. If it succeeds, it will return the value 1, and if it fails it will return the value 0. Here we indicate that we want the SYNTAX condition raised if MyFunc returns a 0:
/* Note an Errors string of "0" */ err = FUNCDEF('MyFunc', '32', 'MyDll', , '0') DO /* Call MyFunc. If it returns a 0, we'll do the CATCH SYNTAX */ MyVariable = MyFunc() CATCH SYNTAX SAY "MyFunc returned an error:" CONDITION('D') ENDIf you would prefer to have Reginald raise the ERROR or FAILURE conditions, you can specify either by beginning your Errors string with an "E" or "F" respectively. For example, here we use the ERROR condition:
/* Note an Errors string of "E" */ err = FUNCDEF('MyFunc', '32[4]', 'MyDll', , 'E') DO /* Call MyFunc. Store the 4 values in MyVariable */ MyVariable = MyFunc() /* Call MyFunc. If it an error, we'll do the CATCH ERROR, and then * bomb out, returning the string "I failed!" */ MyVariable = MyFunc() CATCH ERROR SAY "MyFunc returned an error:" CONDITION('D') RETURN "I failed!" ENDFor ERROR/FAILURE condition, CONDITION('E') will return error/sub-error 81.70.
You can also have Reginald raise one of the USER conditions. In this case, specify a U followed by the USER condition number (1 to 50, inclusive). For example, here we choose to have Reginald raise USER 25 condition, and we use old style condition handling:
/* Note an Errors string of "U25" */ err = FUNCDEF('MyFunc', '32[4]', 'MyDll', , 'U25') /* Trap USER 25 in case MyFunc returns a bad pointer */ CALL ON USER 25 NAME MyFuncErr /* Call MyFunc. Store the 4 values in MyVariable */ MyVariable = MyFunc() /* If an error, then Reginald effectively sticks a "CALL MyFuncErr()" instruction here */ RETURN MyFuncErr: SAY "MyFunc returned an error" RETURNIf you would instead prefer no condition to be raised, then you can begin your Errors string with "O" (the letter o). In this case, when Reginald checks for errors, it will not raise any conditions. Instead, Reginald will force the return of an empty string if the function returns a NUL pointer or matches one of the values in your Errors string. You can then manually check the return value for an empty string.
/* Note an Errors string of "O" */ err = FUNCDEF('MyFunc', '32[4]', 'MyDll', , 'O') /* Call MyFunc. Store the 4 values in MyVariable */ MyVariable = MyFunc() /* Did MyFunc return a NUL pointer? */ IF MyVariable == '' THEN SAY "MyFunc returned a NUL pointer" /* Otherwise, MyVariable really did get set to the values */ ELSE DO SAY "Value 1 =" MyVariable.1 SAY "Value 2 =" MyVariable.2 SAY "Value 3 =" MyVariable.3 SAY "Value 4 =" MyVariable.4 ENDYou can combine both a condition letter as well as various error values. Here we raise an ERROR condition if MyFunc returns either a NUL or -1 pointer:
/* Note an Errors string of "E -1" */ err = FUNCDEF('MyFunc', '32[4]', 'MyDll', , 'E -1')Here we have an empty string returned if MyFunc returns either a NUL or -1:
/* Note an Errors string of "O -1" */ err = FUNCDEF('MyFunc', '32[4]', 'MyDll', , 'O -1')When you specify some Errors string, and the function returns a NUL pointer, or some value that matches one of the values in your Errors string, then you can subsequently call RXFUNCERRMSG() to retrieve an error number from the operating system, and pass that to UNIXERROR() to get an error message. For example, we know that FindWindowA returns 0 if it can't find a window. But that doesn't tell you why the window couldn't be found. In order to get a more informative message, you need to have Reginald check for that 0 return. Here we tell Reginald to raise USER 10 condition if FindWindowA returns a 0:
/* Register FindWindowA(). Note an Errors string of "U10 0" */ err = FUNCDEF('FindWindowA', 'void, str, str', 'user32', , 'U10 0') DO /* Call FindWindowA() to locate our Tool Window. * If FindWindowA returns 0, then we jump to CATCH USER 10 */ ToolWindow = FindWindowA('TOOLS', 'Tool Window') CATCH USER 10 /* RXFUNCERRMSG() returns an error number from the operating * system here, and UNIXERROR() gets a message for it. */ SAY "Window not found:" UNIXERROR(RXFUNCERRMSG()) RETURN /* Get out of here */ END SAY ToolWindow =" ToolWindow RETURNOr, we could manually check for that error:
/* Register FindWindowA(). Note an Errors string of "o 0" */ err = FUNCDEF('FindWindowA', 'void, str, str', 'user32', , 'o 0') /* Call FindWindowA() to locate our Tool Window */ ToolWindow = FindWindowA('TOOLS', 'Tool Window') /* If FindWindowA returned 0, then ToolWindow is an empty string */ IF ToolWindow == '' THEN SAY "Window not found:" UNIXERROR(RXFUNCERRMSG())
Omitting an argument
If you omit an argument to a function then a numeric value of 0 is passed to the function in its place. Some functions may let you substitute a 0 for an argument if that argument is optional. For example, FindWindowA lets you substitute a 0 for the class name if you don't know it and simply wish to search for a window with a particular title. So, here is how we can search for "Tool Window" without any class name:
/* Note we omit the first arg which is the class name (string) */ MyVariable = FindWindowA(, 'Tool Window')Be careful that you do not omit an arg unless the documentation for the function says that you can pass a 0 (or NULL) for that argument. Doing otherwise could case your script to crash.
The calling convention
Different DLLs may be created so that they expect arguments passed in a particular way, and may require the REXX interpreter to do special "clean up" after the call. The CallType argument to FUNCDEF() allows you to specify what sort of requirements the function has in this regard.
For example, the FindWindowA declaration in some .H file is actually as so:
HWND WINAPI FindWindowA(LPCTSTR, LPCTSTR);The WINAPI gives some information about how the function requires the args to be passed, and whether the interpreter needs to do any clean up.
If you omit the CallType, then FUNCDEF() assumes a call type of "api" (ie, WINAPI or APIENTRY). This is suitable for most operating system functions. But the declaration for some functions may be PASCAL instead. This requires the "pascal" call type, so you need to specify that when you FUNCDEF the function. Again, you'll have to check the documentation with for the function in order to determine the call type.
WINFUNC Option
Many of the functions in the Windows operating system have two versions: an ANSI version (which is the one you want for REXX), and a Unicode version. So, there are two real names for that function. For example, the FindWindow() function we used above has an ANSI version named FindWindowA() (which is what we registered and called above) and a Unicode version named FindWindowW().
To make it easier to deal with the Windows OS, Reginald allows you to turn on the "WINFUNC" option. When on, FUNCDEF() will automatically append an "A" to the REXXname if it can't find the REXXname you supply. So, for example, instead of registering FindWindowA(), we can register and call FindWindow() as so:
/* Enable the WINFUNC option */ OPTIONS 'WINFUNC' /* Register FindWindow(). Reginald will actually register * FindWindowA, but we call it as FindWindow() */ err = FUNCDEF('FindWindow', 'void, str, str', 'user32') MyVariable = FindWindow(, 'Tool Window')The above is just a simple way of doing the following:
/* Register FindWindowA(), but make it callable as * FindWindow() */ err = FUNCDEF('FindWindow', 'void, str, str', 'user32', 'FindWindowA') MyVariable = FindWindow(, 'Tool Window')But by using the WINFUNC option, you don't need to know which Windows' functions need to end with an A because Reginald will figure it out.
For a listing of which Windows OS functions are inside of which DLLs, see Windows OS functions.
func type
When specifying a 'func' type for some argument passed to a DLL function, you must initialize some variable to contain the Definition string for your subroutine. For example, let's assume that MyFunc is passed one arg which is some REXX subroutine in your script that the DLL function will call. MyFunc returns no value. And let's say that MyFunc is going to pass two args to your subroutine, a str and a 16-bit numeric value, and expects your subroutine to return a 32-bit numeric value. Here is how you would FUNCDEF MyFunc:
/* Let's set up a Definition string for our subroutine. Since the * DLL function is going to pass a str and a 16-bit numeric, * and then expects our subroutine to return a 32-bit numeric, * our string is as so. Also, let's call this type of callback * a "MyCallback", so we'll use a variable with that name. */ MyCallback = '32, str, 16' /* Register MyFunc. Note it gets passed a 'func MyCallback' */ err = FUNCDEF('MyFunc', ', func MyCallback', 'MyDll') /* Now that MyFunc is registered, we can DROP MyCallback */ /* Call MyFunc. Tell it to call the subroutine named MySub1 */ CALL MyFunc(MySub1) /* Call MyFunc again. This time, tell it to call the subroutine named MySub2 */ CALL MyFunc(MySub2) RETURN /* Here's MySub1. It will be called by MyFunc, and is passed a * str and a 16-bit numeric value. It expects us to return * a 32-bit numeric. */ MySub1: SAY "First arg is a string:" ARG(1) SAY "Second arg is a 16-bit number:" ARG(2) RETURN 1 /* Here's MySub2. It will be called by MyFunc, and is passed a * str and a 16-bit numeric value. It expects us to return * a 32-bit numeric. */ MySub2: SAY "First arg is a string:" ARG(1) SAY "Second arg is a 16-bit number:" ARG(2) RETURN 2When you specify some array (ie, [] is used, except on a str/char) or struct, or you qualify a str or char as 'stor' or 'dual', then what is passed to your subroutine is not the actual data, but rather, a numeric "address" that represents the data. You can pass this address to the special built-in function CONVERTDATA() to stuff it into a particular REXX variable. CONVERTDATA is passed that address, the name of the REXX variable, and a Definition string that contains one data type. For example, let's say that MyFunc passes one arg -- an array of two 32-bit numbers. Here is how you would deal with that:
/* Let's set up a Definition string for our subroutine. Since the * DLL function is going to an array of 2 32-bit numbers, * and expects our subroutine to not return anything, then * our string is as so. Also, let's call this type of callback * a "Callback2", so we'll use a variable with that name. */ CallBack2 = ', 32[2]' /* Register MyFunc. Note it gets passed a 'func Callback2' */ err = FUNCDEF('MyFunc', ', func Callback2', 'MyDll') /* Call MyFunc. Tell it to call the subroutine named MySub */ CALL MyFunc(MySub) RETURN /* Here's MySub. It will be called by MyFunc, and is passed an * array of 2 32-bit numbers. It expects us to return nothing. */ MySub: /* Since the arg passed to us is an array, we need * to use CONVERTDATA. Let's stuff the values into * a variable named MyVariable. We need quotes here. */ CONVERTDATA(ARG(1) /* the special address */, 'MyVariable', '32[2]') SAY "First number:" MyVariable.1 SAY "Second number:" MyVariable.2 RETURNYour definition string can also contain information about what calling convention needs to be used for your REXX function. The calling conventions are "api", "pascal", "c", or "pc". You need to specify the calling convention at the end of the definition string, following a | character. Here, we specify that we will be writing a REXX function that takes two args, a str and a 16-bit unsigned number, and will return a 32-bit signed number. It uses c calling convention.
MyCallback = '32, str, 16u | c'By default, REXX assumes the "api" calling convention, so if that is what you need, then you do not need to add the calling convention to your definition string.
You can also supply an error value that is automatically returned if, for some reason, REXX has a problem calling your function (and some value needs to be returned to whomever called your function). The desired error value must follow the calling convention (if you have also supplied a calling convention -- if not, then the error simply follows the | character). For example, here we specify a calling convention of "pascal" and we tell the interpreter to return a value of -1 if there is any error calling our function).
MyCallback = '32, str, 16u | pascal -1'By default, REXX returns a 0 if there is a problem calling your function, so if that is a suitable error return, then you do not need to add an error value to your definition string.
If you specify the 'stor' modifier with a func type, then what you are defining is not some REXX function you've written, but rather, some function you wish to call in a DLL that is not written from REXX. The actual address of this function will be returned to you by some other function in that DLL. For example, assume the DLL has a function called GetFunction(), which returns the address of the function you need to call. GetFunction() itself is passed no arguments. The function it returns will be passed a 32-bit number, and will return a string. First, let's build the definition string for the function that will be returned. We use a variable named FUNC1 for the definition string.
FUNC1 = 'str, 32'Now we need to FUNCDEF GetFunction() so we can call it.
/* Register GetFunction. Note it returns a 'func FUNC1 stor' */ err = FUNCDEF('GetFunction', 'func FUNC1 stor', 'MyDll')Now when we call GetFunction(), we can assign the return value to some REXX variable, and then use that REXX variable as the name of the function.
/* Call GetFunction, and assign the returned function to "SomeFunction" */ SomeFunction = GetFunction() /* Now call SomeFunction, passing a 1, and display its returned string */ SAY SomeFunction(1)If you need to specify a calling convention other than "api", and want to list some error values to check for, add then to the definition string. For example, here we specify that "SomeFunction" will use the "c" calling convention, and we want to check for a return of -1 as an error and if so, trigger a USER 1 condition:
FUNC1 = 'str, 32 | c U1 -1'Here, we specify that we wish to specifically check for any error values returned (ie, disable REXX's default of raising SYNTAX if a 0 is returned for certain datatypes). In that case, we specify an "O" (not a zero).
FUNC1 = 'str, 32 | O'
More real world examples
/* Register the 'GetWindowRect' function in * the Windows operating system. This is found in * the user32 DLL. The 'GetWindowRect' function * takes two args. An HWND (ie, window address), and * a pointer to a RECT structure. It returns a * BOOL (32-bit number). NOTE: We could define the * RECT arg as a struct. But since it consists of * four 32-bit numbers, it's easier to simply * define it as a pointer to an array of four * such numbers. Also, note that 'GetWindowRect' * stores values in this RECT for us, so we also * define it as 'stor' to indicate that we want our * REXX compound variables to receive the stored * values. */ err = FUNCDEF('GetWindowRect', '32, void, 32[4] stor', 'user32') /* Register was ok? */ IF err == 0 THEN DO /* Call GetWindowRect and receive back its return in 'err'. * NOTE: Assume that 'myWindow' was previously assigned * the return value of CreateWindowEx, which was * registered to return a type of 'void'. */ err = GetWindowRect(myWindow, myRect) /* GetWindowRect was successful? */ IF err == 1 THEN DO /* Print out stored RECT fields */ SAY 'left =' myRect.1 SAY 'top =' myRect.2 SAY 'right =' myRect.3 SAY 'bottom =' myRect.4 END END RETURN /* Register the 'SetWindowPlacement' function in the * Windows operating system. This is found in the user32 * DLL. The 'SetWindowPlacement' function takes two * args - a window address (given to us by the operating * system and not directly accessed by us, so we declare * it as 'void'), and a pointer to a WINDOWPLACEMENT * structure. It returns a 32-bit number. Since this * involves a structure, we must first set up a REXX * variable (WINDOWPLACEMENT) to define the fields of that * structure. Note that the WINDOWPLACEMENT struct itself * contains a RECT struct and two POINT structs, so we * also define those 2 structs with two more REXX variables. */ /* Define RECT structure. It has 4 fields of the following types: */ RECT = '32, 32, 32, 32' /* Define POINT structure. It has 2 fields of the following types: */ POINT = '32, 32' /* Define WINDOWPLACEMENT structure. It has 6 fields of the following types: */ WINDOWPLACEMENT = '32u, 32u, 32u, struct POINT, struct POINT, struct RECT' /* Register SetWindowPlacement() */ err = FUNCDEF('SetWindowPlacement', '32, void, struct WINDOWPLACEMENT', 'user32') /* Register was ok? */ IF err == 0 THEN DO /* We don't need these after registering */ DROP WINDOWPLACEMENT, POINT, RECT /* Initialize a WINDOWPLACEMENT struct using the variable 'myPlacement' */ myPlacement.1 = 44 /* size of WINDOWPLACEMENT struct in bytes */ myPlacement.2 = 0 /* flags = none */ myPlacement.3 = 1 /* SW_SHOW_NORMAL */ myPlacement.4.1 = 0 /* ptMinPosition.x */ myPlacement.4.2 = 0 /* ptMinPosition.y */ myPlacement.5.1 = 0 /* ptMaxPosition.x */ myPlacement.5.2 = 0 /* ptMaxPosition.y */ myPlacement.6.1 = 10 /* rcNormalPosition.left */ myPlacement.6.2 = 10 /* rcNormalPosition.top */ myPlacement.6.3 = 400 /* rcNormalPosition.right */ myPlacement.6.4 = 200 /* rcNormalPosition.bottom */ /* Call SetWindowPlacement(), and check for error */ err = SetWindowPlacement(myWindow, myPlacement) IF err == 0 THEN SAY "SetWindowPlacement failed" END RETURN /* Register and call SystemParametersInfoA() to return the width of a border. * Note: When we use SystemParametersInfoA() to return this info. The third * argument must be '32 stor'. * We're going to give this function a REXX name of BorderWidth(). You'll see * why later. */ err = FUNCDEF('BorderWidth', '32u, 32u, 32u, 32 stor, 32u', 'user32', 'SystemParametersInfoA') IF err == 0 THEN DO err = BorderWidth(5 /* SPI_GETBORDER */, , width) IF err == 1 THEN SAY "Border width:" width ELSE SAY "Can't get border width" END ELSE SAY "Can't register SystemParametersInfoA()" /* New let's call SystemParametersInfoA() to return the working area (ie, the * portion of the desktop not obscured by the system tray). Ok, when we get * this info, the third parameter we pass must be a 'struct RECT stor'. But above, * we're already FUNCDEF'd SystemParametersInfoA() with a third arg of '32 stor'. * We can't FUNCDEF SystemParametersInfo twice unless we give it two entirely * different REXX names. And that's what we're going to do. Above, we gave it * a REXX name of BorderWidth(). Now, we're going to give it a REXX name of * WorkArea() in order to define it so that we can get this info. */ RECT = '32u, 32u, 32u, 32u' err = FUNCDEF('WorkArea', '32u, 32u, 32u, struct RECT stor, 32u', 'user32', 'SystemParametersInfoA') IF err == 0 THEN DO err = WorkArea(48 /* SPI_GETWORKAREA */, , area) IF err == 1 THEN DO SAY "Work area width:" area.3 - area.1 SAY "Work area height:" area.4 - area.2 END ELSE SAY "Can't get work area" END ELSE SAY "Can't register SystemParametersInfoA()" /* Register and call GetSystemDirectoryA() */ err = FUNCDEF('GetSystemDirectoryA', '32u, str[260] stor, 32u', 'kernel32') IF err == 0 THEN DO len = GetSystemDirectoryA(Dir, 260) IF len \== 0 THEN SAY "System directory:" Dir ELSE SAY "Can't get system directory" END ELSE SAY "Can't register GetSystemDirectoryA()"