AdoLib 1.0 - A complete solution for reliable database development.


1 - Requirements - Getting started
2 - Introduction
3 - Core components
4 - Intermediate abstractions
5 - User interface components
6 - Tutorial and code samples
7 - Deployment
8 - Feedback - Support
9 - Reference
10 - How to purchase



1 - Requirements - Getting started

	Unzip the AdoLib file maintaining the directory structure (-d). This
library requires Visual C++ 6.x installed on the development system. The
evaluation version is fully functional, with a limit of 10 controls per
dialog and an annoying reminder.



2 - Introduction

	Ado provides a very flexible and intuitive interface to OLE DB,
allowing developers to connect to a wide variety of data stores. Though it
was engineered targeting scripting languages such as Visual Basic and VBA,
it provides a dual interface, which allows straightforward C and C++ handling
of its methods and properties.

	The C/C++ developer has several options:
	
	a) Deal with COM interfaces such as Command, Recordset and the like
using the OS built-in OLE support. This could be tedious and error prone,
since the developer has to deal with reference counting, object releasing and
other COM annoying aspects.

	b) Go up one level through a C++ proxy. Microsoft provides such a
proxy-wrapper in its ADO library. This library is rowset-oriented and
provides a valuable variable binding mechanism, but it is not the best
solution for handling record-oriented applications.

	c) Use some framework which integrates the database specifics with a
user interface management. MFC-ADO is an example of this approach. The 
problem here is that the integration is best suited for a Doc/View
architecture, and it adds some complexity when dealing with dialog intensive
applications.

	AdoLib was built with this three-level separation of concerns. It
deals with all the COM burden, such as exception handling, reference counting
and object lifespan. It provides a wrapper level you can access directly, and
it integrates to MFC with little overhead.



3 - Core components

	The classes at this level are wrapped around ADO components. In 
addition, we provide a handful of helper classes such as _bstr_t, _variant_t 
and Currency, which encapsulate the BSTR, VARIANT and CURRENCY semantics,
allowing a very robust parameter handling. _com_error manages error handling,
converting COM error objects into C++ exceptions.

	The instantiation of ADO objects was simplified through smart
constructors, many of which take no arguments or a single string object.
The enumerator semantics is replaced by a simpler indexing mechanism. The use
of the 'property' Microsoft extension allows a 'VB-like' syntax in many
cases: using _variant_t indexes, you can refer to a specific field as:
	Item[0] or as: Item["CustomerID"]
In addition, the same property (if marked read/write) can be a left or right
value.

	These are the core components and their mapping to ADO components:

		AdoLib		MS ADO

		FieldPtr 	Field
		AdoRecordset	Recordset
		ConnectionPtr	Connection
		AdoCommand	Command
		ParameterPtr	Parameter
		ErrorPtr	Error
		PropertyPtr	Property

	These are the helper classes and their COM counterparts:


		AdoLib		COM

		_bstr_t		BSTR
		_variant_t	VARIANT
		Currency	CURRENCY

	This has no counterparts:

		_com_error 	Error handling through exception throwing.



4 - Intermediate abstractions

	In order to connect database objects with user interface elements 
such as controls, windows or dialogs, a couple of intermediate level abstract
classes must be added to the hierarchy. These are AdoWindow and AdoControl. 
A windowing user interface element must inherit from AdoWindow and a control 
element from AdoControl in order to have complete control of its underlying 
database objects. They interact tightly with MFC transfer mechanisms. 



5 - User interface components

	AdoLib comes with several user interface components, but its open 
architecture allows you to add your own custom components. The basic 
components are:

	Windowing components:
	
		AdoDialog
		AdoPropSheet
		AdoPropPage

	Control components:

		AdoEdit
		AdoCheck
		AdoRadio
		AdoGroup
		AdoCombo
		AdoListCtrl

	Complete components:

		ListDialog

	The windowing components are intended to be used as base classes of 
your dialog objects. You can create regular dialogs or property pages using 
the ClassWizard and then replace the base class in the declaration and add 
the constructor arguments. ClassWizard will still recognize your class and it
even will use AdoDialog or AdoPropPage when adding base class references.

	The control classes can be used directly. Once again, you create 
control objects with ClassWizard and then replace their declaration (you can 
use the 'Replace' command if you have many controls). You can manage some 
attributes of the controls at declaration time, such as enabling or writing 
denial.

	AdoLib includes a powerful dialog-based component called ListDialog. 
It manages the display of Recordset objects through a list control, naming 
each header column upon the field's name and formatting the data according to
each data type. In addition, it provides complete printing support for the 
list control's contents, sorting through column header clicking, keyboard 
support (Insert for adding records, Delete for deleting, Enter for viewing), 
and a mouse driven context menu with those same functions.



6 - Tutorial and code samples

	The use of the library does not require any special skill. If you use
Visual C++ and feel comfortable with the ClassWizard, you can produce AdoLib
code immediately. The steps to follow are:


	1 - You can create a project without database support, or use an 
existing project. Add AdoLib.lib to the libraries in the project settings 
(Link page). Map the include path accordingly (C/C++ - Preprocessor).


	2 - In the declaration of your App class, add the core #include:

#include "resource.h" // main symbols

becomes:

#include "resource.h" // main symbols
#include "adolib.h"


	3 - In InitInstance, add the Connection initialization:

    	CMainFrame* pFrame = new CMainFrame;
	m_pMainWnd = pFrame;
	
becomes: (in case you use the Jet provider)

    	CoInitialize(NULL);
    	
	try {
		OpenConn("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=mydata.mdb");
        	//full path here, unless same directory as .EXE
    	}
    	catch(_com_error& e) {
        	Show(e);
        	return FALSE;
    	}
    
    	CMainFrame* pFrame = new CMainFrame;
    	m_pMainWnd = pFrame;

Or, with MSSQL provider:
  
  	CoInitialize(NULL);
    	
	try {
		OpenConn("Provider=sqloledb;Data Source=MYSERVER;Initial Catalog=mydatabase;UID=myuid;PWD=mypass");
    	}
    	catch(_com_error& e) {
        	Show(e);
        	return FALSE;
    	}
    
    	CMainFrame* pFrame = new CMainFrame;
    	m_pMainWnd = pFrame;


	4 - Add a destructor to the Application class and add a call to 
CoUninitialize() in that destructor.


	5 - Create the appropiate dialog resource. Assign each control an ID 
that resembles the name of the field with which it is associated. You can 
optionally add a Modify button (ID = 302). It must be the first in the 
control creation order. You may also add a Enter button (ID = 301). It must 
be non-visible, non-tabstop and marked as default button (unmark the OK 
button).


	6 - Open the ClassWizard and create the CDialog as usual. For each 
control, assign a member variable (Category: Control) of the required type. 
If you want to use the macros provided with AdoLib (recommended), give each 
control the exact name of the corresponding field, prefixed with 'm_' (for a 
field named CustomerID it should be m_CustomerID).


	7 - Close the ClassWizard and go to the dialog's class declaration 
(*.h file). Replace CDialog with CAdoDialog in the inheritance tag and add 
two parameters to the constructor before the existing one. You must also 
define the virtual member TableName.

class CMyDialog : public CDialog
{
// Construction
public:
    CMyDialog(CWnd* Parent = NULL);

becomes:

class CMyDialog : public CAdoDialog
{
// Construction
public:
    CMyDialog(LPCSTR s = 0, bool a = 0, CWnd* Parent = NULL);
    LPCSTR TableName() {return "MyTable";}


	8 - Replace the member control objects with the appropiate ones:

// Dialog Data
    //{{AFX_DATA(CMyDialog)
    enum { IDD = IDD_MYDIALOG };
    CEdit m_CustomerID;
    CButton m_NewCustomer;

becomes:

// Dialog Data
    //{{AFX_DATA(CMyDialog)
    enum { IDD = IDD_MYDIALOG };
    CAdoEdit m_CustomerID;
    CAdoCheck m_NewCustomer;


	9 - Go to the implementation file (*.cpp) and add the #includes you need 
before the declaration file #include. In this case:

#include "stdafx.h"
#include "MyDialog.h"

becomes:

#include "stdafx.h"
#include "AdoDialog.h"
#include "AdoEdit.h"
#include "AdoCheck.h"
#include "MyDialog.h"


	10 - Modify the constructor accordingly.

CMyDialog::CMyDialog(CWnd* pParent /*=NULL*/)
    : CDialog(CMyDialog::IDD, pParent)
{

becomes:

CMyDialog::CMyDialog(LPCSTR s, bool a, CWnd* pParent /*=NULL*/)
    : CAdoDialog(CMyDialog::IDD, pParent, s, a)
{


	11 - In the body of the constructor, add the initialization macros.

    //}}AFX_DATA_INIT
}

becomes:

    //}}AFX_DATA_INIT
    ADOCONTROL(CustomerID);
    ADOCONTROL(NewCustomer);
}


	12 - Replace CDialog in the message map macro with CAdoDialog

BEGIN_MESSAGE_MAP(CMyDialog, CDialog)
    //{{AFX_MSG_MAP(CMyDialog)

becomes:

BEGIN_MESSAGE_MAP(CMyDialog, CAdoDialog)
    //{{AFX_MSG_MAP(CMyDialog)


	13 - That's it. Now you can construct an instance of your dialog and execute 
it. This can be done from inside a menu response function:

    CString sql = "select * from Customers where CustomerID = 5477"; 
    try {
        CMyDialog Cust(sql);
        Cust.DoModal();
    }
    catch(_com_error& e) {
        Show(e, *this);
        return;
    }

or, for inserting a record:

    CString sql = "select * from Customers";
    try {
        CMyDialog Cust(sql, true);
        Cust.DoModal();
    }
    catch(_com_error& e) {
        Show(e, *this);
        return;
    }

or, if you want to use a ListCtrl:

    AdoRecordset rs;
    try {
        rs = "select * from Customers";
	CMyDialog Cust;
        CListDialog Lis(rs, &Cust);
        Lis.SetCaption("Listing of Customers");
        Lis.DoModal();
    }
    catch(_com_error& e) {
        Show(e, *this);
        return;
    }

	Don't forget to #include the AdoLib specific headers in this file too,
before the dialog declaration #include. If you use the ListCtrl component, 
you must add the corresponding resources from AdoLib.rc to your resource file.

#include "AdoDialog.h"
#include "AdoEdit.h"
#include "AdoCheck.h"
#include "MyDialog.h"
#include "ListDialog.h" //if using ListDialog

	If you don't use all the functionality of AdoLib, the linker may 
issue warnings about not referencing some libraries: this is the normal 
behavior. If you compile and link a Debug version, you can get some other 
warnings about conflicting libraries. The registered version of AdoLib 
comes with a Debug static library and complete source code.


	14 - The procedure for a property sheet is very similar. Take a look at the 
accompanying sample code.



7 - Deployment

	Programs built using AdoLib need no additional dlls. Of course 
MDAC 2.x is required. An AdoLib license allows unlimited deployment.



8 - Feedback - Support

	We are aware that other developers may have quite different needs,
so we will appreciate feedback. Please write to:

	feedback@argsoft.com

	ARGSOFT provides full support for the registered version. Evaluators
may ask for support, and in most cases they get it. Please write to:

	support@argsoft.com



9 - Reference

	In order to avoid repetition, we don't reference methods and 
properties of the core components that are documented in the ADO reference.
Only those functions with extended functionality are mentioned. You can 
browse header files for more information.

Core components:


- AdoRecordset 

AdoRecordset(_bstr_t);

	Constructs a AdoRecordset object from a SQL string.
	
AdoRecordset(AdoRecordset &);

	Copy Constructor.
	
AdoRecordset& operator=(_bstr_t);
	
	Assignment operator.

bool Create(_bstr_t);
	
	Creates a read-write recordset from a string.
	
bool Create(AdoCommand &, long = adCmdStoredProc);
	
	Creates a read-write recordset from a Command object.
	
bool Open(_bstr_t);

	Opens a read-write recordset.


- AdoCommand 

AdoCommand();

	Default constructor.
	
bool Create(_bstr_t);

	Creates a command from a string.
	
_RecordsetPtr Execute();

	Executes a command.
	
void AddParameter(_bstr_t name, _variant_t value, 
		DataTypeEnum type = adInteger, long size = 0);

	Adds parameters to a command.


- Globals

_variant_t getdate(); 

	Retrieves current date/time.

_bstr_t shortdate(_variant_t); 

	Returns date part.
	
void OpenConn(_bstr_t connstr, _bstr_t user = "", _bstr_t pass = "",
  		ConnectOptionEnum opt = adConnectUnspecified); 

	Opens a connection to a data source.
	
void Show(_com_error &, HWND = 0); 

	Displays error messages.
	
_bstr_t VarToStr(_variant_t, int = 0); 
	
	Converts variants to strings, with an optional number of decimals.

Abstract base classes:

- AdoWindow 

AdoWindow(LPCSTR s = 0, bool a = false);

	Regular constructor. It takes a SQL string and an editing/adding 
condition.

AdoWindow(AdoRecordset& r, bool a = false);

	Constructor for child objects, taking an existing recordset.
	
bool alta;

	Attribute that determines if we are editing or adding a record.
	
virtual bool Execute() = 0;

	Mandatory execution method.
	
virtual bool Exec(char* sql, bool a = false);
	
	This method launches a dialog object from an external user interface.

virtual bool Baja(char* sql);

	Method for managing record deletion

- AdoControl 

virtual void Transfer(BOOL bSaveAndValidate) = 0;
	
	This forces derived classes to implement the transfer mechanism.
	
_variant_t index;

	Attribute for the field class. It can be a field name or a field index.
	
void DDX_Control(CDataExchange *pDX);

	Binding method. It manages collaboration between the GUI and database
components.

AdoControl& operator=(_variant_t const &);

	Assignment operator. Each derived class must override this.
	
virtual void Enable(bool e = true) = 0;

	User interface enabling/disabling mandatory method.

Windowing user interface:

- CAdoDialog

This class combines a regular CDialog with a AdoWindow interface. It 
trasparently manages several aspects of the user interface, such as 
initialization of control objects, enabling of controls, update or insert of
data and control navigation. 

bool cont;

	Attribute for controlling survival of the object after an OnOK event. Useful for continued data entry.
	
bool child;

	This condition is true if the object was created with the child 
creation constructor.

CAdoDialog(UINT i, CWnd* p = 0, LPCSTR s = 0, bool a = false);

	Normal constructor. It takes the usual CDialog parameters plus a SQL
string and a insert/update flag.

CAdoDialog(UINT i, AdoRecordset& r, CWnd* p = 0, bool a = false);

	Child creation constructor. It references an existing Recordset 
object.

virtual void OnOK();

	It manages inserting or updating data.
	
void SetCaption(LPCTSTR str);
	
	As name suggests.
	
bool Execute();

	Virtual execution member function. Returns true if OK is pressed; 
false otherwise.

void SetModify(int Id, bool mod = true);

	Helper function for edit controls.
	
void SetDlgItemText(int nID, LPCTSTR lpszString);

	Override of the CWnd function. It sets the modify flag in edit 
controls.

virtual void OnModificar();

	Manages enabling of controls.
	
virtual void OnEnter();

	Moves focus among controls on each return.

- CAdoPropSheet

	Objects of this class, unlike CAdoDialog, can be used directly. It 
adapts the functionality of CAdoDialog to property sheets.

CAdoPropSheet(LPCTSTR pszCaption = 0, LPCSTR s = 0, 
              bool a = false, CWnd* pParentWnd = NULL);

	Constructor. Takes the normal CPropertySheet parameters plus a SQL
string and an insert/update flag.

bool modif;

	Modification flag. It determines enabling of controls.
	
bool Execute();

	Virtual execution method. Returns true if OK is pressed; 
false otherwise.

- CAdoPropPage

	This is a base class for property pages linked to CAdoPropSheet 
objects. In addition to the regular CAdoDialog features, it manages wizard 
interfaces for inserting new data.

CAdoPropPage(UINT resId);

	Constructor. It takes a resource identification.
	
bool GetModif() const;
	
	It returns the property sheet modification flag.
	
void SetModify(int Id,bool mod = true);

	Helper function for edit controls.
	
bool GetAlta() const;
	
	It returns the insert/update flag.
	
virtual void OnModificar();

	Manages enabling of controls.

Control components:

- CAdoCheck 

	This control binds to a checkbox and a boolean data field.
	
CAdoCheck& operator=(_variant_t const & v);

	Initialization method. It associates the Field object with an index.
	
void Enable(bool e = true);

	User interface enabling function.
	
friend void AFXAPI DDX_Control(CDataExchange* pDX, int nIDC, 
                               CAdoCheck& rControl);

	This global function overrides the regular DDX_Control function for 
this kind of control.

- CAdoCombo

	This is a more complicated control: it manages a combo box related to
a recordset, usually from another table (lookup). It binds to an integer data
field in the master table.

void Init(_variant_t v, LPCSTR s);

	Initialization function. It takes an index for the Field component 
and a SQL string for the Recordset component.

void Enable(bool e = true);

	User interface enabling function.
	
bool Disabled;

	Setting this property disables the component.
	
friend void AFXAPI DDX_Control(CDataExchange* pDX, int nIDC, 
	                       CAdoCombo& rControl);

	This global function overrides the regular DDX_Control function for
this kind of control.

- CAdoEdit 

	This control binds to an edit control and a data field of any type. 
Data type conversion is made transparently.

CAdoEdit& operator=(_variant_t const &);

	Initialization method. It associates the Field object with an index.
	
void Enable(bool e = true);
	
	User interface enabling function.
	
bool ReadOnly;
	
	Setting this property avoids writing to the component.
	
friend void AFXAPI DDX_Control(CDataExchange* pDX, int nIDC, 
                               CAdoEdit& rControl);

	This global function overrides the regular DDX_Control function for 
this kind of control.

- CAdoGroup

	This control binds to a group of automatic radio buttons and an 
integer data field.

CAdoGroup& operator=(_variant_t const & v);

	Initialization method. It associates the Field object with an index.
	
void Enable(bool e = true);

	User interface enabling function.
	
friend void AFXAPI DDX_Control(CDataExchange* pDX, int nIDC, 
                               CAdoGroup& rControl);

	This global function overrides the regular DDX_Control function for 
this kind of control.

- CAdoRadio 

	This is a simpler control, managing two auto radio buttons and 
binding to a boolean data field.

CAdoRadio& operator=(_variant_t const & v);

	Initialization method. It associates the Field object with an index.
	
void Enable(bool e = true);

	User interface enabling function.
	
friend void AFXAPI DDX_Control(CDataExchange* pDX, int nIDC, 
                               CAdoRadio& rControl);

	This global function overrides the regular DDX_Control function for 
this kind of control.

- CAdoListCtrl

	This control binds to a list control and a AdoRecordset. In addition,
it provides custom test drawing and interfacing to dialogs and property 
sheets for adding and editing data.

CAdoListCtrl(AdoRecordset& r);

	Constructor taking a Recordset reference.
	
int Llenar();

	Filling function.
	
void Linea(int index);
	
	This function takes care of filling each row.
	
LPCSTR GetIndexText(int index);
	
	This retrieves the text of an item given its index. 
	
int Update(int index);

	Updating function.

Complete components:

- CListDialog 

	This dialog-based class displays the contents of an entire Recordset.
It formats columns according to the fields of the recordset.

CListDialog(AdoRecordset& r, AdoWindow* d = 0, CWnd* pParent = NULL); 

	Constructor. It takes a AdoRecordset reference and an optional 
AdoWindow descendant, which it can open for inserting or modifying data.
	
void SetCaption(LPCSTR str);

	As name suggest.
	
int* lencol;

	Optional array of column lengths, for custom formatting.
	
int indcol;

	Optional index for the column used in the WHERE clause of the SQL statement.
	
int* totcol;

	Optional array of column indexes. The component computes totals on 
these columns and show them at the bottom of the list, in each corresponding
column.
	
CAdoListCtrl m_Lista;
	
	The CAdoListCtrl member.
	
virtual void OnOK();

	If a AdoWindow descendant was specified on construction, the 
component builds a SQL statement for the selected row and executes the 
dialog object.

afx_msg void OnImprimir();

	Prints the content of the list control.

Macros:

ADOCONTROL(fieldname)

	Initializes the control object. It is included normally in the 
window object's constructor.
	
ADOCOMBO(fieldname, tablename)
	
	Initializes a AdoCombo object. It takes as arguments the field name 
in the master table and the lookup table name.



10 - How to purchase

You can buy the full version of the library online from a secure server. 
Go to http://argsoft.com and follow the 'How to order' link.


Buenos Aires, May 1999.
