Click or drag to resize

Returning Errors from IVI-COM Drivers

The IVI specifications place special requirements on IVI-COM drivers with respect to error reporting. IVI defines a number of standard errors that drivers should use for common situations. These standard errors include COM HRESULT error code values as well as formatted error messages that need to be exposed via the COM error object.

Error Reporting Basics

Consider a situation where a method in an IVI-COM driver receives a bad input parameter value. The IVI specifications say that the driver is supposed to return the IVI-defined error HRESULTE_IVI_INVALID_VALUE. In addition, the driver method is supposed to compose an error message of the following form:

Invalid value (%1) for method %2, parameter %3.

The %1, %2, and %3 replacement tags are supposed to be filled in with the invalid value itself (for %1), the name of the method that generated the error (for %2) and the name of the parameter (for %3). A properly formatted error message might look something like the following:

Invalid value (-2) for method ConfigureRamp, parameter Time.

In addition to returning a valid HRESULT value and a properly formatted error message, IVI-COM drivers are required to populate the COM error object with error code, error message, and other information. This is typically done by first creating a COM error object using the Windows API function CreateErrorInfo. Then, the SetErrorInfo API is used to populate the error object with the desired information. The COM error object is then used by any COM compatible IDE to present the error information to the client program. In C# applications for instance, the .NET runtime interprets the contents of the COM error object and presents it as a .NET exception with the expected exception fields populated.

All of the tedious operations related to message formatting and COM error object management are handled by the Error Reporter object. This object is managed by Nimbus and contains error definitions for IVI-defined errors as well as for custom driver-defined errors. See the discussion of the Error Reporter object in the Nimbus Template Library section for detailed information.

Using the EnumToName Function for Better Error Reporting

Enums are very commonly used as parameters in IVI drivers. When the user passes in an unsupported enum value or when an error related to an enum value is otherwise generated, it is very helpful to the end user to have the name of the enum value reported. Since the driver implementation receives enum values as numeric constants and since the C++ language provides no mechanism for translating between an enum numeric constant and its symbolic name, the driver developer is often left with the choice of returning a poor error message containing only the enum numeric value or writing tedious code to explicitly translate numeric values to symbolic names. The following example code shows the kind of code that would be necessary to include the symbolic name of an enum member in a driver error message:

C++
// CoAcme4321.cpp
HRESULT Acme4321::ConfigureTrigger(Acme4321TriggerSourceEnum eSource)
{
  HRESULT hr = S_OK;

  // Only internal and external triggering are supported
  switch (eSource)
  {
    case Acme4321TriggerSourceInternal:
    case Acme4321TriggerSourceExternal:
      hr = InstrPrintCommand();
      break;
    case Acme4321TriggerSourceSoftware:
      hr = err.InvalidValue(_T("eSource"), _T("Acme4321TriggerSourceSoftware"));
      break;
    case Acme4321TriggerSourceTTL0:
      hr = err.InvalidValue(_T("eSource"), _T("Acme4321TriggerSourceTTL0"));
      break;
    case Acme4321TriggerSourceTTL1:
      hr = err.InvalidValue(_T("eSource"), _T("Acme4321TriggerSourceTTL1"));
      break;
    case Acme4321TriggerSourceTTL2:
      hr = err.InvalidValue(_T("eSource"), _T("Acme4321TriggerSourceTTL2"));
      break;
    case Acme4321TriggerSourceTTL3:
      hr = err.InvalidValue(_T("eSource"), _T("Acme4321TriggerSourceTTL3"));
      break;
    case Acme4321TriggerSourceRTSI1:
      hr = err.InvalidValue(_T("eSource"), _T("Acme4321TriggerSourceRTSI1"));
      break;
    case Acme4321TriggerSourceRTSI2:
      hr = err.InvalidValue(_T("eSource"), _T("Acme4321TriggerSourceRTSI2"));
      break;
    case Acme4321TriggerSourceRTSI3:
      hr = err.InvalidValue(_T("eSource"), _T("Acme4321TriggerSourceRTSI3"));
      break;
    case Acme4321TriggerSourceRTSI4:
      hr = err.InvalidValue(_T("eSource"), _T("Acme4321TriggerSourceRTSI4"));
      break;
    case Acme4321TriggerSourceRTSI5:
      hr = err.InvalidValue(_T("eSource"), _T("Acme4321TriggerSourceRTSI5"));
      break;
    case Acme4321TriggerSourceRTSI6:
      hr = err.InvalidValue(_T("eSource"), _T("Acme4321TriggerSourceRTSI6"));
      break;
    // ... more cases for all possible enum values
  };

  return hr;
}

The code above can be greatly simplified by the use of the Nimbus Template Library function EnumToName. This function automatically converts a driver enum value into its symbolic name. The code below demonstrates use of EnumToName.

For more information, see the description of EnumToName in the Nimbus Template Library.

C++
// CoAcme4321.cpp
HRESULT Acme4321::ConfigureTrigger(Acme4321TriggerSourceEnum eSource)
{
  HRESULT hr = S_OK;

  // Only internal and external triggering are supported
  switch (eSource)
  {
    case Acme4321TriggerSourceInternal:
    case Acme4321TriggerSourceExternal:
      hr = InstrPrintCommand();
      break;
    default:
      CString strEnum;
      EnumToName<Acme4321TriggerSourceEnum>(eSource, strEnum);

      hr = err.InvalidValue(_T("eSource"), strEnum);
      break;
  };

  return hr;
}
See Also

Download a complete CHM version of this documentation here.