One thing I’ve missed from VisualC++ was a way to develop fast
a database application. There are some data bound ActiveX
controls that come with VisualC++ 5.0, but a data source for DAO
is missing. The RDO is buggy (or at least was in VC 5.0 SP1 –
with SP3 it seems more stable, but I didn’t tested it very hard
:-)). And RDO works through ODBC, which makes it a very bad
choice for mdb databases. After I searched the Internet for an
example how to make a data source, I found very little
information and nowhere the source code for such a control. So I
started with a base code from Data Binding SDK, and I made a
control that works (almost :-)).
Notes about the code:
Since there is a lot of code involved, I emphasize here just
some basics. I recommend to get the Data Binding SDK for more
information. First, the IDE will recognize an ActiveX control as
a data source if it will support the VBDSC interface. That
interface allows creating a cursor interface that can be used by
any control that requests it. There are two kind of data-binding:
simple and complex. For simple bound controls the container will
handle the data binding. The simple bound ActiveX control will
talk with the container. A complex bound control will obtain a
cursor interface and will talk with the data source control
rather than the container.
There are two kinds of cursors: that handles the actual data
(recordsets), and that handles the information about those
recordsets (meta data). The CColCursor class implements the meta
data cursor, and the CRowCursor class implements the data cursor.
There are several kinds of interfaces that can be requested, each
one extending the previous: ICursor – the basic one, that allows
to obtain the meta data cursor, to bound columns and to get rows
in a forward only fashion; the next one is ICursorMove, that
allows moving through cursor in any direction; the next one is
ICursorScroll that allows moving through cursor using approximate
positions (it is needed by controls that have scroll bars, for
example). There are another two special interfaces: ICursorFind,
that allows finding a record using field values, and
ICursorUpdateARow, that allows changing field values, adding a
row and deleting one.
The CMyDaoRecordset is an extension of the CDaoRecordset
class, with some changes in GetBookmark and SetBookmark (they use
absolute position for snapshot recordsets), and there is a new
method called Clone, that makes a clone from the current
recordset. The Reclone method makes the current recordset being
the clone of the given recordset.
The CRowCursor implements also the IConnectionPoint interface.
The VisualC++ framework (and with high probability Visual Basic,
too) will try to connect to the control instead of the rowset, so
I changed both GetConnectionHook (to pass the connections to the
recordset), and GetInterfaceHook (to pass the queries about
cursor interfaces to the rowset). The source control uses
INotifyDBEvents interface to notify the bound controls about the
changes in cursor’s state (this interface is implemented by the
container and/or the data bound control – not by the source). The
data source supports IConnectionPointContainer and
IConntectionPoint interfaces, that allows data bound controls to
be connected to the data source (so they will be notified about
changes that occur).
Now, how to use it:
Use regsvr32 /v "path"dscdao.ocx to register the
control. You need to have the jet engine installed on the
computer you will use this control, the runtime library and mfc
dlls. After that you will be able to use this control like any
other ActiveX (it should work with Visual Basic, too, but I
tested it only with VisualC++). Please drop me an e-mail if
somebody tests it with Visual Basic.
Known bugs (I will fix them when I will have
Since I don’t have very much free time, the code is written
very fast, and can be full of bugs :-(. And remember, it’s only
the first version (1.0).
If compiled with debug info, the container program will crash
at the exit. It seems that the mfc framework tries to call an
interface function after the rowset object is released. If you
edit a field on the last record in DBGrid, and after that go on
he new record, the other bound columns will not be updated (it
seems that the current record will remain the last one – the
dbgrid uses the clone to add the new record, and for some reason
the other controls are not notified by the change when the record
is updated). DBGrid works with two rowsets (at least), the
original one for moving/editing (usually), and a cloned one for
drawing, so it’s pretty hard to spot the reason of that error.
Another bug (and the worst one), is that the DBGrid will behave
strange if an index is used, or if the table is edited by more
than one user.
It’s possible that the control will not work for some data
types. It is sure that it will not work for BLOB fields. In order
to fully support blob fields, it needs to implement IEntryID
interface. Instead of field value, there will be an entry id,
that will allow to ask IEntryID interface for IStream interface
for that entry id. The IStream interface is used to get the BLOB
field. I also not tested the control for VARIANTs, but it should
work. I recently implemented the ICursorFind interface, and I
didn’t test it yet, so probably it will not work.
I want to hear if you found any bugs, or make some
improvements to the code.
Allow to draw the control vertically. Suport
for BLOB fields. Better support for different kind of data.
Extending the meta data cursor to support Clone method and
bookmarks. Possible allowing "master-slave" (relations)
between two data source controls…
And well, a copyright notice:
You can use the code free of charge, you can modify it, but in
any case the author (Adrian Roman) is not responsible of any kind
of damage or loss of data or loss of profit, incidental or
consequential, occurred using this code. You cannot claim that
the code is written by yourself, even the code is modified. If
you use this control, you have to make a notice (in About box
and/or startup splash screen and/or help file) that the program
contains code developed by Adrian Roman, e-mail:
Changes to version 188.8.131.52:
I used BstrToVector and VectorFromBstr to convert the
bookmarks. I did something wrong, and somtime the conversion
failed. On small recordsets it worked, but on large ones the
result was an empty recordset. I changed the code working
directly with the safarray, and now it works well.
I allso added a check box that allows to set the type of
In the previous version there was a memory leak (when a clone
was made, a recordset was alloced twice), now it’s fixed.
Also in the previous version, the FindByValues interface was
not functionl. Now it works, but only for dynaset or snapshot
recordset. For table type it returns E_NOTIMPL.