VIX API Concepts

 

This chapter contains the following topics:

Objects and Handles

The Vix API is object-based. Most API functions either create objects or operate on the properties of existing objects.

Client applications reference Vix objects with handles. Handles are opaque identifiers (actually integers) that can be passed as parameters to functions. Handles are run-time only and are unique only within a client's address space.

Most functions in the C-language API take a handle as a parameter. Because a handle value represents an object to the API, this document uses the terms "handle" and "object" interchangeably.

There are several handle types, but a few of the key types are:

Reference Counting on Handles

Handles are reference counted, so you must call a "release" function on the handle when you are done using it. A handle remains valid until you call the Vix_ReleaseHandle() function. The Vix_ReleaseHandle() function releases any type of handle:

void Vix_ReleaseHandle(VixHandle handle);

For example, consider the following code to open and release a handle:

    Example 2-1.
VixHandle handle1;
handle1 = MyOpenVMWrapper(...various parameters...);
// handle1 is assigned a unique integer value.
 
// Now you can perform various operations on handle1.
 
Vix_ReleaseHandle(handle1);
handle1 = VIX_INVALID_HANDLE;
// handle1 has been released and should no longer be used.

Handle Independence

Generally, you can have any number of handles active at one time. Each handle represents a different object, and handles can be created and destroyed independently. Consider the following example:

    Example 2-2.
VixHandle handle1;
VixHandle handle2;
 
handle1 = MyOpenVMWrapper(... parameters for virtual machine 1...);
// handle1 is assigned a unique value - for example, 11.
 
handle2 = MyOpenVMWrapper(... parameters for virtual machine 2...);
// handle2 is assigned a unique value - for example, 12.
 
// Now you can perform various operations on handle1 or handle2.
 
Vix_ReleaseHandle(handle1);
handle1 = VIX_INVALID_HANDLE;
// handle1 has been released and should no longer be used.
 
// You can still perform operations on handle 2.
 
Vix_ReleaseHandle(handle2);
handle2 = VIX_INVALID_HANDLE;
// handle2 has been released and should no longer be used.

Deleting Handles

Clients must still call the release function even when the data stored in that handle has been deleted by another function. For example, to delete a virtual machine, you first call a delete function on the virtual machine's handle, and then you call the release function on the handle itself. This design avoids confusion over which functions do and do not release handles; only the release function can release a handle.

The client application is responsible for calling the release function on every handle. In the case of releasing a handle after deleting a virtual machine, the delete function updates the internal handle state, so most functions except the release function will recognize that the virtual machine has been deleted and will immediately return an error.

Opening Handles

If you open a handle to the same virtual machine twice, without releasing the first handle, the open function will return the same handle both times. The handle reference count is incremented each time the open function returns, so you must call the release function twice, once for each time you open the handle. Consider the following example:

    Example 2-3.
VixHandle handle1;
VixHandle handle2;
 
handle1 = MyOpenVMWrapper(...various parameters...);
// handle1 is assigned a unique value.
// handle1 has reference count 1.
 
// Now you can perform various operations on handle1.
 
handle2 = MyOpenVMWrapper(...the SAME parameters identifying the SAME virtual machine...);
// handle2 has the same value as handle1.
// handle1 has reference count 2.
ASSERT(handle1 == handle2);
 
Vix_ReleaseHandle(handle1);
// handle1 has reference count 1.
 
// The handle still can be safely used here.
 
Vix_ReleaseHandle(handle1);
handle1 = handle2 = VIX_INVALID_HANDLE;
// The handle has been released and should no longer be used.

In general, every function that returns a handle increments the handle's reference count. This means a client application should call the release function once for every time the handle has been returned by a Vix function.

Internal Handle References

Some handles keep an internal reference to another handle. For example, a device keeps a reference to its virtual machine, and a virtual machine keeps references to all its devices. As a result of these internal references, some handles might not be deleted when the client is done with them. This situation should not impact the client, because the internal reference counting is always correctly maintained. When an internal reference keeps a handle open, the client receives the same handle value when it opens the same object again after releasing its handle.

Error Codes

All synchronous Vix functions return an error code, which is an integer value defined by a global type. Asynchronous Vix functions (described below) report an error code when the function eventually completes.

Some Vix functions can return an internally defined error code value, such as an integer value that is not part of the public type. This is unusual; it indicates an internal error that cannot be translated to a public error code.

The error code type is VixError. It is defined in the public Vix header. Error codes are listed in the VIX API Reference Guide.

A Vix error is a 64-bit value. A value of VIX_OK indicates success, but if there is an error then several bit regions in the 64-bit value might be set. The least significant 16 bits are set to the error code described for Vix errors. More significant bit fields might be set to other values.

Error Code Functions

Vix provides the following defines for working with error codes:

Error Code Bit Masking

If you prefer to do your own bit masking on error codes, here are some examples:

    Example 2-7.
VixError err;
 
err = VixJob_GetError(jobHandle);
 
// CORRECT!
// This is legal. Success is always indicated with VIX_OK.
if (VIX_OK == err) {
   // Handle success case...
}
 
// CORRECT!
// This is legal. Success is always indicated with VIX_OK (all zeroes).
// Anything else is an error.
if (VIX_OK != err) {
   // Handle error case...
}
 
// WRONG!
// If an error code is not VIX_OK, several bit fields may be set.
if (VIX_E_FILE_NOT_FOUND == err) {
   // This will not work...
}
 
// CORRECT!
// If an error code is not VIX_OK, the least significant 16 bits
// will be the Vix error code.
if (VIX_E_FILE_NOT_FOUND == (err & 0xFFFF)) {
   // Handle error case...
}

Multithreading

The Vix library is intended for use by multi-threaded clients. Vix shared objects are managed by the Vix library to avoid conflicts between threads. Clients need only be responsible for protecting user-defined shared data.

Thread Safety of Handles

All Vix objects are thread safe so they may be used from several threads at the same time.

The Vix library handles locking of objects when they are modified. As a result, client code does not need to be concerned with the protection of objects when they are used from multiple threads.

Performance Implications

Locking shared objects implies some performance degradation in certain situations. For example, when you power on a virtual machine, the Vix library needs to modify both the virtual machine handle and the host's list of running virtual machines. If two clients power on two virtual machines at the same time, the Vix library needs to lock the list of running virtual machines on behalf of one client, causing a small delay for the second client.

Handle Properties

The Vix API defines a set of properties for each type of handle. A property is a typed name/value pair. A type name is a unique integer ID and the type may be one of the following:

Vix defines a different set of properties for each handle type. For example, a virtual machine handle has a string property that stores the file path name of the virtual machine, but a job handle does not. The complete set of handle properties for each type is defined in the VIX API Reference Guide.

If a property is defined for a particular handle type, all handles of that type always have some value for that property. For example, every virtual machine has a property that stores its file path name, whether the virtual machine is powered on or not, or whether it is stored on the local host or not.

Clients can modify handle properties only by invoking functions on the handle. For example, the function VixVM_PowerOn() modifies the VIX_PROPERTY_VM_IS_RUNNING of the virtual machine handle.

Properties are the main mechanism for reading both the persistent configuration state and the runtime status of handles. Properties allow Vix to be language independent and backward compatible.

GetProperties() Function

Vix provides one function that can get properties from any handle. This function has a varargs signature, which means you can use it to retrieve any number of properties by passing in sufficient parameters. The argument list must be terminated with a special property, VIX_PROPERTY_NONE.

VixError Vix_GetProperties(VixHandle handle, 
                           VixPropertyID firstPropertyID,
                           ...);

Here is an example of retrieving properties from a virtual machine handle:

    Example 2-8.
VixError err;
VixHandle handle1;
int vmPowerState;
char *vmVmxPathName;
 
handle1 = MyOpenVMWrapper(...various parameters...);
 
err = Vix_GetProperties(handle1, 
                        VIX_PROPERTY_VM_VMX_PATHNAME,
                        &vmVmxPathName,
                        VIX_PROPERTY_VM_POWER_STATE,
                        &vmPowerState,
                        VIX_PROPERTY_NONE);

Property Lists

Vix defines a special runtime data structure, the property list, as a convenient way to store properties and pass them as arguments. Property lists are runtime-only data structures, and they behave as Vix objects. You can reference a property list with a handle and you can pass the handle to functions such as Vix_GetProperties().

Here is an example of a callback function that retrieves a value from a property list.

    Example 2-9.
void MyFunction(VixHandle propertyListHandle)
{
   char *url = NULL;
 
   err = Vix_GetProperties(propertyListHandle, 
                           VIX_PROPERTY_VM_VMX_PATHNAME,
                           &url,
                           VIX_PROPERTY_NONE);
   if (VIX_OK != err) {
      // ...Handle the error...
   }
   // ... Use the url property, then release the buffer ...
   Vix_FreeBuffer(url);            
}

Asynchronous Operations and Job Objects

Many Vix functions are synchronous, which makes them easy to use. Some functions, however, such as powering on a virtual machine, are asynchronous. These asynchronous functions either implement time-consuming operations or interact with the persistent virtual machine state, which can be on a remote host.

All asynchronous Vix functions allocate and return a "job" handle, which is a Vix object that represents the execution of an asynchronous operation. A job handle can be used to signal when the asynchronous operation has completed, and also to retrieve the results of a completed asynchronous function. A job has a single "completed" state, which indicates when the job has finished. Additionally, a job may have several result properties that are set when the job has completed. These result properties contain information returned by a completed job. Different kinds of jobs have different return values.

A new job object is created for each active asynchronous call. For example, if you invoke three asynchronous calls, they all might complete at different times and they each have a different job object. Vix always creates a job handle for every asynchronous call. Even if you do not use the handle, you are still responsible for releasing it.

Monitoring a Job Object

Currently, a client application can use several mechanisms to detect when a job object has been signaled:

A typical asynchronous call looks similar to VixVM_Open(), and there are several common patterns shared by all asynchronous Vix calls. For reference, the following code shows the signature of VixVM_Open().

VixHandle VixVM_Open(VixHandle hostHandle,
                     const char *vmxFilePathName,
                     VixEventProc *callbackProc,
                     void *clientData);

This function creates a Vix job object and returns a VixHandle for this new job object. The caller is responsible for releasing this job object, even if the job object is not used for anything else.

Polling the Job Object for Completion

The job object tracks the status of an asynchronous operation. You can interrogate the completion status of a job object using the function VixJob_CheckCompletion(). This is a non-blocking function that returns a Boolean value representing the completion state of the asynchronous operation. The following example shows the use of VixJob_CheckCompletion() in a polling loop.

    Example 2-10.
Bool OpenVMWithPolling(const VixHandle hostHandle,
                       const char *vmName,
                       VixHandle *vmHandle)
{
   VixError err = VIX_OK;
   VixHandle jobHandle = VIX_INVALID_HANDLE;
   Bool completed = FALSE;
   
   if (!vmHandle) {
      return FALSE;
   }
   *vmHandle = VIX_INVALID_HANDLE;
 
   // Start asynchronous operation.
   jobHandle = VixVM_Open(hostHandle,
                          vmName,
                          NULL, // callbackProc,
                          NULL); // clientData
         
   // Poll the job object for completion of asynchronous operation.
   while ( !completed ) {
      sleep(1);
      
      err = VixJob_CheckCompletion(jobHandle, &completed);
      if (VIX_OK != err) {
         Vix_ReleaseHandle(jobHandle);
         return FALSE;
      }
   }
   
   err = Vix_GetProperties(jobHandle,
                           VIX_PROPERTY_JOB_RESULT_HANDLE,
                           vmHandle,
                           VIX_PROPERTY_NONE);
   if (VIX_OK != err) {
      Vix_ReleaseHandle(jobHandle);
      *vmHandle = VIX_INVALID_HANDLE;
      return FALSE;
   }
   
   Vix_ReleaseHandle(jobHandle);
   return TRUE;
   // Caller must release vmHandle.
}

Using the Job Object to Block Calls

The job object allows a client to block until an asynchronous call completes. This achieves the same result as if the asynchronous call were a synchronous call. Here is an example of how this can be done, using the VixJob_Wait() function.

    Example 2-11.
VixHandle hostHandle = VIX_INVALID_HANDLE;
VixHandle jobHandle = VIX_INVALID_HANDLE;
VixHandle vmHandle = VIX_INVALID_HANDLE;
 
jobHandle = VixHost_Connect(VIX_API_VERSION,
                            VIX_SERVICEPROVIDER_VMWARE_VI_SERVER,
                            "server2.example.com", // hostName
                            0, // hostPort
                            "root", // username
                            "secretpw", // password
                            0, // options
                            VIX_INVALID_HANDLE, // propertyListHandle
                            NULL, // callbackProc
                            NULL); // clientData
 
// Wait for completion of operation.
err = VixJob_Wait(jobHandle, VIX_PROPERTY_NONE);
if (VIX_OK != err) {
   goto abort;
}
 
// Release handle when done.
Vix_ReleaseHandle(jobHandle);
jobHandle = VIX_INVALID_HANDLE;

Retrieving Results from Job Object Properties

The job object can also be used to retrieve results from an asynchronous operation once the asynchronous operation has completed. You can get these properties by calling Vix_GetProperties() on the job handle. The following example shows how this is done.

    Example 2-12.
VixHandle hostHandle = VIX_INVALID_HANDLE;
VixHandle jobHandle = VIX_INVALID_HANDLE;
VixHandle vmHandle = VIX_INVALID_HANDLE;
 
jobHandle = VixHost_Connect(VIX_API_VERSION,
                            VIX_SERVICEPROVIDER_VMWARE_VI_SERVER,
                            "server2.example.com", // hostName
                            0, // hostPort
                            "root", // username
                            "secretpw", // password
                            0, // options
                            VIX_INVALID_HANDLE, // propertyListHandle
                            NULL, // callbackProc
                            NULL); // clientData
 
// Wait for completion of operation.
err = VixJob_Wait(jobHandle, VIX_PROPERTY_NONE);
if (VIX_OK != err) {
   goto abort;
}
 
// Collect desired result of operation.
err = Vix_GetProperties(jobHandle, 
                        VIX_PROPERTY_JOB_RESULT_HANDLE,
                        &hostHandle,
                        VIX_PROPERTY_NONE);
 
// Release handle when done.
Vix_ReleaseHandle(jobHandle); 
jobHandle = VIX_INVALID_HANDLE;

For convenience, you can also extract properties from the job object with the VixJob_Wait() function. The following example shows how this is done.

    Example 2-13.
// Start asynchronous operation.
jobHandle = VixVM_Open(hostHandle,
                       "[standard] WindowsXP/WindowsXP.vmx",
                       NULL, // callbackProc
                       NULL); // clientData
 
// Wait for completion of operation. Collect result handle.
err = VixJob_Wait(jobHandle,
                  VIX_PROPERTY_JOB_RESULT_HANDLE,
                  &vmHandle,
                  VIX_PROPERTY_NONE);
 
// Release handle when done.
Vix_ReleaseHandle(jobHandle); 
jobHandle = VIX_INVALID_HANDLE;

For simplicity, most of the examples in this document use the approach of calling VixJob_Wait() and requesting the desired properties at the same time. Some kinds of applications might increase responsiveness by using callback functions in a multithreaded client.

Callback Functions

All asynchronous Vix functions, such as VixVM_Open(), include a parameter for a callback procedure and a parameter that is passed to the callback procedure. These parameters are optional, so a caller can pass NULL for either. The prototype of this callback procedure parameter is:

typedef void (VixEventProc)(VixHandle handle,
                            VixEventType eventType,
                            VixHandle moreEventInfo,
                            void *clientData);

Using a Callback Function

If the caller provides a callback procedure, that procedure is registered with the job object and is invoked when the job object is signaled. For example, if a caller passes a callback procedure to VixVM_Open(), that callback procedure is invoked when the virtual machine has been opened. This situation could happen either before or after VixVM_Open() returns. It also can happen on any thread.

This mechanism allows Vix functions to run asynchronously. Asynchronous Vix functions should complete their work quickly and then return from the callback. Asynchronous functions must not block, and Vix applications should never call VixJob_Wait() while using a callback function.

When a callback procedure is invoked, it is passed the clientData parameter that was passed in the original call to the asynchronous function. This allows a callback procedure to associate some context with an outstanding asynchronous call.

    Example 2-14.
void myCallback(VixHandle jobHandle,
                VixEventType eventType,
                VixHandle moreEventInfo,
                void *clientData)
{
   VixError err;
   VixError asyncErr;
   VixHandle vmHandle = VIX_INVALID_HANDLE;
 
   /*
    * Ignore progress callbacks. Check only for final signal.
    */
   if (VIX_EVENTTYPE_JOB_COMPLETED != eventType) {
      return;
   }
    
   err = Vix_GetProperties(jobHandle,
                           VIX_PROPERTY_JOB_RESULT_HANDLE,
                           &vmHandle,
                           VIX_PROPERTY_JOB_RESULT_ERROR_CODE,
                           &asyncErr,
                           VIX_PROPERTY_NONE);
    
   if (VIX_OK != asyncErr) {
      /*
       * The open failed.
       */
   }
}
  
int main()
{
   VixError err = VIX_OK;
   VixHandle hostHandle = VIX_INVALID_HANDLE;
   VixHandle jobHandle = VIX_INVALID_HANDLE;
   VixHandle vmHandle = VIX_INVALID_HANDLE;
   char *contextData = "Hello, Vix";
 
   jobHandle = VixHost_Connect(VIX_API_VERSION,
                               VIX_SERVICEPROVIDER_VMWARE_VI_SERVER,
                               "server2.example.com", // hostName
                               0, // hostPort
                               "root", // username
                               "secretpw", // password
                               0, // options
                               VIX_INVALID_HANDLE, // propertyListHandle
                               NULL, // callbackProc
                               NULL); // clientData
 
   // Block for host connection to complete.
   err = VixJob_Wait(jobHandle, 
                     VIX_PROPERTY_JOB_RESULT_HANDLE,
                     &hostHandle,
                     VIX_PROPERTY_NONE);
   Vix_ReleaseHandle(jobHandle);
   jobHandle = VIX_INVALID_HANDLE;
   if (VIX_OK != err) {
      goto abort;
   }
 
   // Use callback function to capture completion of virtual machine open.
   jobHandle = VixVM_Open(hostHandle,
                          "[standard] WindowsXP/WindowsXP.vmx",
                          myCallback,
                          contextData);
   /*
    * Do something, like pump a message pump. 
    * Later, myCallback will be invoked on another thread.
    */ 
    
abort:
   Vix_ReleaseHandle(jobHandle);
   jobHandle = VIX_INVALID_HANDLE;
}

Callback Events

Note that a callback might be called several times, for several different reasons. For example, it might be called for periodic progress updates. The eventType parameter indicates why the callback is being called. The supported event types are:

Event Models

The Vix API provides the flexibility to handle events in different ways, depending on the needs of the client. The "event pump" mechanism allows you to process asynchronous operations in single-threaded clients. This section discusses both ways to manage asynchronous operations.

Multi-Threaded Event Model

The multi-threaded event model is the default model for Vix clients. Its use is described in detail in the section above called Callback Functions. This model is the easiest to use, if your client code is thread-safe.

Using this model, the Vix library creates worker threads as needed to process asynchronous operations in parallel. Callback functions are invoked on the worker threads under the control of the Vix library.

Single-Threaded Event Model

Using the single-threaded model, all asynchronous processing and all event reporting is deferred until the thread calls Vix_PumpEvents(). Each call to Vix_PumpEvents() does more work on an asynchronous operation. At appropriate times during the operation, control is transferred to the callback routine, if one was specified in the call to the asynchronous function that began the operation.

The single-threaded event model is useful for clients that are not thread safe, or that depend on libraries that are not thread-safe. All processing is done in a single thread. You have to write extra code for a polling loop, but you need not be concerned with synchronizing multiple threads.

Using the Event Pump

To use the event pump:

  1. Connect to the host with a flag indicating that events are to be triggered by Vix_PumpEvents() and received by a callback function on the same thread. Otherwise, events are reported asynchronously and callback functions are invoked on a pool of Vix worker threads.

    The flag is a value of VIX_HOSTOPTION_USE_EVENT_PUMP passed in the options parameter to the VixHost_Connect() function.

    Example 2-15.
    VixHandle hostHandle = VIX_INVALID_HANDLE;
    VixHandle jobHandle = VIX_INVALID_HANDLE;
    VixError err;
     
    jobHandle = VixHost_Connect(VIX_API_VERSION,
                                VIX_SERVICEPROVIDER_VMWARE_WORKSTATION,
                                NULL, // hostName
                                0, // hostPort
                                NULL, // userName
                                NULL, // password,
                                VIX_HOSTOPTION_USE_EVENT_PUMP, // options
                                VIX_INVALID_HANDLE, // propertyListHandle
                                NULL, // callbackProc
                                NULL); // clientData
    err = VixJob_Wait(jobHandle,
                      VIX_PROPERTY_JOB_RESULT_HANDLE,
                      &hostHandle,
                      VIX_PROPERTY_NONE);
    if (VIX_OK != err) {
       // Handle the error...
       goto abort;
    }
  2. Call Vix_PumpEvents() at appropriate times to advance processing of the asynchronous operation.
    Vix_PumpEvents(hostHandle, VIX_PUMPEVENTOPTION_NONE);

    If your application is multithreaded, you can call this function from a separate thread. You can never call VixJob_Wait() on the same thread that also calls Vix_PumpEvents(). However, you can call Vix_PumpEvents() on one thread and VixJob_Wait() on another thread.

  3. Check the results of the asynchronous operation, either by polling or by callback. The polling method is explained in Polling for Completion in a Single-Threaded Client. The callback method is explained in Using a Callback Function in a Single-Threaded Client.

Polling for Completion in a Single-Threaded Client

Single-threaded clients must share the thread between the main logic of the program and the processing that takes place as a result of calling an asynchronous function. In a single-threaded client, the processing can only take place during a call to Vix_PumpEvents().

That fact implies that the single-threaded client cannot call VixJob_Wait(). In a multi-threaded client, a call to VixJob_Wait() returns when the asynchronous operation is completed by another thread. However, in a single-threaded client, VixJob_Wait() never returns because there is no processing happening on any thread during the call to VixJob_Wait().

One solution is to poll for completion using VixJob_CheckCompletion() instead of calling VixJob_Wait(). You alternate pumping with polling, as in the following example.

    Example 2-16.
// Function to poll for completion.
// Returns TRUE if successful; otherwise returns FALSE.
Bool CheckCompletion(VixHandle hostHandle,
                     VixHandle jobHandle,
                     int timeOut)
{
   Bool completed = FALSE;
   VixError err = VIX_OK;
   int secondCount = 0;
 
   while (!completed && secondCount < timeOut) {
      Vix_PumpEvents(hostHandle, VIX_PUMPEVENTOPTION_NONE);
      
      err = VixJob_CheckCompletion(jobHandle, &completed);
      if (VIX_OK != err) {
         return FALSE;
      }
      
      secondCount++;
      sleep(1);
   }
   
   if ((!completed) && (secondCount == ONE_MINUTE)) {
      return FALSE;
   }
   
   return TRUE;
}
 
// Part of virtual machine processing below.
VixError err = VIX_OK;
VixHandle jobHandle = VIX_INVALID_HANDLE;
VixPowerState powerState = 0;
 
// Suspend the virtual machine.
jobHandle = VixVM_Suspend(vmHandle,
                          0, // powerOnOptions,
                          VIX_INVALID_HANDLE, // propertyListHandle,
                          NULL, // callbackProc,
                          NULL); // clientData
 
// The powerState may be VIX_POWERSTATE_SUSPENDING for a few seconds.
// Wait for completion.
if (CheckCompletion(hostHandle, jobHandle, 30)) {
   // Now the powerState should be ready.
   err = Vix_GetProperties(vmHandle,
                           VIX_PROPERTY_VM_POWER_STATE,
                           &powerState,
                           VIX_PROPERTY_NONE);
   if (VIX_OK != err) {
      // Handle the error...
      goto abort;
   }
} // CheckCompletion

Using a Callback Function in a Single-Threaded Client

Single-threaded clients also have the option to use callback functions that handle completion. Because the callback has to share the single thread, it cannot happen asynchronously. As with the polling method of completion described in Polling for Completion in a Single-Threaded Client, the client cannot call VixJob_Wait().

In the single-threaded client, processing takes place only during calls to Vix_PumpEvents(). Each call does more work on the operation. If a callback was specified in the call to the asynchronous function that began the operation, the Vix library invokes the callback function during one of the calls to Vix_PumpEvents().

The code to use this method looks like the following example. The main function initiates a host connection operation (specifying a callback function) and then enters a loop to pump events. When the operation has completed, the callback function returns a handle to the host (or an invalid value in case of failure).

    Example 2-17.
Bool handleCallbackDone = FALSE;
 
void handleCallback(VixHandle jobHandle,
                    VixEventType eventType,
                    VixHandle moreEventInfo,
                    void *clientData)
{
   /*
    * This function is invoked on completion of an asynchronous operation.
    * If the operation succeeded, this function returns a handle
    * that resulted from the asynchronous operation.
    */
   VixError err;
   VixError asyncErr;
   VixHandle resultHandle = VIX_INVALID_HANDLE;
 
   /*
    * Ignore progress callbacks. Check only for final signal.
   */
   if (VIX_EVENTTYPE_JOB_COMPLETED != eventType) {
      return;
   }
 
   err = Vix_GetProperties(jobHandle,
                           VIX_PROPERTY_JOB_RESULT_HANDLE,
                           &resultHandle,
                           VIX_PROPERTY_JOB_RESULT_ERROR_CODE,
                           &asyncErr,
                           VIX_PROPERTY_NONE);
   if (VIX_OK != err) {
      // Failed to get properties. Bail.
      *clientData = VIX_INVALID_HANDLE;
      handleCallbackDone = TRUE;
      return;
   }
 
   if (VIX_OK == asyncErr) {
      *clientData = (void) resultHandle;
   } else {
      // The asynchronous operation failed. Bail.
      *clientData = VIX_INVALID_HANDLE;
   }
   
   handleCallbackDone = TRUE;
} // end handleCallback
 
int main()
{
   VixHandle hostHandle = VIX_INVALID_HANDLE;
   VixHandle jobHandle = VIX_INVALID_HANDLE;
   char *contextData = "Hello, Vix";
 
   jobHandle = VixHost_Connect(VIX_API_VERSION,
                               VIX_SERVICEPROVIDER_VMWARE_VI_SERVER,
                               "server2.example.com", // hostName
                               0, // hostPort
                               "root", // username
                               "secretpw", // password
                               VIX_HOSTOPTION_USE_EVENT_PUMP, // options
                               VIX_INVALID_HANDLE, // propertyListHandle
                               handleCallback,
                               (void *) &hostHandle);
 
   // Release handle, if not using it.
   Vix_ReleaseHandle(jobHandle);
   jobHandle = VIX_INVALID_HANDLE;
 
   // Pump events and wait for callback.
   handleCallbackDone = FALSE;
   while (!handleCallbackDone) {
      Vix_PumpEvents();
   }
   
   if (VIX_INVALID_HANDLE == hostHandle) {
      printf(":-(\n");
   } else {
      printf(":-)\n");
   }
} // end main

Local and Remote Host Handles

Vix defines a "host" object (identified by a handle) that allows access to virtual machines. Typically, a Vix application begins by getting a handle to a host object, and then passing that host handle to other functions. A host handle can represent either a local or remote machine.

Vix provides a single API function to open any host. To open an arbitrary host, the function requires a machine name and password authentication.

When you have a host object, you pass it as a parameter to several Vix functions, such as the function that opens a handle to a virtual machine. When the client completes all operations, it releases the host handle with a call to VixHost_Disconect(). All handles and all states associated with that host are erased by the call.