t e m p o r a l 
 d o o r w a y 

Accessing Properties and Calling Methods Not Generated

 

Introduction

Sometimes the BCB 4 ActiveX header file generation process (part of Import ActiveX Control) fails to create entries for properties and methods which are documented for that control. In that situation, if you need those properties and methods, you need a way to access them. In all liklihood, you do not have the DISPID (which is the internal ID number of the property or method). This article describes how to deal with that situation.

Accessing A Property

The following code shows how to access a property whose name is the only thing known.

 int __fastcall get_Count(void)
 {
   DISPID dispid;
   HRESULT hr;

   WideString Member("Count"); // Name of the property
   BSTR szMember = (BSTR) Member; // BSTR Name of the property

   hr =
      GetIDsOfNames
      (
         IID_NULL,
         (LPOLESTR __RPC_FAR *)&szMember,
         1,
         0,
         &dispid
      );

   _TDispID _dispid(dispid); // Make the dispid usable by OlePropertyGet
   TAutoArgs<0> _args; // Create a TAutoArgs structure with room only for a return value - there are no parameters

   OlePropertyGet(_dispid, _args); // Ask the control for the property
   return _args.GetRetVariant(); // Extract the control return value from the passed args
 };

Calling A Method With One Parameter

This is very similar to accessing a property, except that the OlePropertyGet function is not used - it is replaced with OleFunction. The example also gets back a return value - the value is a "Ptr" object, used to access the IDispatch interface of the returned object.

SomethingPtr __fastcall Item(TVariant Index)
{
   DISPID dispid;
   HRESULT hr;

   WideString Member("Item");
   BSTR szMember = (BSTR) Member;

   hr =
      GetIDsOfNames
      (
         IID_NULL,
         (LPOLESTR __RPC_FAR *)&szMember,
         1,
         0,
         &dispid
      );

      _TDispID _dispid(dispid);
      TAutoArgs<1> _args;
      _args[1] = Index; // Parameter - note, this is 1-based, not 0-based

      OleFunction(_dispid, _args);
      return (Some_tlb::SomethingPtr*)(LPDISPATCH)_args.GetRetVariant();
  };

If you need to pass more arguments, simply set the TAutoArgs<1> to use the number of parameters to be passed.

Where This Goes

These functions should be placed, body and all, in the class definition to which they pertain in the _TLB.h file for the control. They will be inlined if your compiler options allow.

Performance Optimization

Calls to GetIDsOfNames are potentially expensive. Though I have not tested this, the following seems a likely optimization:

 int __fastcall get_Count(void)
 {
   static DISPID dispid = -1; // A DISPID of 0 may be valid
   HRESULT hr;

   WideString Member("Count"); // Name of the property
   BSTR szMember = (BSTR) Member; // BSTR Name of the property

   if (dispid == -1)
   {

      hr =
         GetIDsOfNames
         (
            IID_NULL,
            (LPOLESTR __RPC_FAR *)&szMember,
            1,
            0,
            &dispid
         );
   };

   _TDispID _dispid(dispid); // Make the dispid usable by OlePropertyGet
   TAutoArgs<0> _args; // Create a TAutoArgs structure with room only for a return value - there are no parameters

   OlePropertyGet(_dispid, _args); // Ask the control for the property
   return _args.GetRetVariant(); // Extract the control return value from the passed args
 };

Obviously, one could also add error checking, to deal with the possibility that the name is incorrect, or that something else goes wrong.

This code, however, is patterned after generated code and should be safe.

Conclusion

Controls which do not follow the standard expected by BCB for responding to interrogation by the IDE at import time can still be used to their fullest with this technique.

Copyright © 2004 by Mark Cashman (unless otherwise indicated), All Rights Reserved