├── .gitignore ├── EHLLAPI.py ├── README.md ├── constants.py ├── errors.py └── programming manual for EHLLAPI.pdf /.gitignore: -------------------------------------------------------------------------------- 1 | *.bak 2 | # .txt are merely temporary notes. Only .md files should be loaded. 3 | *.txt 4 | *.pyc -------------------------------------------------------------------------------- /EHLLAPI.py: -------------------------------------------------------------------------------- 1 | # *-* coding: utf-8 *-* 2 | import constants 3 | from ctypes import c_int, c_char_p, c_void_p, byref, WinDLL, WINFUNCTYPE 4 | from errors import EmulatorError 5 | 6 | class Emulator(object): 7 | _hllApi = None 8 | 9 | def _func_num(self, arg): 10 | return c_int(arg) 11 | 12 | def _data_str(self, arg): 13 | return c_char_p(arg) 14 | 15 | def _lenght(self, arg): 16 | return c_int(arg) 17 | 18 | def _ps_position(self, arg): 19 | return c_int(arg) 20 | 21 | def __init__(self): 22 | # Load DLL. 23 | hllDll = WinDLL ("ehlapi32.dll") 24 | # Set up prototype and parameters for the desired function calls. 25 | hllApiProto = WINFUNCTYPE (c_int, c_void_p, c_void_p, c_void_p, c_void_p) 26 | hllApiParams = (1, "func_num", 0), (1, "data_str", 0), (1, "lenght", 0), (1, "ps_position", 0) 27 | # Map the call ("HLLAPI(...)") to a Python name. 28 | self._hllApi = hllApiProto (("HLLAPI", hllDll), hllApiParams) 29 | # Now each method sets up the variables and call the Python name with them. 30 | 31 | def hllApi(self, func_num, data_str, lenght, ps_position): 32 | return_value = self._hllApi(byref(func_num), data_str, byref(lenght), byref(ps_position)) 33 | if return_value != 0: 34 | raise EmulatorError (func_num.value, return_value) 35 | return return_value 36 | 37 | def __enter__(self): 38 | self.connect() 39 | 40 | def __exit__(self, *args): 41 | self.disconnect() 42 | 43 | def connect(self, name="A"): 44 | if len(name) != 1 or not name.isalpha: raise ValueError("Argument name must be 1-character short name of the host presentation space") 45 | return self.hllApi (self._func_num(constants.CONNECT_PRESENTATION_SPACE), self._data_str(name), self._lenght(1), self._ps_position(0)) 46 | 47 | def disconnect(self, name="A"): 48 | if len(name) != 1 or not name.isalpha: raise ValueError("Argument name must be 1-character short name of the host presentation space") 49 | return self.hllApi (self._func_num(constants.DISCONNECT_PRESENTATION_SPACE), self._data_str(name), self._lenght(1), self._ps_position(0)) 50 | 51 | def send_keys(self, keys): 52 | # Possible cases: 1) the argument ''keys'' is just a regular string; 2) it is a string inside <>; 3) it is inside <> because it is a keyboard command. 53 | snd_txt = keys 54 | txt_lenght = len(snd_txt) 55 | if snd_txt[0] == '<' and snd_txt[txt_lenght-1] == '>': 56 | try: 57 | snd_txt = constants.keyboard_mnemonics[keys.strip('<>').upper()] # Try to associate the string inside the <> with one object in the dictionary 58 | txt_lenght = len(snd_txt) 59 | except KeyError: 60 | snd_txt = keys # Couldn't find a match in the dictionary. 61 | return self.hllApi (self._func_num(constants.SEND_KEY), self._data_str(snd_txt), self._lenght(txt_lenght), self._ps_position(0)) 62 | 63 | def get_cursor(self): 64 | byte_pos = self._lenght(0) # byte_pos will be modified with the cursor position value 65 | self.hllApi (self._func_num(constants.QUERY_CURSOR_LOCATION), self._data_str(""), byte_pos, self._ps_position(0)) 66 | return byte_pos.value 67 | 68 | def set_cursor(self, byte_pos): 69 | return self.hllApi (self._func_num(constants.SET_CURSOR), self._data_str(""), self._lenght(0), self._ps_position(byte_pos)) 70 | 71 | def get_field(self, field_pos, field_len): 72 | txt = self._data_str("") 73 | len = field_len 74 | pos = field_pos 75 | self.hllApi (self._func_num(constants.COPY_FIELD_TO_STRING), txt, self._lenght(len), self._ps_position(pos)) 76 | return txt.value 77 | # To use this, one must know where a field starts and what's its lenght. TODO: get_text(row1, col1, row2, col2) 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PyEHLLAPI 2 | EHLLAPI implementation for IBM 3270 terminal emulator 3 | 4 | PyEHLLAPI module makes it easy to call ehlapi32.dll methods from Python 2.7 and automate terminal operation with an emulator such as the web based HOD (Host On-Demand). 5 | 6 | IBM Documentation: http://www-01.ibm.com/support/knowledgecenter/SSEQ5Y_5.9.0/com.ibm.pcomm.doc/books/html/emulator_programming08.htm 7 | -------------------------------------------------------------------------------- /constants.py: -------------------------------------------------------------------------------- 1 | # Dictionary for keyboard commands 2 | keyboard_mnemonics = {'TAB':'@T', 'ENTER':'@E', 'ERASEEOF':'@F', 'BACKSPACE':'@<', 'BACKTAB':'@B', 'CLEAR':'@C', 'DOWN':'@V', 'LEFT':'@L', 'RIGHT':'@Z', 'CURSEL':'@A@J', 'UP':'@U', 'DELETE':'@D', 'PF1':'@1', 'PF2':'@2', 'PF3':'@3', 'PF4':'@4', 'PF5':'@5', 'PF6':'@6', 'PF7':'@7', 'PF8':'@8', 'PF9':'@9', 'PF10':'@a', 'PF11':'@b', 'PF12':'@c', 'PF13':'@d', 'PF14':'@e', 'PF15':'@f', 'PF16':'@g', 'PF17':'@h', 'PF18':'@i', 'PF19':'@j', 'PF20':'@k', 'PF21':'@l', 'PF22':'@m', 'PF23':'@n', 'PF24':'@o', 'PA1':'@x', 'PA2':'@y', 'PA3':'@z', 'PAGEUP':'@u', 'PAGEDN':'@v', 'RESET':'@R', 'SYSREQ':'@A@H', 'HELP':'@H', 'HOME':'@0', 'INSERT':'@I', 'NEWLINE':'@N', 'DUP':'@S@x', 'ERINP':'@A@F', 'FLDEXT':'@A@E', 'FIELDMARK':'@S@y', 'FIELD-':'@A@-', 'FIELD+':'@A@+'} 3 | 4 | # IBM EHLLAPI Function numbers 5 | 6 | CONNECT_PRESENTATION_SPACE = 1 7 | DISCONNECT_PRESENTATION_SPACE = 2 8 | SEND_KEY = 3 9 | WAIT = 4 10 | COPY_PRESENTATION_SPACE = 5 11 | SEARCH_PRESENTATION_SPACE = 6 12 | QUERY_CURSOR_LOCATION = 7 13 | COPY_PRESENTATION_SPACE_TO_STRING = 8 14 | SET_SESSION_PARAMETERS = 9 15 | QUERY_SESSIONS = 10 16 | RESERVE = 11 17 | RELEASE = 12 18 | COPY_OIA = 13 19 | QUERY_FIELD_ATTRIBUTE = 14 20 | COPY_STRING_TO_PRESENTATION_SPACE = 15 21 | PAUSE = 18 22 | QUERY_SYSTEM = 20 23 | RESET_SYSTEM = 21 24 | QUERY_SESSION_STATUS = 22 25 | START_HOST_NOTIFICATION = 23 26 | QUERY_HOST_UPDATE = 24 27 | STOP_HOST_NOTIFICATION = 25 28 | SEARCH_FIELD = 30 29 | FIND_FIELD_POSITION = 31 30 | FIND_FIELD_LENGTH = 32 31 | COPY_STRING_TO_FIELD = 33 32 | COPY_FIELD_TO_STRING = 34 33 | SET_CURSOR = 40 34 | START_CLOSE_INTERCEPT = 41 35 | QUERY_CLOSE_INTERCEPT = 42 36 | STOP_CLOSE_INTERCEPT = 43 37 | START_KEYSTROKE_INTERCEPT = 50 38 | GET_KEY = 51 39 | POST_INTERCEPT_STATUS = 52 40 | STOP_KEYSTROKE_INTERCEPT = 53 41 | LOCK_PRESENTATION_SPACE_API = 60 42 | LOCK_WINDOW_SERVICES_API = 61 43 | START_COMMUNICATION_NOTIFICATION = 80 44 | QUERY_COMMUNICATION_EVENT = 81 45 | STOP_COMMUNICATION_NOTIFICATION = 82 46 | SEND_FILE = 90 47 | RECEIVE_FILE = 91 48 | CANCEL_FILE_TRANSFER = 92 49 | CONVERT_POSITION_OR_CONVERT_ROWCOL = 99 50 | CONNECT_WINDOW_SERVICES = 101 51 | DISCONNECT_WINDOW_SERVICE = 102 52 | QUERY_WINDOW_COORDINATES = 103 53 | WINDOW_STATUS = 104 54 | CHANGE_SWITCH_LIST_LT_NAME = 105 55 | CHANGE_PS_WINDOW_NAME = 106 56 | START_PLAYING_MACRO = 110 57 | CONNECT_FOR_STRUCTURED_FIELDS = 120 58 | DISCONNECT_FROM_STRUCTURED_FIELDS = 121 59 | QUERY_COMMUNICATIONS_BUFFER_SIZE = 122 60 | ALLOCATE_COMMUNICATIONS_BUFFER = 123 61 | FREE_COMMUNICATIONS_BUFFER = 124 62 | GET_REQUEST_COMPLETION = 125 63 | READ_STRUCTURED_FIELDS = 126 64 | WRITE_STRUCTURED_FIELDS = 127 -------------------------------------------------------------------------------- /errors.py: -------------------------------------------------------------------------------- 1 | # *-* coding:utf-8 *-* 2 | # IBM Emulator function errors 3 | import constants 4 | 5 | class EmulatorError(Exception): 6 | """Base class for IBM emulator errors""" 7 | # Each function has a list of possible errors. These dictionaries associate error codes and descriptions 8 | connect_error_list = {1:"An incorrect host presentation space ID was specified. The specified session either does not exist or is a logical printer session. This return code could also mean that the API Setting for DDE/EHLLAPI is not set on.", 9 | 4:"Successful connection was achieved, but the host presentation space is busy.", 10 | 5:"Successful connection was achieved, but the host presentation space is locked (input inhibited).", 11 | 9:"A system error was encountered.", 12 | 11:"This resource is unavailable. The host presentation space is already being used by another system function."} 13 | disconnect_error_list = {1:"Your program was not currently connected to the host presentation space.", 14 | 9:"A system error was encountered."} 15 | send_key_error_list = {1:"Your program is not connected to a host session.", 16 | 2:"An incorrect parameter was passed to EHLLAPI.", 17 | 4:"The host session was busy; all of the keystrokes could not be sent.", 18 | 5:"Input to the target session was inhibited or rejected; all of the keystrokes could not be sent.", 19 | 9:"A system error was encountered."} 20 | query_cursor_error_list = {1:"Your program is not currently connected to a host session.", 21 | 9:"A system error was encountered."} 22 | set_cursor_error_list = {1:"Your program is not connected to a host session.", 23 | 4:"The session is busy.", 24 | 7:"A cursor location less than 1 or greater than the size of the connected host presentation space was specified.", 25 | 9:"A system error occurred."} 26 | get_text_error_list = {1:"Your program is not connected to a host session.", 27 | 2:"An error was made in specifying parameters.", 28 | 6:"The data to be copied and the target field are not the same size. The data is truncated if the string length is smaller than the field copied.", 29 | 7:"The host presentation space position is not valid.", 30 | 9:"A system error was encountered.", 31 | 24:"Unformatted host presentation space."} 32 | 33 | # This dictionary associates each function code with an error list defined above. 34 | function_errors = {constants.CONNECT_PRESENTATION_SPACE:connect_error_list, 35 | constants.DISCONNECT_PRESENTATION_SPACE:disconnect_error_list, 36 | constants.SEND_KEY:send_key_error_list, 37 | constants.QUERY_CURSOR_LOCATION:query_cursor_error_list, 38 | constants.SET_CURSOR:set_cursor_error_list, 39 | constants.COPY_FIELD_TO_STRING:get_text_error_list} 40 | 41 | def __init__(self, func_num, return_code): 42 | self.error_case_list = self.function_errors[func_num] # returns a dictionary 43 | self.error_text = self.error_case_list[return_code] # selects a case from the error_case_list dictionary 44 | def __str__(self): 45 | return self.error_text -------------------------------------------------------------------------------- /programming manual for EHLLAPI.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sapiensmagno/PyEHLLAPI/0f1a956d8a178e68ece73f6a0fc05389b7ffd095/programming manual for EHLLAPI.pdf --------------------------------------------------------------------------------