VX-REXX EXTRAS SOURCE CODE VX-REXX Extras is an extension library for Watcom VX-REXX version 2.1. It contains a number of useful functions and two new object classes (a Warp 4 style notebook control and an improved graphical pushbutton). This is the source code, written in C and SOM. BUILDING Building this library from source requires the following: * The Watcom VX-REXX Object Development Kit (ODK). This was originally sold by Watcom but is now abandonware like VX-REXX itself. If you don't have a copy, it is quite easy to find it available for download from various sources. (Of course, the legality of obtaining it this way is ambiguous at best, so I'm not actually advocating it... use your own judgement.) To install the ODK, make sure to add SET VXODK= to your CONFIG.SYS, so that the class browser can locate the *.cls files. Create the desktop objects if necessary, including the necessary associations, by running the BUILDODK.CMD script in the VXODK directory. * The IBM OS/2 Developer's Toolkit version 2.1. This specific version of the toolkit is required, as the build scripts and header files provided by the ODK make assumptions which depend on it. It may well be possible to rewrite things to allow use of the latest Toolkit (v4.52 at the time of writing), but in spite of various attempts I've never succeeded in accomplishing this. Note that the 2.1 Toolkit doesn't have to be properly installed through your CONFIG.SYS and other system files - it's enough that the files and directories exist. The included SETTINGS.CMD (which is called by all the build scripts) sets up the path and environment settings as needed, as long as you provide it with the path to the top-level directory. * IBM C/Set++ version 2.1. Again, as with the toolkit, you must have this specific version of the IBM compiler, as the ODK link libraries seem to have fixed dependencies on it. As with the toolkit, only the actual files and directories of C/Set++ are required. SETTINGS.CMD will set up the necessary environment automatically when you run any of the build scripts. Just make sure that SETTINGS.CMD has the correct path to the top-level C/Set++ directory. Note: It should also be possible to use Watcom C version 10, although I haven't tested this. OpenWatcom does not work with the current codebase, although it may be possible to hack the build scripts and/or header files to support it. Since I'm not very familiar with the Watcom compilers I'm not up to doing this myself. (If anybody does manage to do this, I'd appreciate it.) * MAKEDESC.CMD somewhere in your PATH. This is a REXX script included with a number of open source projects from NetLabs and elsewhere, so I haven't bothered to include it here. (If you need to disable it, comment out the line in BUILD.CMD referring to it.) To build the library (VROBJEX.DLL), first edit SETTINGS.CMD and either delete everything between the first two comment blocks (in which case you will be prompted for the necessary paths the next time you run BUILD.CMD), or else directly edit the path names at the top of this file to point to the correct locations for your own system. Run BUILD.CMD to build a release version of the library, or BUILDDBG.CMD to build a debug version which can (sometimes) be used with the IBM debugger. If you want to change the version number, simply edit version.h and update the "VERSION" and "VERSION_STRING" definitions. VERSION_STRING is only used to write the BLDLEVEL string. VERSION is used to generate the version number reported by the actual code (i.e. in VRXVersion), as follows: the number is divided by 100 to generate the major version; the remainder divided by 10 yields the minor version; and the leftover remainder is the refresh level. (This means that the minor and refresh numbers both have a possible range of 0-9.) SUPPORTING FILES The 'vxrexx' directory contains the VX-REXX extensions for adding drag-and-drop support for VROBJEX classes to the Designer. MTO and MTC files are plain text files whose format is described in the ODK Programmer's Guide. VRM and VRW files contain custom macros used by some controls; these are compiled from the VX-REXX project source files in the 'macrosrc' directory. LEARNING HOW TO USE THE ODK The documentation provided by the VX-REXX ODK is terse, to say the least, and the programming reference is rather incomplete. The best way to learn how to use the ODK is simply practice. It's not necessary to be intimately familiar with SOM programming (the ODK automates pretty much all the SOM code generation), but you should at least try to understand the basic principles involved. Read through the Programmer's Guide from start to finish, and actually do the programming tutorial - there's no substitute for experience. You will also find that you can learn quite a bit from the sample project code which isn't otherwise documented. Basically, you use the Class Browser to create a new object type and define its methods, events, properties, etc. Always run MAKE.CMD (in the SOM directory) after making changes in the Class Browser. A .C file will be created in the C directory for each object class in a project. - For every property you add, .C will contain a pair of functions Instance_vrGet_ (called when VRGet is used) and Instance_vrSet_ (called when VRSet is used). Both of these functions should return TRUE if the function completed successfully, no matter what the property's actual return value is. The property value itself will be stored in the final parameter to the function call (in the case of the _vrGet... function this is a reference parameter which you need to update with the correct property value). Events are actually a special kind of property which are set when a PM window message gets triggered. So in the Class Browser you will need to define both the property name and the window message that triggers it. See the 'simple object' example in the Programmer's Guide for details. - It is sometimes also necessary to handle PM window messages even if they don't get passed on to VX-REXX as an event. (One example is the page-list menu in VRTabbedDialog, which is supported by intercepting WM_CONTEXTMENU in the notebook control and simply telling Presentation Manager to pass the message back to the default notebook window procedure. It has to be done explicitly in this case because VRTabbedDialog is a subclass of VRNotebook, which normally overrides WM_CONTEXTMENU in order to generate a VX-REXX "ContextMenu" event. What we do circumvents that, with the obvious trade- off that VRTabbedDialog controls will never receive "ContextMenu" events from VX-REXX.) You can pass an event through to the default PM window procedure by calling _vrCallDefProc( somSelf, hWnd, msg, mp1, mp2 ); this should yield the default behaviour of the native PM control. Alternatively, calling parent_vr( somSelf, hWnd, msg, mp1, mp2 ) will cause VX-REXX to handle the event by passing it up to your object's parent class. (If you're not clear on what the difference is, consider the example just mentioned. VRTabbedDialog handles WM_CONTEXTMENU by passing it to PM using _vrCallDefProc; this causes PM to handle the page-list menu internally. If, on the other hand, it were to call parent_vrWM_CONTEXTMENU() instead (which is the default behaviour), the standard VX-REXX VRNotebook control (of which VRTabbedDialog is a subclass) would handle the WM_CONTEXTMENU according to its own implementation - in this case, doing nothing besides generating a "ContextMenu" event in the application, so the page-list menu would never appear). - Methods are added in much the same way as properties. Remember that a method has to deal with two different return values: the internal value which the implementation code returns, and the REXX return value which gets passed back to the VX-REXX application. The latter will obviously vary in format depending on what the function does and how you choose to implement it. The REXX return value is stored in the result RXSTRING; in this way it is similar to writing a 'normal' REXX API function. VX-REXX provides several APIs you can use to copy values of various types into the RXSTRING, such as VRCopyResult (copies an array), VRLongToRXString and VRShortToRXString (long or short integer) or VRBooleanToRXString (boolean). The internal return value, on the other hand, is always a boolean (bool) value. It should be TRUE if the method completed successfully, or FALSE if an error condition occurred. (When a method returns FALSE, the application will terminate with a REXX code 40: Incorrect call to routine.) - You can maintain internal state data for an object through what are effectively global instance variables. These are defined in the 'instance data' category through the Class Browser. Any function can access these variables by simply prefixing the variable name with an underscore (_), as long as the somThis object has been initialized (by calling the function GetData). If you need to define your own types, data structures, etc. for your object class, these can be entered in the "SOM Passthrough" section via the Class Browser. If you want to add standalone REXX library functions to a project, you can put these in a separate C file and link it into the library by modifying BUILD.CMD. VROBJEX already defines several APIs in this way, in the file 'vrxfuncs.c'. Some other miscellaneous tips: - If you want to set variables through the REXX variable pool interface, you'll need to be aware of the client/server thread architecture that VX-REXX uses. (This is mentioned briefly in the ODK programming guide, but it's not very clear.) Basically, any code within a class that gets called through VRSet(), VRMethod(), event routines, etc. are run on the VX-REXX server thread. However, the actual REXX code for your application runs on a CLIENT thread. This means that if you try to directly modify the variable pool from within (say) a class method, your application code won't see it. You have to use the VRCallFunctionClient() function to execute a callback routine on the client thread in order to modify that client thread's REXX variable pool. (VROBJEX uses CB_WriteStemElement() in utility.c for this purpose; see the QueryRegistered() method implementation in tooltip.c for an example of how it is used.) - Related to this, if you want to map the name of a window (e.g. "PB_1") to a VX-REXX object handle using VRRXStringToSOMObject(), you need to pass the thread ID of the client thread which owns that object. Using DosGetInfoBlocks() to get the current thread ID from inside class code will not work, because that will return the ID of the server thread. Use the _vrGetRexxIds() method instead (assuming that the current object is on the same thread as the one you want to resolve, which is likely). Of course, you can use DosGetInfoBlocks() in standalone API functions (like those in vrxfuncs.c), as those always run on the current thread. - If you ever have to allocate memory which will persist beyond the current function call, you must use VRMemAlloc() and VRMemFree() instead of the standard C library malloc() and free(), in order to avoid possible memory corruption (which can lead to random-seeming traps in VROBJ.DLL). There are one or two specific circumstances (e.g. reallocating a result RXSTRING) when DosAllocMem() and DosFreeMem() should be used instead; see the ODK documentation for the details (such as they are). NOTE: There's one thing that the ODK fails to properly automate, and the Programmer's Guide doesn't mention this. If you add methods, properties, events, etc. to an object class via the Class Browser, and then subsequently delete them, references to the deleted items may remain left-over in the SOM source files. This can cause problems when you try to recompile. I've found that the following trick seems to work to get rid of cruft: - Delete the obsolete items in the Class Browser, and Exit when done. - Run MAKE.CMD in the SOM directory (I'm not sure if this step is actually necessary, but it doesn't hurt). - Edit SOM\SC\.csc where is the base filename of the class you have just modified. Look for references to the items you deleted (usually these are under the "ReleaseOrder" section). Delete any you find, and save the file. - Repeat the above, this time with the file SOM\.rel. - Run MAKE.CMD from the SOM directory again. - Edit C\.C and remove any functions which refer to the deleted properties/events. - Now you can build the project with BUILD.CMD. LICENSE VX-REXX Extras Library, (C) 2010,2017 Alex Taylor Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.