Monthly Archives: January 2016

When C++ Builder 6 fails to properly import COM Objects

In Borland C++ Builder 6, the code generated to import COM objects slightly differs from the one generated by previous versions of C++ Builder.
Specifically, the VARIANT date type is used instead of TVariant. Giving up its TVariant “smart wrapper” was a good move from Borland, but it broke backward compatibility with output parameters of some COM methods, which don’t receive a value anymore.

For example, below is the code generated by C++ Builer 4 for the FarPoint TfpSpread COM object (from file FPSpreadADO_TLB.h) :

  VARIANT_BOOL __fastcall GetText(long Col, long Row, TVariant* Var)
 {
    _TDispID _dispid(/* GetText */ DISPID(252));
    TAutoArgs<3> _args;
    _args[1] = Col /*[VT_I4:0]*/;
    _args[2] = Row /*[VT_I4:0]*/;
    _args[3] = Var /*[VT_VARIANT:1]*/;
    OleFunction(_dispid, _args);
    return _args.GetRetVariant();
  }

When generated using C++ Builder 6, the code becomes :

  VARIANT_BOOL __fastcall GetText(long Col, long Row, VARIANT* Var)
 {
    _TDispID _dispid(/* GetText */ DISPID(252));
    TAutoArgs<3> _args;
    _args[1] = Col /*[VT_I4:0]*/;
    _args[2] = Row /*[VT_I4:0]*/;
    _args[3] = Var /*[VT_VARIANT:1]*/;
    OleFunction(_dispid, _args);
    return _args.GetRetVariant();
  }

Notice the Var parameter, whose type was changed from TVariant* to VARIANT* : that’s the source of the problem.

The issue comes from the TAutoArgs class, more specifically from its array of Variant.
The Variant assignment operator (=) behaves differently depending if its RHS is a TVariant* or a VARIANT* :

  1. Variant = TVariant* : The Variant is initialized with a reference to the TVariant* object (vt = VT_BYREF|VT_VARIANT, and pvarVal receives a pointer to the TVariant*
  2. Variant = VARIANT* : the Variant ‘=’ operator assumes the VARIANT* is a VT_BOOL. Changes to the Variant are not reflected on the VARIANT*

To fix the problem, one must explicitly set the Variant as a reference to the VARIANT*. For example, the previous example must be changed to :

  VARIANT_BOOL __fastcall GetText(long Col, long Row, VARIANT* Var)
 {
    _TDispID _dispid(/* GetText */ DISPID(252));
    TAutoArgs<3> _args;
    _args[1] = Col /*[VT_I4:0]*/;
    _args[2] = Row /*[VT_I4:0]*/;
    _args[3].vt = VT_BYREF|VT_VARIANT /*[VT_VARIANT:1]*/;
    _args[3].pvarVal = Var;
    OleFunction(_dispid, _args);
    return _args.GetRetVariant();
  }