4.7. improvement #6: Sending back escape messages

This time, we're going to improve the way error messages are returned to the calling program or user.

Currently, we're sending back responses using the DSPLY op-code. Yuck. Blech. Ewww.

Instead, error messages should be sent back to the calling program using a typical AS/400 *ESCAPE message! This way, the program that called us will know that we ended abnormally, and can even monitor for a message to handle.

The most convienient way to implement this is to create a subprocedure that gives us a simple interface to the QMHSNDPGM (Send Program Message) API. The subprocedure will fill in all of the details that the API needs, except the actual message data -- we'll pass the message data as a parm.

The result is a subprocedure that looks like this:

         P die             B                                                  
         D die             PI                                                 
         D   peMsg                      256A   const                          
         D SndPgmMsg       PR                  ExtPgm('QMHSNDPM')             
         D   MessageID                    7A   Const                          
         D   QualMsgF                    20A   Const                          
         D   MsgData                    256A   Const                          
         D   MsgDtaLen                   10I 0 Const                          
         D   MsgType                     10A   Const                          
         D   CallStkEnt                  10A   Const                          
         D   CallStkCnt                  10I 0 Const                          
         D   MessageKey                   4A                                  
         D   ErrorCode                32766A   options(*varsize)              
         D dsEC            DS                                                   
         D  dsECBytesP             1      4I 0 INZ(256)                         
         D  dsECBytesA             5      8I 0 INZ(0)                           
         D  dsECMsgID              9     15                                     
         D  dsECReserv            16     16                                     
         D  dsECMsgDta            17    256                                     
         D wwMsgLen        S             10I 0                                  
         D wwTheKey        S              4A                                    
         c                   eval      wwMsgLen = %len(%trimr(peMsg))           
         c                   if        wwMsgLen<1                               
         c                   return                                             
         c                   endif                                              
         c                   callp     SndPgmMsg('CPF9897': 'QCPFMSG   *LIBL':  
         c                               peMsg: wwMsgLen: '*ESCAPE':            
         c                               '*PGMBDY': 1: wwTheKey: dsEC)          
         c                   return                                             
         P                 E                                                    

Now, instead of code that looks like this:

         c                   eval      sock = socket(AF_INET: SOCK_STREAM:      
         c                                           IPPROTO_IP)                
         c                   if        sock < 0                                 
         c                   eval      msg = %str(strerror(errno))              
         c                   dsply                   msg                        
         c                   return                                             
         c                   endif                                              

We'll make our error handling look more like this:

         c                   eval      sock = socket(AF_INET: SOCK_STREAM:      
         c                                           IPPROTO_IP)                
         c                   if        sock < 0                                 
         c                   callp     die('socket(): ' + %str(strerror(errno)))
         c                   return                                             
         c                   endif                                              

Cute, huh? The die() procedure will cause it to send back an escape message when the socket() API fails. The format of the parms in the die() subprocedure makes it very easy to insert into our code.

Having said that, however, here's a slightly more complicated usage:

         C                   if        connect(sock: p_connto: addrlen) < 0    
         c                   eval      err = errno                            
         c                   callp     close(sock)                            
         c                   callp     die('connect(): '+%str(strerror(err))) 
         c                   return
         c                   endif

The close() API is a UNIX-type API and can return an error via errno just like the connect() API can! Therefore, we save the value of errno before calling close() just to make sure that we don't lose the value.

You should also note that the 'die' procedure will actually end the program. Unless something goes wrong with the 'die' procedure, the 'return' statement will never be executed. This means that if you have something to close, such as a socket, you need to make sure you do it before calling 'die'.

I'll now go through and replace DSPLY with die() throughout my program.