LIBRARIES

Summary:
 
This article describes a Microsoft Access library and discusses some
basics on how to create and debug a library, as well as some things to
watch out for.
 
This article assumes that you are familiar with Access Basic and with
creating Access applications with the programming tools provided with
Access.
 
More Information:
 
Access Basic Library Defined
----------------------------

When you write an Access application, such as the NWIND application
with the package, the application works only within the database in
which it was created. This is satisfactory for many applications that
specifically use the data that resides in the application's database.
 
However, many Microsoft Access developers write generic applications,
programs, and utilities that are designed to work on any user
database. An example of this is Wizards. Wizards are Access Basic
programs that reside in their own database, but are available to the
user in any database the user has open. If this weren't the case, you
could not use a Wizard outside of the database that the Wizard program
and system objects reside in. In order to use a program (such as a
Wizard) so that its code and objects are available to any user
database, you must load the database containing the program and its
objects as a library.
 
To load a database as a library, you must open MSACCESS.INI and add an
entry to the Libraries section. When you open MSACCESS.INI initially,
you will probably see a Libraries section with an entry for the
Wizards library:
 
   [Libraries]
   wizard.mda=ro
 
Note: If there is no Libraries section, add it to the end of the file
and continue. The MSACCESS.INI file can be found in your Windows
directory.
 
The "ro" in the Wizard entry means that the library is read-only. If
you have an application that uses system tables that are to be written
to at any point in your program, you would specify "rw" rather than
"ro". For example, suppose you have an application in a file called
STOCKAPP.MDB that employs the use of system tables that can be
modified. You would add the following entry to use the MDB file as a
library:
 
   [Libraries]
   wizard.mda=ro
   stockapp.mdb=rw
 
Given these library entries, the WIZARD.MDA library will be loaded
read-only, and the STOCKAPP.MDB library will be loaded as read- write.
You can now open an Immediate window in a user database and invoke sub
and function procedures from STOCKAPP.MDB, or open tables, queries,
forms, or reports with DoCmd commands. Even though you can access the
code and the database objects, you cannot see them in the Database
window.
 
When a database is loaded as a library, it cannot be opened as a user
database.
 
Writing and Debugging Access Basic Library Code
-----------------------------------------------

When you write an Access Basic application for use as a library, you
are doing little more than writing the application in a user database
with the intention of using it as a library at a later point. Because
of this, a rule of thumb is to make sure the application works
completely before trying to use it as a library.
 
Although this rule of thumb is enough to successfully create many
types of library applications, there are some important pitfalls to
watch for when writing a library application, even if the application
works perfectly as a user database.
 
Debugging an Error in a Library Database
----------------------------------------
 
If the library database generates an error that only occurs while it
is a library, it can be very difficult to locate. An error might occur
that gives you some idea of the general area of the problem, but there
may be little or no indication of the offending line. Because you
cannot set and use breakpoints and stepping in library applications,
you should design error traps that convey meaningful messages and
indicate the location of the problem.
 
Another debugging tip is to place MsgBox's at milestone areas of the
code so that you always have an idea of which code is being executed.
 

CodeDB() Versus CurrentDB()
--------------------------- 

Access Basic includes the CodeDB() function for opening library
databases. CodeDB() works identically to CurrentDB() if you are
running the application as a user database. However, if you are
running the application as a library, CodeDB() returns the database
object for the library database from which it was called, while
CurrentBD() returns the database object from the current open user
database. Because of this, it is easy to confuse one for the other,
which results in logical errors that do indicate this is the problem.
 
 
Domain Functions
----------------
 
Domain functions include a parameter that is used as criteria for
applying the function to a specified set of records. The criteria is
in the form of a SQL WHERE statement and assumes first that any table
in the criteria is located in the user database. This could pose a
problem for your application if you intend to perform a domain
function on a library table that happens to have the same name as a
user table. Of course, this does not happen frequently; but it becomes
very important when you work with Microsoft Access system tables which
will have the same name in both databases. If you are to include a
table within your library, you should  give this table a unique name
that you do not expect a user to duplicate such as "My Library
Table1."
 
Macros in a Library Cannot Be Called from a User Database
---------------------------------------------------------
 
Of all the objects you can create in a Microsoft Access database,
macros are the only type of object you cannot use in a library
application. The most obvious problem this presents is that forms
require the use of macros in order to have menus. Because of this
limitation, you must make sure to use only Access Basic code for
programming.


WIZARDS PART 1

Summary:

This article discusses the Microsoft Access undocumented functions 
CreateForm() and CreateReport().  If you intend to write your own
Forms and/or Reports Wizard, you can use these functions to create a
blank form or report to which you can add controls and otherwise
customize.

This article assumes that the reader has read and understands the
Knowledge Base article entitled, "Creating, Debugging, and Using an
Access Library" (Q88175).  This article also assumes you are familiar
with Access Basic and with designing forms and reports.

This article applies to Microsoft Access Version 1.0.


More Information:

CreateForm()/CreateReport() is roughly equivalent to choosing 'New 
Form/New Report' from the 'File' menu at the top of the screen.  Upon
executing the function, a new, empty form/report will appear in an
iconized state.

Both functions return an object value that you can use for further
manipulation and neither function requires parameters.

To use these functions, you must first define a form or report object
variable, then Set the variable to the function name.  An example of
how to do this is shown below:

   Dim MyForm As Form
   Set MyForm = CreateForm()

After opening the form/report in design mode after executing the
commands above, you can bind the form to a table or query by modifying
the form's/report's RecordSource property as shown below:

   MyForm.RecordSource = "Categories"

With the form/report available in design mode, you can access and 
change any of the other design-time properties of the form/report.  You 
can also access and change properties of each of the form's/report's 
sections via the Section property.  The Section property is actually an 
array with each array value being a reference to a form's/report's 
section.  For forms, the Section property array is broken down as shown 
below:

   Section(0) - Detail Section
   Section(1) - Form Header
   Section(2) - Form Footer
   Section(3) - Page Header
   Section(4) - Page Footer

For reports, the section property array is broken down like this:

   Section(0) - Detail Section
   Section(1) - Report Header
   Section(2) - Report Footer
   Section(3) - Page Header
   Section(4) - Page Footer
   Section(5) - Group Level 1 Header
   Section(6) - Group Level 1 Footer
   Section(7) - Group Level 2 Header
      ...etc.

With this information, you could customize the design of a form/report
section programmatically.  The following example creates a new report,
hides the Page Footer by setting its Visible property to False, sets
the Height of the Detail section, and enables its the KeepTogether 
property:

   Dim MyReport As Report
   Set MyReport = CreateReport()
   MyReport.Section(4).Visible = False
   MyReport.Section(0).Height = 1760
   MyReport.Section(0).KeepTogether = True


WIZARDS PART 2


Summary:

This article discusses the Microsoft Access undocumented functions 
CreateControl() and CreateReportControl().  If you intend to write your 
own Forms and/or Reports Wizard, you can use these functions to create 
controls on a form or report that is available in Design mode.

This article assumes that the reader has read and understands the
Knowledge Base articles entitled, "Creating, Debugging, and Using an
Access Library" (Q88175), and "Wizards Part I - CreateForm(), 
CreateReport(), Section Property" (Q?????).  This article also assumes 
you are familiar with Access Basic and with designing forms and 
reports.

This article applies to Microsoft Access Version 1.0.


More Information:

CreateControl() and CreateReportControl() can be used to create new
controls for a form/report opened in Design mode.  These functions
require the name of the form/report represented as a string value,
and a numeric code that represents the type of control.  The list of 
control types and their associated numeric codes are shown below:

   Label .......... 100
   Box ............ 101
   Line ........... 102
   Picture ........ 103
   Button ......... 104
   Radio Button ... 105
   Check Box ...... 106
   Ole Object ..... 108
   Text Box ....... 109

The CreateControl() and CreateReportControl() functions return a
control object value, so you must first define a control variable,
then Set the variable to the function name.  Note the code example
below which creates a form, then adds a button to the form:

   Dim MyForm As Form, MyControl As Control
   Set MyForm = CreateForm()
   Set MyControl = CreateControl(MyForm.FormName, 104)

After executing the code example above, you can modify the properies
of the new control via the control variable you defined.  For example:

   MyControl.Width = 2000
   MyControl.Caption = "&Sum All Records"

For controls that are frequently associated with fields in a table or
query, you can modify the ControlSource property to bind the control.
Some controls are created with Height and Width properties set to 0,
so that they are - in effect - not visible.  In addition, controls
appear at the uppermost left-hand corner of the form by default. 
Because of this, you should make it a habit to adjust the size and
position of the control immediately after creating it.  The example
below shows how to create, size, move, and bind a text box.

   Set MyControl = CreateControl(MyForm.FormName, 109)
   MyControl.Width = 1500
   MyControl.Height = 200
   MyControl.Top = 440
   MyControl.Left = 200
   MyControl.ControlSource = "[Category ID]"

In addition to specifying the form name and type of control, you can
optionally specify the Section in which the control should be created.
Note the syntactical structure of CreateControl(), (which is identical 
to the syntactical structure of CreateReportControl()):

   Function CreateControl (FormName As String, 
                           ControlType As Integer
                           [,SectionNumber As Integer])

Use the SectionNumber parameter to indicate what section the control is
to be placed into.  The section that is associated with the 
SectionNumber is the same as the index representing a section in a
Section property array.  


WIZARDS PART 3

Summary:

This article discusses the Microsoft Access undocumented commands 
DeleteControl, DeleteReportControl, and the undocumented function 
CreateGroupLevel().  If you intend to write your own Forms and/or 
Reports Wizard, you can use these functions to delete controls on a form 
or report that is available in Design mode, and create groups on a 
Report that is available in Design mode.

This article assumes that the reader has read and understands the
Knowledge Base articles entitled, "Creating, Debugging, and Using an
Access Library" (Q88175), "Wizards Part I - CreateForm(), CreateReport(), 
Section Property" (Q?????), and "Wizards Part II - CreateControl(), 
CreateReportControl()" (Q?????).  This article also assumes you are 
familiar with Access Basic and with designing forms and reports.

This article applies to Microsoft Access Version 1.0.


More Information:

DeleteControl and DeleteReportControl can be used to delete a control 
that exists on a form/report in design mode.  Both of these functions 
require a string value that represents the name of the form and a string 
value that represents the name of the control.

DeleteControl and DeleteReportControl are commands - not functions.  
They do not return a value.

The example below creates a form, then creates a button on the form,
displays a message, then deletes the button on the form:

   Dim MyForm As Form, MyControl As Control
   Set MyForm = CreateForm()
   Set MyControl = CreateControl(MyForm.FormName, 104)
   MsgBox "About to Delete " & MyControl.ControlName
   DeleteControl MyForm.FormName, MyControl.ControlName

When you delete a control using DeleteControl or DeleteReportControl,
there is no visual feedback that the control is gone if the form/report
is not iconized.  The control still appears as if it were not deleted
even though you could not click it into focus.  To make the deleted 
control disappear in such a case, you would have to repaint the form
design window by minimizing and restoring it.

If you have a report available in design mode, you can create groups 
for it programmatically by using the CreateGroupLevel() function.  The
syntactical structure of CreateGroupLevel() is shown below:

   Function CreateGroupLevel(ReportName As String,
                             Expression As String,
                             HasHeader As Integer,
                             HasFooter As Integer)

ReportName is a string expression that indicates the name of the Report.

Expression is a string expression that represents the field name or
calculated field on which the group will be based.

HasHeader and HasFooter indicate whether the group includes a group
header or group footer, respectively.

When CreateGroupLevel() is executed, it adds a group at the innermost
level.  For example, if one group already exists, CreateGroupLevel()
will create a second group within the first group.  CreateGroupLevel()
returns a number indicating which level it created, beginning with zero.  
In the example just mentioned, CreateGroupLevel() would return 1 since
it is the second group, and the first group was Group 0.
