Synergex.SynergyDE.IOExtensions.IOHooks
WTSupported in traditional Synergy on Windows
|
WNSupported in Synergy .NET on Windows
|
USupported on UNIX
|
VSupported on OpenVMS
|
namespace Synergex.SynergyDE.IOExtensions class IOHooks protected readonly channel, int protected syserr, int protected sysstv, int
The IOHooks class makes it possible to perform custom I/O without changing any code in your existing I/O routines. Unlike the other system-supplied classes, you don’t call the hook routines yourself. Instead, IOHooks provides 4 constructors and 17 pre- and post-operation virtual hook methods that you create using the specified names and method signatures to do whatever you want them to do. If implemented, these methods are then called automatically in conjunction with other operations (for example, a FIND, a READ, a WRITE, etc.). (See Pre-operation hook methods and Post-operation hook methods for specific details.) Your implementation of these methods must exactly match the method signature you’re overriding; otherwise, you’ll get a “No suitable Method found to override” error (NOOVR) when compiling. You can extend these methods to implement dynamic operation logging, perform pre- or post-I/O processing in your application, or whatever else you need them to do.
The class that you create by extending the IOHooks base class must implement at least one constructor and override at least one hook method, depending on which I/O operations you need. In your application itself, you’ll create an instance of your class and register it as a file I/O event handler for a channel or Select statement.
For example, if you want to perform some processing before and after a READS operation, you’ll override the READS pre- and post-operation hook methods. Your custom pre-operation method will then be executed before any READS occurs. The READS itself is issued as soon as your method exits, followed immediately by your post-operation hook method. Upon exit of the post-operation hook method, your application resumes.
You can use I/O hooks globally on Windows and UNIX when you create a SYN_GLOBALHOOKS_OPEN routine. With regular (non-global) I/O hooks, you add an IOHooks constructor somewhere in your code, and then when that code is executed, your I/O hooks become active. For global I/O hooks, you instead add the IOHooks constructor to your SYN_GLOBALHOOKS_OPEN() implementation, and then after each OPEN, your routine can activate I/O hooks on chosen files and/or open modes.
Using global I/O hooks instead of non-global I/O hooks offers one big advantage. With standard I/O hooks, you need to link in a source module that contains your IOHooks logic and explicitly call a routine or execute some code that references that logic. With global hooks, you can activate the hooks simply by linking in the source module containing your IOHooks logic. Your application can be running without hooks, and then when you link in the module with IOHooks (and your SYN_GLOBALHOOKS_OPEN() implementation), your hooks become fully active, without modifying the rest of your application. In other words, you don’t need to conditionalize your application to instantiate the hooks object; all you need to do is link it in.
To use global I/O hooks with Synergy .NET, your I/O hooks module must be included in your compile.
public IOHooks(channel)
Creates or replaces a new instance of the I/O event handler class associated with the specified channel opened to a file (n).
or
public IOHooks(Select.From)
Creates or replaces a new instance of the I/O event handler class associated with a file referenced in a From object.
or
public IOHooks(channel, premask, postmask)
Creates or replaces a new instance of the I/O event handler class associated with the specified channel opened to a file (n) and two bit masks (IOEventMask).
or
method IOHooks(Select.From, premask, postmask)
Creates or replaces a new instance of the I/O event handler class associated with a file referenced in a From object and two bit masks (IOEventMask).
public IOReset(premask, postmask), void
Dynamically changes hook methods that are to be used in an existing event handler instance. See the Discussion.
The pre-operation hook routines to be used. (IOEventMask)
The post-operation hook routines to be used. (IOEventMask)
public virtual method find_pre_operation_hook, void mismatch in optional key, n in optional in_rfa, a in optional keynum, n in flags, IOFlags proc . . . endmethod
If implemented, called before a FIND operation.
(optional) Key value passed to the FIND. (MISMATCH n)
(optional) Incoming RFA/GRFA via the RFA qualifier. (a)
(optional) Numeric KEYNUM value. (n)
Set to any IOFlags present. (IOFlags)
public virtual method read_pre_operation_hook, void mismatch in optional key, n in optional in_rfa, a in optional keynum, n in flags, IOFlags proc . . . endmethod
If implemented, called before a READ operation.
(optional) Key value passed to the READ. (MISMATCH n)
(optional) Incoming RFA/GRFA via the RFA qualifier. (a)
(optional) Numeric KEYNUM value. (n)
Set to any IOFlags present. (IOFlags)
public virtual method reads_pre_operation_hook, void in flags, IOFlags proc . . . endmethod
If implemented, called before a READS operation.
Set to any IOFlags present. (IOFlags)
public virtual method write_pre_operation_hook, void inout buffer, a in optional recnum, n in optional in_rfa, a in flags, IOFlags proc . . . endmethod
If implemented, called before a WRITE operation (or AlphaEnumerator.Current set property or AlphaEnumerator.SparseUpdate when using Select).
Record to be written. (a)
(optional) Alphanumeric record number to be written (non-ISAM) or not ^PASSED (ISAM). (n)
(optional) Incoming RFA/GRFA via the RFA qualifier. (a)
Set to any IOFlags present. (IOFlags)
public virtual method writes_pre_operation_hook, void inout buffer, a in flags, IOFlags proc . . . endmethod
If implemented, called before a WRITES operation.
Record to be written. (a)
Set to any IOFlags present. (IOFlags)
public virtual method store_pre_operation_hook, void inout buffer, a in flags, IOFlags proc . . . endmethod
If implemented, called before a STORE operation.
Record to be written. (a)
Set to any IOFlags present. (IOFlags)
public virtual method delete_pre_operation_hook, void proc . . . endmethod
If implemented, called before a DELETE operation (or the AlphaEnumerator.DeleteCurrent() or RestrictedAlphaEnumerator.DeleteCurrent() method when using Select).
public virtual method unlock_pre_operation_hook, void in optional rfa, a in flags, IOFlags proc . . . endmethod
If implemented, called before an UNLOCK or FREE operation. When UNLOCK is called, the rfa parameter will either contain the RFA that was specified on the UNLOCK statement or it won't be passed. When FREE is called, -1 is passed in the rfa parameter. Therefore, you can detect the difference between (UNLOCK CH), (UNLOCK CH, RFA), and XCALL FREE() by using the syntax
(^i(rfa(1:4)) == -1 .and. ^i(rfa(5:2)) == -1)
(optional) Incoming RFA/GRFA if ^MATCH:RFA and RFA qualifiers were specified. (a)
Set to any IOFlags present. (IOFlags)
public virtual method close_pre_operation_hook, void in flags, IOFlags proc . . . endmethod
If implemented, called before a CLOSE operation.
Set to any IOFlags present. (IOFlags)
Close_pre_operation_hook() is called only when a CLOSE statement is explicitly called (in traditional Synergy only) on normal program termination or an explicit STOP statement. Under abnormal termination (an unhandled exception or untrapped error), the close_pre_operation_hook() method will not be called. Therefore, if you want to rely on close_pre_operation_hook() always being called, but you haven’t guarded against unhandled exceptions (or untrapped errors), you can do one of the following:
- Add a TRY-CATCH block around your main routine to explicitly catch all unhandled exceptions.
- If you’re using ONERROR, add the $ERR_CATCH literal to your error list.
In either case, when an error is caught, you can explicitly close all opened channels. (If your CLOSE hook routine does I/O, be careful of the order in which you close your channels.)
The first example below uses TRY-CATCH to catch unhandled exceptions.
main ... proc try begin {Main routine code} end catch (e) ;Catch anything unexpected begin close 1 thru 1024 end finally begin close 1 thru 1024 end end
The following example uses $ERR_CATCH to handle untrapped errors.
main ... proc onerror ($ERR_CATCH) close_files {Main routine code} close_files, close 1 thru 1024 end
public virtual method find_post_operation_hook, void mismatch in optional key, n in optional out_rfa, a in optional keynum, n in flags, IOFlags inout error, int proc . . . endmethod
If implemented, called after a FIND operation.
(optional) Key value passed to the FIND. (MISMATCH n)
(optional) RFA/GRFA being returned via the GETRFA qualifier. (a)
(optional) Numeric KEYNUM value. (n)
Set to any IOFlags present. (IOFlags)
Set to a runtime error number that occurred during the FIND or else 0. (int)
public virtual method read_post_operation_hook, void inout buffer, a mismatch in optional key, n in optional out_rfa, a in optional keynum_or_table, n in flags, IOFlags inout error, int proc . . . endmethod
If implemented, called after a READ operation (or AlphaEnumerator.Current get property, RowEnumerator.Fill(), or RowEnumerator.indexer[]), or during an iteration of a FOREACH on a Select object).
If the reads_post_operation_hook method is implemented and part of the I/O hooks creation mask, it will override read_post_operation_hook. To enable read_post_operation_hook, you must disable or remove reads_post_operation_hook. |
Record that was read. (a)
(optional) Key value passed to the READ. (MISMATCH n)
(optional) RFA/GRFA being returned via the GETRFA: qualifier. (a)
(optional) Numeric KEYNUM value or the table index on a Join. (n)
Set to any IOFlags present. (IOFlags)
Set to a runtime error number that occurred during the READ or else 0. (int)
public virtual method reads_post_operation_hook, void inout buffer, a in optional out_rfa, a in flags, IOFlags inout error, int proc . . . endmethod
If implemented, called after a READS operation
If the reads_post_operation_hook method is implemented and part of the I/O hooks creation mask, it will override read_post_operation_hook. To enable read_post_operation_hook, you must disable or remove reads_post_operation_hook. |
Record that was read. (a)
(optional) RFA/GRFA being returned via the GETRFA: qualifier. (a)
Set to any IOFlags present. (IOFlags)
Set to a runtime error number that occurred during the READS or else 0. (int)
public virtual method write_post_operation_hook, void inout buffer, a in optional recnum, n in optional out_rfa, a in flags, IOFlags inout error, int proc . . . endmethod
If implemented, called after a WRITE operation (or AlphaEnumerator.Current set property or AlphaEnumerator.SparseUpdate when using Select).
Record that was written. (a)
(optional) Alphanumeric record number that was written (non-ISAM) or not ^PASSED (ISAM). (n)
(optional) RFA/GRFA being returned via the GETRFA: qualifier. (a)
Set to any IOFlags present. (IOFlags)
Set to a runtime error number that occurred during the WRITE or else 0. (int)
public virtual method writes_post_operation_hook, void inout buffer, a in optional stored_rfa, a in flags, IOFlags inout error, int proc . . . endmethod
If implemented, called after a WRITES operation.
Record that was written. (a)
(optional) To be loaded with an RFA or GRFA if GETRFA: qualifier was specified. (a)
Set to any IOFlags present. (IOFlags)
Set to a runtime error number that occurred during the WRITES or else 0. (int)
public virtual method store_post_operation_hook, void inout buffer, a in optional stored_rfa, a in flags, IOFlags inout error, int proc . . . endmethod
If implemented, called after a STORE operation.
Record that was written. (a)
(optional) To be loaded with an RFA or GRFA if GETRFA: qualifier was specified. (a)
Set to any IOFlags present. (IOFlags)
Set to a runtime error number that occurred during the STORE or else 0. (int)
public virtual method delete_post_operation_hook, void inout error, int proc . . . endmethod
If implemented, called after a DELETE operation (or the AlphaEnumerator.DeleteCurrent() or RestrictedAlphaEnumerator.DeleteCurrent() method when using Select).
Set to a runtime error number that occurred during the DELETE or else 0. (int)
public virtual method unlock_post_operation_hook, void in flags, IOFlags inout error, int proc . . . endmethod
If implemented, called after an UNLOCK or FREE operation.
Set to any IOFlags present. (IOFlags)
Set to either a runtime error number that occurred during the UNLOCK or FREE or else 0. (int)
protected override method Dispose, void in disposing, boolean proc . . . endmethod
(Synergy .NET only) If implemented, called before the dispose operation.
Returns true if Dispose() was called explicitly, or false if Dispose() was called implicitly as the object was being destroyed. (boolean)
The exact time when the finalizer executes is nondeterministic and occurs on a different thread. To know when the finalizer executed, you can implement a Dispose() method, which is called when either the hooked channel is closed or the IOHooks object is overwritten; you can also explicitly call IOHooks.Dispose(). You should implement the Dispose() method on .NET in situations where you would have created an overridden destructor in traditional Synergy (for example, to close a log file channel). |
public virtual method reconnect_hook, boolean in opflag, IOEventMask in error, int proc mreturn false endmethod
Called during connection recovery after the retry time interval has expired and prior to signaling $ERR_SRVCONRTY. If this method is not implemented, false is returned and the $ERR_SRVCONRTY error is generated. By implementing reconnect_hook() and simply returning true, you can choose a different behavior (for example, popping up a message box to query the user or issuing code to do some exception processing). See the Discussion below for more information.
Set to the IOEventMask flag, indicating the operation being retried. (IOEventMask)
Set to the error $ERR_SRVCONRTY. Also, IOHooks.Syserr will be set to the system error number. (int)
Because the runtime calls the hook routines, it controls which optional parameters are passed, based on the information available and the context. For hook methods that include optional parameters at the statement level (such as RFA:), you must use the ^PASSED function to test whether the parameter was passed before referencing it. If you don’t use ^PASSED on an optional argument and that argument isn’t passed, an “Argument missing” error ($ERR_ARGMIS) will occur.
The IOHooks constructors take either one or three parameters and have the following signatures:
IOHooks(channel) new IOHooks(channel, premask, postmask)
or
new IOHooks(Select.From) new IOHooks(Select.From, premask, postmask)
Only one IOHooks class object can be registered to each channel.
You will need to add code to create a new instance of your event handler class, passing the channel or the Select.From object on which the desired file is open. All event handler methods implemented in the class will be hooked and used during I/O operations.
It is not necessary to assign the resulting object to a handle unless you intend to use the IOReset() method. |
For example,
(void)new myhooks(ch) ;Hook a channel
or
(void)new myhooks(fobj) ;Hook a Select channel
or
hnd = new myhooks(ch) ;Save hnd for use later
When managing file channels using one of the Select class From constructors that specifies the filename, the channel number is not available, so you will need to use one of the IOHooks constructors that takes a Select.From object. |
When using the Select class, an iteration on the selection (or AlphaEnumerator.MoveNext() or RowEnumerator.MoveNext()) should be hooked using the READS pre- and post-hooks; a record update using the RowEnumerator.Current property should be hooked using the WRITE pre- and post-hooks; and a record deletion using the AlphaEnumerator.DeleteCurrent() method should be hooked using the DELETE pre- and post-hooks. No other I/O operations are hooked while a Select is occurring on a channel (except STORE, UNLOCK, and CLOSE, which can still be done on a Select channel).
A general IOHooks class can be used to control several different channels where different I/O operations are required. The premask and postmask values are used to specify those operations; each bit in the premask and postmask parameters represents an I/O hook method that has been overridden in a class that extends the IOHooks class. If you don’t want to hook all of the methods implemented in the class, you can pass the mask values for the pre- and post-operation flags to indicate which hook methods in the event handler class you do want to use. For example,
hnd = new myhooks(ch, IOEventMask.Read, IOEventMask.Read)
Bits that are set create pre- or post-event handler hooks for the I/O operations that the bits represent. Bits that are not set clear pre- or post-event handler hooks for the I/O operations that the bits represent.
To change which implemented hook methods are to be used during I/O operations, using an instance of the event handler class, you can explicitly call the IOReset() method with new mask values for pre- and post-operation flags. For example,
hnd.IOReset(IOEventMask.Reads, IOEventMask.Reads|IOEventMask.Store)
To unregister the event handler without closing the file, call IOReset() with the Clear mask values for both pre- and post-operation mask. For example,
hnd.IOReset(IOEventMask.Clear, IOEventMask.Clear)
To reset the mask values for both pre- and post-operations back to all methods implemented in the class, you can explicitly call IOReset() like this:
hnd.IOReset(IOEventMask.Auto, IOEventMask.Auto)
As the application executes, I/O operations on hooked channels execute your event handler methods immediately before the operation (pre-hook) and/or immediately after the operation (post-hook). When the registered channel is closed, the event handler object is unregistered automatically.
Parameters that are defined INOUT, such as the read or write buffer, can be modified by the hook methods, resulting in altered data read or written. This feature can be very useful (if you want to encrypt data transparently, for example), but it can also be very dangerous, because it hides what goes into and comes out of a file. |
To trap an $ERR_SRVCONRTY error during an I/O operation, you must implement your own hooks class that extends the IOExtensions.IOHooks class and overrides the reconnect_hook() method. Returning true from your reconnect_hook() method tells the I/O operation to reset the retry time interval and try again. This method will continue to be called until either the connection is re-established or your reconnect_hook() method returns false. For more information, see Using connection recovery (Windows).
See also
- Synergex.SynergyDE.IOExtensions.IOEventMask enumeration
- Synergex.SynergyDE.IOExtensions.IOFlags enumerator