[ChipChat Tech. Group - 32-bit OS/2 text paging software and Sound Cards. (click here).][Indelible Blue - OS/2 software and hardware solutions for customers worldwide. (click here).]
Using EHLLAPI in REXX- by Gary Pool
Summary: Use Communications Manager/2 or Personal Communications for OS/2? Learn how to control both with Rexx by using the Emulator High Level Language Programming Interface that they both come with.

If you are using Communications Manager/2 (CM/2) or Personal Communications for OS/2 (PCOM/2) for 3270 or 5250 connectivity, read this article! These emulators come with a file called SAAHLAPI.DLL that provides access to the Emulator High Level Language Programming Interface (EHLLAPI). Using REXX to call these APIs, you can programmatically read host screens, send keystrokes, send and receive files, and make changes to the PM presentation space.

We have recently been testing PCOMM version 4.2 and Object REXX for Windows 95/NT and have found that most programs developed under OS/2 will also work in those environments.


CM/2 and PCOM/2 allow keyboard redefinitions which can certainly help to automate repetitive tasks, but keyboard redefinition has none of the flexibility that a REXX EHLLAPI program can offer. Tasks that would ordinarily take many keystrokes over many screens can be reduced to a few keystrokes or automated completely. For example, resetting a user's password could require checking permissions in one application, resetting the password in another, and filling out a problem report in a third. Those steps could be reduced to entering the userid. Or perhaps there is data that is stored on the PC which needs to be entered at the host, such as a list of userids for a Distribution list. Any repetitive tasks can be easily automated with REXX and EHLLAPI.

How do I program with REXX and EHLLAPI?

I thought you'd never ask. As an example, I will step through a program that I use to sign on to OfficeVision MVS. Then, just for fun, we'll report how many files are in the inbasket. It uses most of the EHLLAPI calls that you would ever need to use.

Load the DLL

If you look at lines 1 and 2 of figure 1, you will see how to query and load SAAHLAPI.DLL. Its directory must be found in the LIBPATH statement of CONFIG.SYS. All EHLLAPI function calls are invoked from the one function, HLLAPISRV. By custom, it's usually registered as "HLLAPI", but it's not required.

Check for problems

Sometimes (believe it or not) I make mistakes when programming! If the program bombs or I want to interrupt the process for some reason, I like to make sure that I have a procedure to disconnect my host session. Lines 3 through 5 check for problems or interruptions. If processing is interrupted, it sends control to "CLEANUP", which cleanly disconnects from the host session.

Get Input from the user

Since this particular program is for my personal use, I have hard-coded my userid into the program. For security reasons, I don't want to have my password hard-coded into the program, so I run a little procedure that prompts me for my password. Lines 7-8 and 35-73 deal with getting the user input. Almost half the lines in the program are used to get the password!

Set session parameters

Line 9 of figure 1 is optional. It's possible to change the default behavior of many of the HLLAPI calls by using "Set_session_parms". In this instance, the "CONPHYS" parameter forces the session to come to the foreground when a successful "Connect" occurs. Without this statement, the session would stay in the background. For a complete list of session parameters that can be set, refer to IBM Communications Manager/2 Version 1.0 EHLLAPI Programming Reference.

Connect to the host session

Line 10 of figure 1 illustrates how to connect to a host session. There are actually two ways to connect to the host: CONNECT, which is illustrated, and CONNECT_PM, which allows Presentation Manager window manipulation. Lines 11-14 ensure that I was able to successfully connect to the host screen.

Read the host screen

Before I send any keystrokes, I have to make sure I'm on the right screen! I use Search_PS in line 15 of figure 1. See figure 4 (.GIF, 4K) to see the screen being searched. If I want to manipulate the data, I have a number of options:
  • data=hllapi("Copy_PS") would copy the entire screen to a string.
  • data=hllapi("Copy_PS_To_Str",pos,length) would copy just a portion of the screen.
  • data=hllapi("Copy_OIA") would copy the Operator Information Area to a string.
  • data=hllapi("Copy_Field_To_String",pos,length) would copy characters from a host-defined field to a string.

Search_PS scans the entire presentation space an returns the cursor position where the string is found or a 0 if it is not found. In an 80 column screen, Search_PS would return row 10, column 11 as 731 ((9*80)+11).

Lines 16-18 cause the program to exit if I am not at the screen necessary to logon.

Send keystrokes to the host

Lines 20-30 and 74-83 are concerned with sending keystrokes to the host session. Text can be sent as well as special control keys such as PF keys and the Enter key. Figure 3 shows many of the keyboard mnemonics for the special control keys.

The HLLAPI call Sendkey is used to send keystrokes to the host session. The most difficult aspect of HLLAPI programming is determining if the host is available for input. I use one of the safest ways. In lines 78-82 of figure 1, I first use the HLLAPI call "Wait" to wait for the keyboard lock to be free. Unfortunately, sometimes the keyboard can be unlocked, but the screen may not be refreshed. Therefore I look for a particular string that should be on the screen I am expecting. This has its weaknesses, but is generally the safest for the projects I undertake. In the IBM Redbook, REXX: From Bark to Byte, the chapter on EHLLAPI programming offers several ways of checking for host availability.

Note that on line 80 of figure 1, I do a SysSleep for one second. I do this to increase performance. When the host session is in the background looping, looking for a string, the loop eats up so many strokes that the host screen cannot refresh in a timely manner. In this example it's not necessary for the call to SysSleep because I'm bringing the session to the foreground, but I wanted to point out the potential problem before you encountered it.

Retrieve any information from host

Just for fun, I'll pull off how many inbasket items I have. In line 30 of figure 1, I use the HLLAPI call, Copy_PS_To_Str, giving it the cursor position to start from (76), and the length of the string to return (5). That returns the last 5 positions of row 1 in figure 5 (.GIF, 11K).

How did I know to start at 76? I could have counted the characters on the screen, but whenever I'm writing a HLLAPI program, I use the GETCUR.CMD (1K) to help me figure out screen locations of information. I position the cursor in the session I am testing, then run GETCUR.CMD to report the cursor position.

I also added another way to get the information using the "Word" command. I have encountered some screens where the number of words on a line are always the same, but the position on the screen varies.

Disconnect from the host

Line 33 of figure 1 is one way of disconnecting from the host. The Reset_System function reinitializes the session parameters (set by the Set_Session_Parms function) to their defaults and disconnects from all connected resources. Because I regularly change the session parameters, this is my preferred way of disconnecting. It is also possible to Disconnect using a HLLAPI call to "Disconnect" or "Disconnect_PM".

Where do I go from here?

This sample contains almost all of the HLLAPI calls that I ever use and can be used for a starting point for your own applications. If you would like more information, REXX: From Bark to Byte (GG24-4199-00) is a good starting place, but the most complete information can be found in IBM Communications Manager/2 Version 1.0 EHLLAPI Programming Reference.
Figure 1
/* OV.CMD REXX program to logon to OfficeVision MVS. */

Call RxFuncAdd 'SysLoadFuncs', 'RexxUtil', 'SysLoadFuncs'
Call SysLoadFuncs                                        

if rxfuncquery('hllapi') then                   /* 001 */
call rxfuncadd 'hllapi','saahlapi','hllapisrv'  /* 002 */

signal on failure name CLEANUP                  /* 003 */
signal on halt name CLEANUP                     /* 004 */
signal on syntax name CLEANUP                   /* 005 */

UserId = 'POOLMWV'                              /* 006 */

parse upper arg PW                              /* 007 */
if PW='' then call GetPass                      /* 008 */

rc = hllapi('Set_session_parms', 'CONPHYS')     /* 009 */
rc = hllapi("Connect", "A")                     /* 010 */

if (rc <> 0) then do                            /* 011 */
   say 'Did not connect to Host.  RC = 'rc      /* 012 */
   signal CLEANUP                               /* 013 */
end                                             /* 014 */

rc=hllapi("Search_ps", "/Starpro", 1)           /* 015 */
if rc = 0 then DO                               /* 016 */
   say "You are not at the correct screen"      /* 017 */
   signal CLEANUP                               /* 018 */
end                                             /* 019 */

call sendwait "wpx@E","WELCOME"                 /* 020 */
rc=hllapi("sendkey","@C")                       /* 021 */
rc=hllapi("wait")                               /* 022 */
rc=hllapi("sendkey","cesn@E")                   /* 023 */
*/rc=hllapi("wait")                             /* 024 */
rc=hllapi("sendkey", UserId"@T")                /* 025 */
rc=hllapi("sendkey", PW"@E")                    /* 026 */
rc=hllapi("wait")                               /* 027 */
call sendwait "TALK@E","SOSMMAIN"               /* 028 */
call sendwait "wib@E","DMDBIB10"                /* 029 */
numinbas=strip(hllapi(Copy_PS_To_Str,76,5))     /* 030 */
Here's another way to get the information
numinbas=word(hllapi(Copy_PS_To_Str,1,80),9)    /* 030 */
Say numinbas "inbasket items found."            /* 031 */

CLEANUP:                                        /* 032 */
call hllapi("reset_system")                     /* 033 */

exit                                            /* 034 */

GetPass:                                        /* 035 */

Valid='ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890#$@' /* 036 */
Maxlength=8                                     /* 037 */
PW=''                                           /* 038 */

ETK=d2c(13)                                     /* 039 */
BS=d2c(8)                                       /* 040 */
X1=d2c(0)                                       /* 041 */
X2=d2c(224)                                     /* 042 */

say 'Enter Your Password:'                      /* 043 */
do forever                                      /* 044 */
   Ch = translate(SysGetKey('NOECHO'))          /* 045 */
   select                                       /* 046 */
      when Ch=ETK                               /* 047 */
         then do                                /* 048 */
            say ''                              /* 049 */
            leave                               /* 050 */
         end                                    /* 051 */
      when Ch=BS                                /* 052 */
         then if PW=''                          /* 053 */
            then call Beep 262, 200             /* 054 */
            else do                             /* 055 */
               call charout ,BS BS              /* 056 */
               PW = left(PW, length(PW)-1)      /* 057 */
            end                                 /* 058 */
      when pos(Ch, Valid) > 0                   /* 059 */
         then if length(PW) = MaxLength         /* 060 */
            then call Beep 262, 200             /* 061 */
            else do                             /* 062 */
               call charout , '*'               /* 063 */
               PW= PW||Ch                       /* 064 */
            end                                 /* 065 */
      otherwise do                              /* 066 */
         if Ch = X1 | Ch = X2                   /* 067 */
            then call SysGetKey('NOECHO')       /* 068 */
         call beep 262, 200                     /* 069 */
      end                                       /* 070 */
   end                                          /* 071 */
end                                             /* 072 */
return                                          /* 073 */

SendWait:                                       /* 074 */
keys=arg(1)                                     /* 075 */
lookfor=arg(2)                                  /* 076 */
rc = hllapi('sendkey', keys)                    /* 077 */
rc = hllapi('wait')                             /* 078 */
do until rc > 0                                 /* 079 */
   rc=SysSleep(1)                               /* 080 */
   rc=hllapi("Search_ps", lookfor, 1)           /* 081 */
end                                             /* 082 */
return                                          /* 083 */

* * *

Gary Pool is a Certified OS/2 Engineer employed by the Missouri Department of Social Services.

Copyright © 1998 - Falcon Networking ISSN 1203-5696