Receiving and Handling Events
Parallels Service and all running virtual machines are constantly monitored for any changes in their state and status. When something important changes, an event of the corresponding type is triggered. A client program can receive the data describing the event and take appropriate action if needed. Events are received asynchronously (it is not possible to receive event-related data on-demand). All possible event types are defined in the PRL_EVENT_TYPE enumeration. Most of them are triggered automatically when the corresponding action takes place. Some event types are generated in response to client requests and are used to pass the data to the client. For example, the PET_DSP_EVT_HW_CONFIG_CHANGED event triggers when the host configuration changes, the PET_DSP_EVT_VM_STOPPED event triggers when one of the virtual machines is stopped, etc. On the other hand, an event of type PET_DSP_EVT_FOUND_LOST_VM_CONFIG is generated in response to the PrlSrv_StartSearchVms function call and is used to pass the information about unregistered virtual machines to the client (see Searching for Virtual Machines for more info).
In order to receive an event notification, a client program needs an event handler. An event handler (also called callback) is a function that you have to implement yourself. We've already discussed event handlers and provided code samples in the Asynchronous Functions section. If you haven't read it yet, please do so now. To subscribe to event notifications, you must register your event handler with the Service. This is accomplished using the PrlSrv_RegEventHandler function. Once this is done, the event handler (callback) function will be called automatically by the background thread every time it receives an event notification from the Service. The code inside the event handler can then handle the event according to the application logic.
The following describes the general steps involved in handling an event in a callback function:
- Determine if the notification received is an event (not a job, because event handlers are also called when an asynchronous job begins). This can be accomplished using the
PrlHandle_GetType function (determines the type of the handle received) and then checking if the handle is of type PHT_EVENT (not PHT_JOB ). - Determine the type of the event using the
PrlEvent_GetType function. Check the event type against the PRL_EVENT_TYPE enumeration. If it is relevant, continue to the next step. - If needed, you can use the
PrlEvent_GetIssuerType or PrlEvent_GetIssuerId function to find out what part of the system triggered the event. This could be a host, a virtual machine, an I/O service, or a Web service. These are defined in the PRL_EVENT_ISSUER_TYPE enumeration. - If, in order to precess the event, you need a server handle, you can obtain it by using the
PrlEvent_GetServer function. - A handle of type
PHT_EVENT received by the callback function may include event related data. The data is included in the event object as a list of handles of type PHT_EVENT_PARAMETER . You can use the PrlEvent_GetParamsCount function to determine the number of parameters the event object contains. Some of the events simply inform the client of a change and don't include any data. For example, the virtual machine state change events (started, stopped, suspended, etc.) indicate that a virtual machine has been started, stopped, suspended, and so forth. These events don't produce any data, so no event parameters are included in the event object. The type of the data and the number of parameters depends on the type of the event received. If you know that an event contains data by definition, continue to the next step, if not, skip it. - This step applies only to the events that contain data. Iterate through the event parameters calling the
PrlEvent_GetParam function in each iteration. This function obtains a handle of type PHT_EVENT_PARAMETER which contains the parameter data. Use the functions of the PHT_EVENT_PARAMETER handle to process the data as needed. In general, an event parameter contains the following:- Parameter name. To retrieve the name, use the
PrlEvtPrm_GetName function. This is an internal name and is, most likely, not of any interest to a client application developer. - Parameter data type. Depending on the event type, a parameter can be of any type defined in the
PRL_PARAM_FIELD_DATA_TYPE enumeration. To retrieve the parameter data type, use the PrlEvtPrm_GetType function. - Parameter value. Depending on the parameter data type, the value must be retrieved using an appropriate function from the
PHT_EVENT_PARAMETER handle. For example, a boolean value must be retrieved using the PrlEvtPrm_ToBoolean function, the string value must be retrieved using the PrlEvtPrm_ToString function, if a parameter contains a handle, it must be obtained using the PrlEvtPrm_ToHandle , etc. The meaning of the value is usually different for different event types. For the complete list of PHT_EVENT_PARAMETER functions, please see the Parallels C API Reference.
- When finished, release the received event handle. This step is necessary regardless of if you actually used the handle or not. Failure to release the handle will result in a memory leak.
The following is a simple event handler function that illustrates the implementation of the steps described above. We are not including an example of how to register an event handler here, please see the Asynchronous Functions section for that.
static PRL_RESULT simple_event_handler(PRL_HANDLE hEvent, PRL_VOID_PTR pUserData)
{
PRL_RESULT ret = PRL_ERR_UNINITIALIZED;
PRL_HANDLE_TYPE nHandleType;
// Get the type of the handle recevied.
PrlHandle_GetType(hEvent, &nHandleType);
// If this is a job, release the handle and exit.
// It is up to you if you want to handle jobs and events in
// the same callback function or if you want to do it in
// separate functions. You can have as many event handlers
// registered in your client program as needed.
if (nHandleType == PHT_JOB)
{
PrlHandle_Free(hEvent);
return 0;
}
// If it's not a job, then it is an event (PHT_EVENT).
// Get the type of the event received.
PRL_EVENT_TYPE eventType;
ret = PrlEvent_GetType(hEvent, &eventType);
// Check the type of the event received.
switch (eventType) {
case PET_DSP_EVT_VM_STARTED:
// Handle the event here...
printf("A virtual machine was started. \n");
break;
case PET_DSP_EVT_VM_STOPPED:
// Handle the event here...
printf("A virtual machine was stopped. \n");
break;
case PET_DSP_EVT_VM_CREATED:
// Handle the event here...
printf("A new virtual machine has been created. \n");
break;
case PET_DSP_EVT_VM_SUSPENDED:
// Handle the event here...
printf("A virtual machine has been suspended. \n");
break;
case PET_DSP_EVT_HW_CONFIG_CHANGED:
// Handle the event here...
printf("Parallels Service configuration has been modified. \n");
break;
default:
printf("Unhandled event: %d\n", eventType);
}
}
|