Introduction

Recently I did a project
on online banking at NIIT, Bangalore. The project was coded mostly using VB with a few ATL
components thrown in, if only to teach us programming distributed applications. One middle tier component that
I programmed was built with ATL and uses ADO to query the backend (SQL Server). Parts of that
code appears here.

I assume the reader knows or at least has a fair idea about COM
programming using ATL as well as ADO programming with VB.

What is ADO?

ADO stands for ActiveX Data Objects. ADO provides an object-oriented
programming interface for accessing a data source using the OLEDB data provider.
It is the succesor to DAO and RDO object models and combines the best features
DAO and RDO.

Programming OLEDB in C++ is easy. However, for languages like Visual Basic, that do not support
pointers and other C++ features, implementing OLEDB is difficult.

This is where ADO really shines. ADO is an high level interface to OLEDB that is based on COM
interfaces. Thus any application that supports COM can implement ADO.

ADO Features

Access to all types of data - Various data sources include e-mail, text files, RDBMSs, ISAM/VSAM databases and all ODBC data sources.

Supports free threading - ADO supports multiple client connections through multiple threads in such a way that these threads do not interfere with each other.

Supports asynchronous queries - This basically means that after an SQL query is submitted to the database server, the control returns immediately to the calling application, allowing the user to continue working while the query is being processed. On completion of the query, the results are sent to the client.

Supports client-side and server-side cursors - Cursor is a mechanism that allows access and navigation of data in a recordset. They are implemented as client-side or server-side. Traditionally, frequently updated recordsets are implemented as server-side while read-only recordsets are implemented as client-side.

Supports disconnected recordsets - After a recordset is returned on execution of a query, it is stored as a client-side cursor and the active connection is closed. After changes have been commited to the recordset, the connection is reestablished and all updates are sent in a batch to the data store. This helps in reducing network traffic to a great extent.

Supports Commands as connection methods - An unique feature of ADO is that when a command is executed, a connection is first established internally before that command gets submitted for execution. Compare this to traditional object models like DAO/RDO where a connection has to be established explicitly before a command can be submitted.

ADO Architecture

In the ADO model, we'll be using three main types of objects-

Connection

Command

Recordset

The Connection object sets up a connection to the data source. First, the data source name, its location, user
ID, password etc is stored in a ConnectionString object, which is passed
to the Connection object.to establish a connection to the
data source.

The Command object is used to execute SQL commands, queries and stored
procedures.

When a query is executed, it returns results that are
stored in the Recordset object. Data in a recordset can be manipulated and then
updated to the database.

Using ADO

First, we'll be building an ATL DLL component. This component has a
method that takes one input parameter (customer ID in the project) and returns a reference
to the
corresponding Recordset object to the VB client. The client then
displays the data in a form.

To create the DLL, use the ATL COM AppWizard to generate
the framework for the application. Name the project FindCust
and choose the server type as Dynamic Link
Library. Also choose the option to
support MFC library.

Insert a New ATL Object of type
Simple Object to the project. Use the name Search
in the Short Name textbox of the ATL Object
Wizard Properties and click OK to add the object.

In classview, right click the interface name and add a
method. Name the method SearchCust and type the following in
the Parameters
textbox :

[in] BSTR bstrcustid,[out,retval] _Recordset **ptr

Click the OK button to add the method.

Since the SearchCust method returns a reference to a
Recordset object, we need to import the ADO library. To do this, open the file,
StdAfx.h and add the following code :

This step will help the Visual C++ compiler to
understand the ADO objects defined in the type library,
MSADO15.DLL. The rename_namespace
function renames the namespace into which the DLL has been imported to the
specified name. The rename option has been used to rename
the EOF keyword to EndOfFile, because EOF is already defined in the standard header
files.

Also the .idl file contains the method
SearchCust which returns a reference to a Recordset
object. To make the MIDL compiler understand the ADO objects, import the type
library in the .idl file using the importlib statement in the library
section (after importlib "stdole2.tlb") using:

importlib("C:\Program Files\Common Files\System\ADO\MSADO15.DLL");

Also move the interface definition in the .idl file to just
after the importlib statement to make the MIDL
compiler.understand ADO objects.

To do that, cut the interface definition block and paste it after the
imporlib statement that was
added. My interface definition block looks
like:

The Open function takes four parameters. The first one is the
connection string, which contains the name of the provider and name of SQL
Server for connection. The second and third parameters are the user name and the
password to establish the connection. The fourth parameter is the type of
cursor to be used. The _T macro ensures UNICODE compatibility of the
strings.

Note that your connection string will be different than
the one that I'm using here. You may need to use other providers
as well as connect to a different datasource. Obviously, your username and
password will be different. The Initial Catalog
parameter defines the database table to be used. For SQL
Server, the OLEDB provider is SQLOLEDB. You can use
MSDASQL or Microsoft Jet.OLEDB provider
to connect to a MS Access database (.mdb) as well.

Execute the SQL commands

To pass the SQL command, create a command object pointer by
passing the CLSID of the Command object.

_CommandPtr cmd(__uuidof(Command));

Set the ActiveConnection property of the Command
object to the open Connection object pointer

cmd->ActiveConnection=conptr;

Now store the SQL statement to be executed in the
CommandText property of the Command object.

cmd->CommandText="<Your SQL statement goes here>"

Return the Recordset object

Create a Recordset object and specify the Command
object as the source of the records as follows:

_RecordsetPtr rst(__uuidof(Recordset));
rst->PutRefSource(cmd);

Now open the Recordset using the Open method of the Recordset
object as :

The Open method takes five parameters. The
first and the second parameter is the data source name and the active connection
to use respectively.Since the data source has already been specified in the
Connection object and the ActiveConnection
property is also set in the Command object, the first and the
second parameter is passed as NULL variant values. The third parameter specifies
the cursor type to use followed by the locking parameter. The fifth parameter
specifies how the database should evaluate the command being sent.

Now the Recordset object pointer created will
have a reference to the records returned by the SQL statement. We need to return
this recordset to the client. Use code like :

rst->QueryInterface(__uuidof(_Recordset),(void **) ptr);

The QueryInterface function
takes the IID of the Recordset object and returns a reference to the records
returned by the SQL statement. When the client calls SearchCust
method, this pointer will be returned to the client.

Uninitialize the COM library

::CoUninitialize();

Now build the component. This will register
the DLL in the registry.

Building the VB Client

Open VB and create a new Standard EXE
project. Set a reference to the Microsoft ActiveX Data Objects 2.1
Library and FindCust 1.0 Type Library through Project->References.

The following VB code can be used to test the DLL component created :

'declare a variable of the Object type
Dim objCust asObject'declare a variable of the type Recordset to store the value
returned 'by the DLL
Dim rst as ADODB.Recordset
'create an instance of the DLL i.e. FindCust.Search
Set objCust = CreateObject("FindCust.Search")
'call the SearchCust method by passing the Customer ID
'Store the returned Recordset object
Set rst = objCust.SearchCust(1)
'display information from the recordset in a message box
MsgBox rst.Fields(1) & " " & rst.Fields(2)

Once you have got the Recordset object, you can manipulate and update the data
it holds in any way that you want. For example, you can use MoveFirst , MoveNext,
MovePrevious and MoveLast to navigate through the recordset and Update
to update the data to the database.

Field Lookup

A recordset object consists of a collection of Field objects that form
a Fields collection. Field objects are used to access fields of each
record within a recordset. They contain information about the name, the type and
the value of the fields in a table.

To illustrate a field lookup, let's consider the online banking scenerio
where we have to generate a unique account number for each new customer. The
database's Customer table has an account number field, iAccountNumber, which for
the sake of simplicity, we'll consider as an integer datatype field.

The SQL command is

select max(iAccountNumber)+1 from customer

Once this command is executed, we'll look up the value from the resultant
recordset.

Add a new method GetMaxValue that has a single parameter [out,retval]
VARIANT *Val. The implementation looks like:

This recordset has a single field (index value is 0) representing the max
account number. This is the value that we are looking up and which is now stored
in Val.

That's it, guys. Now you are on your way to ADO fame and fortune.

Hope ya all find this article useful.

Happy programming!

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

Share

About the Author

Amit Dey is a freelance programmer from Bangalore,India. Chiefly programming VC++/MFC, ATL/COM and PocketPC and Palm platforms. Apart from programming and CP, he is a self-taught guitar and keyboard player.

Hi,
Many people ask me this. Well my answer is that in our project, we had preexisitng MFC dependency, so it didn't make much of a difference. If you notice, except the CString class for string functions, MFC is not really needed at all. There are lots of CString like classes for ATL here at CP and other websites. Then again _bstr_t and CComBSTR work as well, with a little more work.
Glad you found the article informative.

I have written some code to connect to ORACLE database in VB6. When I run this app I watch the memory utilisation which is 2MB to begin with i.e. before the connection is established. Then after I connect to Oracle I watch the memory utilisation fo up to 9MB. After I disconnect the connection I again watch the memory utilisation. It still shows 9MB even though I have closed the connection and set the connection object to nothing.
I don't know if this is normal. Why can't I get back the 7MB after I disconnect??
Have a look at this code.
******************************************************
Private Const CNST = "Provider=OraOLEDB.Oracle.1;User ID=dev;Password=dev;Data Source=bart;Persist Security Info=False"

I was helped by the tutorial of Mr. Dey. I could return a recordset. And I appreciate his efforts. I would like to know how am I supposed to pass recordset object or a connection object from VB to a method of an interface of COM component in VC++ and subsequently use it within the method.

I have 2 different exe files and 1 com component. I cocreate com instance
in one exe. How can I pass to another exe same com-object than the first one
just created. Com is used to transfer data between these 2 exe's.

mr amit
iam a programmer and into atl programming only few months ago, i saw your tutorial for atl with ado, i was able to build the dll with out error or warnings but when i use the dll in vb project, is is saying dll load error, why is this happening, iam using mdb database, and the only change i made is to data source and the provider.so kindly let me know the possible reasons for error as soon as possible.

Hi,
The #import statement should be towards the end of the stdafx.h files( appears grayed) or atleast after all your #include directives. As to the IDL file, I do not have a another sample at the moment(isn't it in the project files?) but will post to you asap.
Hope this helps

See the code that amit has shown should be after this
[
object,
uuid(EB78D558-E071-4D25-80DD-41FD3519934E),
dual,
helpstring("ISearch Interface"),
pointer_default(unique)
]
if it still does not help mail me
ganesh

Could anybody tell me some details about ADO and XML transfering? Something like the methods in VB's handling. Any samples and suggestions are welcome.
such as: ADODB.Stream and ADODB.Recordset... Thanks in advance.

Listen ,
Don't be too hasty!
_variant_t is a wrapper around com variant type and is RPC compatible. So this is NOT INCORRECT and NOT DANGEROUS.Maybe it's time for you to take another look at _variant_t.