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.