| Click here to support our advertisers

SOFTWARE FOR SALE
BOOKS FOR SALE
SEARCH CENTRAL
JOB BANK
CLASSIFIEDS
DIRECTORIES
REFERENCE Online Library
LEARNING CENTER
JOURNAL
NEWS CENTRAL
DOWNLOADS
COMMUNITY
CALENDAR
ABOUT US
Journal:
Get the weekly email highlights from the most popular journal for developers! Current issue
> 3. developer.com developerdirect.com htmlgoodies.com javagoodies.com jars.com intranetjournal.com javascripts.com | All Categories : C/C++ vcg22.htm Preparing a Data Dictionary Writing a Visual C++ Application to Create a Data Dictionary Adding CTL3DV2V2.DLL to Create Three-Dimensional, Shaded Dialog Boxes Running DOCDB Altering the Visual C++ Code in DOCDB Exporting Access's Database Documentor Output Using Access's Database Documentor to Document Databases Documenting Databases Created with CASE Tools Using Total Access to Create a Data Dictionary Creating Manuals for Your Database Applications The Structure of User Manuals Front Matter Text Back Matter Using Doc-To-Help to Create Convertible Manuals Summary # - 22 - Documenting Your Database Applications A database application isn't complete until you've prepared professional-grade documentation for each element of the application. The "Visual C++" qualifier is missing from the preceding truism of professional database developers because this statement applies to all database applications created with any front-end development tool or programming language. Visual C++ doesn't provide any documentation tools specific to database applications, and what is provided (help file shells and source comments) is rather basic. This chapter describes the following three basic documentation elements for all database applications: Data dictionaries that fully describe the tables of the database(s) used by the application and each field of the tables that the application reads or updates. Data dictionaries also specify primary and foreign key fields and relations between tables, as well as security restrictions that apply to the tables. (Some RDBMSs let you enforce security provisions at the field level.) Referential and domain integrity rules that are enforced at the database and table levels, respectively, or that must be incorporated into every application that updates the tables, are also included in the data dictionary. Code and form printouts that include sufficient commentary to make the source code comprehensible to others who need to maintain the application by revising the code and/or the form design. A programmer should be able to totally reconstruct the original application from this documentation element. Code and form printouts also contain version control information that identifies modifications made to the original code, who made the revisions, the purpose of the revision, and when the revision was made (including the version number to which the revision applies). A flow diagram of the application also aids others who need to understand the concept and construction of your database front end. User manuals that provide instructions for installing and using the application the way you intended. Regardless of how intuitive your application is or how complete its built-in help system is, a printed user manual should be provided as a distributable item of any database application. Although users seldom refer to printed manuals (except as a last resort), your online help system can be created from the user manual if you have WexTech Systems' Doc-To-Help application, which is described near the end of this chapter. Using Doc-To-Help to create Windows help files is covered in Chapter 23, "Creating Help Files for Database Applications." The major sections of this chapter deal sequentially with each of the documentation elements in the preceding list. ### Preparing a Data Dictionary Data dictionaries are the most important element of database documentation. If you use a product such as Asymetrix's InfoModeler, the process of documenting the database will be a bit easier. InfoModeler provides a vast array of printed reports, tables, and charts to document your database. This chapter describes some of InfoModeler's capabilities, as well as other database documentation techniques. At the very least, data dictionaries should include the following components: A narrative description of the purpose and overall structure of the database. A detailed description of each table in the database, usually in tabular format. The name of each field of the tables in the database, together with the field data type and field size (if not determined by the field data type). Entries that identify primary and foreign key fields in each table. A list of constraints (validation rules) for field values that are used to maintain domain integrity and an indication of whether these constraints are enforced at the table level by the RDBMS or must be maintained by application code. A graphical or tabular description of the relations between the key fields of the tables and an indication of whether the relational integrity is enforced by the RDBMS or by applications. A list of stored procedures and triggers, including the source code for the stored procedures and triggers, if the RDBMS supports these features. The following sections describe writing your own Visual C++ application to create a data dictionary, using a CASE tool to create an Access database (including a data dictionary for the database), obtaining table data from Access's Database Documentor (described later), and using a commercial Access documentation tool such as InfoModeler to create data dictionaries for Access databases. The emphasis of this chapter is on the .MDB database format, because the Access database structure is the most likely to be used in the majority of new Visual C++ applications. #### Writing a Visual C++ Application to Create a Data Dictionary It's relatively easy to write a Visual C++ database application to create a fully formatted data dictionary as a text file that you can import into Word for Windows or any another word processing or spreadsheet application. It's an even simpler process if you've created (or have access to the source code for) a data dictionary generator for Access databases that relies primarily on C or C++ code. DOCDB, the sample data dictionary application that is described in the following sections, is based on the ODBC access routines described in Chapter 7, "Using the Open Database Connectivity API." DOCDB is a simple form-view Visual C++ program. It was originally developed with Visual C++ 1.5 and was then recompiled as a 32-bit application using Visual C++ 4. It's not uncommon to have to convert 16-bit applications to 32-bit when you're converting to either Visual C++ 2.x or 4. The 32-bit version of DOCDB is available on the CD that comes with this book. DOCDB performs a simple task: It provides all the information that ODBC can provide about the columns in a specified table. This information is presented in a form. It would be easy to add report printing capabilities to DOCDB, and even to add the capability to report on all tables in a given database when you create a printed report. However, printed reports would be secondary to adding additional functionality to DOCDB to enhance DOCDB's retrieval of information about a database. DOCDB is a simple program. It was created using AppWizard, with only two changes to the default "stock" program: It is a Single Document Interface (SDI) program with a CFormView view class. Neither database nor OLE support has been added to DOCDB, because these features weren't needed. After DOCDB has been created, the first step is to incorporate the ODBC routines. Adding this support is detailed in Chapter 3, "Using Visual C++ Data Access Functions," and it consists of these steps: Copy the ODBC helper routines (ODBC1.C, ODBCMISC.H, ODBC2.CPP, ODBCTABL.CPP, and ODBCINFO.CPP) to the \DOCDB directory. Edit the DOCDB project and add the ODBC helper source files (the .CPP and .C files from step 1) to the project. Edit the ODBC C/C++ source files and add this DOCDB header file #include statement:
>        #include "odbcmisc.h"
> Copy the dialog box templates from ODBC.RC to the DOCDB resources. Add the ODBC library to the ODCDB library list. You can refer to Chapter 3 for more-detailed steps if you need to. After you've followed the preceding steps, you can do a test compile of the ODCDB project. If the compilation succeeds without errors, you can continue to create the DOCDB program. The next step is to use the resource editor to add the controls to the DOCDB main dialog box. Figure 22.1 shows this dialog box, including the dialog control IDs. Figure 22.1. DOCDB's main window dialog box. NOTE All unmarked controls in Figure 22.1 are IDC_STATIC. Each control in DOCDB must be bound to a variable, with the exception of the New Database button, which has an attached function, and the Column Name combo box, which calls a function whenever the current selection (the column in the table) has changed. The dialog is managed by the CDocdbView class (found in DOCDBVW.CPP). After the dialog box has been created, in Visual C++ use ClassWizard to bind variables to the controls, as shown in Table 22.1. Table 22.1. Variable names for DOCDB's controls.
> Control | Variable Name | Variable Type | Description
> IDC_COLUMN | m_ColumnList | CComboBox | The list of all the columns in the selected table is stored here. This is both an input and an output control.
> IDC_CONNECT | m_Connect | CString | This output control shows the current connect string.
> IDC_DATASOURCE | m_DataSource | CString | This output control shows the datasource name.
> IDC_DATATYPE | m_DataType | int | This output control shows the column's data type.
> IDC_NULLABLE | m_Nullable | int | This output control indicates whether the column supports nulls.
> IDC_OWNER | m_Owner | CString | This output control shows the table's owner.
> IDC_QUALIFIER | m_Qualifier | CString | This output control shows the table's qualifier.
> IDC_RADIX | m_Radix | int | This output control shows the column's radix, if applicable.
> IDC_REMARKS | m_Remarks | CString | This output control shows remarks on this column, if any.
> IDC_SCALE | m_Scale | int | This output control shows the scale of the column, if applicable.
> IDC_TABLENAME | m_TableName | CString | This output control shows the name of the table.
> IDC_TYPENAME | m_TypeName | CString | This output control shows the column's data type, in character format.
> IDC_PRECISION | m_Precision | int | This output control shows the precision of the column.
> IDC_LENGTH | m_Length | int | This output control shows the length of the column.
> IDC_TABLE | m_Table | CString | This output control shows the name of the current table.
> Although DOCDB can show information about columns in a table in a database, it's important to realize that not all ODBC drivers can return all the information. For example, in a text datasource (such as a CSV file), it isn't reasonable to have remarks, scale, or precision information. In fact, most ODBC drivers don't return all the information that DOCDB tries to display. Any information that is unavailable will be displayed as either a blank or a zero, depending on the variable's data type. When Visual C++'s ClassWizard binds a variable to a control, it creates the code to initialize the variable and to actually transfer the variable's contents to the control. When there must be conversions (such as the conversion of integer variables to text for edit controls), the DDX_Text() macros take care of this conversion automatically. In addition to binding variables to the controls (as described earlier), it's necessary to also have functions for the IDC_NEWDB button and the IDC_COLUMN combo box. Using ClassWizard (see Figure 22.2), create two functions. For the IDC_NEWDB button, create a function for the message BN_CLICKED. This function will be called each time the user clicks the New Database button. For the IDC_COLUMN button, create a function for the message CBN_SELCHANGE. This function will be called each time the user changes the current column in the table. Figure 22.2. Functions bound to controls in DOCDB. The only changes you need to make to the code created by AppWizard are in DOCDBVW.CPP. You must add the functionality of the two functions created by ClassWizard. Listing 22.1 shows DOCDBVW.CPP. All the code that must be added appears in bold. Listing 22.1. DOCDBWV.CPP with added code.
>     // docdbvw.cpp : implementation of the CDocdbView class

>     //

>     #include "stdafx.h"

>     #include "docdb.h"

>     #include "docdbdoc.h"

>     #include "docdbvw.h"

>     #include "odbcmisc.h"

>     #ifdef _DEBUG

>     #undef THIS_FILE

>     static char BASED_CODE THIS_FILE[] = __FILE__;

>     #endif

>     /////////////////////////////////////////////////////////////////////////////

>     // CDocdbView

>     IMPLEMENT_DYNCREATE(CDocdbView, CFormView)

>     BEGIN_MESSAGE_MAP(CDocdbView, CFormView)

>         //{{AFX_MSG_MAP(CDocdbView)

>         ON_BN_CLICKED(IDC_NEWDB, OnNewdb)

>         ON_CBN_SELCHANGE(IDC_COLUMN, OnSelchangeColumn)

>         //}}AFX_MSG_MAP

>         // Standard printing commands

>         ON_COMMAND(ID_FILE_PRINT, CFormView::OnFilePrint)

>         ON_COMMAND(ID_FILE_PRINT_PREVIEW, CFormView::OnFilePrintPreview)

>     END_MESSAGE_MAP()

>     /////////////////////////////////////////////////////////////////////////////

>     // CDocdbView construction/destruction

>     CDocdbView::CDocdbView()

>         : CFormView(CDocdbView::IDD)

>     {

>         //{{AFX_DATA_INIT(CDocdbView)

>         m_Connect = "";

>         m_DataSource = "";

>         m_DataType = 0;

>         m_Nullable = 0;

>         m_Owner = "";

>         m_Qualifier = "";

>         m_Radix = 0;

>         m_Remarks = "";

>         m_Scale = 0;

>         m_TableName = "";

>         m_TypeName = "";

>         m_Precision = 0;

>         m_Length = 0;

>         m_Table = "";

>         m_DbmsVersion = "";

>         //}}AFX_DATA_INIT

>         // TODO: Add construction code here

>     }

>     CDocdbView::~CDocdbView()

>     {

>     }

>     void CDocdbView::DoDataExchange(CDataExchange* pDX)

>     {

>         CFormView::DoDataExchange(pDX);

>         //{{AFX_DATA_MAP(CDocdbView)

>         DDX_Control(pDX, IDC_COLUMN, m_ColumnList);

>         DDX_Text(pDX, IDC_CONNECT, m_Connect);

>         DDX_Text(pDX, IDC_DATASOURCE, m_DataSource);

>         DDX_Text(pDX, IDC_DATATYPE, m_DataType);

>         DDX_Text(pDX, IDC_NULLABLE, m_Nullable);

>         DDX_Text(pDX, IDC_OWNER, m_Owner);

>         DDX_Text(pDX, IDC_QUALIFIER, m_Qualifier);

>         DDX_Text(pDX, IDC_RADIX, m_Radix);

>         DDX_Text(pDX, IDC_REMARKS, m_Remarks);

>         DDX_Text(pDX, IDC_SCALE, m_Scale);

>         DDX_Text(pDX, IDC_TABLENAME, m_TableName);

>         DDX_Text(pDX, IDC_TYPENAME, m_TypeName);

>         DDX_Text(pDX, IDC_PRECISION, m_Precision);

>         DDX_Text(pDX, IDC_LENGTH, m_Length);

>         DDX_Text(pDX, IDC_TABLE, m_Table);

>         DDX_Text(pDX, IDC_DBMSVERSION, m_DbmsVersion);

>         //}}AFX_DATA_MAP

>     }

>     /////////////////////////////////////////////////////////////////////////////

>     // CDocdbView printing

>     BOOL CDocdbView::OnPreparePrinting(CPrintInfo* pInfo)

>     {

>         // Default preparation

>         return DoPreparePrinting(pInfo);

>     }

>     void CDocdbView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)

>     {

>         // TODO: Add extra initialization before printing

>     }

>     void CDocdbView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)

>     {

>         // TODO: Add cleanup after printing

>     }

>     void CDocdbView::OnPrint(CDC* pDC, CPrintInfo*)

>     {

>         // TODO: Add code to print the controls

>     }

>     /////////////////////////////////////////////////////////////////////////////

>     // CDocdbView diagnostics

>     #ifdef _DEBUG

>     void CDocdbView::AssertValid() const

>     {

>         CFormView::AssertValid();

>     }

>     void CDocdbView::Dump(CDumpContext& dc) const

>     {

>         CFormView::Dump(dc);

>     }

>     CDocdbDoc* CDocdbView::GetDocument() // Non-debug version is inline

>     {

>         ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CDocdbDoc)));

>         return (CDocdbDoc*)m_pDocument;

>     }

>     #endif  //_DEBUG

>     /////////////////////////////////////////////////////////////////////////////

>     // CDocdbView message handlers

>     typedef struct {

>         UCHAR  szQualifier[STR_LEN];

>         UCHAR  szOwner[STR_LEN];

>         UCHAR  szTableName[STR_LEN];

>         UCHAR  szColName[STR_LEN];

>         UCHAR  szTypeName[STR_LEN];

>         UCHAR  szRemarks[REM_LEN];

>         SDWORD Precision;

>         SDWORD Length;

>         SWORD  DataType;

>         SWORD  Scale;

>         SWORD  Radix;

>         SWORD  Nullable;

>     } COLUMN_ATTRIBUTES;

>     COLUMN_ATTRIBUTES Columns[70];

>     void CDocdbView::OnNewdb()

>     {

>     // Declare storage locations for result set data

>     UCHAR  szQualifier[STR_LEN];

>     UCHAR  szOwner[STR_LEN];

>     UCHAR  szTableName[STR_LEN];

>     UCHAR  szColName[STR_LEN];

>     UCHAR  szTypeName[STR_LEN];

>     UCHAR  szRemarks[REM_LEN];

>     SDWORD Precision = 0;

>     SDWORD Length = 0;

>     SWORD  DataType = 0;

>     SWORD  Scale = 0;

>     SWORD  Radix = 0;

>     SWORD  Nullable = 0;

>     // Storage locations for returned bytes stored

>     SDWORD cbQualifier;

>     SDWORD cbOwner;

>     SDWORD cbTableName;

>     SDWORD cbColName;

>     SDWORD cbRemarks;

>     SDWORD cbDataType;

>     SDWORD cbTypeName;

>     SDWORD cbPrecision;

>     SDWORD cbLength;

>     SDWORD cbScale;

>     SDWORD cbRadix;

>     SDWORD cbNullable;

>     // Declare miscellaneous variables

>     char    szSource[60];

>     char    szDirectory[132];

>     char    szTable[60];

>     char    szDSN[256];

>     char    szConStrOut[256];

>     char    szDbmsVersion[256];

>     char    szBuffer[513];

>     int     i;

>     int     j;

>     HENV    henv;

>     HDBC    hdbc;

>     HSTMT   hstmt = SQL_NULL_HSTMT;

>     RETCODE RC;

>     int     nConStrOut;

>     SWORD   swReturn;

>         szSource[0] = '\0';

>         szTable[0] = '\0';

>         szDirectory[0] = '\0';

>         GetODBC(

>             szSource, sizeof(szSource),

>             szTable, sizeof(szTable),

>             szDirectory, sizeof(szDirectory));

>         m_DataSource = szSource;

>         m_Table = szTable;

>         SQLAllocEnv(&henv);

>         SQLAllocConnect(henv, &hdbc);

>         sprintf(szBuffer, "DSN=%s", szSource);

>         strcpy(szDSN, szBuffer);

>     //    To display ConnectDlg(), set the DSN to a null-length string:

>     //    szDSN[0] = '\0';

>         RC = SQLDriverConnect(hdbc, m_hWnd,

>             (unsigned char far *)szDSN, SQL_NTS,  // strlen(szDSN),

>             (unsigned char far *)szConStrOut, sizeof(szConStrOut),

>             (short far *)&nConStrOut,

>             SQL_DRIVER_COMPLETE);

>         if (RC != SQL_SUCCESS && RC != SQL_SUCCESS_WITH_INFO)

>         {

>              SQLPrintError(henv, hdbc, hstmt);

>         }

>         else

>         {// Display the connect string returned:

>             for (i = 0, j = 0; i < (int)strlen(szConStrOut); i++)

>             {// Copy and format. Add space after each ';' in string:

>                 szBuffer[j++] = szConStrOut[i];

>                 if (szConStrOut[i] == ';')

>                     szBuffer[j++] = ' ';

>             }

>             szBuffer[j++] = '\0';

>             m_Connect = szBuffer;

>         }

>         RC = SQLAllocStmt(hdbc, &hstmt);

>         if (RC != SQL_SUCCESS && RC != SQL_SUCCESS_WITH_INFO)

>         {

>              SQLPrintError(henv, hdbc, hstmt);

>         }

>     //    Get the columns in the specified table:

>         RC = SQLColumns(hstmt,

>             NULL, 0,    // All qualifiers

>             NULL, 0,    // All owners

>             (unsigned char __far *)szTable, SQL_NTS,  // The table!

>             NULL, 0);   // All columns

>         if (RC != SQL_SUCCESS && RC != SQL_SUCCESS_WITH_INFO)

>         {

>             SQLPrintError(henv, hdbc, hstmt);

>         }

>     // Get and display the DBMS version number:

>         SQLGetInfo(hdbc, SQL_DBMS_VER, szDbmsVersion,

>              sizeof(szDbmsVersion), &swReturn);

>        m_DbmsVersion = szDbmsVersion;

>     //    Now bind variables to columns!

>         RC = SQLBindCol(hstmt, 1,  SQL_C_CHAR, szQualifier, STR_LEN,&cbQualifier);

>         RC = SQLBindCol(hstmt, 2,  SQL_C_CHAR, szOwner, STR_LEN, &cbOwner);

>         RC = SQLBindCol(hstmt, 3,  SQL_C_CHAR, szTableName, STR_LEN,&cbTableName);

>         RC = SQLBindCol(hstmt, 4,  SQL_C_CHAR, szColName, STR_LEN, &cbColName);

>         RC = SQLBindCol(hstmt, 5,  SQL_C_SSHORT,

>              &DataType, sizeof(DataType), &cbDataType);

>         RC = SQLBindCol(hstmt, 6,  SQL_C_CHAR, szTypeName, STR_LEN, &cbTypeName);

>         RC = SQLBindCol(hstmt, 7,  SQL_C_SLONG,

>              &Precision, sizeof(Precision), &cbPrecision);

>         RC = SQLBindCol(hstmt, 8,  SQL_C_SLONG, &Length, sizeof(Length), &cbLength);

>         RC = SQLBindCol(hstmt, 9,  SQL_C_SSHORT, &Scale, sizeof(Scale), &cbScale);

>         RC = SQLBindCol(hstmt, 10, SQL_C_SSHORT, &Radix, sizeof(Radix), &cbRadix);

>         RC = SQLBindCol(hstmt, 11, SQL_C_SSHORT,

>              &Nullable, sizeof(Nullable), &cbNullable);

>         RC = SQLBindCol(hstmt, 12, SQL_C_CHAR, szRemarks, REM_LEN, &cbRemarks);

>     // Then get the column names (clear list and fill array)

>         m_ColumnList.ResetContent();

>         i = 0;

>         j = 0;

>         while(i < 70)  // Maximum number of columns we allow!

>         {

>              RC = SQLFetch(hstmt);

>             if (RC == SQL_ERROR || RC == SQL_SUCCESS_WITH_INFO)

>             {

>                 SQLPrintError(henv, hdbc, hstmt);

>             }

>             if (RC == SQL_SUCCESS || RC == SQL_SUCCESS_WITH_INFO)

>             {

>                 // Save this column for the future!

>                 strcpy((LPSTR)Columns[i].szQualifier, (LPCSTR)szQualifier);

>                 strcpy((LPSTR)Columns[i].szOwner, (LPCSTR)szOwner);

>                 strcpy((LPSTR)Columns[i].szTableName, (LPCSTR)szTableName);

>                 strcpy((LPSTR)Columns[i].szColName, (LPCSTR)szColName);

>                 strcpy((LPSTR)Columns[i].szTypeName, (LPCSTR)szTypeName);

>                 strcpy((LPSTR)Columns[i].szRemarks, (LPCSTR)szRemarks);

>                 Columns[i].Precision = Precision;

>                 Columns[i].Length = Length;

>                 Columns[i].DataType = DataType;

>                 Columns[i].Scale = Scale;

>                 Columns[i].Radix = Radix;

>                 Columns[i].Nullable = Nullable;

>                 j = m_ColumnList.AddString((LPCSTR)szColName);

>                 m_ColumnList.SetItemData(j, i++);

>             }

>              else

>             {// That's all, folks...

>                 break;

>             }

>         }

>         m_ColumnList.SetCurSel(0);

>         OnSelchangeColumn();

>          SQLFreeStmt(hstmt, SQL_CLOSE);

>         SQLFreeStmt(hstmt, SQL_UNBIND);

>     //    END: Got the columns in the specified table:

>         SQLDisconnect(hdbc);

>         SQLFreeConnect(hdbc);

>         SQLFreeEnv(henv);

>     }

>     void CDocdbView::OnSelchangeColumn()

>     {

>     int    nCurSel = (int)m_ColumnList.GetItemData(m_ColumnList.GetCurSel());

>         m_Qualifier = (LPSTR)Columns[nCurSel].szQualifier;

>         m_Owner = (LPSTR)Columns[nCurSel].szOwner;

>         m_TableName = (LPSTR)Columns[nCurSel].szTableName;

>         m_TypeName = (LPSTR)Columns[nCurSel].szTypeName;

>         m_Remarks = (LPSTR)Columns[nCurSel].szRemarks;

>         m_Precision = Columns[nCurSel].Precision;

>         m_Length = Columns[nCurSel].Length;

>         m_DataType = Columns[nCurSel].DataType;

>         m_Scale = Columns[nCurSel].Scale;

>         m_Radix = Columns[nCurSel].Radix;

>         m_Nullable = Columns[nCurSel].Nullable;

>         UpdateData(FALSE);

>     }
> In this listing you see how the ODBC helper routine GetODBC() has been called, which prompts the user for database, table, and (if applicable) directory names. The names will be stored in the locations specified in the call:
>     szSource[0] = '\0';

>     szTable[0] = '\0';

>     szDirectory[0] = '\0';

>     GetODBC(

>         szSource, sizeof(szSource),

>         szTable, sizeof(szTable),

>         szDirectory, sizeof(szDirectory));
> After you have the name of the database and table, you can open the database and determine attributes. This is done using the SQL...() functions, which are described more fully in Chapter 3. The process of using a database consists of the following steps: Allocate the environment using SQLAllocEnv(). This function takes only one parameter, a pointer to an unused environment handle. Allocate the connection using SQLAllocConnect(). This function takes two parameters: the environment handle (returned by the SQLAllocEnv() function) and a pointer to a database connection handle. The connection handle is then used by virtually all the other SQL...() functions. Connect to the database using SQLConnect() (or SQLDriverConnect()) and the connection handle that was returned by the SQLAllocConnect() function, as well as the database connection information such as the database name, user name, and password. After connecting to the database, you must allocate a statement. You do this with the SQLAllocStmt() function. After the statement has been allocated, you can issue an SQL command. For example, you could issue a SELECT * FROM statement to get records from each column in the database. The actual fetching of the data from the columns would be done in a loop that has an SQLFetch() call. When the application is done with the database, you must free the statement handle, disconnect from the database, free the connect handle, and free the environment handle. You do these steps in the opposite order of steps 1 through 4. The following code fragment shows the function that gets information about the columns in a given table. All the lines that aren't directly related to the database process have been eliminated:
>     SQLAllocEnv(&henv);

>     SQLAllocConnect(henv, &hdbc);

>     RC = SQLDriverConnect(hdbc, m_hWnd,

>         (unsigned char far *)szDSN, SQL_NTS,  // strlen(szDSN),

>         (unsigned char far *)szConStrOut, sizeof(szConStrOut),

>         (short far *)&nConStrOut,

>         SQL_DRIVER_COMPLETE);

>     RC = SQLAllocStmt(hdbc, &hstmt);

>     RC = SQLColumns(hstmt,

>         NULL, 0,    // All Qualifiers

>         NULL, 0,    // All owners

>         (unsigned char __far *)szTable, SQL_NTS,  // The table!

>         NULL, 0);   // All columns

>     //    Now bind variables to columns!

>     RC = SQLBindCol(hstmt, 1,  SQL_C_CHAR, szQualifier, STR_LEN,&cbQualifier);

>     RC = SQLBindCol(hstmt, 2,  SQL_C_CHAR, szOwner, STR_LEN, &cbOwner);

>     RC = SQLBindCol(hstmt, 3,  SQL_C_CHAR, szTableName, STR_LEN,&cbTableName);

>     RC = SQLBindCol(hstmt, 4,  SQL_C_CHAR, szColName, STR_LEN, &cbColName);

>     RC = SQLBindCol(hstmt, 5,  SQL_C_SSHORT,

>          &DataType, sizeof(DataType), &cbDataType);

>     RC = SQLBindCol(hstmt, 6,  SQL_C_CHAR, szTypeName, STR_LEN, &cbTypeName);

>     RC = SQLBindCol(hstmt, 7,  SQL_C_SLONG,

>          &Precision, sizeof(Precision), &cbPrecision);

>     RC = SQLBindCol(hstmt, 8,  SQL_C_SLONG, &Length, sizeof(Length), &cbLength);

>     RC = SQLBindCol(hstmt, 9,  SQL_C_SSHORT, &Scale, sizeof(Scale), &cbScale);

>     RC = SQLBindCol(hstmt, 10, SQL_C_SSHORT, &Radix, sizeof(Radix), &cbRadix);

>     RC = SQLBindCol(hstmt, 11, SQL_C_SSHORT,

>          &Nullable, sizeof(Nullable), &cbNullable);

>     RC = SQLBindCol(hstmt, 12, SQL_C_CHAR, szRemarks, REM_LEN, &cbRemarks);

>     while()

>     {

>          RC = SQLFetch(hstmt);

>          // Save this column for the future!

>     }

>     SQLFreeStmt(hstmt, SQL_CLOSE);

>     SQLFreeStmt(hstmt, SQL_UNBIND);

>     SQLDisconnect(hdbc);

>     SQLFreeConnect(hdbc);

>     SQLFreeEnv(henv);
> ##### Adding CTL3DV2V2.DLL to Create Three-Dimensional, Shaded Dialog Boxes No accoutrements, such as progress-metering gauges, are added to DOCDB, because one of the purposes of this sample application is to demonstrate the ease with which databases created with Access can be accessed by Visual C++ applications. However, the CTL3DV2.DLL library is used to add a gray background and a 3-D effect to dialog boxes and message boxes under some versions of Windows. NOTE At this stage in the life of Windows, only Windows 3.x and Windows NT really need to use CTL32V2.DLL. Windows 95 has this functionality built in. However, there is a version of CTL32V2.DLL for Windows 95 for applications that need it. CTL3DV2.DLL is a small (about 25K) library that is included with Visual C++, Microsoft Office, and several other Microsoft applications. CTL3DV2 can be found in the \REDIST directory, where the ODBC and SQL redistributable components reside. Because CTL3DV2.DLL is included as part of ODBC's installation, you can generally assume that most users of Windows database applications have a copy of CTL3DV2.DLL in their \WINDOWS\SYSTEMS directory. Excel 5, Word 6, and other Microsoft products released in late 1993 use CTL3DV2.DLL rather than CTL3D.DLL because of problems caused by incompatible versions of CTL3D. Some software suppliers modified CTL3D for their applications without renaming the files. There are also several versions of CTL3DV2.DLL, some of which might not be compatible with Visual C++ and the current releases of other Microsoft applications. The version used in DOCDB is dated 9/16/95, has a file size of 27,136 bytes, and is for Windows NT. For Windows 95, this file is 26,624 bytes long and is dated 9/16/95. CTL3DV2.DLL files with dates later than mid-1995 (when Windows 95 was released) that have approximately the same file size should work as well, because Microsoft often redates files using the release date of the product that the file is distributed with. TIP You can obtain a copy of CTL3DV2.DLL by copying it from the \MSDEV\REDIST directory from your Visual C++ CD. For applications that will be created to run solely under Windows 95, you don't need to include CTL3DV2, because this functionality is built into Windows 95. Windows NT users should use the file CTL3D32.DLL (or copy it and rename it CTL32NT.DLL), which is in the \MSDEV\REDIST directory on the Visual C++ 4 CD. NOTE The CTL3DV2.DLL and CTL3D32.DLL libraries are copyrighted by Microsoft, and the license to distribute Microsoft's copyrighted files in conjunction with your Visual C++ applications does include the right to distribute CTL3DV2.DLL as part of the OLE and ODBC distribution kit you include with your applications. You need to determine whether prospective users of your applications currently have access to CTL3DV2.DLL from \WINDOWS\SYSTEM on their local computers. If it isn't installed, you must install it. If it's already installed, and it's an older version than the one your application is supplying, you should replace it. ##### Running DOCDB To run DOCDB, follow these steps: Create and compile the DOCDB project. The source is on the source code disk. Alternatively, you can create your own project using the listings from this chapter. If you're not comfortable using Visual C++, practice by creating your own version of DOCDB. Start the DOCDB program, shown in Figure 22.3. You can run DOCDB either from the Visual C++ workbench or from Windows. Figure 22.3. DOCDB's dialog box to open the source database file. Select the from the list the database you want to open. If your database hasn't been installed in the ODBC system, install it and rerun DOCDB. Click the New Database button to display the SQL Data Sources dialog, shown in Figure 22.4. Figure 22.5 shows the NorthWind example opened in DOCDB. Figure 22.4. DOCDB and the dialog allowing a database file to be opened. Figure 22.5. The DOCDB program with the NorthWind sample database open. NOTE If you receive an Error Opening File message, or if the Column Name combo box is blank, open the database in Access and make sure that the Admin user (or your user ID) has read permissions on all system tables in the database. TIP You can compare the speed of operation of Access and Visual C++ database applications on your computer by timing the execution of both a Visual C++ application and an equivalent application that runs under Access. Also, if you're competent with Visual C++ 4's DAO, you might want to compare DAO with both ODBC and Access. The results might be enlightening! ##### Altering the Visual C++ Code in DOCDB You have the capability to print reports that contain the information that DOCDB displays in its main window. A CFormView application doesn't have printing enabled by default, and there is no code to print the window's contents. However, it's not difficult to take the information contained in the Columns[] array and format a simple report for your records. The SQLGetInfo() function will return a vast amount of information about drivers, SQL, and datasources. ##### Exporting Access's Database Documentor Output Access 2's Database Documentor lets you export a database document report; however, it won't export the subreports. This makes the exporting of database documents virtually useless. There is a solution, however, that requires only a minimal amount of work. You must install a generic, text-only printer in Windows, and then assign this printer to a file instead of a printer port. When the Database Documentor report is printed, it will be printed to a standard ASCII file. You can then import this file into Excel as desired. This problem has been fixed in Access 7, which exports in text formats and as an Excel spreadsheet. NOTE Access 7 allows you to directly save your documentation in an Excel, RTF, or DOS text format. If you will be using this documentation in Excel, you should export it directly in the Excel format rather than trying to load the text-formatted file into Excel. Listing 22.2 shows the NorthWind database documentation saved using the technique just described. This file can be saved directly in an Excel or Word (RTF) format or in a DOS text file format. Of course, any formatting that the report originally contained will be lost if exported in a DOX text file, but the report's information will be complete. In Listing 22.2, I've included only one table for the sake of brevity. The full report as produced by Access is over 50 pages long. (You can find this listing on the CD that comes with this book. It's called rptObjects.txt.) Listing 22.2. NorthWind documented by Access's Database Documentor.
>                                                         Thursday, January 18, 1996

>     Table: Categories                                                      Page: 1

>     Properties

>     Date Created:        3/17/94 3:01:37 PM             Def. Updatable:        True

>     Last Updated:        3/17/94 3:02:46 PM             Record Count:          8

>     Columns

>              Name                                   Type                       Size

>              Category ID                            Number (Long)                 4

>                       Allow Zero Length:         False

>                       Attributes:                Fixed Size, Auto-Increment

>                       Collating Order:           General

>                       Column Hidden:             False

>                       Column Order:              Default

>                       Column Width:              1110

>                       Description:               Number automatically assigned

>                                                                to new category.

>                       Ordinal Position:          0

>                       Required:                  False

>                       Source Field:              Category ID

>                       Source Table:              Categories

>              Category Name                          Text                         15

>                       Allow Zero Length:         False

>                       Attributes:                Variable Length

>                       Collating Order:           General

>                       Column Hidden:             False

>                       Column Order:              Default

>                       Column Width:              1395

>                       Description:               Name of food category.

>                       Ordinal Position:          1

>                       Required:                  True

>                       Source Field:              Category Name

>                       Source Table:              Categories

>              Description                            Memo                          -

>                       Allow Zero Length:         False

>                       Attributes:                Variable Length

>                       Collating Order:           General

>                       Column Hidden:             False

>                       Column Order:              Default

>                       Column Width:              4380

>                       Ordinal Position:          2

>                       Required:                  False

>                       Source Field:              Description

>                       Source Table:              Categories

>              Picture                                OLE Object                    -

>                       Allow Zero Length:         False

>                       Attributes:                Variable Length

>                       Collating Order:           General

>                       Column Hidden:             False

>                       Column Order:              Default

>                                                         Thursday, January 18, 1996

>     Table: Categories                                                      Page: 2

>                       Column Width:              1470

>                       Description:               A picture representing

>                                                             the food category.

>                       Ordinal Position:          3

>                       Required:                  False

>                       Source Field:              Picture

>                       Source Table:              Categories

>     Relationships

>              Reference1_Products

>                                Categories                           Products

>                         Category ID                1        Category ID

>                       Attributes:                Enforced

>                       Attributes:                One-To-Many

>     Table Indexes

>              Name                                     Number of Fields

>              Category Name                            1

>                       Clustered:                 False

>                       Distinct Count:            8

>                       Foreign:                   False

>                       Ignore Nulls:              False

>                       Name:                      Category Name

>                       Primary:                   False

>                       Required:                  False

>                       Unique:                    True

>                       Fields:                    Category Name, Ascending

>              PrimaryKey                               1

>                       Clustered:                 False

>                       Distinct Count:            8

>                       Foreign:                   False

>                       Ignore Nulls:              False

>                       Name:                      PrimaryKey

>                       Primary:                   True

>                       Required:                  True

>                       Unique:                    True

>                       Fields:                    Category ID, Ascending

>                                                         Thursday, January 18, 1996

>     Database: I:\Database Developers Guide with Visual C++ 4\Source        Page: 3

>     CD\NorthWind 7.mdb

>     Properties

>     AccessVersion:         06.68                          Build:                 4

>     Collating Order:       General                        Def. Updatable:     True

>     Query Timeout:         60                             Records Affected:      0

>     Transactions:          True                           Version:             3.0

>     User Permissions

>              admin

>              User

>              User1

>     Group Permissions

>              Admins

>              Users

>                                                         Thursday, January 18, 1996

>     Relationships: All                                                     Page: 4

>     Relationships

>              Reference

>                                 Customers                            Orders

>                         Customer ID                1        Customer ID

>                       Attributes:                Enforced, Cascade Updates

>                       Permissions:               One-To-Many

>              Reference1

>                                 Suppliers                           Products

>                         Supplier ID                1       Supplier ID

>                       Attributes:                Enforced

>                       Attributes:                One-To-Many

>              Reference1_Products

>                                Categories                           Products

>                         Category ID                1       Category ID

>                       Attributes:                Enforced

>                       Attributes:                One-To-Many

>              Reference2

>                                 Products                          Order Details

>                         Product ID                 1       Product ID

>                       Attributes:                Enforced

>                       Attributes:                One-To-Many

>                                                         Thursday, January 18, 1996

>     Relationships: All                                                     Page: 5

>              Reference3

>                                 Shippers                             Orders

>                                                    1     

>                         Shipper ID                          Ship Via

>                       Attributes:                Enforced

>                       Attributes:                One-To-Many

>              Reference4

>                                  Orders                           Order Details

>                                                    1     

>                         Order ID                            Order ID

>                       Attributes:                Enforced, Cascade Deletes

>                       Attributes:                One-To-Many

>              Reference5

>                                 Employees                            Orders

>                         Employee ID                1       Employee ID

>                       Attributes:                Enforced

>                       Attributes:                One-To-Many
Of course, you can simply print the report generated by the Database Documentor using a high-quality printer. Figure 22.6 shows the first page of the report in Access. The actual printout is identical to the screen image. Figure 22.6. The first page from Database Documentor. #### Using Access's Database Documentor to Document Databases Access 2's Database Documentor lets you create tables you can use to develop a data-dictionary application. The Database Documentor is located under Tools | Analyze | Documentor in Access 7. To use the Database Documentor to create information from which you can build a data dictionary, follow these steps: Launch Access's Database Documentor by selecting Tools | Analyze | Documentor. You will see the Database Documentor dialog, shown in Figure 22.7. If you want to, you can use this dialog to display a list of all the objects in the currently open database by changing the Object Type selection. Figure 22.7. Database Documentor's form displaying tables in the database. Click the Select All button to select all the tables, and then click the OK button to display the results of Database Documentor. You can add the tables to any Access database file, including the database that you're analyzing. In this case, select NorthWind.MDB and click the OK button to start the analysis. After a period of intense disk activity, you will see the results of Database Documentor. When you have finished viewing the results (and printing, if desired), you can simply close the Database Documentor window. #### Documenting Databases Created with CASE Tools If you create your Access or client-server databases with a computer-aided software engineering (CASE) tool, such as Asymetrix's InfoModeler, you can expand the contents of your data dictionary to include descriptions of the underlying object types, fact types, and constraints from which the design of the database is created. Using InfoModeler to create Access databases is one of the subjects of Chapter 4, "Optimizing the Design of Relational Databases." Figure 22.8 shows part of one page (Advertising) of the database diagram of the MailOrder tutorial application that accompanies InfoModeler version 1.5. Figure 22.8. A part of the Advertising element of InfoModeler 1.5's MailOrder tutorial application. All the information about object types, fact types, and constraints is stored in a repository, a database that contains information about databases. Using InfoModeler's reporting features, you can create a report that you can integrate with a data dictionary for the database that InfoModeler creates for you. A part of the report that InfoModeler creates for the Advertising element of the MailOrder application is shown in Figure 22.9. (The entire report is eight pages long.) Figure 22.9. Part of an InfoModeler report in print preview mode. After you've completed your database design, InfoModeler creates the database for you. If you have the retail version of Access, you can create an Access database. Otherwise, InfoModeler creates the ANSI (generic) SQL statements required to build the database. Figure 22.10 shows Access's design-mode view of the CoopAd table. InfoModeler creates the CoopAd table from the information stored in the repository that underlies the database diagram shown in Figure 22.8 and that is described in the report file, part of which appears in Figure 22.9. Figure 22.10. An Access table in the MAIL_ORD.MDB database created by InfoModeler. NOTE One of the advantages of using a CASE tool such as InfoModeler is that you can include the table and field descriptions in your data dictionary. The table and field descriptions originate in InfoModeler, not in Access, so the values of the Description property of the Table and Field objects that InfoModeler creates are available to your data dictionary application. You'll need to dig a bit, however, to obtain the data from Raima's Vista database, which is used as InfoModeler's repository. #### Using Total Access to Create a Data Dictionary Financial Modeling Specialists (FMS), Inc.'s Total Access is an Access 2 library database that you attach to the retail version of Access using a setup program supplied by FMS. FMS's Total Access provides comprehensive documentation and analysis of Access databases, not just the simple listing of some table and field properties that Access's Database Documentor generates. Total Access includes a large number of built-in reports that you can use to print a complete description of the database, including a detailed data dictionary for Table and Field objects. You also can modify Total Access's reports to suit your own requirements. Total Access generates thorough documentation for all Access database objects. Figure 22.11 shows Total Access's Document Form, which corresponds to the Database Analyzer, with NorthWind.MDB as the active database. If you're using Access databases only with Visual C++ applications, you'll only be able to take advantage of Total Access's Table and Queries documentation features. Figure 22.11. Total Access's main analysis window. Start Total Access by selecting File | Add-ins | Total Access Analyzer. After you've selected the objects you want to document, click the Next button and wait while Total Access does its job. A progress monitor shows you how much of the documentation process has been completed. Preparing the documentation for the 125 objects of NorthWind.MDB (everything in the NorthWind.MDB database) took about 10 minutes on a Pentium server and a Pentium 90 computer with 32M of RAM and a total of 6 gigabytes of high-speed SCSI fixed-disk space. If you limit your documentation to tables and queries, for example, this processing time will be less. Figure 22.11 shows all objects contained in NorthWind.MDB following an analysis. After Total Access builds the required tables, creating and printing reports such as the Table Dictionary shown in Figure 22.12 is a fast process. Figure 22.12. Total Access's Table Dictionary report in print preview mode. One of the principal benefits of using Total Access for creating data dictionaries is that you can open TA2DOC_C.MDA as a database in your Visual C++ data dictionary generator. Total Access creates one or more internal tables with a TA2_ prefix for each class of Access database object. Figure 22.13 shows part of the TA2_OutputFields table for the NorthWind.MDB database. Total Access includes the Description properties of Table and Field objects in its tables and reports. FMS obtains this information by opening each Table object in design mode and reading the value of the property. Figure 22.13. One of the tables created by Total Access to document Access Table objects. ### Creating Manuals for Your Database Applications Despite the almost universal reluctance of software users to refer to printed manuals, your application isn't complete until you've written its user documentation. Relying on users to read or print the README.TXT file that accompanies your application before installing it is seldom an effective approach. Most users will simply insert your distribution disk 1 in a drive, choose File | Run from Program Manager, type a:\setup or b:\setup in the Command Line text box, and then click the OK button. If this entry doesn't work, the user next tries a:\install or b:\install. When this fails, he might read the "Installation" section of your documentation. It's more likely, however, that the user will call you to find out what's wrong. Microsoft reports that the vast majority of the questions posed to members of the Product Support Staff for Access can be answered in either README.TXT or the documentation that accompanies Access. The same is undoubtedly true for Visual C++. Of course, you could be smart and include installation instructions on the label of the first disk of your application. If the installation instructions don't fit on the label, the installation is probably too complex and should be reworked. Remember that Windows 95 has specific installation requirements, many of which are discussed in Chapter 24, "Creating Distribution Disks for Visual C++ Applications." You should also refer to the Windows 95 documentation for more information about the Windows 95 logo requirements. #### The Structure of User Manuals The structure of user manuals for Visual C++ is similar to that of a book, even if the manual contains only a few pages. The following three sections describe the basic structure of a conventional software user manual. NOTE Much of the material in the following sections is based on information contained in Graphic Design for the Electronic Age by Jan V. White, a Xerox Press book published by Watson-Guptill Publications of New York. The book's subtitle, The Manual for Traditional and Desktop Publishing, aptly describes its content. Graphic Design for the Electronic Age provides background and pointers on type selection, page design, and publication construction. ##### Front Matter Front matter for user manuals usually is more extensive than that for a conventional textbook and follows the general structure of the front matter for computer software tutorial and reference works, such as this book. The following are the elements of front matter for a typical user manual: Title page, which includes the name of the application, the version number(s) of the application to which the manual applies, the publication date, the copyright information, the name of the author and the sponsoring organization (if applicable), and similar information. Introduction and acknowledgments, both of which are optional in application manuals. The introduction might include a brief description of the application and its objectives, as well as the objective of the manual. Typographic conventions, which describe how the text is formatted to distinguish information you read on-screen or type on the keyboard from the rest of the text. Conventions used to describe multikey operations, such as Alt-key or Ctrl-key, also are included in the front matter. The typographic conventions section should precede the first use of the conventions in the manual. Equipment requirements, which list the minimum PC configuration required to run the application. This section should include descriptions of any special hardware (such as a mouse), network connections, or software that is required to make full use of the application. In addition to minimum RAM requirements, you should specify the amount of free disk space required to install the application, separated into space required in the application's home directory and in the \WINDOWS and \WINDOWS\SYSTEM directories. Installation instructions, which describe the procedure required to set up the application. If the application makes changes to the user's CONFIG.SYS, AUTOEXEC.BAT, WIN.INI, or SYSTEM.INI files, these changes should be described in this section. A list of files that the installation requires and the location of those files also should be included here or in an appendix. Uninstall instructions, which describe the process of removing the application from the user's disk drive. This includes undoing changes made to WIN.INI, SYSTEM.INI, and any other private initialization files, such as MSMAIL.INI. Alternatively, uninstall instructions can be located in an appendix. Other front matter that won't be incorporated into the online help file. An example is a bibliography, which lists other publications needed to use the application effectively or that describe how the application is integrated into a suite or collection of applications. Table of contents, which diagrams the book's structure. The table of contents (TOC) should include the titles and page numbers of each of the chapters or the principal elements of the book. The Contents choice of the help file is based on your manual's TOC. Except for the table of contents, front matter normally isn't included in the online help file for your application. You can move the installation instructions to the text section of the book if you want, but the preferred location is close to the front of the manual. Users are more likely to refer to the instructions if they appear in the first few pages of the manual. ##### Text The text of manuals usually is broken into chapters and sections. If the manual consists of more than about 10 chapters, you might want to break the text into collections of related chapters, called parts. If your manual contains a tutorial and a reference guide for the application, separate parts should be assigned to contain the chapters in each category. The text component of the manual is used to create the online help file. Separate help files often are used for the tutorial and reference parts. ##### Back Matter Material that is supplemental to the text or that assists readers in understanding the text is included in the back matter. Back matter may contain the following elements: A glossary, which explains terms that might be unfamiliar to readers. A good practice is to explain unfamiliar terms when they're first used and to include the explanation in the glossary. This lets the reader look up the meaning of the term if he or she missed the initial definition. An appendix, which might include "Most-Frequently-Asked Questions," a list of error messages (and explanations of why the errors occur and what to do when the user encounters them), a general troubleshooting guide, and, if necessary, an abbreviated data dictionary for the database(s) used by the application. An index, with references to the page number(s) for all significant topics covered in the text. The index serves as the list of search terms for your online help file. Reviewers of software documentation consistently pan user manuals that don't include complete and accurate indexes. Adding the glossary to the online help system is optional but recommended. Appendixes don't usually appear in help files, and the index serves as the backbone of the search topics in the help system. #### Using Doc-To-Help to Create Convertible Manuals WexTech Systems, Inc.'s Doc-To-Help application consists of a set of templates for Word for Windows that are specifically designed for writing software user manuals, plus other files that are associated with converting the manuals into Windows WinHelp (.HLP) files. Chapter 23 describes how to use Doc-To-Help to convert the user manuals you write into .HLP files. Standard templates are included for conventional 8 1/2- by 11-inch manuals with conventional section headings, like those used in this book (D2H_NORM.DOT), or a side-head style, in which the headings appear in the left margin (D2H_SIDE.DOT). Doc-To-Help also supports the standard 7- by 9-inch manual format (D2H_SMAL.DOT). Figure 22.14 shows a WexTech sample file, D2HINTRO.DOC, that uses D2H_NORM.DOT opened in Word for Windows. Figure 22.14. A Doc-To-Help sample file with an embedded illustration in metafile format. NOTE Doc-to-Help comes in two versions. Version 1.7 supports both Word 2 and Word 6. Version 2 supports Word 7. There is an upgrade path to let Doc-To-Help 1.6 users upgrade to either version 1.7 or 2 as needed. Although you can write manuals that you can convert to WinHelp .HLP files with any word processing application that supports footnotes and can export text and images as Microsoft rich text format (.RTF) files, using Word for Windows together with Doc-To-Help saves considerable time in creating the printed version of your manual. Doc-To-Help's standard templates provide the foundation for attractively formatted manuals, but you can alter the styles employed by the standard templates if you need to conform to company-wide publication standards. Figure 22.15 illustrates Doc-To-Help's standard 8 1/2- by 11-inch manual style. The major time-saving feature of Doc-To-Help—and thus the principal justification for acquiring Doc-To-Help—is the ease with which you can compile your manuals into WinHelp .HLP files that provide access to almost every feature offered by the Windows 3.1 version of WINHELP.EXE. Figure 22.15. A page that illustrates Doc-To-Help's standard manual style. ### Summary This chapter described several methods of creating data dictionaries for your Visual C++ database applications, how to use a commercial cross-referencing application to document your Visual C++ code, and the basic structure of the printed manual that should accompany every production database application. The next chapter shows you how to use WexTech's Doc-To-Help to convert user manuals to Windows WinHelp files. Contact reference@earthweb.com with questions or comments. Copyright 1998 EarthWeb Inc., All rights reserved. PLEASE READ THE ACCEPTABLE USAGE STATEMENT. Copyright 1998 Macmillan Computer Publishing. All rights reserved. |