Terry and Michael,
Here is the (short) tutorial I promised on using GCI and the native API to
implement a Windows API call.
The Windows API is called GetStdHandle() and it "Retrieves a handle to the
specified standard device (standard input, standard output, or standard error)."
So, you need to pass an argument to specify which of the three you want. In my
case, I wanted a handle to standard output to pass to another API. A Google
search for GetStdHandle leads to the Windows documentation on using this API.
This includes the "name" and equivalent numeric value for each of the standard
devices, the type for the return value, and which Windows library implements
this API. The "name" (which is defined in the windows.h header file) for
standard output is STD_OUTPUT_HANDLE and it is defined as -11. The library is
Kernel32.lib. You will need all of that a bit later.
To use GCI, you might want to first review the presentation slides at
http://rexxla.org/events/2004/fgc.pdf for an overview. In short, you first need
to get the GCI package from the internet (see the link from Jon) and install the
gci.dll somewhere that ooRexx can find it - the current directory or a directory
that is in your path. In your Rexx program, you need to tell ooRexx about GCI
with code like:
call rxFuncAdd RxFuncDefine, 'gci', 'RxFuncDefine'
Now you can use the RxFuncDefine function to create a Rexx function that talks
to the Windows API. To do so you need to tell GCI about that API by setting up
a stem with the necessary parameters. Here is the code for that:
GetStdHandle.calltype = 'cdecl as function with parameters'
GetStdHandle.return.type = 'integer32'
GetStdHandle.return.name = 'return value'
GetStdHandle.0 = 1
GetStdHandle.1.type = 'integer32'
GetStdHandle.1.name = 'handle'
As you can see, there is quite a bit to describe including calling conventions,
what is returned and type and name of the single argument. Once the stem -
GetStdHandle. - is set up, you pass it to the GCI function RxFuncDefine and GCI
creates the Rexx function you can use when you want to get a handle. The code
to do that is:
call rxFuncDefine 'GetStdHandle', 'kernel32', 'GetStdHandle',
'GetStdHandle.'
The first argument is what the Rexx function will be called, the next two tell
GCI where the Windows API is located, and the last is the stem you just created.
Finally, when you want to actually get the handle for standard output, you code:
handle = GetStdHandle(-11) -- STD_OUTPUT_HANDLE
In summary, you first make the GCI function you need available, you define the
Windows API in a stem, you pass the stem to the GCI function which creates the
wrapper function that you finally can use in your program to access that API.
The Native API version will also produce a function that you can use in your
ooRexx program to access the Windows API. However, I chose to make this version
specific to the standard output handle so the name will be slightly different
and the function will NOT take an argument. When I initially developed my
version, I also chose to make it a method rather than a routine/function but for
this tutorial I will describe it as a routine to make it more comparable to the
function produced by GCI above.
The principal part of this implementation is the source file written in C++. It
begins with a couple of include statements to allow the function to use the
ooRexx and Windows API definitions. They look like:
#include <oorexxapi.h>
#include <windows.h>
Then comes the definition of the function you are writing, something like:
RexxRoutine0(uintptr_t, // return type
GetStdOutHandle) // native routine name
{
HANDLE conHandle = GetStdHandle(STD_OUTPUT_HANDLE);
return (uintptr_t)conHandle; // return value
}
The RexxRoutine0 specifies that this is a routine that takes no arguments. The
return type - uintptr_t - allows the function to be compiled for both 32- and
64-bit addressing modes. The (slightly different) routine name will be
GetStdOutHandle. The C++ code to implement the routine is two lines. The first
invokes the Windows API function GetStdHandle with an argument of
STD_OUTPUT_HANDLE (note we don't need to know what numeric value that
represents). The return from that function is assigned to the conHandle
variable which is of type HANDLE. That variable is then cast to type uintptr_t
and returned to the caller of the function. And that is all there is to it!
Except you need to specify some more things so that ooRexx can find and load the
function once it is compiled. The first bit looks like:
RexxRoutineEntry my_native_routine[] = {
REXX_TYPED_ROUTINE(GetStdOutHandle, GetStdOutHandle ),
REXX_LAST_ROUTINE() // end marker
};
The ooRexx Programmer Guide has the documentation on this kind of code so I
won't try to explain it here. Just note that your function name appears as both
arguments to the REXX_TYPED_ROUTINE function and the name you give to the array
produced - my_native_routine - will be used in the next part.
RexxPackageEntry MyExternalRoutine_package_entry = {
STANDARD_PACKAGE_HEADER
REXX_INTERPRETER_4_0_0, // ooRexx version 4.0.0 or higher
"AnExternalRoutine", // name of the package
"1.0.0", // package information
NULL, // no load function
NULL, // no unload function
my_native_routine, // the exported routines
NULL // the exported methods
};
Again, much of this is "boilerplate" that is documented in the Programmer Guide.
The array name from above - my_native_routine - appears as the exported routines
argument and the beginning of the variable name on the first line -
MyExternalRoutine - appears in the final line of the C++ file:
OOREXX_GET_PACKAGE(MyExternalRoutine);
You are going to compile and link this file to a DLL so you will need a DEF file
for the linker. It is simply:
; Module definition for stdouthandle.dll
LIBRARY stdouthandle
EXPORTS
RexxGetPackage
The name after LIBRARY will be the DLL name, stdouthandle, and the rest must be
as shown. I would name the DEF file the same as the C++ file. So if the C++
source is myextrtn.cpp, the DEF file would be myextrtn.def.
Finally you need a MAKEFILE for NMAKE, the utility that will compile and link
your source and produce the DLL. Rony's example will work perfectly well and
the changes needed to customize it for your source should be fairly obvious.
Once you have the stdouthandle.dll created, you need to be able to use it. To
do so, you need tell ooRexx about the routine it contains. Include this line in
your ooRexx program:
::routine GetStdOutHandle external "library stdouthandle GetStdOutHandle"
Now ooRexx knows that the GetStdOutHandle routine is to be loaded from the
stdouthandle.dll and made available to the ooRexx code as GetStdOutHandle. And
to use it, you code something like:
handle = GetStdOutHandle()
Hopefully, this will be enough to get you started. I realize there is a lot to
digest so don't hesitate to ask questions.
--
Gil Barmwater
------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
Oorexx-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/oorexx-devel