| Customize Help

Event handling in MIL with hook-handler functions



In MIL, there are functions that take a callback function and set it up as the event handler for a specific type of event (hooks it to the event). When the event then happens, the callback function is executed. The MIL functions capable of attaching (hooking) a callback function to an event are referred to as hook-type functions (for example, the MappHookFunction() function), and the callback functions are referred to as hook-handler functions.

MIL hook-type functions

MIL hook-type functions allow you to execute a user-defined hook-handler function upon the occurrence of a specified event. These hook-type functions can associate a variety of events with specified hook-handler functions. The supported types of events differ based on the module and the particular hook-type function. For instance, the MappHookFunction() function allows you to, among other things, trap errors (M_ERROR_...) that occur in an application and execute a hook-handler function whenever one of these errors occur; whereas, the MdigHookFunction() allows you to execute a hook-handler function, for example, at the end of a grab.

Within a hook-handler function, you can retrieve information about the event that caused the hook-handler function to be called, using a hook-information (M...GetHookInfo()) function.

The various MIL hook-type functions and their corresponding hook-information functions typically conform to the following naming scheme:

There are, however, a few hook-type functions that do not conform to this standard (for example, MdigFocus() and MdigProcess()).

Depending on the hook-type function, you can attach multiple hook-handler functions to an event. To do so, call the hook-type function multiple times, once for each hook-handler function that you want to hook to the event. MIL automatically chains and keeps an internal list of all the associated hook-handling functions. When a function is hooked, this new function is added to the end of the list. The hook-handler functions will be executed in the order in which they were attached to the event. Note that MdigFocus() and MdigProcess() cannot attach multiple hook-handler functions.

Each MIL hook-type function requires that the hook-handler function that you specify has a particular signature (or prototype); it must have the expected parameters and a particular return type. If the hook-handler function does not conform to the signature that the hook-type function expects, your application won't successfully compile. Refer to the hook-type function's reference for a description of the particular signature required.

Hook-handler functions are typically executed asynchronously on a separate thread when the event deals with hardware (for example, Mdig...() and Msys...() events), or a remote computer. If the event is software related, the hook-handler function will generally be executed synchronously on the same thread as the one on which the event was triggered. The exception to this is the MbufHookFunction() function, which will have its hook-handler function executed on a separate thread if the buffer causing the event (M_MODIFIED_BUFFER) is modified by a function that executes asynchronously (for example, MimArith() executed on a processing FPGA); otherwise, the hook-handler function is executed on the same thread as the one on which the event was triggered.

Note that although there is a small queue to permit a certain amount of overlap, hook-handler functions executed asynchronously should limit their execution length such that the function is executed faster than the time it takes to generate another instance of the associated event. As a general guideline, hook-handler functions should have a shorter execution period than the average period required to generate an event. Typically, the hook-handler function performs the minimum number of operations required, and if necessary, performs longer processes by launching a utility function in another thread. This is more important when dealing with hardware-related event handling but should be held as a general rule for all event handling.

Events and user data

Since hook-handler functions must have a very specific signature (prototype), and you might need information from your MIL application within your hook-handler function, all hook-handler functions have a parameter which takes a pointer to a variable or data structure. You can use the reference to access and/or modify the data from within the hook-handler function, upon its execution. This also allows the main application block to receive user-data from the hook-handler functions.

The reference to the variable/user data structure is received in the hook-handler function as a void pointer. As such, to access the data in the hook-handler function, the pointer to the data must be cast back to the appropriate data type. If you need to pass the hook-handler function more than one piece of data, define a structure or class that can store the set of required data. For example, you can use a data structure to pass the identifier of both the source and destination image buffers on which you want the hook function to operate.

/* Example of a data structure used to pass data to the hook-handler function. */
typedef struct
{
    MIL_ID  MilDigitizer;
    MIL_ID  MilImageDisp;
    MIL_INT ProcessedImageCount;
} HookDataStruct;

Since you are passing the user data with a pointer, the user data is accessible and modifiable in both the primary application block (in which the hook-type function was called), and in the hook-handler function. Note that if the hook-handler function is executed asynchronously, care must be taken to properly synchronize the primary application block and hook-handler functions so that the data is not being modified by both functions simultaneously. For more information regarding synchronization, see the Multi-threading section of Chapter 51: Multi-processing, multi-core, and multi-threading.

Unhooking hook-handler functions

If the hook-handler function was hooked using a M...HookFunction() function, you can unhook the hook-handler function from its associated event. To do so, you must call the appropriate M...HookFunction() with the same parameter values that were passed in the previous call, but you must combine the hook-type (event) with M_UNHOOK so that the event is detached (unhooked) from the hook-handler function as shown below:

/*Unhooking the hook-handler function*/
MbufHookFunction(MilImage, M_MODIFIED_BUFFER + M_UNHOOK,
    UserHookFunction, &UserHookData);

MIL hook information functions

The hook information functions (M...GetHookInfo()) allow you to retrieve information about the event that caused the hook-handler function to be called. You can use hook information functions, for example, to determine the identifier of the buffer that triggered the event (MdigGetHookInfo() with M_MODIFIED_BUFFER + M_BUFFER_ID) so that you can perform some operation on that buffer, or to determine if a grabbed frame is corrupt (MdigGetHookInfo() with M_CORRUPTED_FRAME). The M...GetHookInfo() types of functions can only be called within the scope of a hook-handler function. Each hook information function can return information related to the event to which the hook-handler function was hooked; although, some types of information are available regardless of the event triggering the hook-handler function.

Some events occur under multiple circumstances. For instance, if you hook a function to an I/O change event using MsysHookFunction() with M_IO_CHANGE, the hook-handler function will be invoked when any I/O signal changes state. To determine the particular I/O signal that caused the event, you can call MsysGetHookInfo() using M_IO_INTERRUPT_SOURCE. Based on the result, you can implement a switch statement to execute different code based on the particular I/O signal that caused the event. Another example of an event occurring under multiple circumstances is an M_GRAPHIC_MODIFIED type of event. This type of event is triggered whenever any graphic is modified in a 2D graphics list (GraListId). You could determine the modification performed on the graphic using MgraGetHookInfo() with M_GRAPHIC_CONTROL_TYPE, and implement a switch statement to execute different code based on the particular modification made to the 2D graphics list.

The following code snippet is an example of a function hooked to an M_GRAB_END event using MdigHookFunction(). The hook-handler function receives user data and makes use of a hook information function:

/*User's processing (hook-handler) function*/
MIL_INT MFTYPE UserHookFunction(MIL_INT HookType, MIL_ID HookId, void* HookDataPtr)
{
    /*Void pointer to structure being cast to the structures type*/
    HookDataStruct *UserHookDataPtr = (HookDataStruct *)HookDataPtr;
    MIL_ID ModifiedBufferId;

    /* Retrieve the MIL_ID of the modified buffer. */
    MbufGetHookInfo(HookId, M_MODIFIED_BUFFER + M_BUFFER_ID, &ModifiedBufferId);

    /* Increment the frame counter. */
    UserHookDataPtr->ProcessedImageCount++;

    /* Perform an operation on the modified buffer. */
    MimArith(ModifiedBufferId, M_NULL, UserHookDataPtr->MilImageDisp, M_NOT);

    return 0;
}

Since the hook-handler function receives the reference to the user-data as a void pointer, the pointer is cast back to the appropriate data type (in this case, the HookDataStruct structure defined earlier in this section) so that the structure's various elements can be accessed. To identify the buffer that was modified (written into by the grab), causing the hook-handler function to be executed, a call to MdigGetHookInfo() with M_MODIFIED_BUFFER + M_BUFFER_ID is made. The hook-handler function then performs an operation on the buffer and stores the results in the image buffer specified by the user-defined structure. Since the main application block also has access to the buffer, the results of the operation are accessible in the main application block.