INFO: Passing ADO Objects Between Visual Basic and Visual C++ (190935)



The information in this article applies to:

  • ActiveX Data Objects (ADO) 1.0
  • ActiveX Data Objects (ADO) 1.5
  • ActiveX Data Objects (ADO) 2.0
  • ActiveX Data Objects (ADO) 2.1

This article was previously published under Q190935

SUMMARY

A common customer scenario is wanting to pass a Component Object Model (COM) object between a Visual C++ COM Server and a Visual Basic client. One example of this is passing an ActiveX Data Objects (ADO) recordset opened inside a C++-based Active Template Library (ATL) server to a Visual Basic client. Incorrect syntax within the Visual Basic client could return errors, such as the following:
0x80040002 (-2147467262) "No Such Interface Supported."

MORE INFORMATION

There are basically two kinds of procedures in Visual Basic, Function(s) and Sub(programs). Either type of procedure, with arguments, can be called with one of the following examples:

  • FuncOrSubName Arg1, Arg2, Arg3 'Parentheses MUST be omitted!
  • Call FuncOrSubName (Arg1, Arg2, Arg3) 'Parentheses required!
The default calling convention in Visual Basic is By Reference (ByRef). The By Value (ByVal keyword can be used to override the default. When using the Call keyword, the ByVal and ByRef keywords can only be used when calling a DLL procedure.

Using either of the two preceding techniques ensures that the object you intended to pass is what is actually passed. However, it is possible to pass inadvertently something that you did not intend. Enclosing the parameter list in parentheses without the Call keyword, which a Visual C programmer might do by instinct, causes Visual Basic to pass its arguments ByVal. This is the scenario we described in the following section.

FuncOrSubName (Arg1, Arg2, Arg3) 'Never use this syntax!

This is old BASIC syntax, supported but not recommended. If one of these arguments was, for example, an ADO Recordset, what would actually be passed to the function was the default value of the recordset, which in this case is the Fields Collection.

For example, assume that an ATL server has been set up, with a method called SetInterfacePtr that takes an LPUNKNOWN as a parameter as shown in the following code:
   STDMETHODIMP CComDispatch::SetInterfacePtr(LPUNKNOWN* lpDisp)
   {
      _RecordsetPtr pRS;
      IDispatch* pStream = NULL;
      LPDISPATCH tempPtr;

      HRESULT hr = (*lpDisp)->QueryInterface(IID_IDispatch,
                                                        (void**)& pStream);
      hr = pStream->QueryInterface(__uuidof(_RecordsetPtr), (void** &pRS);

   }
				
Now use the following Visual Basic code to create an ADO Recordset and pass it to the ATL Server:
   Dim cmdByRoyalty As ADODB.Recordset
   Set cmdByRoyalty = New ADODB.Recordset

   Dim MyObj As ATLADOSERVERLib.ComDispatch
   Set MyObj = New ATLADOSERVERLib.ComDispatch

   MyObj.SetInterfacePtr (cmdByRoyalty) ' By value or reference?
				
STDMETHODIMP CComDispatch::SetInterfacePtr(LPUNKNOWN* lpDisp) is expecting a pointer and in order to pass a pointer with Visual Basic you need to pass the parameter by reference.

Within the SetInterfacePtr method, the first QueryInterface for the IDispatch pointer, succeeds in this case because the object that is passed does have an IDispatch interface. However, the second QueryInterface returns
0x80040002 Interface Not Supported
because the object passed to SetInterfacePtr is not an actual Recordset object. Instead the object is an instance of the ADO Fields Collection, which does not support the Recordset interface. The following code displays an example:
   call MyObj.SetInterfacePtr (cmdByRoyalty)
   MyObj.SetInterfacePtr cmdByRoyalty
				
The KB sample Q186387 "Ado2atl.exe Returns ADO Interfaces from COM" has a complete working sample of passing and ADO interface from a ATL COM object to VB.

Modification Type:MinorLast Reviewed:3/2/2005
Keywords:kbDSupport kbinfo KB190935