Access 2002 Development Stephen Forte, Thomas Howe, Kurt Wall, Paul Kimmel, Russ Mullen
Unleashed
Access 2002 Development Unleashed Copyright ©2002 by Sams All rights reserved. No part of this book shall be reproduced, stored in a retrieval system, or transmitted by any means, electronic, mechanical, photocopying, recording, or otherwise, without written permission from the publisher. No patent liability is assumed with respect to the use of the information contained herein. Although every precaution has been taken in the preparation of this book, the publisher and authors assume no responsibility for errors or omissions. Nor is any liability assumed for damages resulting from the use of the information contained herein.
EXECUTIVE EDITOR Rosemarie Graham
ACQUISITIONS EDITOR Angela Kozlowski
DEVELOPMENT EDITOR Kevin Howard
MANAGING EDITOR Charlotte Clapp
PROJECT EDITOR
International Standard Book Number: 0-672-32120-3
Leah Kirkpatrick
Library of Congress Catalog Card Number: 00-139197
COPY EDITOR Mike Dietsch
Printed in the United States of America
INDEXER
First Printing: September 2001 04
03
02
01
4
Cheryl Landes 3
2
1
PROOFREADER
Trademarks
Plan-It Publishing
All terms mentioned in this book that are known to be trademarks or service marks have been appropriately capitalized. Sams cannot attest to the accuracy of this information. Use of a term in this book should not be regarded as affecting the validity of any trademark or service mark.
TECHNICAL EDITORS
Warning and Disclaimer
TEAM COORDINATOR
Every effort has been made to make this book as complete and as accurate as possible, but no warranty or fitness is implied. The information provided is on an “as is” basis. The authors and the publisher shall have neither liability nor responsibility to any person or entity with respect to any loss or damages arising from the information contained in this book or from the use of the CD or programs accompanying it.
Lynne Williams
Jason Merrill Pete Klein Keith Giddeon
MEDIA DEVELOPER Dan Scherf
INTERIOR DESIGNER Gary Adair
COVER DESIGNER Aren Howell
PAGE LAYOUT Ayanna Lacey
Contents at a Glance Introduction 1
PART I
Database Design Unleashed
1
What’s New for Developers in Access 2002 7
2
Planning the Development Process 15
3
Database Design and Normalization 45
4
Advanced Queries 59
5
The Microsoft Jet Database Engine 4.0 99
PART II
Data Access
6
Introduction to ActiveX Data Objects 119
7
Advanced ADO 147
PART III
User Interfaces Unleashed
8
Advanced Form Design 179
9
Enhancing Forms with ActiveX Controls 203
10
Part IV
Reporting Unleashed 241
VBA Unleashed
11
Creating Objects with Class Modules 287
12
Debugging Access Applications 317
13
Professional Error Handling 337
14
Application Optimization 369
Part V
Access Client Server
15
Introducing Access Data Projects and the Visual Tools 413
16
Developing Access Front-Ends to Microsoft SQL Server 435
17
Access 2002 Front-Ends to Oracle 461
Part VI
Interoperability
18
Using ActiveX Automation 505
19
Integrating with Microsoft Office 529
20
Using Visual Basic with Access 571
Part VII
Multiuser Issues
21
Multiple Users and Database Locking 617
22
Security 635
PART VIII
Web Publishing with Access 2002
23
Configuring a Web Site for Web Publishing 669
24
Web Enabling Access 2002 with Office Web Components 701
25
Using Data Access Pages 715
26
Web Publishing with Access 2002 and Active Server Pages 731 Index 773
Contents Introduction
PART I
1
Database Design Unleashed
5
1
What’s New for Developers in Access 2002 7 User Interface Changes............................................................................8 Changes to the VBE ................................................................................9 Compatibility with Access 2000 Databases ..........................................10 Database Conversion Error Logging ....................................................11 Offline Data Access Pages ....................................................................11 Improved Integration with SQL Server 2000 ........................................12 Extended Property Support ..............................................................12 Batch Updates ..................................................................................12 Password Security for Access Projects ............................................13 Other Access 2002 Features ..................................................................13 PivotChart and PivotTable Data Views ............................................13 XML Support....................................................................................13 Linked Table Wizard ........................................................................13 ANSI-92 Query Mode sans ADO ....................................................13 Summary ................................................................................................14
2
Planning the Development Process 15 Gathering Requirements ........................................................................17 Why Are Requirements So Important? ............................................17 Finding the Real Problem ................................................................18 Investigation......................................................................................19 Process Diagrams ............................................................................20 Identify the Scope of the Project......................................................21 Writing Down Requirements............................................................21 Architecture............................................................................................24 Development Planning ..........................................................................24 Delivery Strategy ..............................................................................25 Standards ..........................................................................................26 Construction ..........................................................................................28 Divide and Conquer: Construction Logistics ..................................29 Builds and Releases..........................................................................30 Creating a Detailed Design ..............................................................34 Reviewing the Design ......................................................................35 Implementation ................................................................................36 Code Review ....................................................................................36
vi
Access 2002 Development UNLEASHED Testing ..............................................................................................36 Defect Tracking ................................................................................38 Version Control ................................................................................42 Summary ................................................................................................43 3
Database Design and Normalization 45 The Relational Design Theory ..............................................................48 The Benefits of Using the Relational Model ..................................48 Tables and Uniqueness ....................................................................49 Foreign Keys and Domains ..............................................................50 Relationships ....................................................................................51 Data Normalization ..........................................................................54 Data Integrity Rules..........................................................................57 Summary ................................................................................................58
4
Advanced Queries 59 Using the Query Object ........................................................................60 Creating Queries in Access....................................................................62 Using the Query Design Window ....................................................62 Using the Table Pane ........................................................................66 Using the Query Grid ......................................................................72 Creating Advanced Queries ..................................................................74 Mastering Totals Queries ......................................................................75 Counting Records With Count ..........................................................76 Calculating Averages with Avg ........................................................77 Using Min and Max ............................................................................78 Using First and Last ......................................................................78 Validating Data With StDev and Var ................................................78 Creating Custom Aggregation Queries with Expression ................79 Understanding the Where Clause ......................................................80 Mastering Crosstab Queries ..................................................................83 Mastering Parameter Queries ................................................................87 Creating Parameter Queries in the Query Design Window ............87 Using Parameterized Queries in Code ............................................89 Executing a Query Using a Parameter Collection ..........................90 Mastering Pass-Through Queries ..........................................................91 Mastering Data Definition Queries........................................................93 Creating a New Table ......................................................................94 Defining Field Properties ................................................................95 Altering a Table ................................................................................96 Creating an Index ............................................................................96 Removing a Table ............................................................................97 Summary ................................................................................................97
CONTENTS 5
PART II 6
The Microsoft Jet Database Engine 99 The History of Microsoft Jet ..............................................................100 Jet 1.0..............................................................................................100 Jet 1.1..............................................................................................100 Jet 2.0..............................................................................................101 Jet 2.5..............................................................................................101 Jet 3.0..............................................................................................101 Jet 3.5..............................................................................................101 Jet 3.51............................................................................................101 Jet 4.0..............................................................................................102 Future Versions of Microsoft Jet ....................................................102 Using Jet 4.0 Features in Your Applications........................................103 Native OLE DB Provider ..............................................................103 Row Level Locking ........................................................................103 Full Unicode Support ....................................................................103 Jet Data Types ................................................................................107 Enhanced AutoNumber Capabilities ..............................................109 Searchable Memo Fields ................................................................110 Connection Control/Passive Shutdown ..........................................110 New SQL Grammar........................................................................110 Summary ..............................................................................................116
Data Access
117
Introduction to ActiveX Data Objects 119 History of Data Access ........................................................................120 Proprietary APIs ............................................................................120 Open Database Connectivity (ODBC) ..........................................120 Microsoft Jet/Data Access Objects (DAO) ....................................120 Remote Data Objects (RDO) and ODBCDirect ............................120 Microsoft’s Universal Data Access Initiative ......................................121 ActiveX Data Objects (ADO)..............................................................122 ADO 1.0 ........................................................................................122 ADO 1.5 ........................................................................................122 ADO 2.0 ........................................................................................122 ADO 2.1 ........................................................................................123 ADO 2.5 ........................................................................................124 ADO 2.6: Where We’re at Today ..................................................124 The ADO Object Model ......................................................................125 Connection Object..........................................................................126 Executing a SQL Statement off a Connection Object ..................129 ADO Recordset Object..................................................................130
vii
viii
Access 2002 Development UNLEASHED Using a Recordset with Command and Parameter Objects ............134 Using a Record Object ..................................................................137 Executing an Action Query via a Command..................................138 Field and Property Objects ..........................................................139 Using the Stream Object ................................................................140 Managing Errors with the Error Object ........................................141 Moving from DAO to ADO ................................................................142 Converting from Prior Versions of Access ....................................142 Should I Convert to ADO? ............................................................143 The ADO Object Model Compared to DAO ......................................145 Summary ..............................................................................................146 7
PART III 8
Advanced ADO 147 Using the OLE DB Provider for Jet in Access 2002 ..........................148 CurrentProject.Connection ........................................................151 Using Microsoft Data Links to Connect to a Database ................152 Accessing Non-Relational Data with ADO ........................................157 The Jet User Roster ........................................................................157 Creatable Recordsets ......................................................................158 Data Shaping ..................................................................................159 Advanced Data Manipulation with ADO ............................................162 Modifying Data in a Recordset ......................................................162 Persisted Recordsets ......................................................................165 Data Definition with ADOX ................................................................167 The Catalog Object ........................................................................167 Creating a Database........................................................................168 Creating Tables and Fields ............................................................169 Creating Relationships in ADOX ..................................................172 Creating Queries in ADOX ............................................................173 Summary ..............................................................................................176
User Interfaces Unleashed
177
Advanced Form Design 179 Form Properties....................................................................................180 Data Properties ..............................................................................181 Format Properties ..........................................................................182 Other Properties..............................................................................184 Useful Properties Not Found in the Properties Window................186 Events ............................................................................................187 Access Form Controls..........................................................................189 The Combo Box Control ..................................................................189 List Boxes ......................................................................................192 The Multi-Select List Box Control ................................................193
CONTENTS Subforms ........................................................................................194 Built-In Tab Control ......................................................................195 Option Group..................................................................................195 Pop-up Menus ................................................................................196 Hyperlinks ......................................................................................196 Creating PivotTable and PivotChart Views..........................................197 Creating a PivotTable View ............................................................197 Converting Between PivotCharts and PivotTables ........................201 Summary ..............................................................................................202 9
Enhancing Forms with ActiveX Controls 203 How to Use ActiveX Controls ............................................................204 Types of ActiveX Controls ............................................................204 Where Can You Get ActiveX Controls? ........................................205 Are ActiveX Controls Safe? ..........................................................205 Can I Use and Distribute ActiveX Controls in My Application? ................................................................................206 Using ActiveX Controls ......................................................................206 Installing an ActiveX Control ........................................................206 Registering an ActiveX Control ....................................................207 Adding an ActiveX Control to a Form ..........................................207 Setting the ActiveX Control Properties ..........................................208 Writing Code to Execute Methods or Respond to Events ............209 21 ActiveX Controls ............................................................................210 Animation Control ..........................................................................212 Calendar Control............................................................................213 Common Dialog Control ..................................................................214 DateTimePicker Control ................................................................215 FlatScrollBar Control ..................................................................217 ImageCombo Control ........................................................................218 ImageList Control ..........................................................................219 ListView Control............................................................................221 MAPISession Control ......................................................................222 MAPIMessages Control ....................................................................223 MonthView Control ..........................................................................224 ProgressBar Control ......................................................................226 RichText Control............................................................................227 Slider Control................................................................................228 StatusBar Control ..........................................................................229 SysInfo Control..............................................................................231 TabStrip Control............................................................................232 Toolbar Control..............................................................................233 TreeView Control............................................................................234
ix
x
Access 2002 Development UNLEASHED Control................................................................................235 Control ........................................................................236 Distributing ActiveX Controls ............................................................238 Summary ..............................................................................................239 UpDown
WebBrowser
10
Reporting Unleashed 241 Approaching Reports ..........................................................................242 Understanding the Architecture of Access Reports ............................242 Building a Single Table Report Using the Report Wizard ..................244 Customizing Reports............................................................................247 Changing the Recordsource in a Report ........................................247 Changing the Grouping Structure in a Report ..............................248 Implementing Grouping in a Report ..............................................250 Using Functions in a Report ..........................................................250 Working with Subreports ....................................................................256 Creating a Simple Subreport ..........................................................257 Creating Simple Mailing Labels..........................................................259 Publishing a Report..............................................................................262 Exploring the Methods for Publishing Reports..............................263 Modifying a Report at Runtime ..........................................................266 Filtering and Sorting ......................................................................266 GroupKeepTogether ........................................................................267 HasData ..........................................................................................267 Report Events (Runtime)................................................................268 Section Properties..........................................................................269 Section Properties (Design Time) ..................................................270 Section Properties (Runtime) ..........................................................272 Building Reports Programmatically ....................................................274 Creating the RecordSource ............................................................275 Creating the Report Object ............................................................276 Creating Sections............................................................................278 Tips and Tricks ....................................................................................279 Make a Biweekly Grouping ..........................................................279 Hide Repeating Data ......................................................................279 Group Data Alphabetically ............................................................279 Make Numbered Lists ....................................................................280 Create Empty Lines Every n Spaces ..............................................280 Reset Page Number for New Groups ............................................281 Draw Vertical Lines ........................................................................281 Move Page Numbers for Even-Odd Pages ....................................282 Identify the User Printing the Report ............................................282 Align Pages for Binding ................................................................282
CONTENTS Calculate Page Totals......................................................................283 Fine Control Manipulation ............................................................284 Summary ..............................................................................................284
PART IV 11
VBA Unleashed
285
Creating Objects with Class Modules 287 Exploring the Benefits of Using Objects ............................................289 Hide Complex Functionality ..........................................................289 Use of Microsoft’s IntelliSense Technology ..................................289 Organize Code ................................................................................290 Allow Viewing Objects in the Object Browser ..............................290 Create Multiple Instances of the Object ........................................290 Make Code Easy to Update and Maintain ....................................291 Limit Access to Code ....................................................................291 Make Code Easily Portable ............................................................291 Reviewing Objects, Properties, and Methods......................................291 Creating Classes ..................................................................................292 Inserting a Class Module................................................................292 Creating Properties ..............................................................................292 Using Public Fields ........................................................................293 Using Property Procedures to Modify Fields ................................293 Public Variable or Property Procedures..........................................297 Creating Enumerated Data Types ..................................................297 Creating Methods ................................................................................299 Using Methods................................................................................300 Creating Events....................................................................................300 Using Events ..................................................................................300 Firing the Initialize and Terminate Events ....................................302 Using Objects ......................................................................................302 Creating an Object Variable............................................................302 Assigning an Object Variable to an Object ....................................303 Using the Object ............................................................................303 Releasing the Object ......................................................................303 Creating Multiple Instances of an Object............................................304 Examining More Object Examples......................................................304 Text File Object ..............................................................................304 MyTimer Class ................................................................................306 Sound Object ..................................................................................307 Letter Object ..................................................................................308 Outlook Object ..............................................................................310 Implementing an Error Handler Object ..............................................311 Using Objects with VBA Collections..................................................312
xi
xii
Access 2002 Development UNLEASHED Creating VBA Collections..............................................................313 VBA Collection’s Properties and Methods ....................................313 Summary ..............................................................................................316 12
Debugging Access Applications 317 Eliminating Logic Errors ....................................................................318 Working with the Visual Basic Development Environment (IDE)......318 The Project Explorer ......................................................................319 The Code Window ..........................................................................320 The Properties Window ..................................................................320 The Immediate Window ................................................................321 The Locals Window........................................................................321 The Watch Window ........................................................................321 The Object Browser........................................................................321 The Call Stack Window..................................................................322 Working with the Debug Object ..........................................................322 Debug.Print ....................................................................................323 Debug.Assert ..................................................................................324 Using the Immediate Window ............................................................324 Evaluating Variables ......................................................................324 Changing the Value of Variables ....................................................324 Evaluating Built-in Functions ........................................................324 Running Custom Subprocedures ....................................................324 Tips for Using the Immediate Window ..........................................325 Using the Debugger ............................................................................326 Setting Breakpoints ........................................................................326 Stepping Through Code..................................................................327 Using Microsoft IntelliSense While Debugging ............................329 Using the Locals Window ..............................................................329 Using the Watch Window ..............................................................329 Viewing the Call Stack ..................................................................330 Using Conditional Compilation ..........................................................331 Writing Solid Code ........................................................................332 Application Testing..............................................................................335 Practice Debugging Techniques ..........................................................335 Summary ..............................................................................................336
13
Professional Error Handling 337 Eliminating Syntax Errors ..................................................................338 Eliminating Logic Errors ....................................................................341 Eliminating Runtime Errors ................................................................341 A Simple Error Handler ................................................................341 The Err Object ..............................................................................344
CONTENTS Responding to Errors......................................................................346 The Resume Statements ..................................................................348 Getting Additional Error Information ............................................348 A Comprehensive Error Handler....................................................351 Errors in Various Applications ............................................................364 Error Handling With Nested Procedures ............................................365 Advanced Error Topics ........................................................................365 Error Event Procedures ..................................................................366 On Error GoTo 0............................................................................366 On Error Resume Next ..................................................................366 AccessError Method ......................................................................367 Useful Error Functions ..................................................................367 Settings Error Trapping Options ....................................................367 Summary ..............................................................................................368 14
Application Optimization 369 Strengthening the Foundation: Hardware and Windows Optimization......................................................................................371 Installing the Application for Optimal Performance ..........................373 Optimizing the Configuration of the Jet Database Engine..................374 Safely Changing the Jet Settings....................................................375 Tools to Measure Performance ............................................................381 Looking Behind the Scenes ................................................................383 Optimizing the Database from the Start ..............................................384 Designing Data Tables for Performance ........................................385 Normalizing Data to Enhance Performance ..................................385 Creating Indexes to Speed Queries ................................................386 Establish Relationships Early to Improve Performance ................386 Boosting Query Performance ..............................................................387 Evaluating the Type of Resultset for Optimum Performance ........390 Getting Forms to Run Faster ..............................................................395 In the Beginning… ........................................................................395 Getting Faster Images ....................................................................395 The Basic, Fast Form......................................................................396 Get the Reports Printed Faster ......................................................399 Writing Fast Code................................................................................400 How Code Uses Memory ..............................................................401 Maintaining Your Modules ............................................................401 Compiling and Decompiling Code ................................................401 Coding Tips and Hints ........................................................................402 Use Option Explicit ......................................................................402 Choose Variable Sizes with Care....................................................402 Save Stack Space with String Variables ........................................402
xiii
xiv
Access 2002 Development UNLEASHED Specific Object Type Declarations ................................................403 Toggle True and False ....................................................................403 Len() Instead of Empty String ......................................................404 True and False Evaluations Instead of Zero ..................................404 Fast Object References ..................................................................404 Fast Array Uses ..............................................................................405 Use Constants When Possible ........................................................406 The Proper Use of Bookmarks ......................................................406 Close and Destroy ..........................................................................407 Use SQL Instead of DAO ..............................................................407 Use the Index Number of Collections............................................407 Making Faster Loops......................................................................408 Ban IIF() from Code ....................................................................409 Arranging the Select Case ............................................................409 Use .Execute, Not RunSQL ............................................................409 Use QueryTimer() ..........................................................................409 Test the Efficiency of Transactions ................................................409 Control Refreshes ..........................................................................409 Reference ActiveX and Use Early Binding....................................410 Upsize to Client/Server ..................................................................410 Bread and Circuses ........................................................................410 Summary ..............................................................................................410
PART V 15
Access Client Server
411
Introducing Access Data Projects and the Visual Tools 413 Introducing Access Data Projects ........................................................414 The Pros and Cons of ADPs ..........................................................414 Using ADPs..........................................................................................415 Creating an ADP ............................................................................415 The New Database Container ........................................................417 Working with ADPs and Existing SQL Server Databases ..................419 Working with SQL Server Tables ..................................................419 SQL Server Queries........................................................................421 Stored Procedures ..........................................................................422 Database Diagrams ........................................................................423 Forms, Pages, Reports, Macros, and Modules ..............................424 Administering Your SQL Server via an Access ............................424 Reconnecting to the SQL Server Database ....................................425 Creating a Project Based on a New Database ....................................428 Creating Tables ..............................................................................428 Creating Relationships via a Diagram............................................430 Creating Views................................................................................431
CONTENTS Creating Stored Procedures ............................................................432 Creating the Access Application ....................................................433 Creating—ADE Files ....................................................................433 Summary ..............................................................................................434 16
Developing Access Front-Ends to Microsoft SQL Server 435 Client/Server Architecture: OLE DB Versus ODBC ..........................436 Setting Up Your SQL Server Front-End Connection ..........................436 Setting Up an ODBC Data Source (DSN) ....................................437 Linking Tables ................................................................................438 Stored Procedures and SQL Pass-Through Queries............................440 Basing Access Reports on Stored Procedures via PassThrough Queries ..........................................................................442 Reporting Against SQL Server in Access 2002 ..................................443 Advanced Features: Providing Parameters for the Stored Procedure at Runtime ..................................................................444 Additional Filtering of a Report at Runtime ..................................445 Using Forms in Your Application ........................................................448 Bound Forms ..................................................................................448 Unbound Forms ..............................................................................448 Advanced Features of the SQL Server OLE DB Provider..................450 Next Recordset ..............................................................................451 Executing Commands with Parameters ..............................................452 The Long Way ................................................................................453 Using Create Parameters ..............................................................454 Using Refresh ................................................................................456 Handling Return Values..................................................................457 Not Using a Command Object ..........................................................459 Using a Connection Class ..................................................................459 Using the Connection Class in Your Applications ........................460 Summary ..............................................................................................460
17
Access 2002 Front-Ends to Oracle 461 Accessing Oracle Data with Access ....................................................462 Table Linking..................................................................................462 SQL Pass-Through Queries (ODBC) ............................................465 Using Wildcards/Position Markers ................................................468 Functions in Oracle Versus Access ......................................................470 Strings ............................................................................................470 Mathematical Calculations in Oracle ............................................477 Date Calculations in Oracle............................................................479 Understanding Views and Stored Procedures......................................482 Creating a View ..............................................................................483 Connections to Oracle Through ADO............................................484
xv
xvi
Access 2002 Development UNLEASHED Creating Stored Procedures ............................................................487 Calling a Stored Procedure ............................................................489 Creating an Unbound Interface to Oracle............................................491 Building an Unbound Interface ......................................................491 Summary ..............................................................................................502
PART VI 18
Interoperability
503
Using ActiveX Automation 505 What Is ActiveX Automation? ............................................................506 Why Use Automation? ........................................................................506 Distinguishing Automation Server Versus Automation Client ............506 Determining Automation Resource Requirements ..............................507 Understanding the Big Picture ............................................................507 Creating and Setting a Reference to Another Application ..................507 Setting a Reference to Another Application ..................................507 Reviewing Objects, Properties, and Methods ................................508 Understanding Object Models........................................................509 Using the Object Browser ..............................................................509 Creating an Object Variable............................................................511 Referencing an Application That Is Already Running ..................512 Assigning an Object Variable to an Application..................................512 Creating an Instance of the Application ..............................................513 Using GetObject and the New Keyword Together ..........................513 Using Early Versus Late Binding ..................................................514 Using the CreateObject Function..................................................515 Using the Automation Object’s Properties and Methods ....................517 Setting an Object’s Properties ........................................................517 Setting an Object’s Methods ..........................................................517 Releasing the Automation Object ........................................................517 Putting It All Together ........................................................................518 Closing the Automation Server Application........................................519 Using the UserControl Property to Determine How an Application Was Opened ..................................................................520 Using WithEvents to Expose Events of the Automation Server ........520 Making WithEvents Work ..............................................................522 Using the Automation Tips and Techniques ........................................523 Set References, Use Early Binding, and the New Keyword............524 Use an Existing Application If It Is Already Open ........................524 Turn ScreenUpdating Off ..............................................................524 Give the Users Feedback ................................................................525 Have the Automation Server Run the Code ..................................525 Use the With/End With Construct..................................................526
CONTENTS Release Object Variables ................................................................526 Do Not Display Dialog Boxes and Message Boxes ......................527 Use Error Handling ........................................................................527 Summary ..............................................................................................527 19
Integrating with Microsoft Office 529 Why Integrate with Microsoft Office? ................................................531 Using Word ....................................................................................531 Using Excel ....................................................................................532 Using PowerPoint ..........................................................................532 Using Outlook ................................................................................532 Using Graph....................................................................................532 Use the Right Tool ..............................................................................533 Using the Macro Recorder to Write Code ..........................................533 Using Auto Macros ..............................................................................535 Microsoft Forms ..................................................................................536 Object Browser ....................................................................................537 Class Arguments for Office Applications ............................................537 Automation Example ..........................................................................538 Automating Word ................................................................................540 The Word Object Model ................................................................540 Using Word Templates ..................................................................542 Inserting Data in Word Documents ................................................543 Word Automation Code Examples ................................................546 Word Fields ....................................................................................552 Document Summary Information ..................................................553 Other Word Features ......................................................................555 Automating Excel ................................................................................555 Excel Object Model........................................................................555 Excel Automation Code Examples ................................................556 Automating PowerPoint ......................................................................559 The PowerPoint Object Model ......................................................559 PowerPoint Automation Code Examples ......................................560 Automating Outlook ............................................................................562 The Outlook Object Model ............................................................562 Adding and Displaying Outlook Folders ......................................564 Adding a New Task and Displaying Task Items ............................564 Creating an Email Message with an Attachment ..........................564 Creating Outlook Items ..................................................................565 Displaying Default Outlook Folders ..............................................566 Display an Outlook Public Folder..................................................566 Finding an Outlook Item ................................................................567 Filtering Outlook Items ..................................................................567
xvii
xviii
Access 2002 Development UNLEASHED Automating Graph ..............................................................................567 Graph Object Model ......................................................................567 Creating Graphs..............................................................................568 Summary ..............................................................................................569 20
PART VII 21
Using Visual Basic with Access 571 Creating ActiveX Code Components ..................................................572 What Is an ActiveX Code Component? ........................................573 What Is the Difference Between an ActiveX EXE and an ActiveX DLL? ........................................................................574 Creating an ActiveX Code Component ..........................................574 Compiling the DLL ........................................................................578 Using the cSound ActiveX Component ..........................................579 Distributing ActiveX Components ................................................581 The Error Handling Component ....................................................591 Global Error Handling Data ..........................................................592 The cError Component..................................................................593 Creating ActiveX Controls ..................................................................593 Types of ActiveX Controls ............................................................593 ActiveX Control Attributes ............................................................593 Creating a Design-Time ActiveX Control......................................594 Creating a Runtime ActiveX Control ............................................607 Summary ..............................................................................................614
Multiuser Issues
615
Multiple Users and Database Locking 617 Understanding Multiuser Issues ..........................................................618 Reviewing Jet’s Multiuser Design ......................................................619 Understanding Jet’s Multiuser Locking ..............................................619 The Database Mode........................................................................620 The Locking Scheme......................................................................621 Lock Granularity ............................................................................622 Selecting the Proper Architecture ........................................................623 Working With Locks ............................................................................623 Testing the Existence and Type of a Lock ....................................623 Responding to Locking Errors ......................................................627 Using Transactions ........................................................................629 Optimizing Multiuser Applications ....................................................631 Indexing Tables ..............................................................................631 Partitioning the Application............................................................631 Oracle/SQL Server Locking ................................................................632 Summary ..............................................................................................633
CONTENTS 22
PART VIII 23
Security 635 Elements of Security............................................................................636 Database Password Security ..........................................................636 Workgroup-Based Security ............................................................637 Workgroup Creation ............................................................................638 Users and Groups ................................................................................641 Understanding Default User and Group Settings ..........................642 Creating Users ................................................................................643 Setting/Changing User Passwords..................................................643 Creating Groups..............................................................................644 Assigning Users to Groups ............................................................645 Distinguishing Between Default and Special Users and Groups..........................................................................................646 Understanding Permissions ............................................................646 Implementing Security Using Startup Options....................................649 Security Concerns with Replication ....................................................650 Security for Split Databases ................................................................651 With OwnerAccess Option ..............................................................653 Security for Client/Server ....................................................................653 Managing Users..............................................................................654 Enumerating Groups and Users and List Membership ..................655 Identifying Current Users with ADOX ..........................................657 Identifying Users With Blank Passwords ......................................658 Setting/Clearing Passwords ............................................................660 Managing Group Object Ownership ..............................................660 Managing Multiple Apps................................................................661 Implications When Using SQL ......................................................662 Securing a Database Step by Step ......................................................663 Common Security Errors ....................................................................664 Summary ..............................................................................................665
Web Publishing with Access 2002
667
Configuring a Web Site for Web Publishing 669 Development Versus Production Environment ....................................670 Choosing Your Platform ......................................................................671 Personal Web Server and Peer Web Services ................................672 Internet Information Server ............................................................672 What Is the Option Pack? ....................................................................673 Setting Up Your Web Server ................................................................675 Installation ......................................................................................675 NT Option Pack for Windows 95/98..............................................675 Microsoft Transaction Server 2.0 ..................................................680
xix
xx
Access 2002 Development UNLEASHED Managing and Configuring Your Web Server......................................682 Personal Web Manager ..................................................................682 Microsoft Management Console ....................................................684 Securing Your Web Applications ........................................................692 What Is the Difference Between a Site and a Virtual Directory?........693 What Is IUSER? ............................................................................695 File System Type ............................................................................696 Directory Structures and Required Permissions ............................696 ASP/HTML—Placement and Permissions ....................................697 Databases—Placement and Permissions ........................................697 Summary ..............................................................................................699 24
Web Enabling Access 2002 with Office Web Components 701 What Are the Office XP Web Components? ......................................702 What Do the Office Web Components Do? ..................................702 What Are the License Requirements for Using Web Components?................................................................................703 Using the Office Spreadsheet Control................................................703 Getting Started................................................................................703 Using the Control in Access ..........................................................705 Using the Office Chart Control ..........................................................707 Getting Started................................................................................708 Using the Control in Access ..........................................................708 Using the Office PivotTable Control..................................................711 Getting Started................................................................................711 Summary ..............................................................................................713
25
Using Data Access Pages 715 What Is a Data Access Page? ........................................................716 Data Access Pages Architecture and Requirements ......................716 Creating Your First Data Access Page ................................................717 Viewing a Data Access Page ..........................................................721 Implementing Interactive Drill-Downs................................................722 Incorporating the Office Web Components with DAPs ......................724 Adding an Excel Spreadsheet Web Component ............................725 Adding a Chart Web Component ..................................................726 Scripting Data Access Pages................................................................726 Changing the DAP’s DataSource at Runtime ................................728 Summary ..............................................................................................729
26
Web Publishing with Access 2002 and Active Server Pages 731 Using Active Server Pages ..................................................................732 Understanding Active Server Pages ....................................................733
CONTENTS Active Server Pages Versus CGI ....................................................734 Obtaining Active Server Pages ......................................................734 Getting Started with Active Server Pages............................................734 Understanding the ASP Code ........................................................738 Understanding the Limitations of Exporting ASP Pages ..............739 Active Server Pages 101 ......................................................................739 The Active Server Page Engine......................................................739 Server-Side Scripting......................................................................740 The Application and Session Objects ............................................747 Using the Response and Request Objects ......................................749 The Global.asa File ........................................................................750 Examples of Active Server Page Objects ......................................752 Using ADO in Your Active Server Page Applications ..................753 Real-Life Example: Establishing a Self-Maintaining Membership-Based Web Page ..........................................................754 Access 2002 Web Publishing with XML ............................................763 Understanding the Basics of XML ................................................764 Creating XML Programmatically ..................................................766 Creating Charts with the Chart Control ..............................................770 Summary ..............................................................................................772 Index
773
xxi
About the Authors Stephen Forte runs the New York City consulting firm, The Aurora Development Group (www.auroradev.com), which specializes in Microsoft Database and Web technology. He has authored and co-authored several books, including Microsoft Jet Database Programmer’s Guide (Microsoft Press), Access 97 Unleashed, Second Edition, and Microsoft Access 2000 Development Unleashed (Sams Publishing). He has written several magazine articles and is a contributing editor of Access/VB Advisor. Stephen also serves as president of the NYC Access and VB Developers group (www.nycaccessvb. com) and travels around the world speaking at Microsoft Developer Conferences. Tom Howe graduated from Northwestern School of Law in 1982 and has been a practicing attorney ever since. Having tired of the practice of law, he decided to join the most dynamic profession in the world—software development. So now he spend most of his time developing software and speaking at developer conferences. Tom specializes in application development using Microsoft Access, Visual Basic, Outlook, Exchange, and SQL Server. Tom is a regular speaker at TechEd, Advisor, Informannt and other developer conferences around the world. Kurt Wall has been using and programming with Access and Visual Basic since 1994. In no particular order, he enjoys coffee, cooking, coding, staying up late, and sleeping even later, the latter of which makes any day job a challenge. When he gets tired of computers, he reads the occasional novel, historical fiction, political theory, American history, and cookbooks, and dreams of going to culinary school. Computers and cooking have nothing to do with his cum laude degree in American history from the University of Utah. A working stiff for nine years, Kurt is now a freelance writer, editor, and consultant. While in the Land of Cubicles, Kurt has been Caldera Systems’ technical publications manager, an enterprise application technician (pager slave) for Qwest, an Informix and Visual Basic programmer with Virtual Solutions, an EDI administrator (pager slave) for First Health Solutions, a lowly help desk grunt for First Health Solutions, and a network administrator for the Utah Division of State History. Kurt is the author of Linux Programming Unleashed (first and second editions), and Linux Programming by Example. He is currently coauthoring Red Hat Linux Network and System Administration and Red Hat Linux Weekend Crash Course. Kurt has also contributed chapters to The Informix Handbook, The Linux Bible, forthcoming titles on Linux clustering, Linux Performance Tuning and Capacity Planning, UNIX Unleashed, and too many Caldera OpenLinux user guides, manuals, and white papers to count.
Kurt is also the technical editor for all or part of Practical Linux, Red Hat Administrator’s Handbook, Linux Security Toolkit, Sams Teach Yourself KDE 1.1 in 24 Hours, Sams Teach Yourself Linux in 24 Hours, 2nd Ed., Linux: The Complete Reference, 4th Ed., Linux Database Bible, KDE 2.0 Development, Caldera OpenLinux Secrets, Linux Administrator’s Bible, Caldera OpenLinux Bible, an upcoming book on Linux performance tuning and capacity planning, and other titles. Kurt is the vice president of the Salt Lake Linux Users Group, former president of the Informix on Linux SIG of the International Informix Users Group, and former maintainer of the Informix on Linux FAQ. Having survived Marine Corps boot camp, Kurt has an extreme dislike for talking about himself in the third person. Paul Kimmel is the author of many books on Access programming and Visual Basic programming. Paul is a freelance contributing author to the CodeGuru Newsletter at www.codeguru.com and is available for development work at pkimmel@softconcepts. com. Russ Mullen has been using and programming with Access and Visual Basic for more years than he cares to remember. He has technically edited a number of books both on Access and Visual Basic. He enjoys getting up very early in the morning and coding with coffee in hand long before the sun rises; then, retiring early to prepare for the next day’s work. In recent years he has been doing Web site design including databases. He is always available for Web site or database development work at
[email protected].
Dedication In memoriam, Michael Parrish Pruyn. —Kurt Wall My contributions to this work are dedicated to David Benavides, Sr—a good, decent, and honorable man. Thank you for being responsible. —Paul Kimmel In memory of my father, Enos Ross Mullen. —Russ Mullen
Acknowledgments Above all, I thank God for giving me the desire to write and blessing with me the opportunities to do so. Once again, I’d like to thank my editor, Angela Kozlowski. Some people sparkle; count yourself lucky to meet someone who does. At the end of the day, you only have your people, so thanks to Ashleigh, Zane, Dad, Rick, Amy, Morgan, Isaac, Marty, the whole El Paso gang, and the Dallas Santa. —Kurt Wall Special thanks to Angela Kozlowski, Sondra Scott, and Leah Kirkpatrick at Sams. These wonderful and talented people could teach software developers a thing or two about project management. Thank you for all of the wonderful opportunities to write and work with you. It is truly my pleasure. A thank you to co-conspirators on other fronts, including Michael Standen in Ottawa and Frank Arndt in Michigan for putting up with my often-times distracted behavior. Thanks for making it possible for me to multi-task. As always I would like to thank my family. They are my first line of defense and best supporters. I would like to thank each of them by name. Thank you to Lori Kimmel; Alex Kimmel; Noah Kimmel; Douglas MacDonald and Trevor MacDonald; Jacqueline, David, Nicholas, and Robert Benavides; David, Cindi, Ben, Patti, and Becky Kimmel; Dan and Angie Kimmel; Richard, Kathy, Rich, Tim, and Johnny Hemenway; James and Sarah Kimmel; Jennifer and Brent Ladd; Gerald and Deb Kimmel; Dick and Diane Bourbonais; Pauline Blumenthal; Rick, Lori, Daniel, Jennifer, Gillie, and Annie
Bourbonais; Frank and Clinton Bourbonais; Tom, Shelley, Kendra (and Josh), Kynice, and Tommy Bogan. Thank you, and God bless and keep you all. —Paul Kimmel Special thanks to Angela Kozlowski, Sondra Scott, and Leah Kirkpatrick at Sams for all their help and understanding. Without their help the chapters I authored would have been impossible to complete. I would also like to thank the technical editor for all the help and insight given during the review stage of each chapter. —Russ Mullen
Tell Us What You Think! As the reader of this book, you are our most important critic and commentator. We value your opinion and want to know what we’re doing right, what we could do better, what areas you’d like to see us publish in, and any other words of wisdom you’re willing to pass our way. As an Executive Editor for Sams, I welcome your comments. You can email or write me directly to let me know what you did or didn’t like about this book—as well as what we can do to make our books stronger. Please note that I cannot help you with technical problems related to the topic of this book, and that due to the high volume of mail I receive, I might not be able to reply to every message. When you write, please be sure to include this book’s title and author as well as your name and phone or fax number. I will carefully review your comments and share them with the author and editors who worked on the book.
E-mail:
[email protected] Mail:
Sams Publishing 800 East 96st Street Indianapolis, IN 46240 USA
Introduction Why Read This Access Development Book? Earlier this evening, I sat in the Salt Lake Roasting Company, a gourmet coffee roaster in Salt Lake City, and, over several cups of hot, fresh, delightfully aromatic Costa Rican coffee, scribbled the notes for this introduction. It occurred to me that selecting an Access development book is much like choosing your next pound of coffee beans: Without some sort of guide, one Access book looks pretty much like the next save for differences in their tables of contents and covers, just as two pounds of coffee from different regions of the world appear seem indistinguishable but for slight variations in their aroma and appearance. This book is different from the typical Access books sitting right next to it on the bookshelf. Some of the reasons are • This book is written by Access developers for Access developers. So many computer books these days are written by professional trainers and writers. The authors who wrote this book have the same job as you—to make Access solutions for their users. We share our real-world experience and knowledge throughout this book. We focus on the features of Access development that you will need to use, rather than droning endlessly on about every button and tab of every dialog box. Whether you write Access applications professionally, are making a transition from Visual Basic or another development environment for database access, are evaluating Microsoft Access as a front-end to SQL Server, Oracle, Informix, or Sybase, or are simply an experienced, technically savvy Access end user, this book is for you. • This book is the only Access book to have extensive coverage of working with Oracle. The first time I had to make an Access front-end to Oracle, I was totally disappointed to find that there was absolutely no literature on the topic. James Ralston’s experience with Oracle and Access is distilled into a complete chapter on using Access with Oracle. Moreover, you will find two chapters devoted entirely to using Microsoft Access with SQL Server and many parts of other chapters that address using Access with SQL Server.
2
Access 2002 Development UNLEASHED
• We have the best coverage of using Access on the Internet. The Internet’s popularity and growth not only caught every major segment of the IT industry, including Microsoft, by surprise, it totally changed the face of software development. While most Access books still treat the Internet as an afterthought, this book, like Microsoft Access itself, puts the Internet on par with other development environments. Indeed, if the Internet is the next development platform, Access is the database for creating and running data-driven applications. This book brings six years of real-world experience of using Access and the Internet to you, if only to help you avoid the same blunders and mistakes the authors made! The book you hold in your hands also benefits from the expertise of a Windows 2000/Windows NT expert, showing you how to build a Web server from scratch and how to develop on Windows 95 with Personal Web Server. The material in this book covers the range of Access-based Internet development, from XML, ASP, DAPs, and Microsoft Office Web Components to all the latest development trends. • We do not ignore the design, planning, and “human” side of Access development. Most studies show that the coding, table design, maintenance, and so on is only about 55 percent of a software project. So most computer books ignore the other side of development—the design side. Tom Howe is not only an Access developer, he is a professional lawyer with his own practice. With this legal and academic background, Tom is in a unique position to talk about the “human” side of software development, the importance of design, writing specifications, and project plans. • This book has been completely rewritten from the ground up—a completely fresh approach to the latest version of the product by experienced developers. Tom, James, and I have been working closely with Microsoft and the Access Development team all through the beta development cycle. Few other books can make both those claims. We also tested all the code examples on the full Release version of Office XP that you have, not like some other books that are rushed to the market.
What You Can Learn from This Book Part I of this book, “Database Design Unleashed,” shows you important concepts from a perspective of experience. These chapters will help you evaluate and modify your current design and planning skills to include business object modeling and the latest Jet engine capabilities.
INTRODUCTION
In Part II, “Data Access,” you will find out everything that you really need to know about using the new data access method: ActiveX Data Objects (ADO). We show you how to make the transition from DAO to ADO and ADOX and show you the parts of ADO and ADOX that you will need to use and work with on a daily basis. Anyone doing Access development knows that using Access effectively requires the use of forms and reports. Part III, “User Interfaces Unleashed,” shows you ways to program your application’s interface with powerful technologies, such as ActiveX controls and components. In Part IV, “VBA Unleashed,” you will see the importance of the powerful programming environment behind Access: Visual Basic for Applications (VBA). You will learn invaluable techniques and have ready-to-run sample code right on the CD-ROM to plug into your applications. Part V, “Access Client Server,” teaches you how to use Access in a client/server environment. Access Data Projects, introduced in Access 2000, are devoted client/server development. In Part VI, “Interoperability,” you will learn how to use Access with other products, including Microsoft Office. In addition, a whole chapter will try to answer the question “Which tool to use, Access or VB?” The material in Part VII, “Multiuser Issues,” shows you how to exploit Access’s rowlevel locking, replication, and security enhancements. Because so many Access applications are multiuser, this is one of the most important parts of the book. “Web Publishing with Access 2002,” Part VIII, digs deeply into using Access 2002 on the Internet. Since Access 2002 has embraced the Internet, we decided to really drill down into the topic with coverage on how to create dynamic Web sites powered by Access 2002 and a review of the new tools in Office 2002 that make Internet development easier. Appendix A, “References for Further Reading,” directs you to books and online sites where you’ll find valuable information to supplement what you learn in this book. This book started one rainy morning in a London laundromat with Tom and me cranking out code and an outline. We wanted to give something to the Access developer community that you could use, but something that came from our personal experiences. We then recruited James, who is equally dedicated to that vision. The result is in your hands; let us know what you think. We did it for you.
3
4
Access 2002 Development UNLEASHED
Conventions Used in This Book The following conventions are used in this book: • Code lines, commands, statements, variables, and any text you type or see on the screen appears in a computer typeface. • Placeholders in syntax descriptions appear in an italic computer typeface. Replace the placeholder with the actual filename, parameter, or whatever element it represents. • Italics highlight technical terms when they first appear in the text and are being defined. • A special icon ➥ is used before a line of code that is really a continuation of the preceding line. Sometimes a line of code is too long to fit as a single line in the book, given the book’s limited margin width. If you see ➥ before a line of code, remember that you should interpret that “line” as part of the line immediately before it. • As a part of the Unleashed series, this book also contains notes, tips, and cautions to help you spot important or useful information more quickly. Some of these are helpful shortcuts to help you work more efficiently.
Database Design Unleashed
PART
I IN THIS PART 1 What’s New for Developers in Access 2002 2 Planning the Development Process 3 Database Design and Normalization 4 Advanced Queries 5 The Microsoft Jet Database Engine 4.0
CHAPTER 1
What’s New for Developers in Access 2002 IN THIS CHAPTER • User Interface Changes • Changes to the VBE
8
9
• Compatibility With Access 2000 Databases 10 • Database Conversion Error Logging 11 • Offline Data Access Pages
11
• Improved Integration with SQL Server 2000 12 • Other Access 2002 Features
13
8
Database Design Unleashed PART I
There is an old saying that says from a tiny acorn, a mighty oak tree grows. If this could be said of a software product, it would never be more apt than for Microsoft Access. Those who remember Access 1.0 recall an application with a file size limit of 128MB, no transaction capability, no real programming capability, static toolbars, a lack of referential integrity, and agonizingly slow performance. Despite those shortcomings, Access quickly became the most popular desktop database product because of its easy to use tools for building user interfaces, dynasets, and the visual query builder. With almost every update, Access has continuously evolved into a powerful and useful productivity tool and development platform. Access users and developers have seen constant improvement. The underlying database engine, Jet, now at release 4.0, supports transaction processing, referential integrity, replication capabilities, better concurrency management, increased speed, and greater stability. Its developer tools have evolved from simple macros to a full-blown VBA development environment almost identical to Visual Studio 6.0. The interface tools have improved also. Third-party tools and ActiveX controls expanded the design options available to developers. With Access 95 and 97, developers gained the ability to create multiple instances of forms. Access 97 also enabled developers to use Access’s Active Server Pages, thereby extending Access’s reach to the Internet. The improvements in Access 2002 are evolutionary, not revolutionary like the dramatic changes from Access 97 to Access 2000. Nevertheless, Access 2002 offers a friendlier, more productive, and more efficient development than ever. This chapter highlights Access 2002’s most significant enhancements and new features and, hopefully, entices you to read deeper into the book to learn how to use them in your own projects.
User Interface Changes Changes to Access 2002’s user interface are subtle. For example, it retains the Outlookstyle interface introduced in Access 2000, but its overall appearance seems simpler, cleaner, and more appealing. You can still create logical groups of tables, queries, forms, and reports using shortcuts to the actual objects. The “Orders and Sales” group, shown in Figure 1.1, shows this grouping capability and Access 2002’s more refined appearance. The streamlined menus remain, by default showing only the most commonly used options. Complete menus are available with a single click. Access’s renowned wizards for creating and editing objects have been enhanced and extended in Access 2002.
What’s New for Developers in Access 2002 CHAPTER 1
FIGURE 1.1
Changes to the VBE Access developers should be prepared for small, but thoughtful, changes to the VBE (Visual Basic Environment). Access 2002’s rich, highly customizable development environment, first introduced in Access 2000, has undergone few changes in its overall appearance. Similarly, Access 2002 introduces no earth-shaking functionality changes relative to Access 2000, but the smaller changes discussed in the next few paragraphs do suggest that Microsoft’s Access programming team thought about lowly Access developers while creating the product. Access developers will appreciate the new undo/redo capabilities. Access 2002 has finally implemented multiple levels of undo/redo when you are working in design view. This extended undo/redo feature applies to any Access database object and also to views, stored procedures, and functions in an Access Project. Another notable change in the VBE simplifies working with subforms and subreports. While working on a container report or form in Design view, simply double-click the subreport or subform or select Design View on the View menu and the subreport or subform will open in its own window. Putting subforms and subreports in their own design windows saves a few flicks of the wrist, makes the VBE a bit more intuitive and developer friendly, and is simply more convenient. Figure 1.2 shows the Customer Orders Subform1 in its own window in Design view.
1 WHAT’S NEW FOR DEVELOPERS IN ACCESS 2002
Everything related to orders and sales is in one convenient place.
9
10
Database Design Unleashed PART I
FIGURE 1.2 Modifying subforms is easier than ever with Access 2002.
You might also notice, while designing subforms and subreports, that a single click on the scrollbar no longer abruptly moves the focus from the top to the bottom or from one side to the other. Scrolling in Design view, especially in subforms and subreports, is much easier because the scrollbar moves in smaller increments than it did in earlier versions of Access. Again, this is a small enhancement, but one that is sure to reduce hair loss for Access developers.
Compatibility With Access 2000 Databases Developers and users upgrading to Access 2002 will appreciate a feature Microsoft calls round-tripping. Round-tripping refers to opening an Access 2000 database using Access 2002, modifying data, and then closing the database without ever having to convert the database file format. Compatibility between the Access 2000 and 2002 database file formats makes it easier for users to share data. From a developer’s perspective, it reduces the likelihood of data corruption or loss during database conversions. It also eliminates, at least in the short term, data conversion requirements from project plans, making the development project that much simpler. The title bar in Figure 1.1 shows that Access 2002 has opened the Access 2000 version of the Northwind database.
What’s New for Developers in Access 2002 CHAPTER 1
Inevitably, you or your development team will be compelled to convert a database to Access 2002’s format and will probably encounter conversion errors of one sort or another. Historically, Access developers have written custom code to catch and record conversion errors and to save the ostensibly “bad” data for later analysis. Access 2002 might nullify the need for such painstaking precautions. When importing or converting a database created with any version of Access back to Access 95, if Access 2002 encounters a data conversion error, it creates and populates a special table named Conversion Errors in the new Access database file. The data stored in the Conversion Errors table will help you identify and resolve the data conversion problem. The Conversion Errors table has three columns: Object Type, Object Name, and Error Description. Object Type identifies the type of database object (a report, query, table, and so forth) involved in the error. If the error was more general or did not apply to any particular database object, this field will have the value “Database.” Object Name contains the name of the object in which the error occurred. Unfortunately, if the error occurs in a module that is being compiled during the conversion process, the module’s name is not stored in the table. The Error Description column describes the error in greater detail.
Offline Data Access Pages Access 2000 introduced Data Access Pages to the Access user and development community. Data Access Pages (DAPs, henceforth) are simply Web pages, tied to an Access database backend, that present data stored in the database. Access 2002 extends DAPs by adding support for offline data access. Offline DAPs (ODAPs) function like the Windows Briefcase. Download data to your laptop Friday afternoon before leaving work. Update it over the weekend. Monday morning, plug the laptop into the network and use Internet Explorer’s Synchronize function to upload the modified data. The DAP in Figure 1.3 uses HTML intrinsics and COM components for its functionality. DAPs also have a rich object model and support Visual Basic Scripting Edition, JavaScript, and Office 2000 Web components. This support enables you, or your clients, to leverage existing Web development skills and make a smooth transition to Access 2002.
1 WHAT’S NEW FOR DEVELOPERS IN ACCESS 2002
Database Conversion Error Logging
11
12
Database Design Unleashed PART I
FIGURE 1.3 Access data and functionality can be used via a Web browser.
Improved Integration with SQL Server 2000 Access 2002 includes several new features that improve its usefulness as a front-end for Microsoft SQL Server 2000. Access now includes extended property support, batch update capability, and password security for Access Projects.
Extended Property Support Using extended SQL database properties within a Microsoft Access Project enables your Access Project to use SQL database validation rules, predefined text formatting, subdatasheets, input masks, and more. Extended properties can be used with many databases objects, including tables, fields, views, and stored procedures. Extended property support makes it easy to convert an Access database application into an Access Project using Microsoft SQL Server as the database backend.
Batch Updates Batch updates simply enable all data entry to be submitted to Microsoft SQL Server in a single operation when a user moves to a record, closes a form, or pushes a button. In fact, batch updates enable you to save or rollback multiple record changes using a single command.
What’s New for Developers in Access 2002 CHAPTER 1
Password Security for Access Projects Microsoft has also created new tools to create and manage the server-side objects from the Design view of the client. Other common administrative tasks, such as security, backup, and replication, can also be managed from Access 2002.
Other Access 2002 Features The features discussed in this section are primarily features intended for end users rather than developers, but some Access developers might find them useful, so they are mentioned here for completeness’ sake.
PivotChart and PivotTable Data Views Microsoft introduces PivotChart and PivotData views in Access 2002. You can use PivotChart and PivotTable views with most database objects, including forms, tables, views, stored procedures, and queries. PivotTable and PivotChart views include new events, making it easier to write code behind PivotChart and PivotTable forms. Finally, PivotTable and PivotChart views can be saved as both online and offline Data Access Pages.
XML Support Access 2002 includes support for XML, Extensible Markup Language, which is rapidly becoming the preferred method for data interchange between applications and businesses. Data from Jet or SQL Server database can be exported as XML simply by selecting the Export option from the File menu. Queries can also query data in XML format stored in a variety of applications. Figure 1.4 shows part of the Sales by Category report saved as XML data.
Linked Table Wizard Less experienced Access developers will appreciate Access 2002’s Linked Table Wizard, which makes it much simpler to link Access tables to SQL Server databases.
ANSI-92 Query Mode sans ADO Access 2000 introduced more ANSI-compliant SQL syntax and, if you used ADO (ActiveX Data Objects), you could use the ANSI-92 query mode. Access 2002 removes this restriction. That is, queries can now use ANSI-92 query mode without having to use ADO.
1 WHAT’S NEW FOR DEVELOPERS IN ACCESS 2002
If your Access Project is connected to a Microsoft SQL Server database, you can change the Project’s login password directly from the Database Utilities menu.
13
14
Database Design Unleashed PART I
FIGURE 1.4 Reports and tables can be saved as XML data.
Summary Microsoft Access 2002 polishes and fine-tunes the dramatic changes made in Access 2000. The Outlook-style interface is cleaner and the many wizards are more helpful than ever, but Access’s overall appearance is largely the same. Indeed, Access 2002 is a developer’s release, containing features that Access developers have been requesting for years. Database conversion error logging will make data conversion simpler to troubleshoot. DAPs have been improved to support disconnected recordsets and are easily resynchronized using Internet Explorer. Access 2002 represents another milestone on the road to full interoperability with SQL Server 2000 because it now supports password security for Access Projects, can perform batch updates, and also supports SQL extended properties. Access 2002 also includes a new batch of tools for creating and managing the server-side database objects from the Design view at the client. Some of Access 2002’s new features do cater to end users, of course, including XML support, the new PivotChart and PivotTable data view, a wizard that simplifies linking Access tables to SQL Server databases, and, as you just read, the freedom to use SQL-92 without having to use ADO. As observed at the beginning of the chapter, Microsoft Access 2002 continues the steady, upward trend in usability, power, features, and developer-friendliness.
CHAPTER 2
Planning the Development Process IN THIS CHAPTER • Gathering Requirements • Architecture
24
• Development Planning • Construction
17
28
24
16
Database Design Unleashed PART I
This chapter focuses on the development process rather than the nuts and bolts of VBA, queries, forms, and reports. The development process is a general term referring to the methods or techniques developers use to transform a need (“We need a simpler, faster way to track and process employee expense reports.”) into an application that fills that need. Just as a road map shows you how to get from one place to another, successfully completing any nontrivial development project depends on following a proven and repeatable development process that enables a development team (even a team of one) to progress from identifying a problem to providing a software product that solves the problem. Mastery of Access 2002’s development environment, in-depth knowledge of database design, and star-quality SQL programming skills—all of which are useful and important—matter little if you do not understand the problem you need to solve. This chapter discusses the central issues and activities that determine the success or failure of the development process. By the time you finish reading this chapter, you will be equipped with the basic tools necessary to enable you to create an effective development process of your own, based on proven methods. However, what you will learn in this chapter is only a starting point. Comprehensive coverage of the software development cycle would easily consume the entire book. The appendix includes references for further study. The development process consists of four phases: • Requirements • Architecture • Development planning • Construction During the requirements phase, you gather information and analyze the users’ real needs. During the architecture phase, you create the top-level design, determine the technology requirements, such as the server and client hardware and the software components, most appropriate for solving the problem at hand, and choose the development tools that are best suited for the project. Development planning, the third phase, is the period during which the design team creates a comprehensive plan describing the entire project. Development plans, even for small projects, answer the following questions: • What is the problem or need the project addresses? • What solution is being proposed? • How will the solution be implemented? • How long will it take? • How many people are needed? • How much will it cost?
Planning the Development Process CHAPTER 2
17
The final development phase is the construction phase, during which the project is created. Construction includes ordering and installing hardware, modifying network configurations, installing the necessary software, and, of course, writing and testing code. Figure 2.1 depicts the four phases of the development process from gathering requirements through construction of the application. It also shows some of the software releases created during the construction phase. This chapter details each of these areas. FIGURE 2.1 The development process.
Requirements
2
Time
Development Plan Construction
V1.0 Prototype 1
V1.0 Alpha 1
V1.0 Beta 1 V1.0 Beta 2 Version 1.0 RC1 Version 1.0
Gathering Requirements Gathering and analyzing requirements is the single most important activity in delivering a successful application. Conversely, many project failures result from inadequate or incorrect requirements.
Why Are Requirements So Important? Requirements are vital because they directly determine how the resulting application works. Requirements also control how long a project takes to complete, how many developers it will take to complete it, and how much the completed application will cost. If nothing else, if the development team does not understand what users want and the details of the business requirement the application is supposed to address, the developers will create the wrong application. Of course, the business need and the end users’ desires must also be clearly articulated.
PLANNING THE DEVELOPMENT PROCESS
Architecture
18
Database Design Unleashed PART I
Clearly understood requirements also affect a project’s completion time. Requirements that are well-researched and analyzed at the beginning of a project, before a single line of code gets written, reduce the number of unanticipated changes that lead to rework later. A general rule of thumb is that the cost to make a requirements change to an application goes up five to ten times at each major step in the development process. For example, if resolving a problem identified during the requirements phase takes one hour, it could take ten hours, or more, to fix during the construction phase. Similarly, adding functionality to a module that has already been tested and integrated into the rest of the application might take closer to fifty hours to implement correctly. A program bug not detected until the application has been deployed will probably take closer to 100 hours to resolve. Why so long? Consider the process involved. First, the requirements need to be updated. Next, the design has to be modified and the relevant code must be analyzed to assess where changes are needed. After making the necessary changes, the new feature or the bug fix must be tested against the existing modules, queries, forms, and reports. The application itself must be re-tested to ensure that the changes did what was anticipated and did not create new problems. Finally, the revised application must be re-deployed to each of the end users. Of course, these factors are software industry averages, not absolutes. Depending on the changes made, the financial cost and time delay might be more or less than these averages. If the error causes damage to your customer’s business, for example, the cost could be astronomical.
Finding the Real Problem Begin the requirements-gathering process with the perceived scope of the application. The perceived scope refers to your customer’s understanding of her business problem and the software product she believes will solve that problem. The customer’s perception of the problem might or might not be correct or complete.
Note An Employee Expense Reporting example will be used throughout this chapter to illustrate concepts of the development process.
For example, the customer might want a system for employees to enter and print expense reports. After some analysis, you might find that the real need is to automate the entry, distribution, approval, and processing of the entire expense-report process. Ferreting out
Planning the Development Process CHAPTER 2
19
differences like this is one of the challenges. Sometimes, customers want a solution that is too small to solve the problem. Other times, customers want a solution that is too big and complex. To understand how the desired application interacts with other parts of the business, it is vital to use the requirements gathering process to investigate the expected project scope.
Investigation To find the problem that really needs to be solved and to accurately determine the requirements, you have to put on a trench coat and become a business process detective. Here are a few tips for your sleuthing:
• Many business processes have two faces: the official way and the actual way. You need to understand both how a process is supposed to work and the way it really works. • Keep an open mind and listen carefully. Put aside your (and the clients’) first vision of the problem and the solution until they are verified through interviews and analysis. Table 2.1 lists examples of the types of requirements about which to ask while conducting your requirements investigation. The first step in your investigation is to identify all the stakeholders. A stakeholder is a person, group, or department with an interest in the process or the application. Managers, supervisors, and lead employees responsible for every key department involved in the process are stakeholders. Users of the current process are always stakeholders: they will be users of the new application you are developing. Some groups of stakeholders will be so large that you will need to select representatives to speak for the group. As the investigation proceeds, you might discover additional stakeholders to include. An additional benefit of involving stakeholders in gathering requirements is that involvement increases their feelings of ownership in the new system. You will see greater cooperation and acceptance during application deployment. Consider the expense report example. Interview a few representative users, perhaps one who rarely has any expenses, a second who travels constantly, and one or two average users. To obtain another perspective, you might interview one or two people in the payroll department who process those printed expense forms and receipts every month. Ask lots of questions, listen well, and take copious notes. Ask how the current process works
PLANNING THE DEVELOPMENT PROCESS
• Never rely on any one person for key information about how the current process works. Talk to as many people as possible or practical who are involved with the current process, especially end users, the people who will use the new solution and whose insight into the process most benefits the company.
2
Database Design Unleashed PART I
and what they like and dislike about it. Ask how they wish it worked. These notes will enable you to put together an accurate picture of the current process and business rules. Analyze the current process and document any error prone, slow, or redundant steps. Remember to document all the likes, dislikes, wishes, and dreams you heard during the interviews.
Process Diagrams
An example of a process diagram of a travel and expense report.
Need for Travel
1 Fills out: 1. Travel Request 2. Cash Advance
Cash
5
6
Travels
Fills out Expense Report
Requests
Check Expense Report with Receipts
2
Manager
FIGURE 2.2
Employee
Process diagrams are a terrific way to describe how a business process works. They clarify the interactions among the key individuals, groups, or departments involved in a process. They aid understanding because visual illustrations are frequently clearer than textual descriptions. After diagramming and analyzing the current process and the needs of the stakeholders, a second process diagram can be drawn to represent the revised process. Figure 2.2 shows the process diagram of the expense report example.
No
Approve Request?
7 Yes Approved Cash Advance Request
No
Approve Expenses ?
Yes
Accounting
3
Approved Travel Request
Cash Advance Rules
Authorization Limits
Travel Rules
4 Makes Travel Arrangements
Approved Expense Report with Receipts 8
Processes Cash Advance
Travel Agency Business Rules
20
20th of month
Processes Expense Report
Reservations and Tickets
Error Prone
Expense Report Rules
Delay
Each process stakeholder is represented by a layer. Figure 2.2 shows four stakeholders: Employee, Manager, Accounting, and Travel Agency. Individual steps or activities in the process, represented by rectangles, progress the move from left to right. In Figure 2.2, the first activity, numbered 1, is filling out a travel request form and a cash advance form. These steps process inputs from the left and produce outputs to the right. In Figure 2.2, the output of the first step is the requests, which might be approved or rejected. Diamonds represent decisions, such as approving the travel request made within the process. Vertical arrows into a process represent the influence of internal business rules or external regulations and other constraints. Constraints shown in Figure 2.2 include rules controlling cash advances and a manager’s maximum approval authority. Because
Planning the Development Process CHAPTER 2
21
the horizontal axis of the diagram represents time, any delays and time-consuming activities are identified easily. Process diagrams are fairly straightforward to create and easy for stakeholders to understand. After they see the diagrams drawn out, many stakeholders will be shocked at the complexity and convolution of their current processes. If you begin your process diagramming as early as possible, you can revise them during stakeholder interviews. Process diagrams are excellent tools to help structure and guide these interviews. Each stakeholder interviewed will further clarify and detail the overall process.
Tip Process diagrams should begin life not on a PC, but on a large sheet of paper. Designing on paper can be done more quickly and is more interactive. It is also less intimidating and easier to read. One trick to minimizing the inevitable erasing and reorganizing is to use sticky notes as the process blocks. After the diagrams have stabilized, then draw the process diagram using either the Microsoft Word 2000 drawing tools or Visio.
Identify the Scope of the Project Work with the customer to determine the appropriate scope for the application. Remember, this should be a long-term scope, not just centered on this phase of the project. In the expense report example, the developers and decision makers came to an agreement on the project scope. They agreed that the application should eventually automate the cash advances, travel requests, and the entire expense reporting process, from the employee’s creation of a travel request to Accounting’s receipt of the balance due/owed request. They decided to ignore the accounting systems and the actual travel booking process because they are outside the application’s scope.
Writing Down Requirements Written requirements are one of the best methods of communication between the developer and customer. Written requirements reduce misunderstandings because they state unambiguously what the application will provide. The writing need not result in a
2 PLANNING THE DEVELOPMENT PROCESS
You might find it useful to create a hierarchy of process diagrams. The one top-level diagram will describe the big picture, whose process steps will be rather large in scope. Sub-processes can then be detailed on their own process diagrams. This hierarchical approach often simplifies the diagrams and makes them easier to understand.
22
Database Design Unleashed PART I
formal, unwieldy document. There are many effective ways to communicate requirements: sample reports, forms, process diagrams, or drawings. There is no magic to writing requirements. The most important measure is that the customer can read and understand them. Ask the following questions about each requirement: • Does it describe what is needed, rather than how to implement it? • Can it be verified after implementation? • Is it unambiguous? • Does it completely describe the need? • Is it consistent with the other requirements? Requirements serve as part of the work contract. Like a contract, both the development team leader and the customer should sign it. This document is the baseline against which the project will be measured and the basis for proceeding with the application development. From this point forward, carefully consider how requirement changes will impact the planned budget and schedule. Remember to revise the budget, schedule, and any related requirements or design documentation when changes are made. Table 2.1 shows examples of many kinds of requirements you should consider using. Tip Create a database of all the requirements. For each requirement, include an identification number, type, description, date, source, benefit, how to verify the requirement after implementation, priority, and more.
TABLE 2.1
Requirement Types
Type of Requirements
Description
Example
Functional
These requirements describe functions or features that are needed, plus the business rules that apply.
The user will be able to set the priority of the customer to Critical High Medium Low
Planning the Development Process CHAPTER 2 TABLE 2.1
23
continued
Description
Example
Performance
Performance requirements include • Speed • Response time • Capacity • Database size
The inventory database will support up to 20 users across the corporate LAN with a mean order status response time of 200ms.
Security
Security requirements limit usage and access to application features and data.
All users must log in to the application. Every user will be a member of only one of the following security groups: User Administrator Manager
Scalability
Scalability concerns the capability to expand the quantity of data, users, sites, or frequency of data collection.
The application must support the current 50 users and an additional 50 added over the next two years.
Extensibility
Extensibility requirements detail the capability to add features to the application in future releases.
The application will be designed and implemented to allow addition of customer invoicing.
Configurability
Configurability is the measure of how easily software parameters can be modified by the customer without requiring the development team to make the changes.
The Administrators will have the capability to update the insurance rates annually.
Compatibility
Compatibility is the ability to be installed and operational in various hardware/software environments, and to interact with other software applications.
The software product will be installed on Win95, Win98 and Win NT 4.0 Workstation with a minimum of 8MB memory and 200MB hard drive available.
Availability
Availability is the measure of how many hours, or what percentage of time, a software product is available to perform the work for which it was developed.
The database administrator will reserve from 10:00 to 11:00 p.m. for backup and maintenance of the backend database.
2 PLANNING THE DEVELOPMENT PROCESS
Type of Requirements
24
Database Design Unleashed PART I TABLE 2.1
continued
Type of Requirements
Description
Example
Usability
Usability, or ease of use, is the measure of how well-trained users are able the system.
The application will include online Help that will cover 90% of the features.
Ease of learning
Ease of learning is the measure of how easily and quickly users are able to learn to use the system.
One day of training will be sufficient for users to become proficient before initial use of the application.
Architecture After analyzing the written requirements, choose the appropriate architecture, technologies, and development tools. Should the client application use Excel, Access, or VB? Should it be Web-based and use ASP and XML? Is a two-tier architecture acceptable, or would n-tier be more appropriate? Will the backend database be Jet/MDB, SQL/MSDE, SQL Server, or Oracle? These are important questions that will each have a large impact on the final product. An accurate budget or schedule cannot be produced until these decisions are made.
Development Planning Development planning refers to deciding how to construct the application. Should all features and functionality be implemented in one comprehensive version 1.0 release, or should you defer some of the features for later versions? Whatever method you choose, you will need a roadmap with a budget and schedule before proceeding. You might have created a preliminary project plan to get the necessary approvals to get this far in the project. That preliminary version was based on the anticipated requirements and scope, and it included only rough estimates of schedule and budget. If you had a preliminary project plan, this is the time to revise it. If not, this is the time to write one. With the Requirements phase complete, you now have enough information to create an accurate task list, resource list, schedule, and budget.
Planning the Development Process CHAPTER 2
25
Tip Developers should always track their actual time. The best way to improve your project estimating skills is to compare your original estimates with the actual time required.
Delivery Strategy
Consider again the expense report example. During the design meetings, developers and decision-makers agreed that version 1.0 should focus on automating the creation, approval, distribution, and status-checking of expense reports. The next release, version 2.0, would add automating the creation, approval, distribution, and status checking of travel and cash advance request process. Figure 2.3 shows the top-level development process steps and an example of possible releases. Figure 2.4 shows a revised process diagram identifying the process steps each release addresses.
FIGURE 2.3 A delivery strategy example.
Requirements Architecture Development Plan
Time
Construction Phase 1
Prototype 1 V1.0 Alpha 1 V1.0 Beta 1 Version 1.0 RC1 Version 1.0 RC2 Version 1.0
Construction Phase 2
V2.0 Beta 1 V2.0 Beta 1a Version 2.0 RC1 Version 2.0
2 PLANNING THE DEVELOPMENT PROCESS
A delivery strategy specifies what will be released and when. Avoid the urge to include every idea and cool feature into one huge release. Rather, plan on several incremental releases. Doing so reduces the strain on the development team and enables the customer to validate the application as it progresses.
Database Design Unleashed PART I
Manager
The revised process diagram incorporates the delivery strategy.
Employee
FIGURE 2.4
Need for Travel
1 Fills out: 1. Travel Request 2. Cash Advance
Cash
5
6
Travels
Fills out Expense Report
Requests
2 No
Approve Request?
Check Expense Report with Receipts
7 Yes Approved Cash Advance Request
No
Approve Expenses ?
Yes
Accounting
3
Travel Agency
26
8
Processes Cash Advance
Approved Travel Request
4 Makes Travel Arrangements
Approved Expense Report with Receipts
20th of month
Processes Expense Report
Reservations and Tickets
Phase 2 - Version 2.0 Travel Request & Cash Advance: 1. Creation 3. Distribution 2. Approval 4. Status
Phase 1 - Version 1.0 Expense Report: 1. Creation 3. Distribution 2. Approval 4. Status
Standards Without standardization between developers, applications become very difficult to maintain and reuse. Rule #1: Standards make a big difference. Rule #2: The standard itself makes little difference. Translation: Consistency between developers is the primary goal of standardization. Whether everyone does it the “best” way is a secondary benefit. Caution Avoid the temptation to argue about what is “best”—just pick a good method, get everyone to use it, and stick with it.
The most common types of standards concern an application’s interface and naming and coding conventions.
Planning the Development Process CHAPTER 2
27
Interface Professional applications always have a consistent appearance and functionality. If some forms use a Cancel button to exit and others use a Quit button, users will quickly get confused, and training and support costs will increase. If one developer likes the look of each field in its own sunken box with data in bold type, while another designs forms with labels with bold type and flat text boxes, the application will look thrown together. Make these interface decisions early in the development process and have all the developers follow them before each decides to do his or her own thing.
2 Rather than spend a lot of time writing an official looking document, just create an Access database (MDB) with a set of forms and reports that includes examples of all the common uses of controls. This can be a growing, evolving example of your preferred user interface styles. Better yet, include the code behind the forms to handle navigation and other common functionalities. These forms will serve as your standard templates.
Here are some types of style issues to cover in your standard: • Form layout (for example, how you arrange controls on the form, form size, and proportions) • Field grouping (for example, sunken frames versus simple lines) • Use of colors, fonts, font size, and bold/italic typestyle for fields and labels • Button names and behaviors (for example, Cancel, Close, or Quit?) • Indication to user whether forms are read-only or can be edited • Dialog wording (for example, terse or verbose)
Naming Standards Naming standards make reading and reviewing the code much easier. They also greatly simplify maintenance tasks later on. The following example uses a datatype, prefixnaming standard. The code is easy to read and understand. dblLodgingTotal = Double(intNights) * dblRate
PLANNING THE DEVELOPMENT PROCESS
Tip
28
Database Design Unleashed PART I
However, the next example, although functionally identical to the first one, leaves everyone, including the developer, in the dark. x = Text7 * Text12
These are key parts of most naming standards: • Object prefixes (for example, txt, cbo, lst . . .) • Datatype prefixes (for example, sgl, int, str . . .) • Standard base names (for example, use Acct or Account) • Standard abbreviations (for example, stat = status)
Coding Standards Like naming standards, consistently applied coding conventions ease code maintenance and make code easier to read and understand. The following are examples of the issues you might want to cover in your coding standard: • Comment header blocks • Comments in code • Indentation • Size of subs and functions (for example, maximum of 60 lines) • Use of “Option Explicit” Tip Several naming standards are published and available for Access/VBA/Office/VB development. The two most common are the Reddick and Leszynski naming conventions. One advantage of these common standards is that new developers hired by your company might already be familiar with the standard. Also, books, technical articles, and conference materials tend to follow these naming conventions. Again, which one is “best” is much less important than selecting one and using it consistently.
Construction Several techniques are available to you for improving your team’s effectiveness during the construction phase of the project. This section discusses how to divide development tasks between developers and encourages you to test thoroughly and track all defects found along the way.
Planning the Development Process CHAPTER 2
29
Divide and Conquer: Construction Logistics There are many ways to approach the construction phase. You could start by implementing the most difficult or risky parts first, implement the easy parts first, or build the forms first and finish with the reports. The method recommended here is to create a solid foundation and build upward. This foundation consists of the table design, the most class modules and functions, and simple first attempts at the “core” forms. The goal is to establish a basic functioning core upon which each developer can build. For the forms, you want to spend the time necessary to design and build one or two as solidly as possible. These can be used as design templates for subsequent forms.
While creating the development foundation, invest the time and effort to create and test reusable components. The initial effort spent at the beginning will pay off quickly and repeatedly later in the project.
The development team must work together while building the foundation. At the outset of the construction phase, the most important design decisions are worked out and all the interfaces are defined. After completing the foundation, then split the team into smaller groups or assign task to individual developers. Two effective ways to divide the implementation among teams is to do so by Access objects or by application objects. If assigning tasks by Access objects, one team focuses on the forms, another on the reports, and, perhaps, a third coding business rules and providing support routines. If you select a division of duties based on application functionality, especially if the overall architecture is object-oriented, one team would develop the Expense Report objects—including the class modules, tables, forms, and reports—and another team would develop the Approval objects. Figure 2.5 shows the process that each team follows during the construction phase. It takes place at several levels of the development process. At a high level, the diagram illustrates the entire construction phase as development progresses from the detailed design to the final application testing. At a lower level, Figure 2.5 also represents how an individual developer might spend an afternoon developing a particular feature. Throughout the construction phase of a project, there are usually several software releases. These often include a prototype to confirm the graphical user interface (GUI) design; several alpha releases to solicit early user feedback; one or two beta releases to allow users to test the application in real, day-to-day operations; and, finally, the official Version 1.0 release distributed to all the users.
PLANNING THE DEVELOPMENT PROCESS
Tip
2
Database Design Unleashed PART I
FIGURE 2.5 Requirements & Architecture
Steps in the construction phase.
Requirements or Architecture Issues
Detailed Design Design Issues Design Review
Implementation Implementation Issues Review
Testing
Software
30
Builds and Releases As the complexity of the application and the project increases, it will become more and more important to track carefully the various builds and releases of your application. Periodically, create a build by integrating the most recent changes and additions from each developer. A build is a new version of the application, including the front-end MDB, related DLLs, EXEs, and back-end data files. Many builds will be created throughout the development process for internal use by the development team. Only a few of these are actually given to any of the users. By doing this every day or two during development, every developer has access to the latest modifications. A release is a build deployed outside the development team. This is usually distributed to one or more of the end users. Table 2.2 shows a project with 178 builds, but only sixteen releases. TABLE 2.2
Release Example
Release
Build
Prototype 1 Prototype 2 V1.0 Alpha 1
12
V1.0 Alpha 2
27
V1.0 Beta 1
42
V1.0 Beta 2
56
Planning the Development Process CHAPTER 2 TABLE 2.2
31
continued
Build
Version 1.0 RC1
88
Version 1.0 RC2
89
Version 1.0
89
Version 1.1 RC1
93
Version 1.1
93
Version 1.2 RC1
97
Version 1.2 RC2
98
Version 1.2
98
Version 2.0
135
Version 3.0
178
As you can see in Table 2.2, there are several types of releases issued throughout the development process. During the concept or requirements phases, one or more prototype releases enable users to evaluate the proposed GUI. After a few key features are working, alpha releases given to a few users provides valuable early feedback that reduces later work. When the application is nearly feature-complete, one or two beta versions for users to test and really put to use will improve the quality of the final product. Most of these releases are done for every major version release (big changes) and minor revision (smaller changes). Release candidates (RC1, RC2, for example) include fixes resulting from betas and, when used, provide further user and deployment testing prior to an official version release for widespread distribution. Figure 2.6 illustrates the process of creating a build. Note The number of builds is not a cause for concern. A large number of builds is not a sign of poor development. It is a sign of careful development. Rather than integrating dozens of large changes all at once, frequent builds allow fewer smaller changes to constantly to be integrated.
2 PLANNING THE DEVELOPMENT PROCESS
Release
32
Database Design Unleashed PART I
FIGURE 2.6 Creating a build. Current Build of Application
Integrate Changes and Additions
New Build of Application
Test
Changes from Developer #1 Changes from Developer #2 Changes from Developer #3
Implementation Issues
Creating a new build (see Figure 2.6) starts by collecting and integrating the changes and additions from all the developers. Sometimes, integration is as easy as importing a new report into the front-end MDB. More often, though, adding a new form or module to the application will require some changes to one or more calling forms to provide a way to use the new functionality. This might require two of the developers to sit down together to successfully integrate their changes. After everyone’s changes and additions are integrated into the current build, you can update the release/build name displayed by the application.
Tip Include the current release name and build number on the application’s start-up form and also on your “about” form opened from the Help menu. This makes it easy to determine in which release a defect is found. For example: “Version 1.0 Build 89 — 05/02/2001”.
This is also an ideal time to make an archival copy of the entire build. Should something happen to the development environment before the next build, the last build would be the most recent reference point. Frequent backups are also good computing practice. This copy should include the front-end and back-end MDBs, as well as any other files that are being changed from build to build. Consider storing the backup offsite, to preserve the substantial resources and time invested in the project.
Planning the Development Process CHAPTER 2
33
Integration testing should be done after every new build is created to ensure that the various parts work together as planned. After the build is proven to work acceptably well, the entire development team must upgrade in order to base its work on the most current version. If possible, take advantage of the features of a source code repository tool, such as Microsoft Visual SourceSafe or PVCS. Such tools provide automated methods for creating and tracking changes to individual files, and to larger builds and releases made during the project lifecycle. It will also reduce problems that can occur when changes are made by multiple developers.
2 If you do not use some sort of source code control, then proper archival procedures are even more important.
To create a release, take a successful build and make an installation package. This might be as simple as zipping up the front-end MDB, or as complex as using the Office 2000 Developer’s Edition installation tool to create a professional installation script. In either case, the release should be tested on several representative PCs to verify that it will work cleanly. Figure 2.7 illustrates the release process.
FIGURE 2.7 Creating a release.
Create Installation Package
Installation Testing
Distribute and Install
User Training
Write Release Notes and Write/Update All User Documentation
After testing is completed successfully, distribute the release to users for installation. Include a short Release Notes document with each release. At a minimum, list any new features, changes, and known problems. Even if the users never look at this Release Notes document, a Release Notes document gives developers a reference point for remembering what has changed from release to release. Remember to update your user documentation whenever the application is revised. Out-of-date documentation is worse than no documentation at all. Also, it is important that the release be given to end users working on as many different hardware and networking configurations as possible.
PLANNING THE DEVELOPMENT PROCESS
Caution
34
Database Design Unleashed PART I
Weeding out problems in this area are best identified very early in the release testing process. Figure 2.8 illustrates a complete build and release cycle. FIGURE 2.8 A build and release cycle example.
Add features & make changes Create a new Build Create a new Release for users
e.g., Build 41 e.g., Beta 1.0 - Build 42
Get feedback from users Add features & make changes Create a new Build
e.g., Build 43 Additional Builds
Add features & make changes Create a new Build Create a new Release for users
e.g., Build 55 e.g., Beta 2.0 - Build 56
Get feedback from users
Many builds, and an occasional release, will be created throughout the life of the project. Figure 2.8 illustrates a single pass through the build and release cycle. Every build offers an opportunity for all developers and testers, and perhaps even users, to test the application and to submit problems and ideas to the rest of the team. These are often called defects or issues. Depending on their priority relative to continuing development, these defects might be addressed by developers in time for an upcoming build, or they might be deferred until later.
Creating a Detailed Design A detailed design gives you the chance to document how the application’s features are to be implemented, particularly complicated or complex ones. The detailed design should cover at least class modules, form navigation, tough algorithms, and unusual automation
Planning the Development Process CHAPTER 2
35
techniques. Recording your design decisions, and how you implement them, aids you and others later, when changes or additions need to be made. Hold one or more design reviews until the design is ironed out. Tip
Public Sub ProcessAllUpdates() ‘ Comments : Get the updated mail from the MAPI Inbox, ‘ unattatch it and update the current database. ‘ Parameters : <none> ‘ Returns : <none> ‘ Created : Tuesday, July 27, 1999 11:44 PM by Joe Developer ‘ Pseudo Code: ‘ -------------------------------------------------------‘ Sub ProcessUpdates() ‘ UpdateCount = 0 ‘ UnattatchAllUpdates() ‘ Open a recordset from tblDownloadReceivedResults ‘ For each in Recordset ‘ ProcessOneUpdate() ‘ If Successful Then ‘ Delete Update file ‘ Increment(UpdateCount) ‘ Next ‘ --------------------------------------------------------
Reviewing the Design Design reviews simply involve asking someone else to go over the design problem and your proposed solution. These reviews might range from an informal “Hey Stephanie, will you look at this for me?” to a formal meeting with a presentation and review by several team members. The value of an external review is that a fresh set of eyeballs might see a solution you overlooked or identify a flaw in your solution. In the process of explaining the design to someone else, you will often see an additional problem you neglected or an even better solution. Another developer will often find something you’ve overlooked. This is an ideal time to find design problems—before you invest extensive time coding and testing the application.
2 PLANNING THE DEVELOPMENT PROCESS
A great way to document non-trivial algorithms is to include pseudocode comments in the sub or function. Pseudocode is simplified VBA code included as comments to describe the overall process you are implementing in the following code. The exact syntax is unimportant; you are simply describing, in general terms, the algorithm that is implemented in VBA. The following example shows the use of pseudocode in a sub header.
36
Database Design Unleashed PART I
Implementation Implementation is the one step of the development process that clients, users, and inexperienced developers think of when they visualize how software is developed. This is when the forms and reports are built, and the code gets written. Most of the other chapters of this book discuss the challenges you face here.
Code Review Code (or implementation) reviews have the same purpose as design reviews: getting a fresh set of eyeballs on your code. Peer reviews should examine each of the forms, reports, and queries, as well as all the code itself. Like the design reviews, just by explaining what you did and how it works, you will invariably notice something you missed. Someone else looking at your work will often see something you have overlooked dozens of times. You or your reviewer might find a problem with your implementation, design, architecture, or perhaps with the requirements. Remember that the earlier in the development process that problems are found, the easier and cheaper it is to fix them. Code reviews are also beneficial as a way to train developers. To this end, ask senior developers to review code written by junior team members. Another valuable benefit is that developers get a better understanding each other’s areas of the specialty. Tip Consistent use of coding and naming standards will make code reviews much easier and quicker. This also helps to avoid lengthy debates and arguments between developers over whose style and naming is better.
Tip If you are an independent developer, not part of a multideveloper team, find a partner and review each other’s code. The time and energy will quickly pay off for both of you. User groups can be a huge benefit here.
Testing Testing involves verifying basic functionality to catch bugs, and also ensuring that the application meets the agreed upon requirements. There are several kinds of testing to
Planning the Development Process CHAPTER 2
37
perform throughout the development process: unit, application, configuration, and installation testing. Part of the design plan will include a test plan that identifies, at least in general, overall testing procedures.
Unit Testing
What should you test? Testing a subroutine, function, or class module should cover all the input parameters by trying the range of expected and unexpected values, including boundary conditions (values immediately above, equal to, and below the range, and values of 0, null, or empty, depending on the datatype). When testing a new form, make sure that every control is tested. Check, among other things, data validation, locked fields, tab order, inserting new records, style consistency, and form navigation.
Application Testing Application testing (often called system testing or integration testing) is done on the overall application after all the features have been integrated. There are several purposes for performing application testing. The most important is to confirm that all the requirements have been met. The second purpose is to determine how ready for release the application is. The following are some examples of the kinds of tests you need to perform: • Test the behavior when tables are empty. • Test with different video resolutions. • Test with the Windows display settings font size set to both Small Fonts and Large Fonts. • Test with a couple of different Windows display color schemes. • Test printing to several different printers (particularly the brand and model the users have). • Test with the Windows taskbar Auto Hide option turned on and off. • Test that “What’s this?” help, ToolTips, and status bar text match their associated fields.
2 PLANNING THE DEVELOPMENT PROCESS
Unit testing refers to testing an individual feature or object, such as a key query, a form, or a report. Before you begin implementation of a feature or object, spend some time deciding how you’re going to test it. Run unit tests as each feature is completed to verify that they work as intended and are ready for integration into the rest of the application. A typical rule of thumb is to allocate about 20 percent of total development time to unit testing. For example, if it took you seven hours of detailed design time to develop the algorithm and three hours to code and debug it, estimate two hours of time to test it.
38
Database Design Unleashed PART I
• Test the tab order on each form. • Verify that all keyboard shortcuts are unique on each form. • Test the use of default keys (Enter and Esc) on all forms. • Test style consistency between all forms and reports (Style Standard).
Installation and Configuration Testing Installation and configuration testing evaluates how well the application behaves when it meets the real world. PCs in the real world are seldom like the developers’ machines. They have different software installed with different amounts of memory, use strange printers, even stranger video cards, and are used in an unpredictable variety and combination of ways. This variety of configurations affects how easily an application can be installed, and how the application operates after installation. If possible, test on the worst-case environments. For larger projects, consider using a company that specializes in this kind of testing. Many offer labs brimming with equipment and software, both new and old.
Tip Test the installation and operation on a clean PC as well as a dirty one. Clean PCs only have an operating system and required software installed on a newly formatted disk (just like a brand-new computer). A dirty PC is one on which multiple versions of many applications have been installed over a period of time. Testing on both kinds of PCs is the best way to find unexpected dependencies on DLLs, registry settings, templates, and other supporting files. Diskimaging software can be very useful in quickly re-creating various configurations for testing purposes.
Defect Tracking A defect is any bug, flaw, error, or problem related to an application. It might be a defect in a document, a tool, or in the implementation of the application. Defects might be found at any time during or after development, and all defects should be reported and tracked. Any of the following activities might expose defects: • Design and Implementation Reviews—Defects discovered during reviews range from disregarding standards to design errors.
Planning the Development Process CHAPTER 2
39
• Document Reviews—During these reviews, many types of potential defects can be discovered. The requirements document might be missing or contain inadequate requirements. The design documentation might not address all the requirements, or one or more of them might be wrong. • Testing—Coding defects are the most common type of defect discovered during testing. Also, documentation defects can be found because the tests should be developed based on the documentation.
• Error Handler—If you are using automated error reporting, such as described in Chapter 13 “Professional Error Handling,” possible defects can be received by e-mail or obtained from the error log at the customer’s site. • Post-Release Rollout—Defects unfortunately surface after a release is rolled out and in widespread use. No matter what the source of the possible defect or who reports it, it must be documented. Collect all defects in one place. Defects scattered through various memos, scraps of paper, and e-mail might be lost or forgotten. You can use a commercially available defect-tracking tool, create your own Access database, or log defects in a three-ring notebook. However you document the defects, you need a process to ensure that possible defects are addressed. Note that addressing does not necessarily mean fixing it. You might decide not to fix some defects based on their frequency or severity. More on this in a moment. Put every defect through the same process from discovery to final resolution. 1. As soon as possible after a defect is discovered, enter the details into your tracking system. Table 2.3 shows a list of suggested information to collect. Tables 2.4 through 2.8 further describe some of the defect information. 2. The development team periodically reviews all unaddressed defects assigning a priority to each. This decision is based on the severity, frequency, and reproducibility of each defect. It is then assigned to one of the developers for resolution. 3. The developer either fixes the defect or determines that it is not a problem, or it updates the resolution description and status. If the defect is fixed the resolution status is changed to “fixed.” 4. If possible, a different team member verifies all fixed defects, and the resolution status is updated to “verified.”
2 PLANNING THE DEVELOPMENT PROCESS
• Customer Support/Help Desk—During beta testing or after deployment, defects reported by the customer will come in through the developer or staff who supports the released product. Any enhancement ideas should also be included in the defecttracking system to assure they will not be forgotten.
40
Database Design Unleashed PART I TABLE 2.3
Defect Information
Data Field
Description
Unique identifier
Using sequential numbers will work. If multiple people enter defects, make sure that they are all are unique.
Description of defect
Describe the behavior of the potential defect, including error codes and steps to repeat the problem.
Found by
Name of person reporting defect.
Found when
Date and time of discovery.
Expected results
What had been expected to happen?
Found where
Create a list of documents and software items to report defects against (for example, requirements document, architecture document, form name, report name, module name).
Version
Document the version of the document or MDB (e.g., Beta 2.1 of software, Build 36, the 6/29/1999 version of the Requirements Document, and so on.)
Software and hardware configuration
List any pertinent configuration information such as display resolution and color depth, amount of memory, available disk space, operating system, other currently executing software, and so on.
Severity
How big a problem is this defect? See Table 2.4.
Frequency
How often is the defect encountered? See Table 2.5.
Reproducibility
How easily can the defect be reproduced? See Table 2.6.
Priority
How important is it to fix this defect? See Table 2.7.
Assigned to
Which developer is going to address this?
Resolution Status
What is the status of the defect? See Table 2.8.
Resolution Description
How was it “Fixed”? Why was it “Deferred” or “Not a Problem”?
TABLE 2.4
Severity of Defect
Severity
Criteria
Critical
There is a loss of functionality, or data is lost.
Serious
A feature does not work as expected, or there is a loss of usability.
Medium
Causes a non-critical aspect of the application to fail with no data loss.
Low
A cosmetic problem or an enhancement request.
Planning the Development Process CHAPTER 2 TABLE 2.5
41
Frequency of Occurrence
Frequency
Criteria
Common
A user could easily encounter the defect with normal use at least once per use. (For example, when closing the main form.)
Occasional
The user is unlikely to encounter the defect every time he or she uses the product, but will probably encounter it sometime. (For example, only when users print a two-page invoice.)
Seldom
A small fraction of the users will ever encounter this defect.
TABLE 2.6
Reproducibility of Defect
Reproducibility
Description
Always
Can always reproduce defect.
Intermittent
Can usually reproduce defect, but not always.
Once
Observed defect once, no further testing for repeatability performed.
Non-repeatable
Observed defect once, unable to repeat it.
TABLE 2.7
Priority of Defect Repair
Priority
Description
Critical
Must be fixed.
High
Should be fixed if at all possible.
Medium
Fix if time is available.
Postpone
Don’t bother fixing it at this time.
TABLE 2.8
Resolution of Defect
Resolution
Description
Unaddressed
New and not reviewed yet.
Not a Problem
Works as designed.
Deferred
Will not fix it at this time.
Fixed
Problem has been fixed and is awaiting verification.
Verified
Fix has been tested, and the defect is resolved.
PLANNING THE DEVELOPMENT PROCESS
(For example, only with a certain video card.)
2
42
Database Design Unleashed PART I
Tip You can improve your development process by looking for the root cause of defects. By determining how and where the defect was introduced, you might learn ways to avoid similar problems in the future. Common root causes are design errors, coding errors, missing requirements, ambiguous requirements, or developer- or customer-training deficiencies.
Version Control The section “Builds and Releases,” earlier in this chapter, suggests using some type of source code repository or version-control software. Why? The most common reason is to synchronize changes. If the development team includes more than one developer, eventually two developers will simultaneously change the same object. Perhaps they will both open a form at the same time, and one will add a field while the other is fixing a function. First one, and then the other, will save their changes back to the “master” MDB. The second one to save will win—overwriting the changes the first developer had just saved. With any luck, the first developer will retest his work and notice that his change has disappeared; otherwise, the problem will simply show up again later. There’s no work like rework! However, this kind of problem is solved by monitoring and controlling who is allowed to make changes to an object. Another reason to implement version control is to identify and re-create a prior working version. There will be times when you will need to verify that a problem exists in an earlier version. Restoring a prior version is also useful when planned changes don’t work as expected, and you want to undo them. These problems apply to development teams of any size. There are several techniques and tools that can address these version-control problems. Probably the best solution is to purchase a commercial tool designed for the job. The most popular is Microsoft’s Visual SourceSafe because it is tightly integrated with Microsoft’s development tools, including Access 2002. The most important elements source code tools provide: • Tracking changes to files and providing the ability to roll back changes to a specific point • Maintaining a version history • Limiting access to specifically identified personnel • Automated version numbering
Planning the Development Process CHAPTER 2
43
• The ability to maintain multiple code branches, such as a “release,” “stable,” and “development” code branches
Caution Access is different from many other development tools because it saves all its objects in a single MDB file. Many commercial version control tools only track individual files. This limitation forces developers to check in and out the entire MDB! This is not very useful in an Access development environment, so make sure any tool you evaluate has support for Microsoft Access at the object level.
This chapter took a galloping survey of some of the methods, issues, and techniques for planning and managing your development process. Oddly, the actual coding is only a small part of it. Always invest the time needed to gather and understand the user’s real requirements. Plan your development approach, and write a project plan to help focus your development efforts on meeting the requirements, budget, and schedule. Hold design and code reviews and track every build, version, and defect throughout the development process. To the degree you successfully incorporate these ideas into your development projects, you can count on the successful completion of projects that come your way.
PLANNING THE DEVELOPMENT PROCESS
Summary
2
CHAPTER 3
Database Design and Normalization
IN THIS CHAPTER • The Relational Design Theory
48
46
Database Design Unleashed PART I
Access applications are database applications, an obvious statement that can get lost in the details of designing a dialog box or crafting the perfect form. To develop smarter, faster, more efficient database applications, you need to understand the concept of database normalization, this chapter’s topic. Relational database management systems, or RDBMSes, such as Microsoft Access, implement a theory of data management and manipulation called, strangely enough, relational theory, pioneered by Dr. E.F. Codd at IBM during the 1970s. This chapter introduces you to just enough relational theory to enable you to design Access databases that take advantage of the way relational database operations work. Note Critics of Microsoft Access argue it does not conform to all of Dr. Codd’s 13 rules defining a relational database and so cannot be called relational. Predictably, Access’ supporters argue it is fully conformant. From a practical standpoint, it behaves like relational database. You may judge for yourself whether it is truly conformant.
Codd’s Rules The following table lists the 13 rules Codd declared to which a database system much conform in order to be considered a true relational database management system. Rule
Description
The Foundation Principle
Any RDBMS must be able to manage databases entirely through its relational capabilities. If a database system depends on a record-by-record data manipulation tools, it is not truly relational.
Information
All data in a relational database is represented explicitly as values in tables. Data cannot be stored in any other way.
Guaranteed Access
Every data element must be accessible logically through the use of a combination of its primary key value, table name, and column name.
Missing Information
Null values are supported explicitly. Nulls represent missing or inapplicable information.
Database Design and Normalization CHAPTER 3
System Catalog
The database description or “catalog” exists at the logical level as tabular values. The relational language (SQL) must be able to set the database design in the same manner in which it acts on data stored in the structure.
Comprehensive Language
An RDBMS must support a clearly defined data-manipulation language (SQL) that comprehensively supports data manipulation and definition, view definition, integrity constraints, transactional boundaries, and authorization.
View Updatability
All views that can be updated by the system. In a true RDBMS, most (although not all) views would be updatable.
Set Level Updates
An RDBMS must do more than just be able to retrieve data sets. It has to be capable of inserting, updating, and deleting data as a relational set.
Whenever possible, application software must be independent of changes made to the base tables. For example, no code should be rewritten when tables are combined into a view.
Integrity Independence
Data integrity must be definable in a relational language and stored in the catalog. Data integrity constraints can be built into applications. However, this approach is foreign to the relational model. In the relational model, the integrity should be inherent in the database design. continues
AND
Logical Data Independence
DATABASE DESIGN
Data must be physically independent of the application program. The underlying RDBMS program or “optimizer” should be able to track physical changes in the data. For example, an RDBMS’s application programs should not have to change when an index is added to or deleted from a table.
3 NORMALIZATION
Physical Data Independence
47
48
Database Design Unleashed PART I
Distribution Independence
RDBMS capabilities will not be limited due to the distribution of its components in separate databases.
Nonsubversion
If an RDBMS has a single-record-at-a-time language, that language cannot be used to bypass the integrity rules or constraints of the relational language. Thus, not only must an RDBMS be governed by relational rules, but these rules must be primary laws.
The Relational Design Theory The relational design theory developed by Dr. Codd consists of the following categories: • Tables and uniqueness • Foreign keys and domains • Relationships • Data normalization • Integrity rules
The Benefits of Using the Relational Model Using the relational design theory, you gain the benefits of years of research into the best way to manage data. Some of the benefits you can achieve by following the relational model are • Ensuring data integrity. • Storing data storage efficiently. • Giving your database application tremendous room for growth. • Creating a database that behaves predictably because it conforms to these welltested rules. • Enabling other database designers to understand your database because it follows the rules. • Ensuring that database schema changes are easy to implement • Improving the speed of data access. I know this ties in with efficiency; however, the mention of the word speed is important.
Database Design and Normalization CHAPTER 3
49
Tables and Uniqueness When you create database applications, each table represents an entity or process in the real world. Tables can represent people, events, financial transactions, and physical items such as products. Relational theory requires that all data is stored in a table consisting of unique rows and columns. The way to guarantee uniqueness for each rule is to set a primary key for each row. A primary key is a field or group of fields that uniquely identifies that row. If the primary key consists of multiple fields, it is called a composite key. The primary key must be unique—that is, it must appear in one and only one row. Access allows you to designate a field as a primary key by setting it as a Key value in table design view. Access will then check to see if the data in that field is unique and does not allow duplicates. Sometimes it can be hard to come up with a unique value for each row while following the business rules in your application. For example, suppose you are creating a contact management system to track your contacts. Your initial database design might resemble the table shown in Figure 3.1.
A common rule of database design is to keep the key as simple as possible and to choose the key from data that is the most unique and least likely to change. A person’s name might change due to marriage, divorce, and so on. Phone numbers and e-mail addresses
AND
As you develop your contact management system, you might select ContactName, ContactEmail, or one of the ContactPhone fields as the key. Possible key fields are commonly referred to as candidate keys because, during the initial database design period, they might be used as primary keys, but the final decision has not been made.
NORMALIZATION
A table storing contact information.
3 DATABASE DESIGN
FIGURE 3.1
50
Database Design Unleashed PART I
change all the time. A good key would be a Social Security number, but what if your contact lives outside the United States? In this situation, Access allows you to create an AutoNumber field to use as a primary key. An AutoNumber is a numeric value that Access maintains and automatically increments each time you add a new record to a table. However, an AutoNumber is not the same as a record number. In record number–based database products, record numbers are recalculated as records are added or deleted. In Microsoft Access, a record’s AutoNumber remains the same as records are added or deleted (except in an unusual circumstance discussed in Chapter 5, “The Microsoft Jet Database Engine 4.0”). AutoNumber values are not reused in the same table after a record is deleted. There are two types of AutoNumber values: • Integers • ReplicationID or GUID (used in Replication). Note You can manipulate a Jet AutoNumber in a few ways. You can set the AutoNumber increment value, reset the starting seed, and start counting backward. See Chapter 5 for more information.
Foreign Keys and Domains When one table’s primary key is used in another table, it is called a foreign key because it is foreign to the second table. Foreign keys are used to relate records from two (or more) tables, a topic discussed in the section titled Relationships later in the chapter. Figure 3.2 illustrates a table using foreign keys. FIGURE 3.2 A table with two foreign keys.
Figure 3.2 shows the Contacts table with two foreign keys, ContactID and PhoneID. ContactID is the primary key of the tblContactName table (not shown) and PhoneID is the primary key of the tblContactPhone table (not shown).
Database Design and Normalization CHAPTER 3
51
When you use foreign keys, they will always be in the same domain. A domain is the pool of possible values from which a column’s value is drawn. For example, in the United States, the domain of ZIP codes consists of all valid ZIP codes. Another example might be a university’s student ID numbers. If the student ID system is based on Social Security numbers, the domain consists of all valid Social Security numbers available to the system.
Relationships When you create primary keys and foreign keys, you are defining relationships. Relationships refer to how the records in one table connect to the records in another table. Access supports three different types of relationships: • One-to-one relationships • One-to-many relationships • Many-to-many relationships To set relationships in Microsoft Access, press the Relationships button on the toolbar or select Tools, Relationships from the menu to bring up the Relationships window.
A one-to-one relationship.
Figure 3.3 shows a one-to-one relationship because each record in the table tblContacts relates to, at most, one related record in tblContactEmail. One-to-one relationships are the least common types of relationships because most databases include related information in one table. For security reasons, however, you might choose to break out information into two tables. Financial transactions often involve many one-to-one relationships.
AND
FIGURE 3.3
DATABASE DESIGN
Two tables have a one-to-one relationship if, for each row in one table, there is at most one related record in the related table. Figure 3.3 shows an example of a one-to-one relationship.
NORMALIZATION
One-to-One Relationships
3
52
Database Design Unleashed PART I
One-to-Many Relationships The most common type of relationship is the one-to-many relationship. A one-to-many relationship occurs when a table has zero or many related records (sometimes called child records) in another table. A typical example occurs in a customer order database, because one customer can have many orders. Similarly, in your contact database, one contact (the “one” side of the relationship) might have several phone numbers (the “many” side). To express the relationship in terms of keys, each contact has a unique ID which is the primary key in one table. This primary key would appear as a foreign key in each related record in a table of telephone numbers. Figure 3.4 illustrates a one-to-many relationship. FIGURE 3.4 A one-to-many relationship.
The ContactID field is tblContacts’ primary key and a foreign key in tblContactPhone. ContactID 2 appears twice in tblContactPhone, meaning that there are two related records in tblContactPhone. Figure 3.4 includes the contents of the table to make the relationships clearer.
Many-to-Many Relationships Given two tables, tblAlpha and tblBeta, a many-to-many relationship occurs when one or more rows in tblAlpha relate to zero or more rows in tblBeta, and one or more rows in tblBeta relates to zero or more rows in tblAlpha.
Database Design and Normalization CHAPTER 3
53
Many-to-many relationships are problematic because it is difficult to select unique records from one table using existing relationships. Suppose you have three contacts (stored in tblAlpha) who use any one of three fax numbers (stored in tblBeta). If you want to send one of those contacts a fax, it is not immediately clear which fax number to use. Conversely, if you receive a fax from one of those three numbers that lists the sender’s fax number but not the sender’s name, you will not immediately know which of the three sent the fax. Unfortunately, Access does not have a way to resolve many-to-many relationships between two tables. The only way to do so is to create a so-called “linking” table. A linking table is a table that contains the primary keys of both tables as foreign keys. Figure 3.5 shows a many-to-many relationship between tblContacts and tblContactType through the linking table tblContactInfo. tblContacts contains all the contacts in your database. tblContactType lists all the types of contact information: Fax, Email, Phone, and Pager. tblContactInfo is the linking table. It contains a reference to a contact (ContactID), a contact type (ContactTypeID), and the contact information itself (ContactInfo), so you can locate either a specific telephone number using a contact’s name or a specific contact name using a particular telephone number.
AND
A many-to-many relationship using a linking table.
DATABASE DESIGN
FIGURE 3.5
NORMALIZATION
Moreover, if your contact adds a new type of communication (like a Web address) to the list, you simply add “Web Address” to the lookup table (tblContactType), and then add the new linking record to tblContactInfo. This is the most flexible way to build a contact management system because it avoids repeating fields (Phone1, Phone2, and so forth), blank fields, and having to add new fields to a table when you add another communication type to the list. A common naming convention when using a linking table this way is to use the “trel” prefix to identify a linking table. In this case, the tblContactInfo would be named trelContactInfo.
3
54
Database Design Unleashed PART I
Data Normalization Data normalization describes the process of designing a database (or modifying an existing database design) and organizing data to take best advantage of relational database principles. Codd defined six levels of normalization that he described as normal forms. The first three, known as First, Second, and Third Normal Form, have the most impact on database design decisions. As you read through the next few paragraphs, keep the following somewhat whimsical but useful summation of Codd’s first three laws in mind: “The values in a row are dependent on the key, the whole key, and nothing but the key, so help me Codd.”
First Normal Form First Normal Form requires that all columns (fields) in a table contain atomic values. That is, each field should contain one value, not a list of values or any repeating groups of data. Many flat file “databases” store data in this fashion, which makes searching quite difficult. Note The term flat file database refers to files, almost always text files but sometimes binary, containing some type of data. They are called ”flat” because they lack any structure or hierarchical arrangement suggesting the relationship between one record and the next. Flat files are often database dumps.
Figure 3.6 shows an example of a table that is not in First Normal Form. FIGURE 3.6 A table not in First Normal Form.
The ContactInfo column violates First Normal Form because it contains multiple pieces of information. Putting the contact type (Fax, Voice, Email) in its own column (ContactType), making separate records for each contact number in the ContactInfo column, creating the ContactInfoID column, and making ContactID the table’s primary key results in Figure 3.7. The table shown in Figure 3.7 is in First Normal Form because each column contains a single, discrete piece of information.
Database Design and Normalization CHAPTER 3
55
FIGURE 3.7 A Table in First Normal Form.
Second Normal Form A table in Second Normal Form is one in First Normal Form and in which every non-key field is fully dependent on the entire primary key. That is, any column in a table that is not the primary key or part of a composite primary key must depend on the primary key or all parts of a composite primary key for its meaning or interpretation. tblContact is not in Second Normal Form. The primary key is a composite consisting of ContactID, ContactInfoID, and ContactInfo. ContactName does depend on ContactID, but not on ContactInfoID or ContactInfo.
AND
A Table in Second Normal Form.
DATABASE DESIGN
FIGURE 3.8
3 NORMALIZATION
To put this table in Second Normal Form, put ContactInfo, ContactInfoID, and ContactType in a separate table. Next, use ContactID as a foreign key in the new table, creating a one-to-many relationship. The result is shown in Figure 3.8. Now both tables are in Second Normal Form because the columns in each table depend on their respective table’s primary keys.
56
Database Design Unleashed PART I
Third Normal Form A table is in Third Normal Form when it is in Second Normal Form and when no nonkey column depends on the primary key. tblContactOld in Figure 3.8 contains a field dependent on another non-key field, so it is not in Third Normal Form. If you do not see it right away, look at it for a few moments before continuing with the next paragraph. If you missed it, the dependent column is ContactType. Why? Given the ContactID and ContactInfoID, you can use a query to obtain the ContactType. The solution is to create a third table named tblContactType using the ContactType descriptions, and use tblContactType’s primary key (ContactTypeID, in this case) as a foreign key in tblContactOld. The result is shown in Figure 3.9. FIGURE 3.9 Tables in Third Normal Form.
All three tables are now in Third Normal Form.
Real-World Benefits of Normalization Why bother to normalize data? The original table in the previous example (Figure 3.6), listed the contact types and information in an array of values, making searching for the contents of a fax number very difficult. In addition, when one of your contacts’ phone number changes or gets a new form of communication method, like a cellular phone, updating the data is difficult and easily botched. First Normal Form improved the
Database Design and Normalization CHAPTER 3
57
situation and gave you more flexibility because it created separate records for each contact’s voice number, fax number, e-mail address, and so forth. First Normal Form, shown in Figure 3.7, only partly solves the problem. Contact data is difficult to manage because the same information is repeated many times and because storing the same name 10 times wastes disk space. Second Normal Form, in Figure 3.8, is almost, but not quite there. It eliminates most of the wasted disk space and repetitious information, but limits your flexibility. Third Normal Form allows you to add and remove contact types from a single table without requiring any changes to the structure of other tables. The tables in Figure 3.9 give you the most flexibility and use the least amount of disk space.
Data Integrity Rules
A cascading delete has the same effect for deleting a primary key. Cascading deletes ensure that deleting a record from a “one” table also deletes all related records from the “many” table(s). This feature has a downside, however. If you delete a customer and cascading deletes were set, all the “many” invoices would be deleted. If you do not have deletes set, the deletion would not be allowed in the “one” table until you deleted all the
AND
Suppose you have a lookup table for states. New York is in this table with a primary key of NY and a description of New York. Imagine that New York City broke away and formed its own state (before you laugh, it almost happened in 1789!) When you change your NY value to NNY (for “New New York”), the values of NY in all the child tables will be changed to NNY. If cascading updates were not set, you would need to add a record to the lookup table for NY1, update all the records in the child table, and then delete the NY record from the lookup table.
DATABASE DESIGN
A cascading update ensures that when you update a record’s primary key value on the “one” side of a one-to-many relationship, the change will be reflected in all records on the “many” side. The change, that is, will cascade through all related records.
3 NORMALIZATION
When normalizing a set of tables, you should also consider data integrity rules. Data integrity rules, sometimes called constraints, are rules, often enforced by the database engine itself, that ensure the consistency of your data. For example, to revisit the contact database example a final time, if you delete a contact from the main table (tblContact), you want to make sure that related records in other tables are also deleted. Deleting only a related record, on the other hand, should not delete other related records or, certainly, the primary contact information. Most data integrity rules in an Access 2002 database are determined by the relationships you define. In addition, Access lets you specify cascading updates and deletes when defining relationships.
58
Database Design Unleashed PART I
records in the “many” table, to avoid leaving any orphans. Orphaned records are records not related to an existing primary key. As a result, they are not directly accessible and constitute wasted disk space.
Summary As a relational database, Access 2002 implements a proven database model. Applying the relational model to your database designs will enable you to take full advantage of relational database advantages. A normalized database is a key element of a properly designed relational database. A database design that eliminates redundant data in any single table, places tightly related datasets into individual tables, keeps unrelated datasets separated in their own tables, and establishes clear relations between tables will enable you fully to exploit the relational database model Access 2002 implements.
CHAPTER 4
Advanced Queries
IN THIS CHAPTER • Using the Query Object
60
• Creating Queries in Access
62
• Creating Advanced Queries • Mastering Totals Queries
74
75
• Mastering Crosstab Queries • Mastering Parameter Queries
83 87
• Mastering Pass-Through Queries • Mastering Data Definition Queries
91 93
60
Database Design Unleashed PART I
The workhorses of any Access database are its queries. Queries come in a variety of forms and types. They can be a permanent part of the database or constructed on the fly. They can simply retrieve records from the database or they can update existing records, add new ones, and delete old ones. You can create queries directly using Structured Query Language (SQL), or you can build them by using the query design window (previously known as the QBE or Query By Example grid), a graphical user interface for query construction. An Access query might know exactly what it is supposed to do when it runs, or it can ask questions beforehand. Queries can work with data that resides in an MDB file, spreadsheets, another desktop database product, client/server databases, such as Microsoft SQL Server or Oracle, or any combination. Queries can even create, delete, or modify database objects. Because queries are the most common way to interact with your data, this chapter reviews query terminology, illustrates basic join queries, and then explores advanced Access 2002 queries.
Using the Query Object Access 2002 queries are database objects just like tables, reports, and forms. In most cases, the query object is the starting point for modifying or deleting existing queries and for creating new ones. They reside inside Database window’s query container. For more convenient access to frequently used queries, you can create shortcuts to them in custom groups. To view a database’s existing queries, click the Queries object in the Groups Window of the Database container. To view a query’s properties, right click a query object and select Properties. This shows a properties sheet resembling the one shown in Figure 4.1. FIGURE 4.1 A query’s Properties sheet displays valuable query characteristics.
One of the most useful pieces of information the Properties sheet shows is a description of the query. A good description will help you understand (or remember!) what the query does if you have to revisit it later. Similarly, other developers will find the description useful when they need to work on your query. This is a very helpful property, so
Advanced Queries CHAPTER 4
61
consistently take a few moments to describe a query when you first create it and to update the description as the query changes. Note Of course, you can also click the Properties icon on the toolbar or select Properties from the View menu. Rather than constantly and tediously listing such alternatives, this chapter uses right clicks whenever possible and assumes you know what the alternatives are.
Other useful information the Properties window shows includes its type (Select, Action, Parameter, Crosstab, or SQL), its creation and last modification dates (updated automatically), its owner (by default, the person who created it), and whether to show the query in the Database window. The owner and hidden status have implications for security, discussed in depth in Chapter 22, “Security.” Tip The standard method to keep users from seeing a query, or any other Access 2000 database object, is to prefix its name with USYS, as in USYS_myquery. You can view hidden objects during development or maintenance; select Tools, Options from the menu bar, click the View tab in the resulting dialog box, and then place a check in the Hidden checkbox.
Caution
The final field a query’s Properties sheet displays is whether it is replicable—that is, whether it is subject to the database’s replication scheme, if any. Replication status is important because it controls if, or when, it will be modified by another, presumably more current, version during replication. The Replicable checkbox shown at the bottom of Figure 4.1 is disabled, meaning that the database is not set up for replication.
ADVANCED QUERIES
Microsoft uses the object prefix MSYS to hide database objects. Never allow users to tamper with these system tables because doing so could corrupt the database. Likewise, developers should not use hidden objects because Microsoft does not promise to maintain them from one version to another.
4
62
Database Design Unleashed PART I
Creating Queries in Access As mentioned in the previous section, most interaction with Access queries starts with the query object. The friendly graphical interface facilitates the mechanics of working with queries but does little to aid in understanding queries. Before examining advanced query features and usage, this section quickly reviews common query terminology and how to construct basic queries.
Using the Query Design Window Access’s query design window makes it easy to create queries. The query design window is a graphical tool enabling you to control almost every part of a query. All data sources, whether native tables, linked tables, or even other select queries, can be displayed on the query design window and manipulated in exactly the same way. The query design window shows the relationships between the tables and queries (tables and queries look the same in the query design window), sorting and grouping instructions, and, naturally, the selection criteria all. Figure 4.2 shows a query in the query design window. FIGURE 4.2 The query design window offers a nearly complete graphical tool for query management.
The query design window is divided into two parts. The top portion is the table pane, which shows all the data sources the query uses, the relationships between the sources, if any, and the type of relation (one-to-one, one-to-many). The bottom portion shows the query properties, such selection criteria, sort order, or whether a field in the selection criteria should appear in the query result. To see a detailed list of a query’s properties, right-click anywhere in the table pane window to display the shortcut menu and then choose Properties. Each property is described in Table 4.1. The text following the table expands on the brief property descriptions.
Advanced Queries CHAPTER 4 TABLE 4.1
63
Query Properties
Property
Description Optional explanation of the query’s design or purpose.
Default View
Specifies the initial view of the query result form. New in Access 2002.
Output All Fields
Controls the output of all fields used in the query.
Top Values
Limits the number of records selected to a specified value based on the output sort order.
Unique Values
Returns records containing unique values in a given field.
Unique Records
Selects only records with unique values in all fields.
Run Permissions
Enables users to execute a query against data otherwise restricted based on the permissions assigned to their username or group.
Source Database
Specifies the database containing the field being queried. The default setting is the current database.
Source Connect Str
Contains the name of the application that created the external source database.
Record Locks
Determines how the query handles contention issues for the records the query affects. Options are No Locks, All Records, or Edited Record.
Recordset Type
Defines the type of recordset the query creates. Options are Dynaset, Dynaset (Inconsistent Updates), or a Snapshot.
ODBC Timeout
Sets the time limit, in seconds, after which an ODBC query will time out.
Filter
Defines a subset of the records to display, temporarily hiding others.
Order By
Controls the order in which the query recordset is displayed.
Max Records
Specifies the maximum number of records the query returns.
Orientation
Sets the order of the query result to the normal reading order for a language. New in Access 2002.
Subdatasheet Name
Creates a query resultset with drill-down capabilities.
Link Child Fields
Names a field on a subdatasheet, preferably a key, matching a field on the main query.
Link Master Fields
Names a query field, preferably a key, matching a field on the subdatasheet. This field must show in the query result in order for the subdatasheet to work.
4 ADVANCED QUERIES
Description
64
Database Design Unleashed PART I TABLE 4.1
contnued
Property
Description
Subdatasheet Height
Specifies the height (in inches) of a subdatasheet when it is displayed.
Subdatasheet Expanded
Controls when a subdatasheet is displayed.
Note Although it is possible to update data through a subdatasheet, as of this writing, the query does not automatically reflect that change in the main data sheet’s calculations without being restarted.
Setting Output All Fields to Yes is the same as clicking the Show checkbox of every field in the query. One reason to use this option is that it makes all the fields in the query available to other queries, forms, and reports. On the other hand, it often simply results in cluttered, hard to query output. You can use either integers or percentages with the Top Values property. You can also choose one of the canned values or percentages, or provide your own. The sort key, which defines what constitutes the top values, is the leftmost sorted field. For example, if a query result contains fields named product name, unit cost, and retail price (from left to right in the grid) and needs to show the five most expensive products based on their retail prices, the retail_price must be the first sorted field (again, reading left to right in the grid) and it must be sorted in descending order. An ascending sort would return the five least expensive products. If the results were also sorted on unit_cost, the top five items would be the most expensive products based on their unit cost. If no fields are sorted, you will get the first five records presented without sorting. The Unique Values property reduces the number of records a query returns. For example, suppose you have an outstanding_orders table that contains, among others, a column named custid showing the customer ID of each outstanding order. If you want to find out which customers have unfilled orders, set the Unique Values property to Yes for the custid field (a customer can have many orders). When securing a database, especially in a multiuser environment, locked tables prevent users from editing data arbitrarily. Certain queries, though, might still need to modify the data. Run Permissions make this possible because a query’s permissions can be set. For example, a query whose Run Permissions are set to those of the query’s owner will
Advanced Queries CHAPTER 4
65
execute with the owner’s permissions, not those of the user. This property makes it relatively painless to secure a database and still allow users to perform actions against it. The Source Database and Source Connect Str property settings work together to give a query access to unattached external data. The Source Database property would contain a path and filename if the data were contained in another database to which there was no attachment. The Record Locks property has three options: No Locks, All Records, and Edited Record. No Locks allows multiple users to make changes to the same record. The first user to write changes to disk “wins,” at least temporarily. Access warns the others that they are working with a record someone else has changed and gives each the option to abandon their own changes, to overwrite the changes, or to copy the changes to the Clipboard and decide what to do after that. All Records locks the records controlled by the query against edits by other users until the first user releases. Edited Record affects only the record the user is currently editing. When setting a query’s Recordset Type property, keep referential integrity and the query’s purpose in mind. If the query is only used to display data, use a Snapshot because Snapshots do not allow the users to edit the data and execute more quickly than the other options. Dynasets should only be used to update returned records on the many side of a one-to-many relationship that needs to be updated. The Dynaset (Inconsistent Updates) setting permits updates to data on both sides of a relationship, but, because it has serious implications for referential integrity, use it only if the relationships specify the referential integrity and cascading updates options.
The Subdatasheet Name property allows you to create a query that returns a recordset supporting drill-down. Suppose you write a query showing total sales by product. To enable drill-down, set the Subdatasheet Name property to the name of the table or query containing the actual order details. The query result will show the summary of total sales by product and a switch to click to see the details underneath the summary values. The Subdatasheet Expanded property, if set to Yes, causes the detail in the subdatasheet to be displayed when the query is opened. If the property is set to No, the user must click a switch to drill down into the detail of the subdatasheet.
4 ADVANCED QUERIES
If Max Records is set, the only way to see records that are not returned is resetting or clearing this property. Use the Orientation property to define the reading order of the results a query returns. For example, an application used by Hebrew readers should use right-to-left reading order, while applications for all Romance languages require a left-toright orientation.
66
Database Design Unleashed PART I
Note The time range for ODBC Timeout is measured in seconds with its default setting of 60 seconds. However, setting the property to zero does not make the query constantly check for a response; it actually turns the Timeout feature off, so no timeout error will occur.
Using the Table Pane All the tables, or queries, involved in a query are represented in the table pane, the top portion of the query design window. Each table appears as a small window with its field name listed inside. If you place a query in the table pane, only the fields that have been marked to show in that query’s resultset will appear. Any relationships, or joins, specifically defined using the Relationships window appear in table pane automatically. You might also see lines between tables even if you have not explicitly established any relationships. Microsoft calls this feature AutoJoin. If you add two tables to a query, the tables each have a field with the same or compatible data type, and if one of those fields is a primary key, Access assumes they are related and automatically creates a join between them. It might be that two fields with the same name are not really related, so be sure to check all the relationships in your query before you use it. Better yet, to disable AutoJoins, select Tools, Options on the menu bar, click the Tables/Queries tab, then clear the checkmark in the Enable AutoJoin checkbox. Tip For the best and most reliable performance, establish your relationships in the Relationships window of the database object. This enables relationships to be used in the engine’s execution plans, an internal method Jet uses to monitor and modify how it performs queries. Explicitly defined relationships also enable referential integrity, cascading updates, and deletes to be enforced at the engine level rather than in code.
Placing a Table or Query in the Table Pane Putting a table or query into the table pane is simple. When you first create the query and enter Query Design view, Access prompts you to select the tables or queries you want to use. If you need to add a table or query later, select Query, Show Table to reopen the dialog box or use the toolbar’s Add Table button.
Advanced Queries CHAPTER 4
67
Establishing Relationships in the Table Pane To create table relationships in the table pane, select a field name in one table representation and drag it to its related field in another table representation. This action draws a join line between those two fields and they will now act as related fields in this query. The join line might also have some characters attached to it such as an infinity symbol and the numeral 1. These characters describe the nature of the relationship between the fields and are displayed when you create the relationship at the database level. If you established the relationship at the database level, the relationship is one-to-one, one-tomany, or many-to-one. The join line represents this by displaying a 1 near the table on the one side of the relationship and an infinity sign on the many side of the relationship. Note that a join line does not mean that records exist satisfying the join, only describes that the potential exists. Like all other Access database objects, the join line (rather, the join itself) has properties you can manipulate. Unlike other Access database objects, you have to double click a join line to view or change them. Figure 4.3 illustrates a join properties window. FIGURE 4.3 Open a join’s Properties window by double clicking the join line.
4 ADVANCED QUERIES
As you can see in Figure 4.3, a join’s properties sheet contains four dropdown boxes and three radio buttons. The dropdown boxes specify the related tables and the columns that relate them. The radio buttons indicate the type of relationship (join) between the tables: an inner join, a left outer join, or a right outer join.
68
Database Design Unleashed PART I
Selecting a Join Type Selecting an improper join type can render a query useless for several reasons, such as selecting the wrong records, selecting records from the wrong table, or, most commonly, causing record inserts, updates, or deletes to fail outright or to create orphaned records in the table on the many side of a relationship. This section explains the differences between the join types available in the Join Properties window (and the Edit Relationships window). Inner Join is the default option, represented by a straight line between fields. Inner joins return a recordset consisting of only those records from both tables in which the related fields are equal. For example, a query of a customer table and an order table, joined on their CustomerID fields, would return only those customers who had placed orders and only orders connected to customers in the customers table. A left outer join (the join created if you select radio button 2 in Figure 4.3) returns all records from the table on the left side of a relationship (the Contacts table in Figure 4.3) and only records with a matching field from the table on the right side of the relation. The line representing this relationship has an arrow at one end that points at the righthand table. If you consider a left outer join one in which the left-hand table dominates the resultset because all of its records appear in a query’s resultset, whether they have a match or not, then a right outer join is the opposite because all records from the right-hand table would appear in the query result, but only records from the left-hand table with matching values appear in the result. Do not let the terms left and right confuse you. They do not refer to the tables’ positions in the query design window. Rather, left and right in this context refer to the tables’ positions relative to the query’s join expression, which controls the records selected. To illustrate, consider the following query: SELECT Customers.CustomerID, Orders.OrderID, Orders.OrderDate FROM Customers INNER JOIN Orders ON Customers.CustomerID = Orders.CustomerID;
As written, this query creates an inner, or simple, join. The join expression is Customers.CustomerID = Orders.CustomerID. The left hand table is Customers and the right-hand table is Orders. To create a left join, the SQL would resemble: SELECT Customers.CustomerID, Orders.OrderID, Orders.OrderDate FROM Customers LEFT JOIN Orders ON Customers.CustomerID = Orders.CustomerID;
Advanced Queries CHAPTER 4
69
The join expression is the same, but the added clause, LEFT JOIN Orders ON, requests a left join, so, as explained earlier, all records from Customers (the left hand table) and only records from Orders with matching values in the join field (CustomerID) will be returned. Similarly, a right join should resemble the following: SELECT Customers.CustomerID, Orders.OrderID, Orders.OrderDate FROM Customers RIGHT JOIN Orders ON Customers.CustomerID = Orders.CustomerID;
eferential integrity. It is likely that you could have customers without orders, for example, but you should not have orders without customers at the same time. If running a left outer join and a right outer join against the same relationship results in unmatched return records, you need to check your data integrity because records on the subordinate side of a relationship lacking a match on the dominant side of the relations are called orphaned records. Orphaned records are usually invisible to the normal operation of the database, but they can result in statistical discrepancies if the subordinate table is queried without the dominant table being involved. In other words, a report showing orders by product might not agree with a report showing orders by customers.
Using Inner and Outer Joins The following figures and listings show the different resultsets obtained using the join types discussed in the last section. The queries vary only slightly, and run against the same data, the Customers and Orders tables in the Northwind sample database. Listing 4.1 shows the SQL statement that creates an inner join. Figure 4.4 shows the result of an inner join.
The ten customers with the fewest orders using an inner join.
ADVANCED QUERIES
FIGURE 4.4
4
70
Database Design Unleashed PART I LISTING 4.1
The SQL Statement for the Query in Figure 4.4
SELECT TOP 10 Customers.CompanyName, Count(Orders.OrderID) AS CountOfOrderID FROM Customers INNER JOIN Orders ON Customers.CustomerID = Orders.CustomerID GROUP BY Customers.CompanyName ORDER BY Count(Orders.OrderID);
This query shows a count of orders for each customer and has an inner join, so the only records displayed are the ones represented on both sides of the relationship. Contrast this result with one created by Listing 4.2 and illustrated in Figure 4.5. FIGURE 4.5 The twelve customers with the fewest orders using an outer join.
LISTING 4.2
The SQL Statement for the Query in Figure 4.5
SELECT TOP 10 Customers.CompanyName, Count(Orders.OrderID) AS CountOfOrderID FROM Customers LEFT JOIN Orders ON Customers.CustomerID = Orders.CustomerID GROUP BY Customers.CompanyName ORDER BY Count(Orders.OrderID);
The second query indicates Figure 4.4’s resultset is inaccurate. Why? The outer join’s results (Figure 4.5) show two customers who have not ordered anything at all, something the inner join did not and could not show. Alternatively, you can use an unmatched query to isolate these customers. The term unmatched query refers to an outer join in which the foreign key’s criterion is set to Null. Unmatched queries make it easier to notice blank fields in a query’s output. Listing 4.3 executes just such an unmatched query, producing the output shown in Figure 4.6.
Advanced Queries CHAPTER 4
71
FIGURE 4.6 Customers without orders.
LISTING 4.3
The SQL Statement for the Query in Figure 4.6
SELECT Customers.CompanyName, Orders.OrderID FROM Customers LEFT JOIN Orders ON Customers.CustomerID = Orders.CustomerID WHERE Orders.OrderID Is Null;
The query in Listing 4.3 eliminates the clutter of extra records, displaying only the two records of customers who have placed no orders, instead of the 10 or 12 returned when the Null criteria is not applied against the foreign key. The SQL statements in the following two queries (Listings 4.4 and 4.5) differ by a single word, yet produce markedly different results (see Figures 4.7 and 4.8). FIGURE 4.7 Output of an inner join.
4 ADVANCED QUERIES
LISTING 4.4
The SQL Statement for the Query in Figure 4.7
SELECT TOP 10 Customers.CompanyName, Count(Orders.OrderID) AS CountOfOrderID FROM Customers INNER JOIN Orders
72
Database Design Unleashed PART I LISTING 4.4
continued
ON Customers.CustomerID = Orders.CustomerID GROUP BY Customers.CompanyName ORDER BY Count(Orders.OrderID);
FIGURE 4.8 Output from an outer join.
LISTING 4.5
The SQL Statement for the Query in Figure 4.8
SELECT TOP 10 Customers.CompanyName, Count(Orders.OrderID) AS CountOfOrderID FROM Customers LEFT JOIN Orders ON Customers.CustomerID = Orders.CustomerID GROUP BY Customers.CompanyName ORDER BY Count(Orders.OrderID);
Listing 4.5 returns the 12 records shown in Figure 4.8, rather than the 10 requested, because the LEFT JOIN forces all records from the Customers table to be displayed. Hopefully, these five examples demonstrate the differences between inner and outer join and the more important point that accurate query output depends not only on querying the right tables and fields, but also making certain that proper relationships between tables exist. As always, GIGO (Garbage In, Garbage Out)!
Using the Query Grid The lower half of the query design window is the query grid. The query grid is the real heart of a query’s design view and where you will do most of your work on a query. The query grid is so important because it is where you list the fields to query, the fields to present, the sorting order, the grouping hierarchy, the calculations, and the crosstab configuration; insert, delete, or update information if appropriate; and, most importantly, the criteria defining the records the query affects.
Advanced Queries CHAPTER 4
73
Placing Fields on the Query Grid To place fields on the query grid, you can • Double-click a field name to move it to the first available position in the query grid. • Click and drag fields to any column position in the query grid. • Control-click one field at a time to select multiple fields, then drag them simultaneously onto the grid as a group. • Double-click a table’s title bar to select all the fields in the table and then drag them to the query grid. • Double-click, or click and drag, the asterisk at the top of a table’s field list to place all of a table’s fields on the query grid. • Select values from the Fields and Tables dropdown boxes on the query grid itself. The first row determines the field and the second determines the table.
Establishing Sort Order Use the query grid’s third row, the Sort row, to set the sort order, if any. Query results can be sorted in Ascending, Descending, or Not Sorted. If more than one field is chosen for sorting, the sort order is applied left to right.
Displaying Results The fourth row controls whether fields used in the query appear in the resultset. Even if it does not appear in the resultset, it can still affect the result of the query. The resultset must display at least one field for the query to execute.
Defining Selection Criteria
• Constants • Mathematical expressions • Logical expressions • Functions • The name of a field, control, or property that evaluates to a single value • An expression that performs a calculation, manipulates string or character data, or tests data
ADVANCED QUERIES
The fifth and subsequent rows define the query’s selection criteria. The selection criteria can consist of one or more of the following:
4
74
Database Design Unleashed PART I
• The name of another query • Raw SQL statements If possible, avoid using raw SQL statements. SQL expressions used as selection criteria execute for each record in a query’s possible resultset, which slows the query’s performance. Use subqueries instead because they have the same overall effect without imposing, in most cases, the same performance penalty. Tip You can add multiple copies of the same fields to a query if need be. If you do, turn off the duplicates’ Show option, do not sort the result using the same field more than once, and do not perform the same calculation on field duplicates.
An optional row in the query grid is the Total row. Hidden by default, you can access it by clicking the Totals button on the toolbar or by selecting View, Totals from the menu bar. When activated, the Totals row becomes the third row in the query grid. Totals queries, variously called aggregate functions, aggregation queries, or rollup queries, impose significant restrictions on query design. Nevertheless, these restrictions become trivial when you realize the flexibility aggregation queries offer. Both advantages and disadvantages discussed in the section titled “Mastering Totals Queries.”
Creating Advanced Queries Depending on one’s experience level, an advanced query might be one that merely looks complex, say, a three-table select that performs a simple join and then dumps the results into a disk file. At the other end of the spectrum, an advanced query might refer to a patently complicated query, such as a long-running, CPU- and RAM-intensive, multimodule query boasting robust error-handling code to handle problems that develop while accessing multiple databases using one or more of a dozen subqueries that preselect data needing to be stored in temporary tables that are created and dropped as necessary using DDL queries that are executed by stored procedures, and which, finally, seven hours later, sends a concise, beautifully formatted two-page report to the CEO’s color printer. For this chapter’s purposes, advanced queries fall into three broadly defined categories. The first group relies on above average familiarity with SQL keywords and statements. Such queries seem advanced because they utilize standard but less familiar, less popular,
Advanced Queries CHAPTER 4
75
or less understood SQL language features. Examples include the aggregate functions discussed in the next section, parameterized queries, pass-through queries, and DDL (Data Definition Language) queries. In fact, the balance of this chapter largely focuses on queries falling into this first group. The second group of advanced queries exploits theory rather than mechanics. That is, they rely on a solid understanding of SQL design and relational database theory. You will see some of this information a few times before reaching the chapter’s end. Those of you who found the preceding section tedious, boring, or repetitive will find this section more rewarding. The third group of queries earning the advanced nom de plume demonstrate more or less intimate knowledge of the underlying operating system, the implementation of the database engine, nonstandard SQL extensions the engine supports, or some combination. This chapter ignores these types of advanced queries because they are almost always non-portable to other operating system platforms or other databases.
Mastering Totals Queries Totals queries summarize their underlying datasets, offering high level and less detailed information. Most select queries, on the other hand, present so much information that users cannot sort the wheat from the chaff. Table 4.2 lists the valid options used in Access 2002’s Totals row and the information they provide. TABLE 4.2
Options Available in the Totals Row
Description
Sum
Calculates the total of all values in fields meeting the selection criteria
Avg
Calculates the average value of all fields meeting the selection criteria
Min
Returns the lowest value of all fields meeting the selection criteria
Max
Returns the highest value of all fields meeting the selection criteria
Count
Returns the number of fields meeting the selection criteria
StDev
Calculates the standard deviation of all fields meeting the selection criteria
Var
Calculates the variance of all fields meeting the selection criteria
4 ADVANCED QUERIES
Option
76
Database Design Unleashed PART I TABLE 4.2
continued
Option
Description
Expression
Defines a valid custom expression applied to all fields meeting the selection criteria
First
Returns the first record in the group on which calculations are being performed
Last
Returns the last record in the group on which calculations are being performed
Group By
Specifies the fields on which to perform calculations
Where
Specifies the selection criteria for records not included in a calculation and which will not be shown in the query results
One key caveat regarding the aggregate functions (Sum, Avg, Min, Max, Count, StdDev, and Var) listed in Table 4.2 is that none of them count Null values in their calculations. If you want the aggregate functions to consider Null values, convert the Nulls to zeroes by running an update query. Alternatively, use an expression like the following. IIF(IsNull([fieldnamereference]),0,[ fieldnamereference])
This expression returns 0 to the caller (the aggregate function in this case) if, and only if, the field value is Null. The next few subsections illustrate how to create, execute, and interpret queries using many of the options available in the query grid’s Totals row. Unless stated otherwise, all queries use an unaltered copy of the Northwind sample database.
Counting Records With Count The Count function returns the number of fields in a query resultset meeting the value specified in the query selection criteria. For example, the query in Listing 4.6 counts how many orders each customer has placed. When creating and running an aggregate query, delete all unused objects from the query design grid, especially tables, fields, and other queries. The result of an aggregate function can easily be skewed by extraneous data, even data not used in the query. Figure 4.9 shows the query results.
Advanced Queries CHAPTER 4
77
FIGURE 4.9 Customer order count.
Notice that there is no need to state a sort order. Simple aggregate queries like this one sort themselves left to right using the Group By field(s) in the Totals row, unless otherwise specified. LISTING 4.6
The SQL Statement for the Query in Figure 4.9
SELECT Customers.CompanyName, Count(Orders.OrderID) AS CountOfOrderID FROM Customers INNER JOIN Orders ON Customers.CustomerID = Orders.CustomerID GROUP BY Customers.CompanyName;
Calculating Averages with Avg
FIGURE 4.10 Average aggregate query as seen from the query design window.
4 ADVANCED QUERIES
To see an average unit price by product category, create a query of only the Products table. Put the CategoryID and the UnitPrice fields into the query grid, make the query a totals query, and set the Totals option for the unit price field to Avg (average). Figure 4.10 shows the query results; Listing 4.7 contains the actual query text.
78
Database Design Unleashed PART I LISTING 4.7
The SQL Statement for the Query in Figure 4.10
SELECT Products.CategoryID, Avg(Products.UnitPrice) AS AvgOfUnitPrice FROM Products GROUP BY Products.CategoryID;
The Avg aggregate function operates on all the Number, Date/Time, Currency, and AutoNumber fields within a grouping, and yields the average for the values in that group.
Using Min and Max The Max and Min functions can be useful for identifying data such as each customer’s biggest and smallest orders (in monetary terms), the highest and lowest outstanding credit balances, or the most popular and least popular items ordered. In fact, you can even display the maximum and minimum values of any given field side-by-side simply by adding two copies of the field that interests you to the query grid, and selecting Min for one, and Max for the other.
Using First and Last The First and Last functions return the first and last items, respectively, presented to the query, sometimes producing surprising or unexpected results. Intuitively, First and Last seem comparable to Min and Max, but they are completely different. First and Last do not evaluate the data they see, but simply retrieve the specified record. Min and Max do evaluate and compare the data they see. The implication is that First and Last should be used cautiously, preferably against presorted data in a known state. It should be clear that using First or Last against unsorted data, or data sorted in a way unrelated to your desires, will yield arbitrary results. First and Last operate based on data’s order of presentation, not its content.
Validating Data With StDev and Var The standard deviation (StDev) and variance (Var) functions allow you to validate your data. Before using them, though, you might want to know what they are. Briefly, StDev and Var are statistical functions measuring data dispersion, or how far the individual data points in a set are from a value derived from all items of the set considered collectively. That is, they provide rough estimations of how well your data represents the entity it describes.
Advanced Queries CHAPTER 4
79
Note Statistical analysis is far more sophisticated and complex than this section suggests. To learn more about it, refer to a statistics textbook.
Variance expresses the average distance (actually, the average squared distance) of each individual data point from the mean of the entire set. In practical terms, standard deviation (technically, the positive square root of the variance) of a data population quantifies the data’s overall consistency and uniformity. The lower the standard deviation, the more uniform and consistent (closer to the mean) the data. What is the practical use of the Var and StDev functions in queries? They can tell you a lot about your data. Suppose you have two stores, NorthStore and SouthStore, identical in every respect except they are located on opposite sides of the city. Your first query uses the Sum function to compare sales, and you see that NorthStore’s mean sales are higher than SouthStore’s. The conclusion most would draw is that NorthStore is the better, more profitable store. However, a query using StDev returns a high standard deviation for NorthStore and a low one for SouthStore. How do you explain this result? One possibility is that SouthStore’s sales, although lower overall, might be concentrated in a particular price range or that SouthStore’s sales pattern seems to be based on many small sales. NorthStore’s high standard deviation might suggest few sales or sales varying widely in dollar value, either one of which might hint that NorthStore’s business base is less solid than SouthStore’s. Additional queries will make the result less ambiguous.
Creating Custom Aggregation Queries With Expression Expression allows you to present a calculation in your result, but that calculation must contain an aggregate function. Use this feature when you want to present the results of multiple functions in your query. For example, the query defined in Listing 4.8 contains a custom expression and returns the results illustrated in Figure 4.11.
4 ADVANCED QUERIES
Now that StDev’s and Var’s existence has been justified, creating a query using them is straightforward. Simply create a new query with the fields and grouping you want. Select the appropriate field and set its aggregate function to StDev or Var.
80
Database Design Unleashed PART I
FIGURE 4.11 A resultset created with a custom expression.
LISTING 4.8
The SQL Statement for the Query in Figure 4.11
SELECT Categories.CategoryName, Count(Products.ProductName) AS CountOfProductName, Sum([UnitPrice])*1.05 AS [ExpressionExample] FROM Categories INNER JOIN Products ON Categories.CategoryID = Products.CategoryID GROUP BY Categories.CategoryName;
The query in Listing 4.8 counts the number of products in each category, increases each category’s average price by 5 percent using the custom aggregate function Sum([UnitPrice])*1.05
and stores the results in a calculated field named ExpressionExample. To create a similar query of your own, set the Total property of the calculated field to and then write a formula that uses one of Access’s predefined aggregate functions. If your expression lacks one of the aggregate functions, you will receive an error message and the query will not run.
Expression
Understanding the Where Clause To apply selection criteria in an aggregate query, you can enter the criteria into the query grid directly, as previous examples have done, or you can select the Where clause on the Totals row. However, a selection criterion entered directly into the grid will not create the same query resultset if it specified using the Where option in the Totals row. In particular, • Selection criteria specified using the Where clause define the records to aggregate and are applied before any grouping. • Selection criteria specified without the Where clause (that is, entered directly into the query grid) affect are applied after grouping, and so affect the aggregated results.
Advanced Queries CHAPTER 4
81
• Selection criteria specified without the Where clause appear as elements of an SQL HAVING statement. The implications of these differences are subtle, but important. To include or exclude records from an aggregate query, use Where. Enter criteria without the Where clause, or use the SQL HAVING statement, to apply selection criteria against finished aggregation calculations. The examples shown in Figures 4.12–4.15 and their corresponding SQL statements (Listings 4.9 and 4.10, respectively) demonstrate the difference. Figure 4.12 shows a query design grid using only a Where clause, Listing 4.9 shows the corresponding SQL statement, and Figure 4.13 shows the query’s result. FIGURE 4.12 The Where clause selects records for aggregation.
This query shows unit sales by product category. The Where clause excludes orders shipped outside of the United States. Listing 4.9 shows the corresponding SQL statement. LISTING 4.9
SQL Statement for the Query in Figure 4.12
4 ADVANCED QUERIES
SELECT Categories.CategoryName, Count(Products.ProductName) AS CountOfProductName, Sum([Order Details].Quantity) AS SumOfQuantity FROM Categories INNER JOIN (Products INNER JOIN ((Customers INNER JOIN Orders ON Customers.CustomerID = Orders.CustomerID) INNER JOIN [Order Details] ON Orders.OrderID = [Order Details].OrderID) ON Products.ProductID = [Order Details].ProductID) ON Categories.CategoryID = Products.CategoryID WHERE (((Orders.ShipCountry)=”USA”)) GROUP BY Categories.CategoryName;
82
Database Design Unleashed PART I
The key detail to note in Listing 4.9 is that the WHERE phrase in Listing 4.9, which corresponds to the Where clause in Figure 4.12, is applied before the grouping specified in the GROUP BY phrase. Figure 4.13 shows the query result. FIGURE 4.13 Total U.S. unit product sales by category.
Suppose, however, that you only want to see the product categories that sold less than 1000 units. To do so, enter this criterion directly into the query grid or, if you are writing the SQL directly, use a HAVING statement. This second criterion will be applied after the aggregation and after grouping. Figure 4.14 shows the query grid for this query. Listing 4.10 shows the equivalent SQL. Figure 4.15 shows the query result. FIGURE 4.14 Selecting a subset of aggregated data.
Note that the criterion =500,”High”, “”)
Note The [Sum of Quantity] field was created when we defined the Sum in the subsection “Using Calculation Functions.”
Using User-Defined Functions from the Report’s Module Sometimes, the expression becomes too complex to keep in a single IIF() statement. When this happens you can create a function tailored exactly to your needs and then call that function from the report. The example in Listing 10.1 shows how Immediate If, evaluating the Sum of Quantity could be rewritten in the report’s module and called from a control on the report.
Reporting Unleashed CHAPTER 10
253
While viewing the report in Design view, choose Code from the View menu. This brings you into the reports module where you can create subroutines and functions used in this report. LISTING 10.1
Shows the Function
Function HiLowTest(Quantity As Double) As String HiLowTest = Array(“”, “High”)(Abs(Quantity >= 500)) End Function
You could have used a pedestrian If Then Else test, but, after all, this is an Unleashed book and you already know how to write If Then tests. Instead HiLowTest is written using an array and the absolute value of the test Quantity >= 500 as the index into the array. Broken out into its component parts, Quantity >= 500 evaluates to a Boolean value: either Quantity is greater than or equal to 500 or it is not. False is equivalent to 0 and True is equivalent to -1. The absolute value of 0 is 0, and the absolute value of -1 is 1. Hence, Abs(Quantity>=500) yields 0 or 1. Array(“”, “High”)(0|1) is a valid expression return and empty string or “High.” As a general rule, you might want to avoid a lot of clever code like this, but that rule does not have to be strictly adhered to. The name of the function HiLowTest is pretty self-explanatory and a comment describing the behavior would satisfy any human reader sufficiently. Now you can replace the IIF() function with the following: =HiLowTest([Sum of Quantity])
By using the function, you can provide a meaningful name to the behavior and more easily step into and debug the code (by placing a breakpoint in the function). Also, placing the function in the report module allows you to reuse the function anywhere else in your report, ensuring behavioral consistency. The basic idea is that a report is an object like any other. It is desirable to have meaningfully named procedures and to reuse code as much as possible.
Using User-Defined Functions from a Separate Module 10 REPORTING UNLEASHED
There are times when the calculation you are using on a report is also used on a form and by queries. In these cases it might be a good idea to write the function in a separate module object so your application’s queries, forms, and reports can always get consistent results. The following example shows a bare-bones function to convert a string to proper case. This is the kind of function that could be called from many places in an application.
254
User Interfaces Unleashed PART III
This function uses the new Split() function in Access’s VBA. Split() will parse a string into different array elements based on a delimiter you define (spaces are used by default). This new string primitive promises to have many uses in Access 2002. In your database it will take care of the improper capitalization of products such as “Chartreuse verte,” “Gravad lax,” and “Valkoinen suklaa.” The function is very simple. It takes a string as an argument, parses it into an array (varArray), and then loops through the array, making the first character of each element uppercase with the UCase() function. In order to return words with their first letter capitalized, the function uses the left and right functions to separate the first letter from the left of the word. These groups of letters are then rejoined or concatenated to form the whole word again. Likewise, the elements of the array are concatenated into one string to re-create the original string, but in proper case. Listing 10.2 shows how this is done. LISTING 10.2
Capitalizing All First Characters and Using the New Split Function
Function MakeProper(ByVal Str As String) As String Dim varArray As Variant Dim I As Integer Dim Upper As Integer Str = LCase(Str) varArray = Split(Str) Upper = UBound(varArray) For I = 0 To Upper MakeProper = MakeProper & UCase(Left(varArray(I), 1)) & _ Right(varArray(I), Len(varArray(I)) - 1) & “ “ Next MakeProper = Trim(MakeProper) End Function
Using Concatenation Concatenation is extremely common in advanced reports. It enables you to join fields together so they are easier to print and present. When you concatenate two data elements, they can no longer be treated independently. In other words, a form that shows a customer’s concatenated last and first name cannot (without a lot of coding) be used to enter new names.
Reporting Unleashed CHAPTER 10
255
Concatenation is performed with the concatenation operator, the ampersand character [&]. It is also possible to use a [+], but formulas concatenating customer numbers with account numbers can be confusing to a developer or user if the [+] is used. You will see more uses for concatenation in a mailing label example later in this chapter. Example: =”Access” & Space(1) & “2002” & Space(1) & _ “Development” & Space(1) & “Unleashed”
returns “Access 2002 Development Unleashed”
You can test this statement through the immediate window. From the module’s View menu, choose Immediate Window, or press Ctrl+G to gain access to the immediate window. In the Immediate window, type ?MakeProper(“access 2002 development unleashed”)
and press Enter. If everything was entered correctly, you should get “Access 2002 Development Unleashed” as a result. When concatenating, there are three syntaxes to be aware of: • Field names must be surrounded by square brackets if there are spaces in the name (for example, [Order ID]), but for clarity, it is a good practice to use brackets as a rule. • Text strings must be surrounded by quotes—for example, “Attention”. Also consider the use of Chr(34), the ASCII character representing the double-quote, when dealing with quotes in code. • Date values must be surrounded by pound signs, for example #7/27/1997#. The next step is to employ this function on the report. It will change the product names to proper case. In the Design view of the report, change the control source property of the ProductName control to the following: =MakeProper([ProductName])
REPORTING UNLEASHED
In some cases, this would be all you have to do. However, in this case, it’s not so simple. Because this control has the same name as a field in the report’s recordsource, the function will get confused and return #Error for each record. To distinguish the control name from the field name, you need to provide a distinct name for the text box control. By
10
256
User Interfaces Unleashed PART III
prefixing the Name property of the text box with TextBox, you end up with a distinct control name: TextBoxProductName. Now the report will not confuse the field ProductName with the control TextBoxProductName. Note Some developers prefer prefix naming conventions. It is a matter of style, but I generally prefer whole words. There is no single, universal naming convention employed in VBA and whole words leave little room for ambiguity.
When you view the report as it appears in Figure 10.9, in Print Preview mode, you can see the few instances where the names were changed to proper case. So far you have seen how to make a simple report and a more complicated one with the wizard. You have also seen how to modify the report after it was created; you’ve examined groupings, calculations, concatenation, and the use of custom functions in reports. These topics will carry you through the creation of 90% of the reports you will have to make for an Access database. FIGURE 10.9 The products now have names in proper case.
Working with Subreports Just as forms have a main form/subform capability, reports have a main report/subreport capability as well. Subreports are reports within a report, and they are often used to show
Reporting Unleashed CHAPTER 10
257
related records. They can also be used to show subsets of data consistently through different reports. When you use a subreport, you are able to take advantage of the formatting flexibility two reports will give. When working with a one-to-many relationship between the records in the main report and the records in the subreport, the main report is known as the parent and the records in the subreport are its children. Though the effect can often be accomplished with a grouping report configuration, subreports are very useful if you want to print records from several different tables. Figure 10.10 shows a category (Beverages) from Northwind and its subreport of specific products. FIGURE 10.10 Third page of Northwind’s Catalog report shows some of the flexibility offered by subreports.
Creating a Simple Subreport A typical subreport has a one-to-many relationship between the parent records and the child records. Make a note of the related fields between the two tables or queries. 1. Create a report for the parent records. This will be the main report. 2. Create another report for the child records. This will become the subreport.
10 REPORTING UNLEASHED
3. In the toolbox deselect the Control Wizards tool button. (This will prevent the Subreport Wizard from running when you paint a subreport on the parent report.) From the toolbox, select the subform/subreport tool. Using the subreport tool, draw a rectangle in the Detail section of the parent report indicating where the subreport should be placed.
258
User Interfaces Unleashed PART III
4. In the Properties window, set the subreport’s SourceObject property to the name of the subreport created in step 2. 5. If two fields—one from each report—have the same name, Access will automatically add the LinkMasterFields and LinkChildFields properties for the subreport. If they have different names, manually enter the field names linking the parent report to the child report by typing a comma-delimited list of linking field names. Note The LinkChildFields properties require the name of the underlying field, not the name of the report’s control displaying the field. The LinkMasterFields will accept the control name.
Caution If the main report and the subreport are related, the LinkMasterFields/ LinkChildFields properties must be set correctly for the report to work. It is possible to have unrelated main report/subreport constructions. Such cases might involve disclaimers or address blocks used in many reports, but displayed as a subreport for consistency and maintainability. It is even possible to have a main report which is based on no records at all.
The display of subreports within Access 2002 has been enhanced from earlier versions. In earlier versions, the subreport appeared as a blank empty box in the Design view of the main report. Now it displays more useful information about which fields are being used by the subreport. Better still, the subreport is editable from the Design view of the main report, as shown in Figure 10.11. You can address the properties and controls of the report as never before. This is sure to save time and aggravation. In earlier versions, the developer had to return to the report container and open the subreport’s report object in a separate window to make modifications to the subreport. There’s another kind of report that doesn’t quite fit this form, but is still quite common: mailing labels. The next section will look at this type of report and some of the peculiar aspects of them.
Reporting Unleashed CHAPTER 10
259
FIGURE 10.11 Selecting controls in the subreport for modification.
Creating Simple Mailing Labels Though you might never have a need to create mailing labels per se, the same techniques can be used for a variety of other things, such as name tags for a conference, tabs for your technical documentation, equipment identification tags, and so on. Access provides a Label Wizard that can prepare a report to print labels against hundreds of different formats provided by Avery, Zweckform, and others. These formats can also be altered with the wizard or later. To create labels, simply create a new report, select Label Wizard from the New Report dialog box, and choose Customers as the records source. Click the OK button to start the Label Wizard.
10 REPORTING UNLEASHED
1. Choosing the format for your labels couldn’t be easier than selecting the matching manufacturer’s product number. The dialog box in Figure 10.12 shows the exact measurements of some of the hundreds of common labels known to Access, and it will automatically format your report to accommodate them. You have to choose the unit of measurement, whether the forms are individually sheet-fed (most common) or continuous, and whether they are measured in English units (inches) or metric units. Each selection you make for measurement and label type changes the choices of product numbers. You can filter the selection by the label manufacturer.
260
User Interfaces Unleashed PART III
Avery is the default because they are the most widely used and many other manufacturers conform to their measurements and will label their products with a compatible Avery product number. Tip If you ever find yourself in a situation where there are no preset label formats that match your needs, you can create your own label format through the Custom dialog box.
2. When you have found the label product that matches the labels you are using, move to the next dialog box in the Wizard to choose the font and size of the lettering. Because Windows allows you to choose any font and font size you like, it is possible that you might choose a font and a size which is too large for your label. The dialog box shows you what your label might look like with the font and size you have chosen, so you are unlikely to be surprised when they are actually printed. FIGURE 10.12 Choose the label template that matches the labels you are printing on.
3. The Next button will take you to the dialog box, shown in Figure 10.13, where you determine how the label will look. The large blank area on the right contains the merge fields for your prototype. You can move the fields from the data source by double-clicking them or by using the > button. In order to get a new line, you must place your insertion point at the end of a prototype line and press Enter. After a line is created, you cannot separate its elements into separate lines. To delete something from the prototype, you can select it with your mouse pointer and press Delete. Entering text in the prototype box is the same as entering text anywhere
Reporting Unleashed CHAPTER 10
261
else: Just point, click, and type. Everything you do in the prototype box will affect every label. FIGURE 10.13 A completed prototype label.
The prototype shown in Figure 10.13 will create a label with the company name at the top, followed by the address and the individual to whose attention the mail will be sent. Notice that a comma and a space follow the {City}. These were typed in and will appear on every label. The same applies to Attn: at the bottom of the label. 4. Moving to the next step in the Label Wizard gives you the option of changing the sort order for the labels. You can choose to sort by more than one field (for example, last name and then first name) or by just one field. The labels will be sorted in the order they are found in the recordsource if no selection is made. When creating mailing labels, you might be able to take advantage of bulk mailing rates if you sort the outgoing mail by ZIP code, so choose PostalCode as the field to sort on (see Figure 10.14). 5. The final dialog box prompts you to name the report. Because these are customer mailing labels, the report could be called “Customer Mailing.” You also have the option of opening the report in Design view or in Print Preview mode. Choosing the Modify the Label Design option will let you see that the label report is just a creative use of the properties of the report.
10 REPORTING UNLEASHED
262
User Interfaces Unleashed PART III
FIGURE 10.14 Choosing the postal code to sort by.
The result of the wizard’s efforts is a small report containing a field for each line you defined in the prototype. Notice that the wizard has concatenated separate fields to make a single line out of city, region, and postal code. Notice also that the wizard has surrounded the fields with the Trim() function. Trim makes sure there are no leading or trailing spaces to throw off the alignment of your labels. If you were to create this label report manually, you would have to create the concatenation yourself and you would also have to call the trim function in each control. The report is also perfectly sized for the type of label you chose. The height and width of the report will keep your data printing on the labels column after column and page after page. The Grp Keep Together property is also set to Per Column. This, combined with the other property settings, gives you a report that is two columns wide and four labels deep on each page. If you have worked with labels before, a couple of questions probably come up right away. What happens if the address is more than one line? How do you move the region and postal codes if the name of the city is really long or really short? The Can Grow and Can Shrink properties for these controls are set to Yes, so they will be able to adjust to these conditions automatically. There is no need to create conditional statements to account for these issues.
Publishing a Report As great a tool as Access is for managing and presenting data, it is not the be-all-end-all of data managing and reporting. It is not always practical to give everyone an Access client so they can review and print the data in an Access report, and distributing a printed report to more than a few users is a clumsy and cumbersome task. Furthermore, some of
Reporting Unleashed CHAPTER 10
263
your users will want to manipulate the data from a report after they receive it. The report might contain exactly what your users are looking for, but its form and layout might leave it nearly useless to them. Sometimes the data of an Access report needs to be published or distributed in other, more creative and accommodating ways than a simple report.
Exploring the Methods for Publishing Reports In this section I will examine ways to publish Access reports through Word, Excel, and e-mail. There are several different formats that can help you tailor your reports to fit your users’ needs. Data Access Pages are another new way to distribute your reports over the Web, but because they are separate objects entirely, they are covered thoroughly in Chapter 25, “Using Access Data Pages.” The first decision you must make when publishing data is whether the users want a summarized and formatted version of the data or whether they want the raw data itself. If the users want the raw data itself and are not interested in layout, calculations, groupings, and so on, you might serve them better by sending them the data through the export of a query or table rather than by sending them a report. The steps for exporting a query or a table are very similar to exporting a report, but there are some different options to choose from when dealing with recordsets. The crucial thing to keep in mind when publishing a report is that the published report will not reflect changes made to its recordsource and the users will not be able to drill down through summary information to see more detail. If these are acceptable limitations, the rest of the chapter is relevant to you and your needs.
Using Access’s Built-In Methods of Exporting Reports Access provides eight options, shown in the dialog box in Figure 10.15, for exporting or publishing reports. Each of the options is a different file format in which to render the report’s information or layout. Table 10.3 describes each. TABLE 10.3
Access Report Export Options
File Extension
Description
Access
*.mdb, *.adp, *.mdw
Exports the whole report to another Access database.
Excel
*.xls
Turns the report’s output into an Excel spreadsheet compatible with versions 5 and 7.
10 REPORTING UNLEASHED
File Format
264
User Interfaces Unleashed PART III TABLE 10.3
continued
File Format
File Extension
Description
Excel
*.xls
Turns the report’s output into an Excel spreadsheet compatible with versions 97 and 2002.
HTML
*.html, *.htm
Generates a Hypertext Markup Language version of the report. Each page of the report is rendered as a separate HTML file and is indexed with its page number (for example, SalesByCategory.html, SalesByCategory2.html…). Hypertext links are automatically created to navigate from one page to another.
Text
*.txt, *.csv, *.tab, *.asc
The most universally accepted file format, it can be read by almost any application. All formatting information is lost.
Rich Text Format
*.rtf
Accommodates most word processors and maintains most standard formatting information in the document.
Snap Shot Format
*.snp
A file format unique to Access that enables you to send the report to people who do not have Access. All they need is Microsoft’s SnapShot Viewer.
XML Documents
*.xml
Exported to an XML formatted document. Any text editor can view the source of an XML document.
To choose any of these, all you have to do is select the report and choose Export from the File menu. From the resulting dialog box, you can choose the location and the file name and format.
Reporting Unleashed CHAPTER 10
265
FIGURE 10.15 The Exporting dialog box, showing the first six export file types in the combobox.
Using Office Links to Publish Reports Another way to publish is through Office Links. While viewing a report in Print Preview mode, you can select the Office Links toolbar button to send the current report to Word or Excel. From there, you can save the report to a location where others can view it.
Watching for Problems when Publishing When using Text and Excel as the output format, you might lose some formatting information, so check to see if the report is intelligible in these formats before using them.
Using E-mail to Publish Reports E-mailing a report can be done from Access through any MAPI-compliant e-mail system (for example, Microsoft Exchange). With the report in Print Preview or with it closed and selected in its container, just choose Send To from the File menu. You can send the report as an e-mail attachment in either HTML, Excel, DOS Text, RTF, SnapShot, or XML format. Someone wanting to view a report rendered in SnapShot format must have the SnapShot Viewer in order to do so. The SnapShot Viewer allows the user to view the report as if she had Access, and check the report through a Print Preview mode. This is especially useful when e-mailing reports because it makes for a hassle-free viewing of the attachment.
REPORTING UNLEASHED
For more on writing the code for e-mailing reports and other objects from Access, see Chapter 18, “Using ActiveX Automation.”
10
266
User Interfaces Unleashed PART III
Tip When exporting or e-mailing a report based on a parameter query or with parameters built into its functionality, the report might not work. Consider basing the export version of the report on a static table created from the same parameter settings.
Modifying a Report at Runtime Because a report is an object like any other object in Access, it is possible to write code to manipulate its properties and respond to its events to suit your purposes. The rest of this chapter reviews the Report object and its properties and events so it can be harnessed most effectively. Because forms and reports are so much alike, this section only deals with the characteristics that are unique to the reports or have some aspect of their behavior that is peculiar to reports. Events for reports and sections can be divided into design-time and runtime. This distinction is important when trying to solve thorny report problems. Design-time properties can only be set and addressed in Design mode. Once the report is running, it is not possible to alter them. The runtime properties can be accessed by code while the report is opening, formatting, or printing, giving you dynamic control of how the report will look and what it will contain. In order to modify a report at runtime (or design-time for that matter), it is crucial to possess a thorough understanding of the report’s properties, events, and objects. Following is a review of these elements.
Filtering and Sorting Filtering and sorting work very much like their counterparts in forms. With the Filter property you can limit the contents of the report to a finer granularity than imposed by the recordsource. The filtering only takes effect if the FilterOn property is set to Yes. To see filtering in action, follow these steps: 1. Open the Categories and Products report in Design view. 2. Change its FilterOn property to Yes. 3. Enter the following statement in the Filter property: [CategoryName]=”Beverages”
Reporting Unleashed CHAPTER 10
267
4. View the report in Print Preview mode. Only Beverages should appear in the report. 5. Return to Design view and set FilterOn to No. 6. View the report in Print Preview mode. All categories are now available in the report. To create the same affect with code, write the following two statements: Reports![Cataegories and Products].FilterOn=True Reports![ Cataegories and Products].Filter=[CategoryName]=”Beverages”
GroupKeepTogether When creating multicolumn reports, it might be necessary to keep the groups intact in a column or on a page. There are a couple of options to choose from: •
WholeGroup
•
This is probably a more realistic option. The header will reside in the same column or on the same page as the first record of its detail. Used in conjunction with the Keep Together property of the Sorting/Grouping property window.
When chosen in conjunction with the Keep Together property found in the Sorting/Grouping property window, it will attempt to keep the entire group on the same page or in the same column as the group’s header. WithFirstDetail
HasData When a main report performs calculations against data in a subreport and there is no data in the subreport, the expression might display #Error?. You can use the HasData property to determine if there is supporting data for a report or control. The HasData property returns three values, as shown in Table 10.4. TABLE 10.4
HasData Property Values
Value
Description
1
The report is unbound, having no recordsource
0
The report is bound but has no records
-1
The report is bound and has at least one record
=”Category has “ & Iff([CategorySubRpt].[Report].[HasData] = -1, _ [CategorySubRpt].[Report]![CatCount],0) & “ products.”
REPORTING UNLEASHED
A simple formula can check to see if the calculation should be performed:
10
268
User Interfaces Unleashed PART III
Report Events (Runtime) The following events are some of the events that fire when a report is run. That is, they are raised when the report is being printed, selected, and so on. These are the events you would use to control behavior that involves interaction with the user or with the report’s data.
Open Open is the first event a report fires. It runs before the underlying query is opened. This makes it a good place to program changes to the query or to pass parameters to the query. Code written in the Open event will execute before anything else happens in the report. After the Open event has completed, the report will take the steps needed to load its data.
Activate/Deactivate and Deactivate fire when the report becomes the active window (or in the case of Deactivate, is no longer the active window) or the report starts printing.
Activate
NoData In the past, Access would produce an #Error? when a report opened on an empty recordset or its filter returned no rows. Thankfully, that problem has been corrected and now controls are simply blank when bound to an empty recordsource. However, the NoData property will still fire when there are no records for the report to present. It might be a courtesy to the users simply to let them know that there are no rows to present and cancel the report. If you have a report for orders by customer, the code in Listing 10.3 would cancel the report when there are no orders for a given customer. LISTING 10.3
Canceling a Report When There Is No Data
Private Sub Report_NoData(Cancel as Integer) MsgBox “There are no orders for this customer.” Cancel=True End Sub
Error If a report’s underlying table is exclusively opened by another user or simply does not exist, the Jet Engine raises an error and the report’s Error event fires. The event is hooked into the AccessError method of the Access Application object, so you cannot use Err.Description in your handling of the error. The best way to handle errors in reports is through the report’s class module.
Reporting Unleashed CHAPTER 10
269
Page The Page event enables you to make last-minute changes to a report before it prints or is viewed in Print Preview mode. Some things (for example, drawing borders around pages and maybe calculating some special totals) are easier to do after the report has been formatted, but before it prints. The report’s Line method will draw a line around the report before printing: Private Sub Report_Page() Line (0, 0)-(ScaleWidth, ScaleHeight), , B End Sub
The arguments refer to a diagonal line from the top-left corner of the report to the bottom-right corner. The B parameter instructs the line statement to draw a box around the edge of the report.
Close occurs when you close the report and just before the Deactivate event. You might want to close a parameter-providing form along with a report, or change the Z position of the other windows. These tasks and other mopping-up routines can be programmed into the Close event.
Close
Section Properties An Access report can have as many as 25 sections: Report Header/Footer, Page Header/Footer, Detail section, and up to ten Group Headers/Footers. The Section property provides an array indexing all the sections in the report. Sections can be referenced by their index number in the array (you can also use an Access constant), as shown in Table 10.5. Sections are different from other Access property arrays in that they do not return a value of their own; rather, they only return a reference to the section. To make the report header visible, you can employ the following code: Reports![Categories and Products].Section(1).Visible = True
or Reports![Categories and Products].Section(acHeader).Visible = True
10 TABLE 10.5
Sections and Their Indexes and Constants
Index
Name/Constant
Detail
0
acDetail
Header
1
acHeader
REPORTING UNLEASHED
Section
270
User Interfaces Unleashed PART III TABLE 10.5
continued
Section
Index
Name/Constant
Footer
2
acFooter
Page Header
3
acPageHeader
Page Footer
4
acPageFooter
Group Level 1 Header
5
acGroupLevel1Header
Group Level 1 Footer
6
acGroupLevel1Footer
Group Level 2 Header
7
acGroupLevel2Header
Group Level 2 Footer
8
acGroupLevel2Footer
Group Level 3…Header/Footer
9…
Field name of Group
Name As noted in Table 10.5, sections of a report also expose a Name property that you can use in a reference. At any time, you can name a section for greater clarity and reference that section by the name you have assigned.
Height This property sets a vertical measurement in inches for the section.
Visible The Visible property controls whether the section is visible. The values are True or False.
Section Properties (Design Time) Sections have a group of properties that can only be read or set at design time. It’s important to distinguish them from other properties that are available at runtime after they are set. Following is a description of the design-time properties.
CanGrow/CanShrink and CanShrink are separate properties that enable you to determine whether a section can vertically resize itself to accommodate the changing size of its controls. If you have placed a control in a section with CanGrow set to True that expands for some data records, setting the CanGrow property of the section will enable that data to be properly displayed. CanGrow
Reporting Unleashed CHAPTER 10
271
Controls might not grow and shrink as expected if they touch one another. Because these property settings do not affect horizontal size or position, it is important to make sure that controls are not overlapping at all. Large controls like OLE objects might inhibit the shrinking of other controls in a section because the growing and shrinking is done line by line. Shrinking would require a blank picture in the OLE control. Page Headers/Footers
cannot grow or shrink, so controls placed within them are confined to the size of the section.
NewRoworCol When you are creating column reports, the settings of the NewRoworCol property give you plenty of control over how your report looks. Experiment with the settings described in Table 10.6. TABLE 10.6
NewRoworCol Property Settings
Name
Value
Description
None
0
Settings in the Page Setup dialog control the breaks between columns or rows. This is the default setting.
BeforeSection
1
Every section starts a new column or row. If there is room, a new section can share the column or row.
AfterSection
2
After a section has printed, a new column or row is created.
Before&AfterSection
3
Each section starts a new column or row.
RepeatSection A common complaint among Access users and developers writing reports occurs when a group spills over to a new page and leaves the group header behind on the preceding page. The RepeatSection property, when set to True, will print the current group’s header on the new page.
10 REPORTING UNLEASHED
272
User Interfaces Unleashed PART III
Section Properties (Runtime) The following properties are available to the developer at runtime to alter the behavior and appearance of reports in response to data and other conditions.
ForceNewPage A step up in sophistication from the page break control, the ForceNewPage property can be addressed while your report is printing. By setting this property based on data in a recordsource, you can control how pages break in relation to your sections. Table 10.7 describes the settings for the property. TABLE 10.7
ForceNewPage Property Settings
Name
Value
Description
None
0
Default setting, current section is printed on current page.
BeforeSection
1
Moves the current section to the top of a new page.
AfterSection
2
When a section has completed, the next section will be printed at the top of a new page.
Before&AfterSection
3
The current section prints at the top of a new page and the next section is moved to a new page as well.
KeepTogether This is a fair-weather property—it only works when all the conditions are right. When set to True, Access attempts to print an entire section on one page. If it is unable to do so, it retreats and tries to make a new page. If this new page fails, the group will be spread across more than one page. Unlike the KeepTogether property for groups, the KeepTogether property for sections only concerns itself with one section at a time.
MoveLayout, NextRecord, and PrintSection The following properties give the developer the finest level of control over what gets printed and how it gets printed. •
A setting of True will enable Access to advance to the next print location on the page. A setting of False will prevent the report from advancing to the next print location on the page. MoveLayout
Reporting Unleashed CHAPTER 10
•
NextRecord Could be likened to the MoveNext method because when set to True, the report will move to the next record in the RecordSource. When set to False, the report does not advance to the next record.
•
PrintSection
True
273
will print the section, False will not.
These properties control how the report reads data from its source, how it prepares the page for printing, and whether to print the section. When used together, the three properties can repeat data, make blank rows, print different rows on the same line, or select rows to suppress in the data. Table 10.8 details how to use MoveLayout, NextRecord, and PrintSection to create specific results. TABLE 10.8
Settings to Control What Gets Printed
Desired Result
MoveLayout
NextRecord
PrintSection
Print every row, one after another
True
True
True
Leave a blank
True
True
False
Repeat a row
True
False
True
Leave a blank, hang on to same row of data
True
False
False
Print different rows on the same line
False
True
True
Stay on the same line, but skip a row of data
False
True
False
FormatCount The FormatCount property is incremented every time a section is formatted for printing. A section can be formatted more than once. This can happen when a section is supposed to fit on a page, but does not. In this case, Access will format it once on the original page and then format that section again on the new page, setting its FormatCount property to 2. This can have important implications if you are performing calculations from your Format event because the calculation might occur more than once if they are not controlled by this property.
10 REPORTING UNLEASHED
274
User Interfaces Unleashed PART III
PrintCount The PrintCount property is incremented every time a section is printed. A section can be printed more than once. This can happen when a section is supposed to fit on a page but does not. In this case, Access will attempt to print it once on the original page and then again on the new page, setting its PrintCount property to 2. This can have important implications if you are performing calculations from your Print event because the calculation can occur more than once if it is not controlled by this property.
HasContinued/WillContinue These are two properties of limited usefulness. They are set when a section has run, or will run, onto a new page. They are of limited usefulness because the Access report writer is unable to distinguish if the overlap is caused by actual data or just white space. Because of this, the WillContinue property is usually set to true for every page in a report. It seems that the only way to defeat this lack of intelligence is to make sure the section height divides evenly into the printable vertical space on a page. The HasContinued Property is set from the Format event. It is True when the section has been continued from the previous page. However, it is very difficult to do anything with the property because its value is set too late to take advantage of the data in the report.
Building Reports Programmatically Probably the largest challenge facing a developer of reports would be to come up with a report for data that is constantly changing. Consider a periodic report of products sold by region. Consider also that the report must look like a crosstabulation. If the report is based on a crosstab query, the column names used as column heads will probably change every month. This would present a problem for the bound controls on the report. One solution for this is to programmatically build the report each time it is requested. This can be done with a VBA module. The VBA module presented could, with some alteration, be placed in a report’s Open event and simply alter an existing report, but to get the full feel of completely creating a report from nothing but code, it needs to be a separate module. The example uses DAO for the sake of simplicity. Because ADO does not create crosstab queries, DAO saves you the step of creating a temporary table and will enable most readers to use a familiar data access technique while concentrating on the report issues.
Reporting Unleashed CHAPTER 10
275
Creating the RecordSource First, you need to create the data source. A function named MakeCrosstabQuery, shown in Listing 10.4, will take two date arguments: FromDate and ToDate. They will define the period for the report. The function will create the QueryDef if it does not exist, but if it does, it will simply update the SQL property of the QueryDef. It will be called from the function that will create the report, but it could also be used by itself for other purposes. LISTING 10.4
How to Make a Crosstab Query Programmatically
Function MakeCrossTabQuery(ByVal FromDate As Date, _ ByVal ToDate As Date) As Boolean On Error Resume Next Dim AQueryDef As QueryDef CurrentDb.QueryDefs.Refresh Set AQueryDef = _ CurrentDb.QueryDefs(“Quantity Shipped by Region”) If (3265 = Err) Then Set AQueryDef = _ CurrentDb.CreateQueryDef(“Quantity Shipped by Region”) CurrentDb.QueryDefs.Refresh Err = 0 End If AQueryDef.SQL = “TRANSFORM Sum([Order Details].Quantity) “ & _ “AS SumOfQuantity “ & _ “SELECT Products.ProductName “ & _ “FROM Products INNER JOIN “ & _ “(Orders INNER JOIN [Order Details] ON Orders.OrderID = “ & _ “[Order Details].OrderID) ON Products.ProductID “ & _ “= [Order Details].ProductID “ & _ “WHERE (((Orders.OrderDate) Between #” & _ FromDate & “# And #” & ToDate & “#) AND “ & _ “((Orders.ShipRegion) Is Not Null)) “ & _ “GROUP BY Products.ProductName “ & _ “ORDER BY Orders.ShipRegion “ & _ “PIVOT Orders.ShipRegion; “
10 REPORTING UNLEASHED
If (Err) Then Exit Function End If
276
User Interfaces Unleashed PART III LISTING 10.4
continued
AQueryDef.Close CurrentDb.QueryDefs.Refresh End Function
With the query created and ready to go, you can now build the report. The function PrepareCrosstabReport() will take two date arguments: FromDate and They will be passed to MakeCrossTabQuery. It will also use a database icon, a recordset object, the all-important Report object, and a couple of other variables to be used as counters. ToDate.
Creating the Report Object PrepareCrosstabReport() will create the new query, open it, and then create a new report object that conforms to the specifics of the new query. With that report object, the function in Listing 10.5 will proceed to place controls in the report’s sections, position them, and bind them to the data source. Because Access enumerates the controls as they are created, it seems easier to make sure that the data controls are sequential with one another, and the labels are sequential with one another, and so on. To this end, the function creates the labels and bound controls in separate loops. The report footer is created using the DoCmd object’s RunCommand method and is then made visible by setting its visible property to True. A separate loop creates and positions the controls which contain the Sum() function. The Detail section height is set, the recordsource is assigned, and finally the report is opened.
LISTING 10.5
Using Code to Create the Crosstab Report
Public Function PrepareCrosstabReport(FromDate As Date, _ ToDate As Date) As Boolean On Error GoTo Except Dim Dim Dim Dim Dim
DocName As String ARecordset As DAO.Recordset AReport As Report I As Integer X As Integer
MakeCrossTabQuery FromDate, ToDate Set ARecordset = _ CurrentDb.OpenRecordset(“Quantity Shipped By Region”)
Reporting Unleashed CHAPTER 10 LISTING 10.5
277
continued
Set AReport = CreateReport(“”, “”) AReport.Caption = _ “Quantity Shipped by Region Between “ & _ FromDate & “ and “ & ToDate Dim AControl As Control If ARecordset.Fields.Count - 1 > 9 Then X = 9 Else X = ARecordset.Fields.Count - 1 End If For I = 0 To X Set AControl = _ CreateReportControl(AReport.Name, acTextBox, acDetail) With AControl .Height = 270 .Width = 1080 .Top = 0 .Left = (60 + .Width) * I AReport(“text” & I).ControlSource = _ ARecordset.Fields(I).Name End With Next For I = 0 To X Set AControl = _ CreateReportControl(AReport.Name, acLabel, acPageHeader) With AControl .Height = 270 .Width = 1080 .Top = 0 .Left = (60 + .Width) * I AReport(“label” & I + X + 1).Caption = _ ARecordset.Fields(I).Name End With Next DoCmd.RunCommand acCmdReportHdrFtr AReport.Section(acFooter).Visible = True
With AControl
REPORTING UNLEASHED
For I = 0 To X Set AControl = _ CreateReportControl(AReport.Name, acTextBox, acFooter)
10
278
User Interfaces Unleashed PART III LISTING 10.5
continued
.Height = 270 .Width = 1080 .Top = 0 .Left = (60 + .Width) * I If I = 0 Then AReport(“text” & I + (2 * X) + 2).ControlSource = “=’Total’” Else AReport(“text” & I + (2 * X) + 2).ControlSource = _ “=sum([“ & ARecordset.Fields(I).Name & “])” End If End With Next AReport.Section(“detail”).Height = 0 AReport.RecordSource = “Quantity Shipped By Region” DoCmd.OpenReport AReport.Name, acViewPreview PrepareCrosstabReport = True Set AReport = Nothing Exit Function Except: MsgBox Error$ End Function
Now there is a report based on a crosstab that is dynamically created each time the report is requested. Because the report uses its intended recordsource, there’s never a problem when the column headings change, or when there are a different number of regions to report against—within the limits of the report.
Creating Sections Sections can be created programmatically. However, not all sections can be created the same way. To create group headers and footers, Access provides the CreateGroupLevel function. Because the standard five sections (Report Header/Footer, Page Header/Footer, and Detail) are not part of the grouping scheme of reports, there is a different way to create them. The CreateGroupLevel function takes four arguments: •
StrReport
•
StrExpr
A string identifying the report you want to modify.
The group level expression, usually a field name, the parsing of a data item, or date.
Reporting Unleashed CHAPTER 10
•
279
FHeader/FFooter
Boolean values to create the complementary group header and group footer sections. True creates the section and False does not.
CreateGroupLevel “_Categories and Products”,”NewGroupSection”,1,1
or Section = CreateGroupLevel(“Categories and Products”, _ “NewGroupSection”,1,1)
To create the report or page header/footers, use the RunCommand method of the DoCmd object: DoCmd.RunCommand acCmdReportHdrFtr
Tips and Tricks Most applications have in them at least one report that poses a particularly vexing problem. To handle some of these problems, this section assembles some tips and tricks that will help add some sparkle to your applications’ reports.
Make a Biweekly Grouping If you are grouping on a date, and you want to group your data biweekly (like a payroll), set the Group On property to weekly and the Interval property to 2.
Hide Repeating Data In a simple tabular report, information is repeated row after row. To prevent this, set the HideDuplicates property of the offending control to Yes. This will give you the look of a grouping without using group sections and will enable the data defining the group and its first line of detail to print on the same line.
Group Data Alphabetically With a report that lists customer by CustomerName, you could break the groups into alphabetically distinct groupings with a character designating the group (like a phone book) by following these steps. 1. Set the GroupOn property in the Sorting/Grouping property window to Prefix Characters.
3. Set the customer name control’s control source to =Left([CustomerName],1)
REPORTING UNLEASHED
2. Set the GroupInterval value to 1, as shown in Figure 10.16. This is very important because it tells Access how many prefix characters to use when making a distinction. This value will pay attention to the first character of the customer name.
10
280
User Interfaces Unleashed PART III
FIGURE 10.16 Grouping settings.
Make Numbered Lists Using the Running Sum property in conjunction with the control source can enable your report to number the items in a list. Figure 10.17 shows an example of a report with numbered items. The sequence can reset for each group or it can run through the entire report. 1. Place a control on a report to hold the number. 2. Set the control’s ControlSource scheme.
= 1.
This will be the seed for the numbering
3. Set the control’s Running Sum property to Overall or Over Group. FIGURE 10.17 Result of numbering.
Create Empty Lines Every n Spaces This chapter discussed how the MoveLayout/NextRecord/PrintSection properties could create blank lines. Listing 10.6 is an example of that code. It could be put in most reports. Simply change the SkipLine constant value to change the spacing.
Reporting Unleashed CHAPTER 10 LISTING 10.6
281
Creating Empty Lines Every n Spaces
Const SkipLine = 3 Private Sub Detail_Format(Cancel As Integer, FormatCount As Integer) If CurrentLine Mod (SkipLine + 1) = 0 Then NextRecord = False PrintSection = False End If CurrentLine = CurrentLine + 1 End Sub Private Sub Report_Open(Cancel As Integer) CurrentLine = 0 End Sub
Reset Page Number for New Groups Because the Page property of the group section is read/write at runtime, it is possible to change it every time the group’s header is formatted. The Page property should not be confused with the Pages property, which cannot be written at runtime. The following code will reset the page numbers and is most useful when groups span more than one page. Sub GroupHeader_Format Page=1 End Sub
Draw Vertical Lines Creating a vertical line on the report can be accomplished with just a few lines of code. Set your positions and call the report’s Line method to draw the line during the Format event. You have to measure in twips (1,440 per inch). An example of drawing a line with code is in Listing 10.7. LISTING 10.7
Drawing Vertical Lines with Code
Private Sub Detail_Format(Cancel As Integer, FormatCount As Integer)
End Sub
REPORTING UNLEASHED
Const InchesToTwips = 3 * 1440 Line (InchesToTwips, 1)-(InchesToTwips, 32000)
10
282
User Interfaces Unleashed PART III
Move Page Numbers for Even-Odd Pages The Detail section OnPrint event, implemented in the listing that follows, demonstrates how to perform some custom formatting of page numbers. The technique is shown in Listing 10.8. LISTING 10.8
Changing the Position of Even and Odd Page Numbers
Private Sub Detail_Print(Cancel If (TextBoxPageNumber Mod 2 = TextBoxPageNumber.TextAlign Else TextBoxPageNumber.TextAlign End If End Sub
As Integer, PrintCount As Integer) 0) Then = 1 ‘ Left = 3 ‘ Right
Identify the User Printing the Report If your Access database is secure, you can identify the user ID of the user printing the report, or use that ID to look up a full name. In an unbound control, enter the following expression as the control source: =CurrentUser()
or =DLookup(“UsersTable”,”[FullName]”,”[UserID]= “ & CurrentUser() & “)
Align Pages for Binding When pages are bound into a book, the contents of the page must allow room for the page to be bound at the spine of the book. This means that the contents of odd pages must be shifted right and the contents of even pages must be shifted left. The code in Listing 10.9 demonstrates how to add left and right margins to report pages. LISTING 10.9
Aligning Report Pages for Binding
Private Sub PageFooterSection_Format(Cancel As Integer, _ FormatCount As Integer) Dim Control As Control If (Page Mod 2 = 0) Then For Each Control In Controls Control.Left = Control.Left + 1440 Next
Reporting Unleashed CHAPTER 10 LISTING 10.9
283
continued
Else For Each Control In Controls Control.Left = Control.Left - 1440 Next End If End Sub
Calculate Page Totals An Access report will generate an error if you try to place an aggregate function (for example, Sum()) in a control located in the page header/footer. Because the request for a page subtotal on a report often comes up, this is more than inconvenient. With some very simple code, it is possible to calculate the page’s subtotal, and then display it through an unbound control in the page footer. For the following code example to work, follow these steps: 1. Build a simple report on the Northwind database’s Invoices query. Place the ProductName field and the ExtendedPrice field in the Detail section. 2. Place the ExtendedPrice field in the Detail section a second time and name it RunningSum. Set the Detail Visible property to No and its Running Sum property to Over All. 3. Add a Group Header based on the CustomerName and add the CustomerName field to the Group Header section. 4. Create an unbound text box control named TextBoxPageSubtotal in the page footer. 5. Enter the code shown in Listing 10.10 in the PageFooter’s OnPrint event. LISTING 10.10
Calculating a Page Subtotal
Option Explicit Option Compare Database Dim x As Double Private Sub PageFooterSection_Print(Cancel As Integer, _ PrintCount As Integer)
Now the report will give a page subtotal.
REPORTING UNLEASHED
TextBoxPageSubtotal = RunningSum - x x = RunningSum End Sub
10
284
User Interfaces Unleashed PART III
Fine Control Manipulation Rather than selecting controls on a report and running the risk of moving them, try some of the following techniques: • Click the ruler to select the controls at that measurement. • Click on the background of the report and drag over controls to select them without moving them. • Use the Tab key to move from one control to another. • Control+Arrow will make fine movements of a control easy. It will also move a control from one section to another. • Shift+Arrow will size controls without moving them.
Summary This chapter has explored the Report object, its parts, and its peculiarities in detail. You have seen several ways to distribute the reports beyond simple paper-based interoffice mail and you’ve reviewed some tips and tricks for accomplishing some difficult tasks. With some practice and experimentation, Access reports can do almost anything you want. Though reports have changed very little over the years, VBA for Access has improved quite a bit and can be harnessed to make your reports shine. There are few instances where some hard work and creativity cannot turn a client’s request into a finished report. Because an application (and its developer) can live or die by the quality of its reports, it is critical that developers understand how to get as much out of them as possible. In Access 2000 a new object, Data Access Pages, was introduced to display and distribute information on the Web. Though Data Access Pages will probably not supplant reports for some time to come, it is an exciting and valuable new feature. Chapter 25, “Using Data Access Pages,” is dedicated to it in this book.
VBA Unleashed
PART
IV IN THIS PART 11 Creating Objects with Class Modules 12 Debugging Access Applications 13 Professional Error Handling 14 Application Optimization
CHAPTER 11
Creating Objects with Class Modules IN THIS CHAPTER • Exploring the Benefits of Using Objects 289 • Reviewing Objects, Properties, and Methods 291 • Creating Classes
292
• Creating Properties • Creating Methods • Creating Events • Using Objects
292 299
300 302
• Creating Multiple Instances of an Object 304 • Examining More Object Examples 304 • Implementing an Error Handler Object 311 • Using Objects with VBA Collections 312
288
VBA Unleashed PART IV
Creating objects is one of the most efficient ways to write and maintain applications. Before discussing the benefits of using objects, let’s review some definitions. Objects are things. People, cars, and buildings are all types of objects. In programming, the word object is used to describe one specific thing, such as a form or control. You undoubtedly have had experience with these types of built-in objects. Microsoft Access has become increasingly object-oriented, empowering us to create custom objects and to add properties and methods to those objects. Examples of custom objects might include a customer object, invoice object, user object, data connection object, and sound object. The code in this chapter includes numerous custom objects for your use. See “Creating Objects with Class Modules.mdb,” as shown in Figure 11.1. FIGURE 11.1 See custom objects in this book’s CD sample code.
Objects are created using a class module. A class module is a portable, self-contained unit of code designed for a specific purpose. The class specifies the fields, properties, and methods, collectively referred to as members, that each object created from a class will have. Developers often confuse the terms object and class. A class is a description or template of the members of an object. When developers want to use the code in the class module, an instance of the class is created. That instance is an object. Multiple objects can be created from the class, each with different property values.
Creating Objects with Class Modules CHAPTER 11
Tip
Exploring the Benefits of Using Objects There are many benefits to creating and using objects, including hiding complex code functionality, using IntelliSense, easier code creation and maintenance, and more.
Hide Complex Functionality Hiding complex functionality is one of the benefits of using objects. An advanced developer can create complex routines such as Windows API procedures, data access code, string routines, and more. Less experienced developers can enjoy the benefits of the object by calling its properties and methods without having to understand the code that makes the object work.
Use of Microsoft’s IntelliSense Technology The only thing a developer needs to do to use an object is to specify that object and choose its properties or methods using IntelliSense technology (see Figure 11.2). For example, one of the objects included in this chapter is an error-handler object that can be used in Access applications. Because all the code for the error handler is included in the cError object, a developer simply needs to specify the object and the property or method she wants to use. For example, if the developer would like to have an e-mail sent whenever an error occurs in the application, all he or she needs to do is call the “e-mail” method: cError.email
Notice how easy it is to use objects. With Microsoft IntelliSense technology, you simply type the object name, type a period (the member-of operator), and all properties and methods of the object will appear. This greatly facilitates writing code quickly and accurately.
11 CREATING OBJECTS WITH CLASS MODULES
This chapter deals with creating objects using class modules. Remember, form modules are class modules too. You can add properties and methods to forms in the same way that you can add properties and methods to custom objects.
289
290
VBA Unleashed PART IV
FIGURE 11.2 Objects allow you to use Microsoft IntelliSense technology.
Organize Code Creating classes helps organize code so it is easier to read, review, and maintain. All code for a particular functionality is contained within the class. Packaging code into one neat bundle with properties and methods characterizing a class is called encapsulation.
Allow Viewing Objects in the Object Browser Packaging code in classes allows developers to use the Object Browser to review properties, methods, and other information. Later in the chapter, I will examine a custom object in the Object Browser.
Create Multiple Instances of the Object Imagine you have code that is reused often in an application. In fact, the code can be used an unknown number of times simultaneously. An example is data connection code. Every time a user searches for a new customer, opens a form, or accesses a combo box, data must be retrieved from the server. Rather than duplicate code in each of these procedures, a better approach would be to create a Data Connection object that enables code to retrieve data from the server.
Creating Objects with Class Modules CHAPTER 11
Make Code Easy to Update and Maintain
Limit Access to Code Classes enable you to control who can use the code and under what circumstances. With classes, you can protect properties and methods by controlling when they are exposed outside the class.
Make Code Easily Portable Because a class module is a self-contained unit of code, it can easily be moved from one Access application to another.
Reviewing Objects, Properties, and Methods Before discussing how to create objects, let’s review the basics. It is essential to understand the terms object, properties, and methods, or the rest of this chapter will make no sense. An object is an item that can be programmed, manipulated, or controlled. In Access, objects include forms, text boxes, command buttons, and more. In this chapter, we will actually create our own custom objects. A property is a characteristic of an object. A property can be thought of as an adjective because it describes or characterizes an object. In Access, examples of properties of a text box include Name, Visible, and Forecolor. Most properties of an object can be both set and retrieved. In this chapter, you will learn how to create your own properties and control whether the properties can be set or retrieved. A method is an action that can be taken on an object. A method can be thought of as a verb. For example, one method of an Access form is Close. Methods are subroutines or functions that are defined as part of a class. I will create custom methods for the object in this chapter.
11 CREATING OBJECTS WITH CLASS MODULES
By using classes, you can use a single unit of code multiple times in an application without repeating the code. If, for example, you repeat data access code in numerous procedures, every single place the data access code is located must be found and revised whenever a change must be made. This is very time-consuming and inefficient. On the other hand, if a Data Connection object is used, all updates or changes to the data access code need to be changed in only one place—the class module.
291
292
VBA Unleashed PART IV
Creating Classes Class modules are used to create a class. The class defines fields, properties, methods, and events providing a clearly defined and well-documented public interface.
Inserting a Class Module Creating a class is actually very simple. Adding properties and methods to the object involves a little more work. To create a class, insert a class module in your access application and name it. The name of the class module is the name of your class. To insert a class module, choose Class Module under the Insert menu. The Visual Basic Editor will open. In the code window, write any properties, methods, and events for the object. Before going any further, name the class module (see Figure 11.3). Tip Take great care in properly naming the class module because it will be the name of your object. This will be how developers access your code through IntelliSense technology. It also might be helpful to precede the name of the object with your company’s initials so you can easily see which custom objects were created at your company.
FIGURE 11.3 Properly name the class module.
Creating Properties There are two ways to create properties of a class: public variables or property procedures.
Creating Objects with Class Modules CHAPTER 11
Tip
Using Public Fields A field can easily be created by simply declaring a variable in the declaration section of the class module. The following examples create a Name and UserType field of the User class: Public Name as String Public UserType as String
After this simple step, users can set and retrieve these field values. To set the value of a field, use the field as the left-hand side of an operation; the following demonstrates using the Name and UserType fields as left-hand values of an assignment operation, as follows: User.Name = “Steve” User.UserType = “Management”
To retrieve the value of a field, use the field as the right-hand side of an operation or as an argument to a procedure; an example of using a field as an argument to the MsgBox function follows (see Figure 11.4): MsgBox User.Name
Using Property Procedures to Modify Fields Properties can also be added to classes to provide you a convenient way of managing fields. There are three types of property procedures: Property Let to assign a nonobject value to a field, Property Get to return the value of a field that does not contain a reference to an object, and Property Set to assign an object to an object field.
11 CREATING OBJECTS WITH CLASS MODULES
The chapter code includes numerous objects. The simplest example, a User class, is used for illustration in the next few pages. The User class will hold information about the current logged-in user, including his or her name. User can be used whenever the current user’s name is needed in the application, such as during error handling and recording the author of notes or documents. In this example, a User object will be used to record the login dates and times, and an event of the object will be used to display a welcome message to the user when he or she logs in.
293
294
VBA Unleashed PART IV
FIGURE 11.4 Easily set and retrieve object properties with IntelliSense.
Property procedures let you restrict who can set and get field values. Property values are generally stored in a private field value. The property getter and setter are designed to support using properties just like fields, but in a constrained way. Because the property getter and setter are essentially functions and subroutines, respectively, you can add validation code to make sure that only suitable values are assigned to and returned from your class’s fields. Properties are not required to use fields for storing and retrieving field values. A Property Get statement might return a value from the registry and the pair, Property Set statement might update that same registry setting. Tip Property procedures can be set as public or private just like any other procedure. Public property procedures are accessible to all other procedures in all modules using an object reference; private property procedures are accessible only to procedures in the module in which they are declared.
Private Module Variable Creation With property procedures, the value of the property is hidden in a private module variable. The creator of the class determines whether a property is exposed to the outside world.
Creating Objects with Class Modules CHAPTER 11
Option Explicit ‘ Create Private Variable in Declaration Section Private FName as String Private FUserType as String
A convention I employ for naming fields is to prefix the field with an F, as in FName. Dropping the F-prefix yields a convenient and readable name for the property, and it is an easy task to match a field with its associated property methods.
Property Let The Property Let procedure is used to set the value of a property. If you do not want others to have the ability to set a property value, do not include a Property Let. The following example creates a Property object:
Let
procedure for the Name property of the
User
Public Property Let Name (ByVal Value as String) FName = Value End Property
Let’s break down this property procedure. First, because the Property Let procedure exists and is defined as a Public property, the Name property can be assigned to by consumers of the User class. In this context, consumer of a class refers to a programmer using the class versus the programmer writing the class. A developer might set this property as follows: User.Name = “James”
When James is passed into the property method, it is received in the variable UserName. The Property Let procedure takes the value of the parameter Value (James) and hides it away in the private module variable, FName. The preceding procedure passes in one parameter, but actually many parameters can be passed into a property procedure. A property value can only be retrieved if a Property Get procedure exists.
Property Get A Property Get procedure enables the retrieval of a property value. If you do not want others to be able to retrieve a property value, do not include a Property Get statement. The Property Get statement obtains the value of the property, which is hidden away in the private variable, and returns it as the property value. The following example shows a Property Get statement for the Name property of the User class:
11 CREATING OBJECTS WITH CLASS MODULES
The following is an example of creating private fields for the Name and Type properties of the User object:
295
296
VBA Unleashed PART IV Public Property Get Name () as String Name = FName End Property
A user can easily retrieve the value of a property (if a Property using the following code:
Get
statement exists) by
MsgBox User.Name
Tip The data type of the Property Let must be the same as the Property Get. For example, the Property Let for the Name property accepts a string argument. The Property Get procedure must return a string data type as well.
Property Set The Property Set statement enables you to create a property procedure that sets a reference to an object. Use the Set keyword within the Property Set procedure when assigning a property whose data type is a class. In this example, the cForm object is used. The cForm object has a Form property that must be passed a Form object. The code in the cForm class module is as follows: Option Compare Database Private FForm As Form Public Property Get Form() As Variant Set Form = FForm End Property Public Property Set Form(ByVal Value As Form) Set FForm = Value End Property
When frmPropertySet is loaded, the form is passed into the Form property. Because it is an object, the Set keyword is used. The form object’s name can then be retrieved for a message box. The following is the code for the frmPropertySet form: Private FForm As FormProperty Private Sub cmdClose_Click() DoCmd.Close acForm, “frmPropertySet”, acSaveNo End Sub Private Sub cmdPropertySet_Click() MsgBox “Form name is: “ & FForm.Form.Name, _
Creating Objects with Class Modules CHAPTER 11 vbInformation, “Property Set Example” End Sub
Private Sub Form_Unload(Cancel As Integer) Set FForm = Nothing End Sub
Public Variable or Property Procedures The easiest way to define class data is to add public fields to a class. However, there are some potential repercussions in doing so. Public variables can be changed at will by consumers of your class, and without a property method, there is no other convenient way for you to ensure that consumers do not abuse your fields. This could create problems in your application if others change a field to an inappropriate value. Another advantage of property procedures is that read-only and write-only properties can be created. For example, if you want to create a Password property, you might want to allow users to set the password but not retrieve it (write-only). To create a write-only property, include a Property Let statement, but do not include a Property Get. With property procedures, you can also perform an action in code when a property is either set or retrieved. For example, suppose you have defined a password property. You might elect to add validation code that ensures that the password is at a minimum eight characters long and contains a number.
Creating Enumerated Data Types An enumerated type is a construct similar to type. The elements of an enumeration are text. Each comma-delimited word in an enumeration represents an ordinal value. The first element of an enumeration is assigned the value of zero, the second is assigned the value of one, and so on. The purpose of enumerations is to define an ordinal range of named-values. Each member of the enumeration has the value of the previous element plus one. In the declaration section of the class module, use the Enum keyword to define a range of possible ordinal values. For example, with the User object, assume you want to provide for the Type property a drop-down list with the following values: Manager, Staff, or Temporary. The code in the declaration section is as follows: Public Enum UserList Manager Staff
11 CREATING OBJECTS WITH CLASS MODULES
Private Sub Form_Load() Set FForm = New FormProperty Set FForm.Form = Forms!frmPropertySet End Sub
297
298
VBA Unleashed PART IV Temporary End Enum
The next step would be to use the enumerated data type (UserList) as the data type of the Type property of the User class. The following example shows how, with a property created as a public field: Public UserType as String Public UserType as UserList
Property values can easily be set by using the drop-down list provided by IntelliSense (see Figure 11.5). FIGURE 11.5 The enumerated data types example.
To determine which property value was chosen from the enumerated data types, each data type is numbered beginning with zero and is incremented by one. Tip Property values entered are not restricted to the enumerated data types. In the preceding example, the enumerated data type has three members: Manager, Staff, and Temporary. VBA does not prevent a developer from ignoring the values in the enumeration, because VBA is not a strongly-typed language. Using the enumeration does make your code more readable and allows the IntelliSense feature to provide the consumer with the list of appropriate values.
Creating Objects with Class Modules CHAPTER 11
Creating Methods A method is a procedure that describes a behavior of a class. To define a class method, simply define a subprocedure or function in the class module. Let’s assume that every time a user logs in to the application, you want to keep track of the date and time. The following code creates a Login method that enters the date and time in a table each time a user logs in. Public Sub Login() Dim SQL As String SQL = “SELECT * FROM tblUsers” Dim RS As ADODB.Recordset Set RS = New ADODB.Recordset RS.Open SQL, CurrentProject.Connection, adOpenKeyset, _ adLockOptimistic RS.AddNew With RS !Name = Name !Date = Date !Time = Time End With RS.Update RS.Close Set RS = Nothing End Sub
The code assigns an SQL string to the variable SQL. A Recordset variable is declared and created using the Dim and New keywords. The RS.Open open statement uses the SQL string to open the tblUsers table. A new row is inserted with RS.AddNew, and the fields or the table are modified. The recordset is updated, closed, and the memory allocated to the Recordset object is released by assigning the RS object to Nothing.
11 CREATING OBJECTS WITH CLASS MODULES
To restrict choices to the items in the list, use the numeric values of the enumerated items. For example, if there are three items in the list (and numbering starts at zero), implement a property setter to validate the value a user is attempting to assign to the underlying field to the acceptable range of values.
299
300
VBA Unleashed PART IV
Using Methods To use the method when the application opens, the code is simply User.Login
Notice that users of the method do not have to understand the ADO code to update a value in the database. They can simply use any properties or methods available for the object that is conveniently displayed by IntelliSense.
Creating Events Objects in Access have events. For example, a form object has a Load event, and a command button has a Click event. You can create events for your custom objects as well. To do so, use the Event keyword in the declaration section and specify the name of the event. For example, let’s add a Welcome event that will occur when a user opens the application. In the declaration section of the class module, include the following code to create the event: Event Welcome()
To use the event, Raise the event in a method of the object. Let’s raise the Welcome event in the Login method of the User object. When the user starts the application, the Login method will be called. This will trigger the Welcome event, which will display a splash screen welcoming the user with a personalized message using his name. The code to raise the Welcome event can be implemented as follows: Public Sub LogIn() RaiseEvent Welcome End If
Those are the only steps needed to create an event and to raise the event in the class module. The next section shows how to use the event in a form.
Using Events To use the Welcome event from a form module, declare the object variable for User in the declaration section of the module and use the WithEvents keyword. The WithEvents keyword is required to expose and use the events of a class User. An example of using the Welcome event from the User class follows: Private WithEvents AUser As User
Creating Objects with Class Modules CHAPTER 11
Tip
After you declare the module-level variable using the WithEvents keyword, the Object combo box on the top-left of the code editor will contain a reference to User. To define an event handler for User Welcome event, select the User object from the Objects combo box on the left and the Welcome event from the procedure combo box on the right. (When you select an object in the combo box on the left, the procedures combo box will be updated to display a list of procedures and events for that object.) Choose Welcome from the Procedure combo box (top-right in the code editor), and the procedure will appear in the code window, where you can write code to respond to the event. The following example shows code responding to the Welcome event (see Figure 11.6). FIGURE 11.6 The procedure using the Welcome event of the User object.
As a result, the user will see a welcome splash screen each time he opens the application (see Figure 11.7).
11 CREATING OBJECTS WITH CLASS MODULES
WithEvents can be used only within class modules and form modules.
301
302
VBA Unleashed PART IV
FIGURE 11.7 The welcome splash screen.
Firing the Initialize and Terminate Events Class modules automatically include initialize and terminate events. To use these events, choose the event from the combo box at the top of the code window. The initialize event fires whenever an object is created. For example, if you want certain code to run whenever the User object is created, put the code in the initialize event. The terminate event occurs when the object is destroyed. This is a good place to put clean-up code to close database connections, release object variables, and so on.
Using Objects At this point, the User contains fields, properties, methods, and events. To create an instance of User, simply type the name of the object followed by the member-of operator, a period, and IntelliSense will show a list of properties and methods for the object. To use the object within the class that creates the object, use the name of the object. For example, assuming you have an instance of User named User, you could assign a value to the Name property with the following code: Dim User As New User User.Name = “Steve”
Creating an Object Variable A variable is memory associated to a name. Undoubtedly, you have a great deal of experience with “simple variables,” such as strings and integers. The following are examples of declaring and using two simple variables: Dim Name as String Dim I as integer Name = “James” I = 10
In the preceding examples, the variables include a specific type of data, and the information is stored and can be retrieved as needed. An Object variable is declared with the Dim statement just like simple variables: Dim AUser as User
Creating Objects with Class Modules CHAPTER 11
Assigning an Object Variable to an Object Set AUser = New User
Using the Object After you create and set the object variable, properties and methods of the object can be used with IntelliSense. Using the dot syntax, properties can be set and retrieved, and methods can be executed. Setting a property value: Object.Property = Value Retrieving a property value: MsgBox Object.Property Tip If you discover, while attempting to use an object, that IntelliSense shows no properties and methods for the object, that is a tip-off that you might not have created or set an object variable. Sometimes, however, IntelliSense does not show items even though your code is correct.
Releasing the Object When you are done with an object, release it by setting the object equal to Nothing. In this way, memory can be reclaimed for other processes. The proper syntax is as follows: Set objUser = Nothing
Tip When you write the Dim statement for an object, immediately go to the bottom of the procedure and set the object variable equal to Nothing. This will prevent you from forgetting to do it when you are finished writing the procedure. Write all of the code that uses the object between the Dim statement and Set object = Nothing statement.
11 CREATING OBJECTS WITH CLASS MODULES
The Set keyword is used to assign a new instance of the class to the variable whose type is a class. For example:
303
304
VBA Unleashed PART IV
Creating Multiple Instances of an Object A class has a defined set of properties and methods. Classes are like blueprints; a class describes the fields, properties, methods, and events that every instance of that class will have. Classes are analogous to the blueprint for a house and an object is the house. One of the benefits of class modules is that multiple instances of the class defined in the module can be created. Each instance begins with the basic set of properties and methods, but each instance can be customized differently after the object is created. For example, let’s say five User objects are created. All five can maintain their own individual state. For example, one User object might have a Name property value of James, and another Steve. To create multiple instances of a class, simply write several Dim statements, each having different variable names and the same type. Initialize each variable to a new instance of the class using the Set and New keywords. Consider the following code: Public Sub ManyUsers() Dim User1 as User Dim User2 as User Set User1 = New User Set User2 = New User User1.Name = “James” User2.Name = “Steve” MsgBox “Current users are: “ & objUser1.Name & “ and “ User2.Name Set User1 = Nothing Set User2 = Nothing End Sub
Examining More Object Examples The chapter examples include numerous classes for your use (see “Creating Objects with Class Modules.mdb” on the CD-ROM accompanying this book). Each object will be briefly summarized.
Text File Object The chapter code includes a TextFile class that can be used to read and write information to a text file (see Figure 11.8).
Creating Objects with Class Modules CHAPTER 11
FIGURE 11.8
The class module includes the following code: Option Explicit Option Compare Database ‘ From “Microsoft Access 2000 Development Unleashed” (SAMS) ‘ Revised by Paul Kimmel.
[email protected] Private FHandle As Integer Private Function OpenInputFile(ByVal FileName As String) As Integer OpenInputFile = FreeFile Open FileName For Input As OpenInputFile End Function Private Function OpenOutputFile(ByVal FileName As String) As Integer OpenOutputFile = FreeFile Open FileName For Output As OpenOutputFile End Function Private Function ReadLine(ByVal Handle As Integer) As String Line Input #FHandle, ReadLine End Function Private Sub CloseFile(ByVal Handle As Integer) Close #Handle End Sub Public Function ReadLineText(FileName As String) As String FHandle = OpenInputFile(FileName) On Error GoTo Finally ReadLineText = ReadLine(FHandle) Finally: Call CloseFile(FHandle) End Function Public Function ReadAllText(FileName As String) As String FHandle = OpenInputFile(FileName) On Error GoTo Finally
11 CREATING OBJECTS WITH CLASS MODULES
The TextFile class example.
305
306
VBA Unleashed PART IV Do Until EOF(FHandle) ReadAllText = ReadAllText & vbCrLf & ReadLine(FHandle) Loop Finally: Call CloseFile(FHandle) End Function Private Sub WriteLine(ByVal Text As String) Write #FHandle, Text End Sub Public Sub WriteLineText(ByVal FileName As String, ByVal Text As String) FHandle = OpenOutputFile(FileName) On Error GoTo Finally Call WriteLine(Text) Finally: Call CloseFile(FHandle) End Sub
The TextFile class defines methods for opening and closing text files, reading lines of text, and reading all lines of text. You can use the FileSystemObject defined in the scrrun.dll library for basic text file management.
MyTimer Class A MyTimer class module is included in the chapter code. This class module is used for two purposes: as a stopwatch to see how many seconds have passed, and to create a “wait state” to hold off code execution for a number of seconds. The code in the MyTimer class module is as follows: Option Compare Database Option Explicit ‘ From “Microsoft Access 2000 Development Unleashed” (SAMS) ‘ Revised by Paul Kimmel.
[email protected] Private FStart As Single Public Sub Wait(lngSeconds As Long) Do Until Timer > msngStart + lngSeconds DoEvents Loop End Sub Public Sub StartTimer() FStart = Timer End Sub
Creating Objects with Class Modules CHAPTER 11
FStart = 0 TimerStop = 0 End Function
The frmTimer in “Creating Objects with Class Modules.mdb” demonstrates how to use the timer to determine elapsed time and create a “wait state” (see Figure 11.9). FIGURE 11.9 Use the timer object for elapsed time and a wait state.
Sound Object A Sound class module is included in the chapter code to play sounds in Access applications. The Sound class module contains only one method (PlaySound) that passes the sound file to a Windows API call: ‘ Windows API call. Private Declare Function sndPlaySound Lib “winmm.dll” Alias _ “sndPlaySoundA” (ByVal lpszSoundName As String, ByVal uFlags _ As Long) As Long Public Sub PlaySound(SoundFile As String) ‘ Play the Sound File. sndPlaySound SoundFile, 1 End Sub
The following code instantiates and uses a Sound object (see Figure 11.10): Dim ASound As Sound Set ASound As Sound ‘ If using Windows NT use the path “C:\WINNT” ASound.PlaySound “C:\Windows\chimes.wav” Set ASound = Nothing
11 CREATING OBJECTS WITH CLASS MODULES
Public Function ElapsedTime() As Long Dim TimerStop As Single TimerStop = Timer ElapsedTime = TimerStop - FStart
307
308
VBA Unleashed PART IV
FIGURE 11.10 The Sound class example.
Letter Object A Letter class module is included in the chapter code to create letters in Microsoft Word. The letter is created using a mail merge. A Word template is used to produce the letter, and a SQL statement obtains the data for the letter. The ShowWord argument determines whether Word is actually made visible to the user. Sometimes, you might want letters sent directly to the printer without editing. The following is the code in the cLetter class module: Option Explicit ‘ From “Microsoft Access 2002 Development Unleashed” (SAMS) ‘ Revised by Paul Kimmel.
[email protected] Private FWord As Word.Application Private FTemplate As String Private FSQLStatement As String Public Property Let SQLStatement(ByVal Value As String) FSQLStatement = Value End Property Public Property Get SQLStatement() As String SQLStatement = FSQLStatement End Property Public Property Let Template(ByVal Value As String) FTemplate = Value End Property Public Property Get Template() As String Template = FTemplate End Property
Creating Objects with Class Modules CHAPTER 11 Public Sub CreateLetter(DatabasePath As String, _ ShowWord As Boolean)
Call FWord.Documents.Add(FTemplate) ‘ Run Mail Merge With FWord.ActiveDocument.MailMerge .MainDocumentType = wdFormLetters .OpenDataSource Name:=”C:\Temp.rtf” .Destination = wdSendToNewDocument .Execute End With If ShowWord Then Show End If End Sub Friend Sub Show() FWord.Visible = True End Sub Private Sub Class_Initialize() On Error Resume Next Set FWord = GetObject(, “Word.Application”) If (FWord Is Nothing) Then Set FWord = New Word.Application If FWord Is Nothing Then MsgBox “MS Word is not installed on your computer” End If End If End Sub Private Sub Class_Terminate() Set FWord = Nothing End Sub
The following code instantiates a Letter object and uses its properties and methods: Private Sub ExampleLetterObject()
11 CREATING OBJECTS WITH CLASS MODULES
‘ Write the Customer data to a temporary temp file to use with ‘ the mail merge. This is faster than getting the data directly ‘ from Access. DoCmd.OutputTo acOutputQuery, “qryCustomers”, _ acFormatRTF, “C:\Temp.RTF”, False
309
310
VBA Unleashed PART IV
Dim ALetter As Letter Set ALetter = New Letter ALetter.Template = CurrentProject.Path & “\Business Services Letter.Dot” ALetter.SQLStatement = “SELECT * FROM tblCustomers” ALetter.CreateLetter CurrentProject.Path & “\Objects.mdb”, True Set ALetter = Nothing End Sub
Outlook Object A MyOutlook class module is included in the chapter code to display an Outlook New Message form (see Figure 11.11). If you want to enable users to enter Outlook messages from within your Access application, simply call the NewEmailMessage method. FIGURE 11.11 Display the Outlook New Message Form in Access applications.
The following is the code in the MyOutlook class module: Option Explicit ‘ From “Microsoft Access 2002 Development Unleashed” (SAMS) ‘ Revised by Paul Kimmel.
[email protected] Private FOutlook As Outlook.Application Private FMyItem As Object
Creating Objects with Class Modules CHAPTER 11
311
Public Sub NewEmailMessage(EmailAddress As String)
11
Set FMyItem = FOutlook.CreateItem(olMailItem) FMyItem.To = EmailAddress FMyItem.Display
CREATING OBJECTS WITH CLASS MODULES
End Sub Private Sub Class_Initialize() Set FOutlook = New Outlook.Application If FOutlook Is Nothing Then MsgBox “MS Outlook is not installed on your computer” End If End Sub Private Sub Class_Terminate() Set FOutlook = Nothing End Sub
The following code instantiates an objOutlook object and calls the NewMailMessage method: Private Sub ExampleMyOutlookObject() Dim Outlook As MyOutlook Set Outlook = New MyOutlook Outlook.NewEmailMessage “
[email protected]” Set Outlook = Nothing End Sub
Implementing an Error Handler Object See the sample code for Chapter 13, “Professional Error Handling.” A class module provides for extensive error handling, including • Logging errors to an Access table • Logging errors to a text file • Handling e-mail errors • Handling record errors on an Outlook calendar See the cError object in the Object Browser, as shown in Figure 11.12.
312
VBA Unleashed PART IV
FIGURE 11.12 The cError object in the Object Browser.
Using Objects with VBA Collections VBA collections provide a way to treat your custom objects as a group. For example, if five User objects exist and you want to perform an action on each object, it is easier and more efficient to treat all five objects as a group rather than individually. You are probably already familiar with built-in collections such as the “forms” and “controls” collections. VBA also has a collection class. A collection is a way of treating a group of objects as a unit. In life, we deal with collections all the time. An orchestra conductor might say, “All wind instruments stand up.” It would be less efficient for the conductor to say, “Mary, Jim, John, Joe, Sally, stand up.” Similarly, we can treat objects that we create as a unit or collection. The most important concept here is that a collection is an object with its own properties and methods. A collection object has the following characteristics: • It is an object with properties and methods. • Different types of objects can be added to the collection. • The number of elements that can be added to a collection is variable, meaning it expands and contracts as items are added and removed from the collection. • Items in the collection are index-based one (start counting at the numeral 1).
Creating Objects with Class Modules CHAPTER 11
Creating VBA Collections
Dim Users as Collection Set Users = New Collection
Tip By convention collections are named as the pluralized form of the objects a particular collection will be used to store. For example, a Collection of User objects would be named Users by convention.
VBA Collection’s Properties and Methods The Collection class has a very simple structure, with only one property and three methods, as shown in Table 11.1. TABLE 11.1
The Collection Object’s Properties and Methods
Name
Type
Description
Count
Property
Indicates how many items are in the collection.
Add
Method
Used to add items to a collection.
Remove
Method
Used to remove items from a collection.
Item
Method
Used to refer to items in the collection. This is the default method.
Adding Items to a Collection To add items to the collection, use the Add method of the collection object. Collection.Add Item [, Key][, Before][, After]
Pass the object variable for each object added to the Collection. For example, to add two Users to a Collection, use the object variable for each user. The next parameter is the Key value (a friendly name) to use to refer to the object. If a key value is not specified, each item in the collection must be referred to by index number.
11 CREATING OBJECTS WITH CLASS MODULES
Because Collection is a class, you create a Collection the same way you create an instance of any other class. First, declare a variable to use as a reference to the Collection. Second, use the Set keyword to create a reference from the object variable to the Collection. The following code creates a Collection named Users:
313
314
VBA Unleashed PART IV
Tip You are strongly encouraged to specify a key value. Key values make code easier to write and are more reliable than index numbers. Index numbers can change if items are removed from the collection or inserted into specific locations of the collection using arguments of the Add method.
In the following example, two users will be added to the Users collection: Dim User1 as User Dim User2 as User Dim Users as Collection Set User1 = New User Set User2 = New User Set Users = New Collection User1.Name = “James” User2.Name = “Steve” Users.Add User1, User1.Name Users.Add User2, User2.Name Set User1 = Nothing Set User2 = Nothing Set Users = Nothing
The preceding code adds the object User1 to the collection. The key is specified with the value of the Name property of the object. The User2 object is added in the same manner. Note that there is a “before” and “after” argument for the Add method of collections to specify the order for an object in a collection. You can use the before or after parameter to insert an object into a collection in a specific position, precisely before or after some other element.
Referring to a Specific Object Objects in a collection can be referred to by number or key value. When referring to items in a collection by number, remember that the items are indexed-based one, which means that you begin counting at l. The easier way to refer to an object in a collection is by its key name. The following code shows how to refer to the specific object User2 using both of these methods. Although Item does not need to be specified (it is the default method), it makes the code easier to read and maintain. ‘ Refer to an object in a collection by index number. MsgBox Users.Item(2).Type
Creating Objects with Class Modules CHAPTER 11 ‘ Refer to an object in a collection by key value. MsgBox Users.Item(“Steve”).Type
The easiest and most efficient way to loop through objects in a collection is to use a For Each loop. A For Next loop can be used as well, but it is slower. To use a For follows:
Each
loop, specify the object to examine in the collection. The code is as
‘ Must declare object variable. Dim User as User For Each User in Users MsgBox User.Type Next User
Tip The preceding example will examine each user in the collection. Notice that the individual users had object variables named User1 and User2. To use the For Each loop, specify a generic object variable named User. This object variable need not be assigned to an object variable with the Set keyword. Its only purpose is use with the For Each loop.
Removing Individual Objects To remove specific objects from the collection, use the object’s index number or key value. ‘ Remove object by index number. Users.Remove 2 ‘ Remove object by key value. Users.Remove “Steve”
Removing All Objects To remove all objects from a collection, do not loop through all items in the collection and call the remove method. A much faster approach is to reassign the collection object to a new collection, effectively releasing the original collection and allowing all of the contained objects to be returned to the memory pool. ‘ Remove all objects from a collection. Set Users = New Collection
11 CREATING OBJECTS WITH CLASS MODULES
Looping Through Items in a Collection
315
316
VBA Unleashed PART IV
Releasing the Object Variable Collections are objects. Just as you would with any other object variable, be sure to remember to release the object variable by setting it to Nothing. ‘ Release the collection object variable. Set Users = Nothing
Summary Creating classes is an efficient way to write and maintain applications. The benefits include code that is better organized, easier to write and maintain, easier to use with IntelliSense, and more portable. Also, complex functionality can be hidden from developers who use the object, and developers can create multiple instances of the object.
CHAPTER 12
Debugging Access Applications
IN THIS CHAPTER • Eliminating Logic Errors
318
• Working with the Visual Basic Development Environment (IDE)
318
• Working with the Debug Object
322
• Using the Immediate Window • Using the Debugger
326
• Using Conditional Compilation • Application Testing
324
331
335
• Practice Debugging Techniques
335
318
VBA Unleashed PART IV
Using the debugging tools in Microsoft Access and the debugging techniques discussed in this chapter can save an enormous amount of development time. Taking the time to study these tools and techniques will result in significant long-term benefits. Don’t assume that you only need these debugging tools to create complex applications. In even the simplest applications, program errors are not readily apparent or easy to find. It is essential that program errors be discovered and resolved as early as possible. Although some errors might have little or no adverse impact on the database, others can corrupt the database or worse.
Eliminating Logic Errors Logic errors occur when the code does not produce the expected result(s). A logic error is defined as code that runs correctly but does not produce the correct result. Most of the time spent debugging an application involves discovering and fixing logic errors. This chapter will provide you with the techniques and tools to resolve these errors. Examples of logic errors include: • Processing data in an inappropriate order • Missing essential information, yielding a syntactically correct result but an inappropriate value • Any error that allows the program run to completion and produces the wrong result
Working with the Visual Basic Development Environment (IDE) A radical change occurred in Access 2002’s design environment. Access now incorporates the Visual Basic Integrated Development Environment (IDE) just like Visual Basic 5/6, Word 97, and other Office 97/2002 products. This editing environment provides a standard means to develop applications within the various products. The debugging techniques in this chapter can be utilized in Access as well as Visual Basic 6 and Microsoft Office development. To open the IDE, while in a form design mode, under the View menu choose Code. The IDE includes various windows that can be opened and closed, such as the Project window, Properties window, Immediate window, and more. To open a window in the IDE, select the window under the View menu. Close the window by clicking the close button in the upper-right corner of the window. The next time you open the IDE, all the windows will be displayed as they were the last time the IDE was closed.
Debugging Access Applications CHAPTER 12
319
When a form is opened in Design view, you can work with controls and properties just like in earlier versions of Access. However, you must open the Visual Basic IDE to view code for the form (see Figure 12.1). Actually the Microsoft Visual Basic Editor is a completely separate application from Access. FIGURE 12.1 The Visual Basic Integrated Development Environment (IDE).
12 DEBUGGING ACCESS APPLICATIONS
Although the Visual Basic IDE is a separate application, it works in conjunction with Access. If the Visual Basic IDE is open, the Visual Basic IDE will also be closed when you quit Access. To close the Visual Basic IDE, choose Close and Return to Microsoft Access under the File menu. Let’s examine the windows in the Visual Basic IDE.
The Project Explorer The Project Explorer displays a list of the forms, reports, and class modules in your Access application (see Figure 12.2). Right-click any of these objects to view the code or go into Design mode for a form. To open the Project Explorer, choose Project Explorer under the View menu or press Ctrl+R.
320
VBA Unleashed PART IV
FIGURE 12.2 The Project Explorer window.
The Code Window The Code window shows the code for forms, modules, and class modules (see Figure 12.3). As a developer, you will most likely spend many hours entering and modifying code in the Code window. To open the Code window, choose Code under the View menu or press F7. There are two drop-down lists at the top of the Code window. The list on the left is used to choose an object such as a form or control on a form. The list on the right side is used to choose the procedure for the selected object. FIGURE 12.3 The Code window.
The Properties Window Use the Properties window to set and view the properties of forms, reports, or controls on forms and reports (see Figure 12.4). To open the Properties window, choose Properties under the View menu or press F4.
Debugging Access Applications CHAPTER 12
321
FIGURE 12.4 The Properties window.
12
The Immediate window can be used to evaluate and set variables, run procedures, and output Debug.Print statements. To open it, choose Immediate window under the View menu or press Ctrl+G. The Immediate window will be discussed in detail later in this chapter.
The Locals Window The Locals window shows expressions, values, and types of all variables that are currently in scope. To open the Locals window, choose Locals window under the View menu. Details related to the Locals window will be discussed later in this chapter.
The Watch Window The Watch window can be used to evaluate expressions while the application runs. To open the Watch window, choose Watch window under the View menu. Details related to the Watch window will be discussed later in this chapter.
The Object Browser To open the Object Browser, choose Object Browser under the View menu or press F2. Use the Object Browser to examine objects, properties and methods (see Figure 12.5).
DEBUGGING ACCESS APPLICATIONS
The Immediate Window
322
VBA Unleashed PART IV
FIGURE 12.5 The Object Browser.
The Call Stack Window The Call Stack window shows procedures that have been previously called. To open the Call Stack window, choose Call Stack from the View menu or press Ctrl+L. Details related to the Call Stack window will be discussed later in this chapter. Tip As shown previously, there are numerous windows that can be shown in the IDE. It is much easier to develop in the IDE with screen resolution set to 1024×768 or higher. Better yet, try the new multi-monitor feature in Windows 98 and Windows 2002. Learn the shortcut keys to quickly open and close the windows. The windows can be resized and moved as needed. But beware that if you undock a window by dragging it to a different location, it is hard to redock it. The trick is to double-click the title bar to dock and undock the window.
Working with the Debug Object The Debug object has two methods that are helpful in debugging applications: Debug.Print and Debug.Assert.
Debugging Access Applications CHAPTER 12
323
Debug.Print With Debug.Print, you can print information to the Immediate window. This method can be used either in the Immediate window itself or in the code. Tip To print information to the Immediate window, use the Debug.Print statement in code. While testing expressions or running functions in the Immediate window, you do not need to type Debug.Print. Instead type a question mark (?), which will yield the same result.
? Len(“Debug”)
FIGURE 12.6 Use Debug.Print in the Immediate window to test built-in functions.
By placing Debug.Print in your code, values and other information can be printed to the Immediate window. When the following code executes, the value of the rst.States will be printed to the Immediate window: Sub Demo ( ) Debug.Print rst.States Select Case rst.States Case “Washington MsgBox “Washington” Case “Oregon” MsgBox “Oregon” Case “California” MsgBox “California End Select End Sub
The Immediate window does not wrap the text; you will have to scroll the Immediate window to view very long text. It is not necessary to remove Debug.Print statements from your code because the end user will never see the Immediate window.
DEBUGGING ACCESS APPLICATIONS
In the Immediate window, you can test built-in functions, values in a recordset, and more (see Figure 12.6). For example, the following statement typed in the Immediate window demonstrates the built-in function Len:
12
324
VBA Unleashed PART IV
Debug.Assert will conditionally suspend execution on a particular line of code. For example, if a statement of code reads Debug.Assert False, the code will stop execution on that line of code. This will allow you to step through the code and debug any errors. Debug.Assert
Tip If you use Stop in your code for testing purposes, make sure you comment out or remove all occurrences of Stop before deploying your code. You can use Edit, Replace to replace all occurrences of Stop with ‘Stop or define your own Stop subroutine, call that subroutine in your code, and then comment out the single Stop statement in subroutine.
Using the Immediate Window To open the Immediate window, go to the Visual Basic Editor and choose Immediate Window under the View menu or press Ctrl+G. The Immediate window can be used to evaluate and set variables, to run functions and subprocedures and to output Debug.Print statements.
Evaluating Variables To print variables in the Immediate window, enter a question mark followed by the variable name, for example, ? strName. Remember, you can also determine the value of a variable by hovering the cursor over the variable while in break mode.
Changing the Value of Variables To change the value of a variable in the Immediate window, enter the variable name followed by an equal sign and the new value, for example, intI = 10.
Evaluating Built-in Functions To print the return value of a function in the Immediate window, enter a question mark followed by the built-in function. For example, ? Now.
Running Custom Subprocedures To run your own subprocedures in the Immediate window, simply enter the name of your subprocedure and any parameters. For example, type MySubProcedure. Do not use a question mark in front of the subprocedure.
Debugging Access Applications CHAPTER 12
325
Tip When running a subprocedure or function in the Immediate window, enter the subprocedure or function name (do not use a question mark in front of the procedure name). If the subprocedure or function is in a form module, enter the name of the form name in front of the procedure name (for example, frmTest.MySubProcedure).
Tips for Using the Immediate Window Running Statements in the Immediate Window To run a statement of code in the Immediate window, place the cursor anywhere in the statement of code. It is not necessary that the cursor be at the beginning or end of the statement of code. If there are several statements of code in the Immediate window, you do not need to delete the statements of code after the statement you want to run. Simply put the cursor anywhere in the statement of code you want to execute and press Enter.
Moving Around in the Immediate Window Use the mouse and arrow keys to move the insertion point. The Home key moves the insertion point to the beginning of the current line of code; the End key moves the insertion point to the end. The Page Up and Page Down keys move through the code a page at a time. To move the insertion point to the beginning of the Immediate Window, press Ctrl+Home. Use Ctrl+End to move the insertion point to the end of the Immediate window.
Deleting Code in the Immediate Window To quickly highlight all the code in the Immediate Window to delete it, press Shift+Ctrl+Home if you are at the end of the code in the Immediate window. This will highlight all the code; then press Delete. If the insertion point is at the beginning of the code in the Immediate window, press Shift+Ctrl+End to highlight all the code. Then press the Delete key.
DEBUGGING ACCESS APPLICATIONS
Because you will spend a great deal of time using the Immediate window, the following tips might prove useful.
12
326
VBA Unleashed PART IV
Using the Debugger It is easy to debug applications in Access 2002 by stopping execution of the code in certain places and stepping through the code to observe the program flow.
Setting Breakpoints A breakpoint stops the execution of code. To set a breakpoint, put the cursor on a statement and choose Toggle Breakpoint under the Debug menu of the Visual Basic Editor, or press F9. Remember that you cannot set a breakpoint on a blank line, a comment only line, or a statement of code containing a Dim statement. Another way to set the breakpoint is to click the gray left margin of the code window next to a statement of code. This will put a large red dot in the left border indicating that a breakpoint is present. When the program is run, execution will stop on the statement where the breakpoint is located (see Figure 12.7). The code module will open automatically and the statement of code will be highlighted in yellow. You can then step through the code, view or change the value of variables, and more. FIGURE 12.7 Code execution stopped at breakpoint.
To eliminate a breakpoint, choose Clear All Breakpoints from the Debug menu or press Ctrl+Shift+F9.
Debugging Access Applications CHAPTER 12
327
Stepping Through Code When code execution stops due to a breakpoint, the statement of code highlighted in yellow has not executed. You can move forward through your code by using several stepping techniques.
Single-Stepping Through Code Choose Step Into under the Debug menu in the Visual Basic Editor or press F8 to execute one statement of code at a time. Single-stepping through code is the most effective way to observe program flow and variable values in code.
Some developers find the Debug toolbar to be a great help. To open the toolbar, go to the View menu, and choose Toolbars, Debug.
Step Over At times, you will have one procedure call other procedures. The procedures you are calling might be rock solid (in other words, fully tested and error-free). Use Step Over under the Debug menu or press Shift+F8 to execute the procedure you are calling at full speed so you do not have to step through each statement of code. After the calling procedure has finished executing, the code will again stop executing. At this point, you can continue to single-step through the code.
Step Out Let’s assume you are stepping through a procedure which calls another procedure. When you are in the called procedure, you can quickly execute the rest of the procedure and return to the original procedure by choosing Step Out from the Debug menu or press Ctrl+Shift+F8. This is useful when you forget to “step over” until you are already into the procedure.
Run to Cursor When stepping through code, you can execute the code at full speed to the location of your cursor. This is helpful when you step through a statement of code and then want to run at full speed to another section of the code. One example is looping structures. After stepping through a loop several times to verify that it is working correctly, put the cursor at the end of the loop and choose Run to Cursor from the Debug menu, or press Ctrl+F8.
DEBUGGING ACCESS APPLICATIONS
Tip
12
328
VBA Unleashed PART IV
Set Next Statement When stepping through code, you can set the next statement that you want to execute. To do so, right-click the line of code that you want to execute next. Then click Set Next Statement from the shortcut menu.
Continuing Code Execution After stepping through code to verify the code is working correctly, you might want to continue executing the code at full speed. To do so, choose Continue under the Run menu or press F5.
Rerunning Code After you discover and have fixed the error while stepping through code, you can rerun the code without stopping and restarting the application. The yellow arrow in the left border shows the statement of code which will execute next. You can click the yellow arrow, drag it up to a previous line of code, and rerun the code that you previously stepped through.
Determining Variable Values While debugging your application, you can easily discover the value of a variable by hovering the mouse over the variable while in break mode (see Figure 12.8). FIGURE 12.8 Determining a variable’s value in break mode.
Debugging Access Applications CHAPTER 12
329
The Immediate window can also be used to get a variable’s value. For example, the following text in the Immediate window will return the value of a variable named strName: ? strName.
Using Microsoft IntelliSense While Debugging
Using the Locals Window The Locals window will show you expressions, values, and types of all variables that are currently in scope (see Figure 12.9). To display the Locals window, choose Locals window under the View menu. FIGURE 12.9 The Locals window shows variables in current scope.
Tip The Locals window can be used to change the value of variables. Double-click the variable value and enter the new value.
Using the Watch Window The Watch window can be used to evaluate expressions while the application runs. For example, if you want to see anywhere in the application anywhere the variable strName is changed, you can use the Watch window. The first step is to choose Add Watch under the Debug menu. In the Add Watch dialog box, enter the expression you want to watch (for example, strName = “Smith”). In the
12 DEBUGGING ACCESS APPLICATIONS
When a syntax error occurs and you have found the offending line of code, check the syntax with the use of IntelliSense. IntelliSense provides for rapid development by suggesting properties and methods of objects as you type code. For example, after typing the name of an object (for example, Recordset or Application), type a period and see if you have used the correct name and spelling of the property or methods. If you do not see the property or method in the drop-down list, this is a tip-off that you have mistyped the name of the object.
330
VBA Unleashed PART IV
dialog box, also choose whether you want to evaluate the expression in certain procedures, modules, or the entire application (see Figure 12.10). Specify the Watch Type in the dialog box: • Watch Expression—Monitors the expression • Break When the Value is True—Stops the code in break mode when the expression is True • Break When Value Changes—Stops the code in break mode when the value of the expression changes FIGURE 12.10 Use the Add Watch dialog box to create a Watch expression.
To create a Watch expression quickly, highlight an expression in your code. Then choose Quick Watch from the Debug menu or press Shift+F9. Choose the Add button on the Quick Watch Dialog box (see Figure 12.11). FIGURE 12.11 Using the Quick Watch dialog box.
Viewing the Call Stack Your application can include procedures that call other procedures that, in turn, can call other procedures, and so on. When debugging your application, it might not be obvious “how you got here” or what list of procedures have been executed. The Call Stack window will show you. Think about it this way. Stepping through code moves you forward, into the future. The Call Stack is a historical view. It shows what procedures have been previously called (see Figure 12.12). To open the Call Stack window, choose Call Stack from the View menu or press Ctrl+L.
Debugging Access Applications CHAPTER 12
331
FIGURE 12.12 Use the Call Stack window to show what procedures have been called.
Tip To jump to a procedure, select it from the Call Stack window and click the Show button (or simply double-click the procedure).
Maintaining a single version of an application is much easier than creating multiple versions. Let’s say you have an application used by many offices. Most of the application is generic and can be used by all the office but a small amount of code needs to be customized for each individual office. If you decide to create multiple versions, updating and maintaining all the versions will be quite a chore. Any new generic code will have to be added to all versions. Also, multiple versions add a great deal of complexity to the maintenance and distribution of the applications. The better choice would be to create a single version of the application that operates differently under different circumstances. You can choose which part of the code in the application is compiled or run based on the circumstances. As an example, assume specific code needs to be run for the Los Angeles office that does not apply to any other office. First, decide on a constant for the office, for example, LA. Enter the constant as a conditional compilation argument for the application. In the Visual Basic Editor, under the Tools menu, choose the Application’s Properties. The Project Properties dialog box will open. Select the General tab. In Conditional Compilation Arguments enter LA = -1. This sets a constant equal to True. Now you can enter code that only executes for the LA version of the application. Do so by putting the code in a conditional compilation construct. For example: #If LA Then ‘ This code will run in the LA version of the application. #End If
Conditional compilation can also be a useful debugging tool. Let’s say you want to comment out sections of code. Set a conditional compilation argument fComment = -1. This
DEBUGGING ACCESS APPLICATIONS
Using Conditional Compilation
12
332
VBA Unleashed PART IV
comment flag can then be used with a conditional compilation construct to comment out sections of code: #If fComment Then ‘ This code will run. #End If
Writing Solid Code There is no way to completely eliminate the need to debug applications. However, if the following guidelines are used, code is more likely to run correctly and be error-free.
Declare Variables on Separate Lines of Code It is much easier to check if a variable is declared by referring to variables that are listed on separate lines rather than in a long text string. In addition, there is no speed penalty for using this technique for compiled applications. Tip You might find that grouping declared variables by data type will make it easier to refer to variables.
Declare Variables with the Narrowest Scope Use local variables as much as possible. This will not only prevent scoping issues but might also help performance.
Use Specific Data Types Always declare a data type and use the smallest data type possible. If you do not declare a data type, a variant data type will be used. This is not only inefficient because of the additional resource requirements, but any type of data can be put in a variable with a variant data type. This can lead to program errors. For example, if a variable should only hold numbers, but a variant data type is used, an error can occur.
Destroy Object Variables Eliminate resource errors by destroying object variables. If an object variable exists named objWord, specifically destroy the object variable at the end of the procedure with Set objWord = Nothing.
Debugging Access Applications CHAPTER 12
333
Use TypeOf Keyword Often you can pass controls to a generic procedure. For example, you pass list box and combo box controls to a generic procedure that loads values using the AddItem method. If you pass a control that is not a list box or combo box (for example, a command button), an error will occur because the control does not have an AddItem method. In the procedure, use the TypeOf keyword to check what type of control was passed in to eliminate the error.
Use Me Instead of Screen.ActiveForm and Screen.ActiveControl
Use an Error Handler When an error does occur, the error handler can be a real timesaver to help find the particular statement of code that failed, the information about the type of error, line number, and much more. See Chapter 13, “Professional Error Handling,” for additional information.
Use Option Explicit Force the declaration of all variables by placing Option Explicit at the top of every code module. You can make this the default by checking the Require Variable Declaration option in the program options. By declaring all variables, errors due to misspelled variable names can be eliminated.
0 or 1 Based? In VBA, some items are 0 based, others are 1 based. This is determined by whether you start counting at 0 or 1. For example, arrays are 0 based while collections are 1 based. If you are not sure if an item is 0 or 1 based, look it up. A wrong guess will certainly produce an error.
Fix Errors Immediately The temptation is to ignore errors when developing an application. For example, you are creating a new feature in an application and you come across a bug in another part of the application. It is best to stop and fix the bug. This is hard because you are focused on your own development. However, bugs can be hard to find later and reproduce. The best rule of thumb is if you come across a bug, fix it—now.
DEBUGGING ACCESS APPLICATIONS
When debugging an application, the Immediate window has focus. Therefore, Screen.ActiveForm and Screen.ActiveControl will not work. Use the Me keyword instead to refer to the current form.
12
334
VBA Unleashed PART IV
Use Comments Let’s face it, months, weeks, or even days after writing code, it is hard to remember all the details about a particular procedure. Comment your code heavily to make it easier to maintain. Comments do not slow down the code because they are stripped out by the compiler. To create a comment, put a single quote in front of the statement of code. Commented lines of code are shown in green in the code window. Tip To block comment and uncomment code, use the Comment Block and Uncomment Block items on the Edit toolbar in the IDE.
Use the Line Continuation Character It is difficult to quickly review code if you have to scroll to the right to see code that is not shown on a single screen. Make sure that all the code is visible in the code window by using the underscore ( _ ) as a line continuation character. The underscore will treat all the lines of code as if it were on a single line. A common example is a SQL statement: Dim strSQL as String StrSQL = “SELECT * FROM tblErrorLog” & _ “ORDER BY tblErrorLog.ErrorLogID;”
Use Small Procedures Lengthy procedures are hard to understand and debug. Break procedures into small logical units. As a rule of thumb, if you cannot print a procedure on a single sheet of paper, you should consider breaking it into smaller procedures.
Use Standard Naming Conventions Using naming conventions will help you as well as other developers more easily understand and debug your code. A common complaint when a developer takes over an application from another developer is how hard it is to understand the code. Using naming conventions to name objects and variables makes the debugging process significantly easier.
Don’t Debug with Message Boxes Using message boxes in the past has been a common technique to debug code. The idea is to place message boxes throughout the code to find the offending statement in the code:
Debugging Access Applications CHAPTER 12
By watching which message boxes appear before the code fails, you can discover which line of code has the error. A much better approach is to set a breakpoint and walk through the code. Setting a breakpoint is better because • It is quicker than entering a bunch of message boxes. • Message boxes are modal so you cannot switch to the code window to review the code. • While stepping through the code with the debugger, you can watch the value of variables, change the value of variables, and more. • When the error is fixed, you do not have to take time to remove the message boxes. • You do not have to worry about accidentally shipping your applications without removing the message boxes.
Application Testing Before distributing an Access application, make sure to fully test the application. See Chapter 2, “Planning the Development Process,” for additional information.
Practice Debugging Techniques The sample code for this chapter gives you the opportunity to practice the debugging techniques discussed in this chapter (see Figure 12.13).
12 DEBUGGING ACCESS APPLICATIONS
Sub Demo ( ) MsgBox 1 Select Case rst.States MsgBox 2 Case “Washington” MsgBox 3 MsgBox “Washington” MsgBox 4 Case “Oregon” MsgBox 5 MsgBox “Oregon” MsgBox 6 Case “California” MsgBox 7 MsgBox “California” MsgBox 8 End Select MsgBox 9 End Sub
335
336
VBA Unleashed PART IV
FIGURE 12.13 Practice debugging techniques with chapter sample code.
Summary Access 2002 has powerful debugging tools. Learning how to use these debugging tools is an essential skill for any Access developer. This chapter included tips for using the Integrated Development Environment (IDE), the Immediate window, the Locals window, and the Watches window. The Debugger and debugging techniques were discussed. Also conditional compilation and tips for writing solid code were covered. With the information and techniques discussed in this chapter, creating and maintaining applications should be quicker and easier.
CHAPTER 13
Professional Error Handling
IN THIS CHAPTER • Eliminating Syntax Errors • Eliminating Logic Errors
338 341
• Eliminating Runtime Errors
341
• Errors in Various Applications • Error Handling With Nested Procedures 365 • Advanced Error Topics
365
364
338
VBA Unleashed PART IV
The hallmark of a professional application is error handling. If the application doesn’t elegantly handle errors, users will be frustrated no matter how sophisticated an application is or how many features it has. Without error handling, a program might abruptly shut down, especially if they’re using a “runtime” version of your application. This can frustrate users because they have no idea what happened, why it happened, or whether it’s safe to continue working. When an error does occur, users should be provided feedback that allows them to clearly understand how to recover. Cryptic error messages are almost as bad as no feedback at all (see Figure 13.1). How many users understand, or find helpful, error messages such as Illegal operation, Overflow, or General Protection Fault? FIGURE 13.1 Example of a cryptic error message.
Without comprehensive error handling, developers also lose valuable feedback about how their application functions under actual use. When support calls are received, it’s time-consuming for developers to find and fix errors. Certainly, it’s in a developer’s best interest to quickly resolve program errors. Without error handlers, developers must rely on users to explain what they were doing when the program error occurred. A user’s typical response might be “I don’t know.” This leads to the unnecessary and time-consuming work of trying to find and fix the errors. This chapter shows how to develop a powerful error handler that helps the application’s users, as well as the developer. There are three types of program errors: syntax errors, logic errors, and runtime errors.
Eliminating Syntax Errors Let’s start with the easiest type of program error to deal with—the syntax error. Syntax errors occur when code is written improperly. An example would be a misspelled variable or keyword or missing code: Dim Name As
Obviously, an essential piece of code is missing in the statement. The data type isn’t stated. A correct statement would read as follows: Dim Name As String
Professional Error Handling CHAPTER 13
339
One key to eliminating syntax errors is to insert Option Explicit on the first line of every module. This will require you to declare all variables you use in the application. It’s important to declare all variables for several reasons. A misspelled variable name can have drastic consequences. If the variable is to insert a value in the database, inappropriate data can be inserted without any notification of the error. After all, VBA treats a misspelled variable as a new, implicitly declared variable. In the following example, the variable BankBalance on the second line is misspelled: BankBalance = 4,576.98 RS!BankBalance = BankBalence
VBA doesn’t recognize BankBalence as a keyword, so it assumes that you want it to create a new variable of Variant data type with a default value of Empty. As a result, the database is updated with Empty instead of the desired value of $4,576.98. Note The Variant data type was implemented to support and facilitate VBScript and working with COM objects. The Variant type was not intended as a generalpurpose device for writing ambiguous code and should be avoided.
Dim Number as Byte
Undeclared variable (numeric data only) Undeclared variable (character or string data)
(1 Byte) (16 Byte) (22 Byte)
In this example, the undeclared variable is assigned as a Variant data type. To force variable declaration, go into the Microsoft Visual Basic Editor by opening a code module. From the Tools, Options menu, open the Options dialog box. On the Editor page, turn Require Variable Declaration on (see Figure 13.2). This will insert Option Explicit at the top of each new module; however, it won’t update old modules. Be sure to insert Option Explicit in any modules you’ve previously created.
PROFESSIONAL ERROR HANDLING
It’s also important to declare variables to optimize your application. Because undeclared variables use the Variant data type, significant storage space can be lost and the Variant type requires additional overhead to process. For example, the difference in the storage size for a variable that holds the number 5 is as follows:
13
340
VBA Unleashed PART IV
FIGURE 13.2 Select Require Variable Declaration to force variable declaration.
Other types of syntax error can occur as well, such as Msggbox “Hi”
or EndSeb
These misspelled reserved words will produce syntax errors. As you type code, you often are informed of these errors with highlighted text and a warning message. Tip In the Visual Basic Editor, under the Tools, Option menu, on the Editor page, turn Auto Syntax Check off. This won’t disable syntax checking, but it will get rid of the annoying dialog box that pops up whenever there’s a syntax error. The error will still be shown as the code is highlighted in red.
In the Options dialog box, you can modify some compiler settings. On the General page, the Compile on Demand option enables the application to run faster because modules aren’t compiled until they’re loaded for execution. On today’s faster computers, it’s recommended that you turn this option on to eliminate errors. Also in the General page is a Background Compile option. Turn this option on to enable background compiling while the computer is idle. Background Compile can improve runtime execution speed. To use this feature, Compile on Demand must also be enabled. It’s also important that all modules are compiled and saved before an application is distributed to assure that all syntax errors have been found. From the Debug menu, choose Compile and Save All Modules to compile and save every module in the database, whether or not they are now loaded.
Professional Error Handling CHAPTER 13
341
Eliminating Logic Errors Logic errors occur when the code doesn’t produce the expected results. The logic might be flawed, or the program might flow incorrectly. When the code executes but doesn’t produce the desired result, expect a logic error. To resolve logic errors, use Microsoft Access’s powerful debugger. See Chapter 12, “Debugging Access Applications,” for a complete discussion of these issues.
Eliminating Runtime Errors There’s no way to eliminate all runtime errors, but you can plan for them. It is not necessary to include an error handler in every procedure. If you can determine a resolution to a potential problem, include an error handler and the incumbent resolution. If it is suitable to simply show the user the error and continue processing, then include an error handler that displays a MsgBox showing the Err.Descripion. However, if your code does nothing more than shows the error and shutdown, adding an error handler simply duplicates the default behavior. Error handlers of this type just mean extra work for you.
A Simple Error Handler Before discussing a comprehensive error handler, let’s start with a simple example: Sub SimpleErrorHandler() Dim I As Integer On Error GoTo Except ‘ Code managed by handler goes here Exit Sub Except: MsgBox “An error occurred” End Sub SimpleErrorHandler
lowing sections.
employs conventions that I use consistently, as described in the fol-
13 PROFESSIONAL ERROR HANDLING
The best error approach is to implement a handler that resolves the problem and allows the user to continue working. The second best approach is to terminate the application in a controlled manner, avoiding the loss or corruption of data. Do-nothing error messages provide no real clue as to the state of efficacy of the application, providing no clue to the user what the next course of action should be.
342
VBA Unleashed PART IV
On Error GoTo Except The On Error Goto statement by convention is put near the top of the procedure, but it can placed anywhere you want to begin writing managed code. If an error occurs in the code after the On Error Goto statement, then the code branches to the label on the On Error Goto statement, Except. Branching literally means that the Instruction Pointer in the CPU is updated to contain the memory location of the line of code after the label, skipping everything in between. Note Other developers use other labels, like ErrorHandler, instead of Except. I prefer Except out of habit from using languages that support exception handling, and the name of the error handling block is a reserved word, generally some form of the word Except. VBA does not support structured exception handling yet but likely will in the future. Because VBA doesn’t support exception handling, we won’t go into detail here. It is sufficient to note that exception handling is a more robust and informative mechanism than On Error Goto branching. Whichever technique label-naming convention you pick for error handling, be consistent and use it dogmatically.
Tip You can easily find labels because they are flush left by the editor and are followed by a colon.
Exit Sub An Exit Sub or Exit Function statement matching the type of the procedure must be included before the error handling label (Except in the example). If you forget to include the Exit statement then your error handling code will always be executed, which is not what you want in an error handler (see protecting Resources for an example of when you want error handling code to always run). When the code gets to the line containing the Exit statement it has the same effect as the End Sub (or End Function) end-of-block statement. That is, the procedure returns to the calling procedure. When an Exit or End of the procedure block is run, the Err object is reset, clearing all errors.
Professional Error Handling CHAPTER 13
343
Protecting Resources As mentioned there are times when you want the error handler to run whether there is an error or not. For example, if you open a file or Recordset, you will want to ensure that the file or Recordset is closed. Elements like files and Recordset are called resources. Because there are a limited amount of resources in a computer, you want to ensure they are returned when you have finished with them. To ensure resources are returned, you can employ an idiom referred to as a resource protection block (in the vernacular of languages with exception handling). Resource protection blocks can be implemented using the On Error Goto statement and intentionally leaving out the Exit statement. An example follows:
If the preceding code fails on the open statement, an error occurs. Such an error is not handled by the code. However, if the Recordset is opened, the On Error Goto Finally statement sets up a resource protection block. Namely, we are ensuring the Recordset is closed and set to Nothing. The code in the example employs an idiom not directly supported by VBA. Specifically, by leaving out the Exit Sub statement the code after the Finally label is always run if the code executes successfully to the On Error Goto statement. It is perfectly acceptable to implement idioms that are not directly supported by a language. The absence of exception handling and resource protection blocks is a limitation in the current implementation of VBA, but the techniques and strategies are still beneficial and valid even in the absence of direct support by the language.
Implementing the Error Handling Block The error handling block is the code that is defined after the Except label (or whichever label you choose to use). If a particular error handling block is simple, then you might want to write the code in-place after the label. Alternatively, if the code requires more than a couple of lines, then pass the Err Object ByRef to a procedure and write the error handling code in the procedure. This strategy will keep your solution code separate from your error handling code, making each procedure a separate but related manageable piece.
13 PROFESSIONAL ERROR HANDLING
Sub ProtectResource() Dim Rs As New ADODB.Recordset Call Rs.Open(“tblErrorLog”, CurrentProject.Connection) On Error GoTo Finally Rs.MoveFirst Call MsgBox(Rs!User) Finally: Rs.Close Set Rs = Nothing End Sub
344
VBA Unleashed PART IV
Silent Error Handlers Occasionally it might be acceptable to just keep processing even in the presence of an error. For example, if you are reading data out of a Field and the value is Nothing, instead of showing an error message and assigning the value to its null-equivalent you might elect to continue processing. This can be accomplished with the On Error Resume Next statement. On Error Resume Next instructs the compiler to process the next line of code following a line that produces an error. Let’s use the code from “Protecting Resources” to demonstrate. Sub ProtectResource() Dim Rs As New ADODB.Recordset Call Rs.Open(“tblErrorLog”, CurrentProject.Connection) On Error Resume Next Rs.MoveFirst Call MsgBox(Rs!User) Rs.Close Set Rs = Nothing End Sub
The revised ProtectResources subroutine will attempt to process every line after the On Error Resume Next statement even if they cause an error. This is optimistic programming. In the new procedure we assume everything will work correctly, but if it fails at any point the code will attempt to complete the remaining steps. Even in the event that no steps can be completed no error is raised due to the resume next. At the end of the subroutine the error handler is invalidated. Occasionally it is suitable to resume next. Suppose you are reading records and initializing a form, if a field contains no value then you cannot display on in the form. If you want the control—for example, a TextBox—to be empty if the field is empty, resume next would in effect yield that result. (Refer to the section “The Resume Statements” later in this chapter for more ways to use the Resume keyword.)
The Err Object VBA provides an Err object that provides much of the information you need for the error handler. The Err object is global in scope and a Singleton object, so you don’t need to create an instance of it. A Singleton object is an object that is created only once in an application. The Err object is effectively a Singleton object; only an Err object exists. Err is an instance of the ErrObject. You will get an error message if you try to manually create an ErrObject using the Dim varname As New ErrObject statement.
Professional Error Handling CHAPTER 13
345
The Err object has the following properties: •
Err.Number—The
•
Err.Description—The
•
Err.Source—The
•
Err.HelpFile—A
error number of the current error. This number can be used to respond to different types of errors that occur.
HelpContext,
description of the error.
object or application that originally generated the error.
path to a Windows help file. This property, when used with can be used to provide a Help button in an error message dialog box.
•
Err.HelpContext—Context
•
Err.LastDLLError
ID for a topic in a help file.
—A system error code produced by a call to a dynamic-link
library (DLL). The Err object has two methods.
Err.Clear The Clear method clears the properties of the Err object. Each of the following will clear the properties of the Err object:
• Use any type of Resume statement. • Exit a procedure. • Use the On
Error
statement.
Err.Raise The Raise method generates a runtime error. Generating a runtime error is useful for testing your application. You can simulate a runtime error by passing the error code to the Raise method of the Err object. Also, when your application calls an external dynamic-link library (DLL), you can pass the error value back to your application to handle the error. When raising errors, you can generate user-defined errors. Make sure that your userdefined error number is unique and add your error number to the constant vbObjectError. For example, to create error number 50, assign vbObjectError + 50 to the number argument: Err.Number = vbObjectError + 50
The following is an example of using the Raise method: Sub RaiseError( ByVal Number As Integer ) If (Number = 110) Then
13 PROFESSIONAL ERROR HANDLING
• Call Clear method of the Err object (Err.Clear).
346
VBA Unleashed PART IV Err.Raise vbObjectError + 50, “MyApplication”, _ “Number must be less than 100”, _ “c:\MyApp\MyApp.hlp”, MyContextID End If End Sub
The Raise method has five arguments: Number, Source, Description, HelpFile, and HelpContext. (These arguments are the same as those of the Err object.) The syntax would be Err.Raise Number, [Source], [Description], [HelpFile], [HelpContext]
or Call Err.Raise (Number, [Source], [Description], [HelpFile], [HelpContext] )
If you use the parentheses, as demonstrated in the second example, then you must include the Call keyword. Only the Number parameter is required. Each of the other parameters in [] represent Optional parameters by convention. By using named parameters, you can pass only the arguments you want and in any order, allowing you to skip the blanks and extra commas for skipped parameters: Err.Raise Number:= vbObjectError + 50, Description:= “My Custom Error”
Tip To quickly gain access to Help information about the Err object and its properties and methods, use the Object Browser. In any code module, press F2 to open the Object Browser, choose the VBA library, and click Err Object under classes. You can then review all the properties and methods. After highlighting a property or method, press F1 for help on that topic.
Responding to Errors Long error handling blocks directly in your code will convolute the intent of a procedure. The purpose of a procedure is to implement part of the solution. The purpose of an error handling block is to make the procedure a little more fault tolerant. Combining the two leads to confusion. Long error handling blocks should be rerouted to a separate procedure. From there you can implement a variety of possible resolutions, including:
Professional Error Handling CHAPTER 13
347
• Displaying a message box can present users with information about the error. • Displaying a message box that provides clues as to a resolution that requires user intercession, such as reminding the user to put a disk in the floppy drive. • Ignoring the error and continue executing. • Ignoring the error and exiting the current procedure. • Automatically performing the corrective action or displaying a message indicating the problem and retrying the procedure. • Calling a helper procedure that resolves the error. The following code demonstrates displaying the error description and allowing the user to retry: Sub TryAgain() Dim Value As Double On Error GoTo Except Value = CInt(InputBox(“Enter a number:”, “Number”)) Value = 10 / Value MsgBox “10 / “ & 10 * Value & “=” & Value
Except: If (MsgBox(Err.Description & “. Try again”, vbYesNo, _ “Try Again”) = vbYes) Then TryAgain End Sub
You might even want to create generalized procedures that handle errors of a particular type. For example, if you review the table of Access and Jet errors in the Access and Jet Database Errors.mdb, you will find that the errors between the numbers 58 and 76 involve file type errors. These include File already exists, Disk full, Too many files, and more. You could create a general procedure that deals with that group of errors and call the procedure in the error handler: Except: If( Not HandledFileError(Err.Number)) Then Call Unexpected(Err) End if End Sub
The listing demonstrates the error handling block. You would need to implement the function HandleFileError. HandleFileError would check to see if Err.Number were between 58 and 76, returning a Boolean and handling those errors. Unexpected could be
PROFESSIONAL ERROR HANDLING
Exit Sub
13
348
VBA Unleashed PART IV
implemented to take a ByRef ErrObject and simply display the unhandled error’s description.
The Resume Statements The following Resume statements enable you to branch the program execution to different statements of code when an error occurs.
Resume Resume transfers execution of the code to the same line of code that failed. When the error handler provides users with feedback on how to resolve the error, use the Resume statement to return to the statement of code that previously failed. This is used when the reason for program error has been corrected and you want to rerun the code that originally failed.
Resume Next This transfers execution of the code to the line of code after the line that failed. This will allow the remaining code in your procedure to execute. Figure 13.3 illustrates the program flow for various Resume statements. FIGURE 13.3 The flow of error handling with Resume.
Start
Resume Statement with the error
Error Handler Statement following the one with the error Resume Next
End
Getting Additional Error Information The error handler should automatically record all error information developers need. The more information the error handler collects, the easier it is for developers to find and fix the error.
Professional Error Handling CHAPTER 13
349
Although the Err object provides a great deal of information, it doesn’t provide everything. A well-designed error handler should provide the following additional information about the error: • Line number—Identifies the line number where the error occurred. Consider assigning a predefined range of line numbers to each module. Line numbers can be inserted on the left side of a module in front of statements of code. With the global Find available in Access, you can quickly go to the exact line number where the error occurred if line numbers aren’t repeated. In a procedure, line numbers don’t have to be in order. Tip You would expect the Err object to provide the line number but it doesn’t. To obtain the line number, use the Erl function, which you can see in the code examples for this chapter.
• Name of procedure—Provides the name of the procedure where the error occurred. • Name of active control—Provides the name of the active control when the error occurred. • Value of active control—Provides the current value in the active control when the error occurred. Often an error will occur only if certain values are entered into a control. If, for example, errors occur when a value greater than 20,000 is entered in a text box, this error type can easily be identified by returning the text box’s value in the error handler for logging. Caution The active control will be passed to the error handler. Some controls have a value; others don’t. The error handler should pass the ActiveControl to a procedure that evaluates the type of control using the TypeOf built-in function. If the control is a text box, obtain the value of the text box for the error handler. However, if, for example, it’s a command button, don’t obtain a value to prevent an error from occurring.
13 PROFESSIONAL ERROR HANDLING
• Name of the form, module, class, or report—Provides the name of the form or report where the error occurred. This is a matter of simply passing the form, module, class, or report name to the error handler.
350
VBA Unleashed PART IV
• ID of current record—Provides the ID of the current record on the form when the error occurred. Have you ever noticed that a certain customer record produces most program errors? By returning the current record ID in the error handler, you can compare that customer record with others that don’t produce the error. Sometimes, a required field is missing data. • Name of program—Provides the name of the application where the error occurred. • Error level—An arbitrary set of error values you can set—for example, 1–5. With this information, the urgency of the support call can be gauged. • User name—The name of the current logged-on user. Ever notice how certain users have the most errors? Finding out who had the error can be invaluable. Many times, you will discover that the problem isn’t program errors, but rather an untrained user. To obtain the current user’s name, an application logon screen can be used, or the Windows 95/98/NT logon name can be obtained with a Windows API call. Tip The Windows API call to obtain the current logged-on Windows 95/98/NT user is in the error handler in the code examples for this chapter. See the UserName property and the GetUserName function.
• Date and time—The date and time the error occurred. This information is helpful to analyze the frequency of errors over time. A simple graph should indicate that program errors occur less frequently over time. • Notes about the error—Users can enter what they were doing or give feedback about the error. This information can be entered in an input box or form. Users can then use a simple form to provide information about what they were doing when the error occurred. Let users know this is optional. Some users will never enter a note about the error, whereas others will appreciate the opportunity to provide feedback. With the information that can be retrieved with the Err object and with other methods described here, developers should have sufficient information necessary to resolve runtime errors quickly and effectively.
Professional Error Handling CHAPTER 13
351
A Comprehensive Error Handler Now that you’ve seen how to construct a procedure-level error handler in a procedure and retrieve information about the error, it’s time to create a comprehensive error handler. The first decision should be how to design the error handler and where to put the code. Certainly, the entire error handler could be placed in each procedure. However, this method would create a great deal of redundant code. Also, if the error handler were later modified, changes would have to be made in every procedure. Tip See the procedure-level error handler in the code examples for this chapter. Although using the global error handler is more efficient, it will be helpful to review the basics regarding procedure-level error handling if you aren’t experienced with error handlers.
Tip To extend the global error handler even further, use Visual Basic to create a component that can be used with any Component Object Model-compliant (COM) application. The error handler could be used to trap errors in Access, Word, Excel, VB, and many other applications. See Chapter 19, “Integration with Office 2000,” for more information.
The Error Class Module (Object) A class module called cError in the chapter code provides the properties and methods needed for an effective error handler. The cError object encapsulates all the code in a class module you can now use. Using this object is easy with the benefit of Microsoft’s IntelliSense technology.
13 PROFESSIONAL ERROR HANDLING
The better approach uses a global error handler for the entire Access application. The global error handler is an object, created with a class module.
352
VBA Unleashed PART IV
Tip If you are new to class modules, see Chapter 11, “Creating Objects with Class Modules,” for details on how to create classes.
cError
Object Properties
The cError object has properties for the values you want to capture with the error handler. Table 13.1 specifies these properties. TABLE 13.1
cError Properties
Property
Description
Application
Name of the program that created the application (for example, Access)
AVIFileLocation
Name and full path of AVI (video) file used in initial error notification form or dialog box
Class
Name of the class module
ComputerName
Name of the computer where the error occurred
ComputerTotalMemory
Total memory on the computer
ComputerAvailableMemory
Available memory on the computer
ComputerOperatingSystem
Operating system and version information
ComputerProcessor
Computer processor information
ControlName
Name of the active control
ControlValue
Value of the active control
CurrentRecordID
ID of the current record
Description
Description of the error as returned by the Err object
EmailAddress
E-mail address used to e-mail error information
ErrorDatabase
Name and full path of the database (such as Access or SQL Server database) containing the error table
ErrorNumber
Error number returned by the Err object
ErrorTextFile
Name and full path of the text file containing the error information
HelpContext
Help file context ID returned by the Err object
HelpFile
The name and full path of the Help file returned by the Err object
Professional Error Handling CHAPTER 13 TABLE 13.1
continued
Description
LastDllError
System error code for the last call to a DLL
Level
Arbitrarily set value to determine error urgency
LineNumber
Line number of statement where error occurred
Note
Note by a user indicating what he was doing when the error occurred
Now
Date and time the error occurred
OfficeID
Value assigned to indicate office deployed to
OfficeName
Name of office deployed to
OfficePhoneNumber
Phone number of office deployed to
OfficeFaxNumber
Fax number of office deployed to
Procedure
Name of the procedure
SoundFile
The name and full path of the sound file
Source
Name of the object or application that originally generated the error
UserName
Name of the user who encountered the error
WaitStateFlag
A flag used to set a wait state to stop code from continuing to execute
UserEnterNoteFlag
A flag to indicate whether users are asked to enter notes about the error
When an error does occur, most, if not all, of these properties are set. The information can be retrieved from the cError object and used in a message box, e-mail, calendar entry, and error table in a database or a text file.
Object Methods
The methods of the cError object provide useful error information for developers. For example, the error information can be sent by e-mail to the developer or logged in a database. Table 13.2 specifies the methods in the cError object. TABLE 13.2
cError Methods
Method
Description
AddToErrorHandlerOutlook Calendar
Add the error information to an Outlook calendar called Error Handler
Clear
Clears the Err object
13 PROFESSIONAL ERROR HANDLING
Property
cError
353
354
VBA Unleashed PART IV TABLE 13.2
continued
Method
Description
Email
Sends the developer an e-mail using Outlook whenever an error occurs
EmailAllErrors
Sends the developer an e-mail with an attachment including all the information in the error table
GetActiveControlValue
Retrieves the value of the active control when the error occurred
MessageBox
Presents users with a message box with details about the error
MsgErrorDetails
Summarizes the error information for the message box or e-mail message
OfficeAssistant
Pops up the Office Assistant when an error occurs and asks users whether they want to enter a note about the error
PlaySound
Plays a sound when the error occurs to get the user’s attention
ProcessError
Controls processing of the error information based on the error options set up by the organization
ShowAVIForm
Pops up a form that includes an AVI file to inform users that an error occurred
UserInputBox
Presents an input box to users so that they can provide feedback about the error
GetUserName
Retrieves the user’s Windows/Windows NT logon name (Windows API function)
WaitState
Creates a wait state to stop code from continuing to execute
WriteErrorToTable
Writes the error information to an error table in a database
WriteErrorToTextFile
Writes the error information to a text file
Reviewing the cError Object in the Object Browser As you can see, the cError object in the chapter code has many properties and methods. To quickly review the objects’ properties and methods, open the Object Browser and choose the cError class module (see Figure 13.4).
Professional Error Handling CHAPTER 13
355
FIGURE 13.4 The cError object in the Object Browser.
Processing the Error
Public Sub ProcessError() Dim rst As ADODB.Recordset Dim strSQL As String Dim strVal As String strSQL = “SELECT * FROM tblErrorOptions” ‘ Create ADO Recordset Set rst = New ADODB.Recordset ‘ Open ADO Recordset rst.Open strSQL, CurrentProject.Connection, adOpenKeyset, _ adLockOptimistic Me.ErrorTextFile = rst!ErrorTextFileName Me.UserEnterNoteFlag = rst!UserEnterNoteAboutError Me.AVIFileLocation = CurrentProject.Path & rst!AVIFileLocation Me.SoundFile = CurrentProject.Path & rst!SoundFile Me.OfficeID = rst!OfficeID
13 PROFESSIONAL ERROR HANDLING
When a program error occurs, the error handler in the procedure passes the information to the cError object. The ProcessError method determines how the error is processed. This method refers to the tblErrorOptions table (discussed later in this chapter) for information such as whether users can enter a note about the error and whether the error information is e-mailed. The code in the ProcessError method follows:
356
VBA Unleashed PART IV Me.OfficeName = rst!OfficeName Me.OfficePhoneNumber = rst!OfficePhoneNumber Me.OfficeFaxNumber = rst!OfficeFaxNumber If rst!PlaySound Then ‘ Play a Sound when the error occurs CError.PlaySound End If If rst!ShowOfficeAssistant Then ‘ Show the user the office assistant CError.OfficeAssistant End If If rst!ShowAVIForm Then ‘ Show the user the Error AVI Form CError.ShowAVIForm ‘ Wait to continue the code until the form is closed. Me.WaitState (True) ‘Close the AVI Form DoCmd.Close acForm, “frmErrorAVI”, acSaveNo End If ‘ Open the Error Note form so the user can enter a note. If Me.UserEnterNoteFlag Then DoCmd.OpenForm “frmErrorNote”, acNormal ‘ Wait to continue the code until the form is closed. Me.WaitState (True) ‘ Close the Error Note form. DoCmd.Close acForm, “frmErrorNote”, acSaveNo End If If rst!ErrorsToAccessTable Then ‘ Write the error to the Error Log Access Table CError.WriteErrorToTable
Professional Error Handling CHAPTER 13
357
End If If rst!ErrorsToTextFile Then ‘ Write the error to a text file CError.WriteErrorToTextFile End If If rst!ErrorsToTextFile Then ‘ Write the error to a text file CError.WriteErrorToTextFile End If If rst!AddToErrorHandlerCalendar Then
CError.AddToErrorHandlerOutlookCalendar End If
‘ Show the user a message box of the error. CError.MessageBox End If ‘ Display a form that indicates that it is safe for the user ‘ to continue working. The form automatically closes with a ‘ timer. DoCmd.OpenForm “frmErrorDone”, acNormal rst.Close Set rst = Nothing End Sub
As you can see, this method relies on properties as well as other methods of the cError object. Review the code in the cError class module for additional information.
The End User’s Experience How should errors be handled from the end user’s viewpoint? The first priority should be to get the user’s attention so she stops working and gets her hands off the keyboard. Remember that the error could be critical, such as causing corrupted data to be inserted in the database.
13 PROFESSIONAL ERROR HANDLING
If rst!ShowMsgBoxErrors Then
358
VBA Unleashed PART IV
By calling a few cError methods, the Office Assistant will pop up, an AVI form will appear, and a sound will provide the user with audio feedback that an error has occurred (see Figure 13.5). The chapter code also includes an AVI form to notify the user of an error (see Figure 13.6). By using the cError class module, the code to make this happen is simple: CError.OfficeAssistant CError.ShowAVIForm CError.PlaySound
FIGURE 13.5 Using the Office Assistant to indicate that an error has occurred.
FIGURE 13.6 Using an AVI form to indicate that an error has occurred.
You then can use the cError object to present an error note form or an input box, which enables users to indicate what they were doing when the error occurred (see Figure 13.7). Some users will appreciate the opportunity to provide feedback; others will skip this step. The Error Note form indicates that entry of a note is optional. FIGURE 13.7 A form to allow users to enter a note about the error.
Professional Error Handling CHAPTER 13
359
The user can also be shown a message box with detailed information about the error (see Figure 13.8). FIGURE 13.8 A message box with detailed error information.
FIGURE 13.9 Inform the user that it’s safe to continue working.
Therefore, when an error occurs, users are assisted every step of the way—they aren’t abruptly thrown out of the application. The error handler allows the code to continue safely and will keep the application running. Users are immediately notified of the error and given a chance to provide input and feedback. Finally, users are assured that the error has been forwarded to the developer and that it’s now safe to continue working. By handling errors in this manner, your users will be enamored of your professional application—as you should be!
Discovering Computer Problems Have you ever noticed that certain computers in the office always seem to generate program errors? This issue is perhaps one of the most difficult to troubleshoot. The problem could be hardware related, configuration issues, conflicts with other applications, or something else.
13 PROFESSIONAL ERROR HANDLING
The last step is to provide a message to inform users that the error has been recorded and they can safely continue their work (see Figure 13.9). This is done with a form that closes automatically in three seconds.
360
VBA Unleashed PART IV
Notice that the error handler has several methods to help deal with this issue. The cError object includes properties (as shown in Table 13.1) to capture the computer name, total and available memory on the computer, operating system, and processor type. All this information might be useful in determining why one workstation is having problems when all the other computers in the organization work fine. The most frequent problem is that of memory—when an error occurs, check the total and available memory information.
Error Reporting Now that you are familiar with the end user’s experience, how does a comprehensive error handler help you as the developer? With the cError object, you can compile and analyze the program errors in several ways: Note The query used for the sample report is designed to return the top 56 rows only. You will need to remove the “DISTINCTROW TOP 56” predicate from the SELECT clause in the qryErrorRptLog query to return all rows.
• Error handler Access report—The error handler report in the Access database contains all the information from the error table for all the errors (see Figure 13.10). FIGURE 13.10 Access report of all errors.
Professional Error Handling CHAPTER 13
361
• E-mail individual errors—Every time an error occurs, the details of the error can be e-mailed to you using the Email method (see Figure 13.11). FIGURE 13.11 E-mail of an individual program error.
13
• Save error information in an Access database—By using the WriteErrorToTable method whenever an error occurs, the error is added to the error table in the Access database. • Save error information in a text file—By using the WriteErrorToTextFile method, all errors can be saved in a designated text file for later review. • Save error information on an Outlook calendar—All errors can be added to an Outlook calendar so the data can be viewed with the many built-in Outlook views as well as custom views. The AddToErrorHandlerOutlookCalendar method makes it easy to send all error information to the error handler calendar (see Figure 13.12). By using one or more of these reporting methods, you receive immediate notification when an error occurs. You will have all the information needed to discover and fix the error, as well as reporting tools for historical analysis.
PROFESSIONAL ERROR HANDLING
• E-mail all errors—All error information in the error table can be e-mailed to you as an attachment to an Outlook message using the EmailAllErrors method. The error log table is saved as an Excel spreadsheet and attached to the e-mail message.
362
VBA Unleashed PART IV
FIGURE 13.12 Outlook error handler calendar.
Error Options As you can see, a comprehensive error handler has a great number of options and possibilities. Some organizations will want to use all the options contained in the cError object; others might not. The options are all contained in the tblErrorOptions table. TABLE 13.3
Options Available in the Error Handler
Option
Description
Data Type
AccessErrorTableName
The name of the Access table that includes the error information
Yes/No
AddToErrorHandlerCalendar
Add the error to the error handler Outlook calendar
Yes/No
AVIFileLocation
The full path to the AVI file used in the AVI form
Text
DatabaseName
The name of the database where the error table is located
Text
DatabasePath
The full path to the database
Text
Professional Error Handling CHAPTER 13 TABLE 13.3
363
continued
Option
Description
Data Type
EmailAddress
The e-mail address to use when errors are sent via e-mail
Text
EmailErrors
E-mail the error information when an error occurs
Yes/No
ErrorsToAccessTable
Save the error information in an Access table
Text
ErrorsToTextFile
Save the error information in a text file
Text
PlaySound
Play a sound when the error occurs
Yes/No
ShowAVIForm
Show the user the AVI form when an error occurs
Yes/No
ShowMsgBoxErrors
Show the user a message box with information about the error
Yes/No
ShowOfficeAssistant
Show the user the Office Assistant when an error occurs
Yes/No
SoundFile
The full path to the sound file
Text
UserEnterNoteAboutError
Have the user enter a note about the error
Yes/No
13
Of course, you wouldn’t want users entering data directly into an Access table. The form allows offices to easily update and change error options (see Figure 13.13). frmErrorOptions
At this point, you not only have a powerful error handler, but also an error handler that’s easy for an office to use and maintain.
PROFESSIONAL ERROR HANDLING
Depending on what options are chosen, the error handler will work differently for each office. A table is used for storing the error options information so the code can be “tabledriven.” Rather than shuffle through the code to hard-code new option values, the options can simply be entered in the Error Options table. Because the code looks to the Error Options table for information, it’s much easier to maintain.
364
VBA Unleashed PART IV
FIGURE 13.13 Error options form to select error-handling options.
Windows API Calls All the functionality of the comprehensive error handler couldn’t be programmed within Microsoft Access; a few Windows API calls were necessary. The error handler obtains the name of the user who encountered the error. Rather than have a user log on to the application to get this information, the error handler obtains the user logon name from the Windows API. This Windows API call obtains the logged-on user’s name under Windows 95/98/NT: ‘ Windows API call to get Windows User’s Name. Private Declare Function GetUserName Lib “advapi32.dll” _ Alias “GetUserNameA” (ByVal lpBuffer As String, nSize As Long)
When the error occurs, a sound can be played to alert users. A Windows API call is also needed for this feature: ‘ Windows API call to play a sound Private Declare Function sndPlaySound32 Lib “winmm.dll” Alias _ “sndPlaySoundA” (ByVal lpszSoundName As String, _ ByVal uFlags As Long) As Long
Other Windows API calls can get the computer name, computer memory information, operating system, and computer processor information.
Errors in Various Applications Access applications today incorporate other applications and components. Accordingly, the errors that can occur can originate from many sources. A typical Access application
Professional Error Handling CHAPTER 13
365
might have an error occur in Access itself, or in VBA, DAO, ADO, or other applications used with Automation, such as Microsoft Word. Each application has its own error codes you can return in the error handler. Tip See the Access and Jet database Errors.mdb in the code samples for this chapter. The Access table in this database includes error numbers and error description of Access and Jet Database errors.
Error Handling With Nested Procedures Often in your code, you might have one procedure call another procedure that, in turn, calls a third procedure and so on. If an error occurs, how is it handled?
FIGURE 13.14 Call stack window.
When an error occurs, the error handler in the current procedure handles the error. However, if one doesn’t exist, the error handler in the calling procedure handles the error. In other words, VBA searches backward through the call list until an error handler is found.
Advanced Error Topics Now that you’ve learned how to handle syntax errors, logic errors, and runtime errors, we’ll look at some more advanced error-handling issues.
13 PROFESSIONAL ERROR HANDLING
A call list is maintained automatically to keep track of all procedure calls (see Figure 13.14). You can review the call list at any time in the Visual Basic Editor by choosing Call Stack from the View menu.
366
VBA Unleashed PART IV
Error Event Procedures Access forms and reports have an OnError event procedure that’s useful to display a custom error message when an Access error occurs. Two arguments are passed to the Error event procedure: •
DataErr
•
Response
is the error number returned by the Err object. By using this argument, you can respond to the particular error type. If, for example, DataErr is 11, a division-by-zero error occurred. determines whether an error message is displayed. By using this argument, you can control how an error is reported. To ignore the error and supply a custom error message, use the constant acDataErrContinue. To display the default Access error message, use the constant acDataErrDisplay.
A typical code example would look something like this: Private Sub Form_Error(DataErr As Integer, Response As Integer) Dim strMessage As String If DataErr = 11 Then Response = acDataErrContinue strMessage = _ “Check the value, you have divided a number by zero.” MsgBox strMessage End If End Sub
On Error GoTo 0 disables error handling within a procedure. The Err object is reset as though the Clear method of the Err object were called (Err.Clear). The Err.Clear method was discussed earlier in this chapter. On Error Goto 0
On Error Resume Next The On Error Resume Next statement can be used to implement silent error handlers. Refer to the section “Silent Error Handlers,” earlier in this chapter, for an example of when you might want to Resume Next after an error. Unless you are sure why you are resuming after an error without an error handling block, don’t use Resume Next. Resume Next will not branch to an error handling block, nor will it notify you or the user that an error ever occurred.
Professional Error Handling CHAPTER 13
367
AccessError Method The AccessError method can be used to return a description of a Microsoft Access error. For example, typing ? AccessError(11) in the Immediate window returns Division by zero (see Figure 13.15). FIGURE 13.15 Using the AccessError
method in the Immediate window.
Useful Error Functions The following are useful error functions: •
IsError
•
CVErr
is used to determine whether a variant is an error data type (returns a Boolean). converts a value to an error data type, assigning it to a variant variable.
In the Visual Basic Editor, some option settings regarding how Access handles errors can be found on the General page of the Tools, Option menu (see Figure 13.16): • Break on All Errors—When an error occurs, the code breaks on that line whether or not there’s an error handler. This choice is best for debugging your application, but be sure to turn it off before distributing your application. • Break in Class Module—When an error occurs, the code breaks on that line only in class modules, if there’s no error hander. • Break on Unhandled Errors—When an error occurs, the code breaks on that line in all procedures that do not contain an error handler. Tip You can disable the Break on All Errors setting in a procedure by placing the following code at the top of the procedure: Application.SetOption “Break On All Errors”, False
PROFESSIONAL ERROR HANDLING
Settings Error Trapping Options
13
368
VBA Unleashed PART IV
FIGURE 13.16 Setting errortrapping options.
Summary Error handling is critical. A professional application that elegantly handles program errors ensures that a user’s experience is pleasant. Error handling also is the best way to save you time in finding and correcting program errors. Hopefully, the comprehensive error handler included in the chapter code will be of great use to you.
CHAPTER 14
Application Optimization
IN THIS CHAPTER • Strengthening the Foundation: Hardware and Windows Optimization 371 • Installing the Application for Optimal Performance 373 • Optimizing the Configuration of the Jet Database Engine 374 • Tools to Measure Performance • Looking Behind the Scenes
381
383
• Optimizing the Database from the Start 384 • Boosting Query Performance • Getting Forms to Run Faster • Writing Fast Code
400
• Coding Tips and Hints
402
387 395
370
VBA Unleashed PART IV
Application optimization is a topic of endless conversation and debate among developers. Everyone wants optimal solutions, but exactly what does “optimal” mean? Some define optimal as raw speed, so the fastest technique would always be the optimal one. Others contend the optimal solution is the one that provides the most reliability, even if the approach is ponderously slow and overly cautious. Still others consider maintainability and extensibility the optimal goal. Who is right? The answer is, as it is so often in life, they are all right and they are all wrong. Optimization is really a balancing act between all three of these goals. An optimal solution is certainly one that provides adequate speed to the user. Users usually reject a slow application. However, if that solution proves to be just a fast way to scramble and lose data, the developer made an unwise trade-off. Similarly, if an application has acceptable speed and demonstrated reliability, the chances are good that users will want the application revised in the future. However, unconventional design decisions and unorthodox execution of that design can make this otherwise well-regarded application difficult to enhance and extend. Applications are like cars in some respects. Few of us want to get behind the wheel of an extremely fast car that handles poorly and has weak brakes. Likewise, we want the car that starts every day and takes us where we need to go with as little fuss as possible. Finally, the repair and maintenance bills should not break the bank. Though this chapter concentrates on optimizing your application for speed, please keep in mind the need for your applications to be reliable, stable, and extensible as well. This chapter explores many techniques for optimizing your applications. Almost all of them pose limitations or impose trade-offs, and some of them are actually counterproductive at times. Even the best ones might not be applicable to your particular problem. Some techniques will cancel out others. Taken as a whole, however, judiciously applying the lessons of this chapter to your development efforts should improve your applications’ performance in some way. The only way to find if these techniques will work for you is to experiment with them. The results will often surprise you. An approach that worked wonders one time might have no discernible effect in another instance. Optimization is complex because the environment in which they are applied is complex and ever-changing and because the goal of true optimization is a truly elusive one. There are hundreds of things to discuss when writing about optimization. In the interest of order, this chapter will cover the topics from the most removed to the most intimate, from the hardware and operating system to different coding techniques.
Application Optimization CHAPTER 14
371
Strengthening the Foundation: Hardware and Windows Optimization Every part of Access 2002 lends itself to application tuning, but no application tweak will amount to much if the machine running the application is antiquated or has insufficient memory. It will usually be cheaper to purchase hardware in order to improve performance than to pay for software development to accomplish the same result. Hardware is cheaper than software and hardware upgrades benefit all the compatible software running on a machine. It should come as no surprise that an Access application (or any application for that matter) runs faster on a faster machine. If you have the opportunity to upgrade your application’s host machine, take it. Microsoft’s published minimum requirements for running Access are exactly that: minimum requirements. Developers will quickly find that any serious attempt to run an Access 2002 application on a machine with those minimum requirements disappointing, at best. The platform for running the applications should really meet a minimum standard of • 133MHz Pentium processor • 32MB of RAM (especially when running on NT)
• Regardless of how much RAM the users’ PCs have, it is still possible to run out of memory. Access, like any serious database, needs lots of memory to operate well. Steps should be taken to ensure as much memory is available to your application as possible. • To this end, remove any screen savers, wallpaper (unless it’s just a tiled bitmap), background pictures, and anything else that might be running needlessly. Only applications that are necessary should be taking cycles from the processor when your database is running. The more memory available to an Access application, the faster it will run.
14 APPLICATION OPTIMIZATION
• If you have to choose between upgrading the processor or installing more RAM, choose the RAM. With the worldwide collapse of RAM prices, adding memory is the cheapest way to boost the performance of a PC. Keep in mind that the requirements for a development machine are much, much higher. A developer should have at least a 200MHz Pentium (II or III) with 64–128MB of RAM, among other things.
372
VBA Unleashed PART IV
• Don’t use RAM disks. There is no place in modern, 32-bit operating systems for them. • Empty the recycling bin and delete your temporary files (especially Internet files and email!) regularly. Databases need lots of disk space to operate, and these files can consume acres of space before you realize it. • Take advantage of disk defragmentation utilities. Depending on the operating system used, defragmentation might be automatic. Computer files are not stored in one piece, especially on FAT, VFAT, and FAT32 filesystems. As you create, edit, save, and delete files, your PC spreads them out over any available space. The .MDB file is no different. If your hard drive is fragmented, even simple searches take much longer than necessary because a drives disk heads must move all over the drive to perform a search. • Avoid disk compression (including NTFS compression). Running the database on a compressed drive will have a significant negative effect on the performance of your application. • Buy more hard drive space. Disk space, like RAM, is cheap. Depending upon what your application does, you might need available free space of as much as 5–10 times the size of your .MDB file. A lack of available space will hinder the performance of large queries, lengthy transactions, compilation, maintenance, imports, and action queries. Hard drive prices have also fallen dramatically in recent years. • Disable Outlook’s Journal feature. It generates a log entry every time you open and close an application. This log can get very large and steal disk space and cycle time from your application. • Reclaim leaked memory. Applications are very good at claiming memory, but they are not as good at giving it back to you. Over time, many applications, including Access, will reduce the real amount of memory available. Closing Access occasionally will allow Windows to reallocate this memory for you. • Install Windows and Office locally. Do not run them over the network. It seems strange to write this now, but there are places out there that still do otherwise. • Adjustments to the swap file (virtual memory) can also help enhance the performance of your application. When Access runs out of RAM, it borrows drive space from the virtual memory allocation and treats this space like additional RAM. If there is not enough virtual memory, Access must continually read and write to the disk in order to perform its operations. Increasing this allocation can boost the performance of your database. However, keep in mind that reading and writing to disk is astronomically slower than working in RAM. Increasing the size of the swap file will only help performance if the disk is defragmented.
Application Optimization CHAPTER 14
373
Tip Make sure the swap file is not running on a compressed drive or partition. Dealing with the compression will slow the reading and writing of the temporary files that will need to be written to this area.
Many of the preceding suggestions might be out of your control, but when developing an Access solution, you should consider and propose them when necessary. As a developer of an Access database, you have more control over whether to use the remaining optimization possibilities. Tip Many developers have machines that are much faster than the machines their applications will run on. Be sure to test your application on a machine comparable to your users’ machines. If the hardware configurations vary, develop for the lowest common denominator machine.
Installing the Application for Optimal Performance The way you configure your application, how it uses certain files, and especially how the application is installed affect its performance. The following guidelines should help your application behave well and make the rest of the chapter useful.
• Use a current version of the workgroup file (system.mdw). Although it is possible to use earlier versions of the workgroup file with your application, the current version provides better performance. • Compact the database regularly. Compact it after large imports, deletes, and updates as well. Compacting the database reclaims the empty space between data
APPLICATION OPTIMIZATION
• Separate the data from the application. Create one .MDB file containing all the tables, place it on the network, and install another .MDB file containing the queries, forms, reports, and so on, on the desktop. It will improve performance, multiuser access, and maintainability. Make this type of application partitioning standard practice. Of all the suggestions in this chapter, you should disregard this one only for a very compelling reason.
14
374
VBA Unleashed PART IV
pages (slack) and recalculates the database statistics used in query optimization (more on this subject later). Compacting the database also frees disk space for other processes. Access 2002 can automatically compact the database when the database closes. To enable this feature, check Compact on Close in the Tools, Options dialog box on the General tab. Some developers have found that earlier versions of Access needed to be compacted twice in order to really benefit from the process. Try launching your database from a separate MDB file and have this launcher application perform a compact when it starts your database. When combined with the Compact on Close option, you will have a double compact on every use. • Install an .MDE version of your application whenever possible. There are a couple of advantages to this. First, an .MDE requires compiled modules, which run faster than uncompiled modules. The .MDE also needs less RAM and less drive space than an .MDB, thus enhancing performance. Tip Before installing an .MDE, make sure users understand that they will be unable to modify the application in this form. Because of Access’ overwhelming popularity, many users insist on having the ability to modify the application later. You should also thoroughly test the application because MDEs kick out error messages that are really difficult to understand.
Optimizing the Configuration of the Jet Database Engine A well-tuned machine and an optimized database installation will not yield all the possible performance improvements if you have not optimized the application itself. The actual database application consists of several things. Most important among them is the Jet database engine. Jet is at the heart of almost everything that takes place in an Access database application, and you can optimize it just as you can optimize other features of Access. However, optimizing Jet should come after you have developed most, if not all, of the application, and have spent sufficient time evaluating different optimization techniques within the application.
Application Optimization CHAPTER 14
375
Jet 4.0, which comes with Access 2002, has some advantages over its predecessors. Features such as new data types, text compression, indexing of memo fields, ANSI SQL92 implementation, SQL security management, greater flexibility with foreign key indexes, a vastly improved replication scheme, and improved locking abilities promise to improve performance in many ways. This book fully explains these exciting features in other chapters. You should see those chapters for more information. Here we will only treat their performance implications. The Jet engine takes care of many optimization issues without any user or developer intervention. Of all the complex things that Jet does, you can only see about 15 of them by way of the Registry. These settings are not very intuitive, so you should tinker with them with care. Changing a setting might have the opposite effect you intended. Even if your application seems to run faster, you might have fatally affected other important things like stability or concurrency management. See Table 14.1 for a listing of the available Jet Registry settings.
Safely Changing the Jet Settings There are three ways to change the Jet registry settings: • Directly edit the default values through RegEdit.exe. • Create a user profile to override the default settings and call that user profile with the /profile command line option. • Enable your application to temporarily override the default settings by using the SetOption method of the database object.
There is no need to employ API calls to temporarily override these Registry values. The database engine provides a method and it takes two arguments. The syntax is as follows: DBEngine.SetOption ConstantNameofSetting, value
14 APPLICATION OPTIMIZATION
The third option is preferred. Not only is it simple to use, it also enables you to tune each application separately. You can even change the settings at different times within the application to really optimize performance. This technique never changes the default settings, so new projects start from a common baseline.
Description In an exclusive environment, the number of milliseconds Jet must wait before it commits implicit transactions. When a value is entered here, the SharedAsyncDelay and ExclusiveAsyncDelay are disabled. This sets the number of milliseconds before starting asynchronous writes, provided no pages have been added to the cache. Determines whether the system must wait for implicit transactions to be completed before continuing with processes. If set to No, Jet will process implicit transactions asynchronously. Number of milliseconds Jet will wait before retrying lock attempts. Number of repeated attempts Jet will make to lock a page.
ExclusiveAsyncDelay
FlushTransactionTimeout
ImplicitCommitSync
LockDelay
LockRetry
Registry Settings for Jet 4.0
Key
TABLE 14.1
20
100
No
500
2000
Default Value dbExclusiveAsyncDelay
Constant for SetOption Method
dbLockRetry \HKEY_LOCAL_ MACHINE\SOFTWARE\ MICROSOFT\JET\4.0\ ENGINES\JET 4.0
\HKEY_LOCAL_ dbLockDelay MACHINE\SOFTWARE\ MICROSOFT\JET\4.0\ ENGINES\JET 4.0
\HKEY_LOCAL_ dbImplicitCommitSync MACHINE\SOFTWARE\ MICROSOFT\JET\4.0\ ENGINES\JET 4.0
\HKEY_LOCAL_ dbFlushTransactionTimeout MACHINE\SOFTWARE\ MICROSOFT\JET\4.0\ ENGINES\JET 4.0
\HKEY_LOCAL_ MACHINE\ SOFTWARE\ MICROSOFT\JET\ 4.0\ENGINES\JET 4.0
Key Location
376 VBA Unleashed PART IV
0
Jet’s cache size in kilobytes.
Maximum number of 9500 locks permitted for a single transaction. If the transaction requires more locks, the transaction is then split into multiple transactions, which can be committed individually and partially. This setting can be used to prevent some problems that occur with Novell NetWare 3.1.
MaxBufferSize
MaxLocksPerFile
APPLICATION OPTIMIZATION
PagesLockedToTableLock (New to Access 2000) Provides 0 exclusive access to a table for updates by providing programmatic control over the number of page locks before which Jet will attempt to lock the entire table. A setting of 50 will cause Jet to attempt a table lock at the 51st page lock. If
Default Value
Description
continued
Key
TABLE 14.1
Constant for SetOption Method
\HKEY_LOCAL_ MACHINE\SOFTWARE\ MICROSOFT\JET\4.0\ ENGINES\JET 4.0
\HKEY_LOCAL_ dbMaxLocksPerFile MACHINESSOFTWARE\ MICROSOFT\JET\4.0\ ENGINES\JET 4.0
dbMaxBufferSize \HKEY_LOCAL_ MACHINE\SOFTWARE\ MICROSOFT\JET\4.0\ ENGINES\JET 4.0
Key Location
Application Optimization CHAPTER 14
377
14
Determines whether Jet will recycle OLE, memo, and binary pages. In a shared environment, the number of milliseconds Jet must wait before it commits implicit transactions. Number of background threads in use by Jet.
Determines whether or not the system waits to complete write operations before continuing with processes. If set to No, the
RecycleLVs
SharedAsyncDelay
Threads
UserCommitSync
the table lock fails, Jet will try again at the 101st page lock. Zero disables the feature.
Description
The amount of time that a non–read-locked page is held in the cache before it is refreshed. The amount of time is measured in milliseconds.
continued
PageTimeout
Key
TABLE 14.1
Constant for SetOption Method
dbPageTimeout \HKEY_LOCAL_ MACHINE\SOFTWARE\ MICROSOFT\JET\4.0\ ENGINES\JET 4.0
Key Location
Yes
3
50
\HKEY_LOCAL_ dbUserCommitSync MACHINE\SOFTWARE\ MICROSOFT\JET\4.0\ ENGINES\JET 4.0
N/A \HKEY_LOCAL_ MACHINE\SOFTWARE\ MICROSOFT\JET\4.0\ ENGINES\JET 4.0
\HKEY_LOCAL_ dbSharedAsyncDelay MACHINE\SOFTWARE\ MICROSOFT\JET\4.0\ ENGINES\JET 4.0
0 (disabled) \HKEY_LOCAL_ dbRecycleLVs MACHINE\SOFTWARE\ MICROSOFT\JET\4.0\ ENGINES\JET 4.0
5000
Default Value
378 VBA Unleashed PART IV
1
access_ path\ system. mdw 1
1
Jet engine processes explicitly defined transactions asynchronously. Determines the sort order for the primary keys. A setting of 1 makes Jet reorder records by their primary key value. A setting of 0 leaves the records in their order of entry (natural order). Full path and name of workgroup file.
Allows or prevents the compression of stored text and memo field data. Compression is permitted by default. Prevents execution of potentially destructive shell commands from VBA code embedded in queries.
`
CompactByPKey
SystemDB
PrevFormatCompact WithUNICODE Compression
SandBoxMode
Default Value
Description
continued
APPLICATION OPTIMIZATION
Key
TABLE 14.1
Constant for SetOption Method
N/A \HKEY_LOCAL_ MACHINE\SOFTWARE\ MICROSOFT\JET\4.0\ ENGINES
\HKEY_LOCAL_ MACHINE\SOFTWARE\ MICROSOFT\JET\4.0\ ENGINES
\HKEY_LOCAL_ N/A MACHINE\SOFTWARE\ MICROSOFT\JET\4.0\ ENGINES
\HKEY_LOCAL_ N/A MACHINE\SOFTWARE\ MICROSOFT\JET\4.0\ ENGINES
Key Location
Application Optimization CHAPTER 14
379
14
When created by the developer N/A and set to On (case-sensitive), Jet appends output to a text file named showplan.out in the current directory. As queries are executed, the execution plans are logged to this file. Disable after development.
JETSHOWPLAN
Default Value
Description
continued
Key
TABLE 14.1
Constant for SetOption Method
\HKEY_LOCAL_ N/A MACHINE\SOFTWARE\ MICROSOFT\JET\4.0\ ENGINES\Debug
Key Location
380 VBA Unleashed PART IV
Application Optimization CHAPTER 14
381
For example, the performance of a batch of updates might benefit from having Jet wait longer before committing the implicit transactions. The code to temporarily lengthen the SharedAsyncDelay wait period to 1 second (1000 milliseconds) would be DBEngine.SetOption dbSharedAsyncDelay, 1000
This setting remains in effect until your application specifically changes it with another SetOption method, the DBEngine goes out of scope, or the application terminates. SetOption does not write values to the Registry. You cannot override the default settings for SystemDB, CompactByPkey, or Threads with the SetOption method. Note If your application uses an ODBC data source, review the Registry settings under \HKEY_LOCAL_MACHINES\SOFTWARE\MICROSOFT\JET\4.0\ENGINES\ODBC.
Tools to Measure Performance It is important to know whether the cumulative effect of your efforts have helped or hindered your application. You can employ several tools to monitor and evaluate the relative performance of one technique over another. The first is a timer to measure the amount of time a process takes to execute. The second is an undocumented function to count disk, cache, and lock operations. The third is Access’s ability to publish a query execution plan for your review. You can apply the first two tools against almost any technique discussed in this chapter. Query execution plans are only relevant to optimizing queries.
14 APPLICATION OPTIMIZATION
Although VBA offers a Timer() function, it might not be sufficiently accurate for evaluating your optimization efforts because its measurements are too coarse. The VBA Timer function records the passage of time in seconds since midnight. Because it measures time in a single-precision floating point value, it might not be accurate enough for your needs—especially for elapsed time under one second. Many developers have also used the GetTickCount function. Although this function appears to return time in milliseconds, because it is tied to the PC’s hardware timer, its result is actually measured in increments of 1/18 of a second and not in true milliseconds. The Windows API offers a timer that actually tracks the time in milliseconds. The timeGetTime() function measures the passage of time from the moment Windows was started. Because it uses a different hardware timer than GetTickCount, it returns time measured with millisecond accuracy.
382
VBA Unleashed PART IV
Using timeGetTime, you can insert a line of code before and after any critical executions and get a very accurate measurement of how long an action has taken to complete. To use this API call, you need two things: a declaration of the function, and a global variable to hold the starting time of the timer. In the declarations section of a module, enter the following three lines: Private Declare Function a2ku_apigettime Lib “winmm.dll” _ ➥Alias “timeGetTime” () As Long Dim lngstartingtime As Long
Now, create a subroutine to start the clock and a function to stop the clock and return the elapsed time. Sub a2kuStartClock() lngstartingtime = a2ku_apigettime() End Sub Function a2kuEndClock() a2kuEndClock = a2ku_apigettime() - lngstartingtime End Function
Listing 14.1 illustrates how to use these procedures to time a query’s performance. LISTING 14.1
Timing the Execution of a Query
Sub QueryTimer(strQueryName As String) Dim db As Database Dim qry As QueryDef Dim rs as Recordset Set db = CurrentDb() Set qry = db.QueryDefs(strQueryName) ‘Start the clock a2kuStartClock Set rs = qry.OpenRecordset() ‘Stop the clock and print the result to the debug window Debug.Print strQueryName & “ executed in: “ & a2kuEndClock & _ “ milliseconds” rs.Close End Sub
Application Optimization CHAPTER 14
383
Note For the most accurate measurement, be sure to place the a2kuStartClock call and the a2kuEndClock call as tightly as possible around the procedure you are reviewing.
Looking Behind the Scenes Although timing is certainly important, by itself it says little about performance during development. Development usually takes place on a fast machine, running against a smaller set of test data than the real application will use. Furthermore, a timer cannot address how your application will run on a different machine with less memory, an older CPU, or a slower drive. You have to dig deeper. To look behind the scenes of your application, use another undocumented function: ISAMStats. ISAMStats is undocumented and unsupported, so use the information it provides as a general guide only. It provides measurements of six important performanceaffecting operations, tallying disk reads, disk writes, cache reads, read-ahead cache reads, lock placements, and lock releases. The syntax is very simple: DBEngine.ISAMStats(option,[reset])
There are six options for the option argument: Argument for option 0 1
3 4 5
The optional reset argument enables you to reset the individual counts to zero. In order to use the function to measure performance, you must either subtract one reading from an earlier reading, or reset the meter to zero and then perform your operation. Listing 14.2 suggests one way to employ the ISAMStats function to measure performance.
14 APPLICATION OPTIMIZATION
2
Description Disk writes Disk reads Cache reads Cache reads (from read-ahead cache) Locks placed Locks released
384
VBA Unleashed PART IV LISTING 14.2
One Way to Use ISAMStats to Gauge Performance
Sub PrintStats() Dim i As Integer Debug.Print Debug.Print Debug.Print Debug.Print Debug.Print Debug.Print Debug.Print
“Disk Reads: “ & ISAMStats(0, False) “Disk Writes: “ & ISAMStats(1, False) “Cache Reads: “ & ISAMStats(2, False) “Read-Ahead Cache Reads: “ & ISAMStats(3, False) “Locks Placed: “ & ISAMStats(4, False) “Locks Released: “ & ISAMStats(5, False)
For i = 0 To 6 ISAMStats i, True Next End Sub
gives you some idea why one technique might be faster than another technique. This information might also help you make an informed choice between two approaches that take the same number of milliseconds to execute. By looking under the hood, you can see how a different hardware configuration or a larger set of data might adversely affect one approach’s performance.
PrintStats
The QueryTimer and the PrintStats functions are only useful if you use them to compare one tuning technique to another. Furthermore, you will only get reliable results if you average their output over many tests under varying conditions. You can easily combine these two functions into one function to conduct and report on such iterative tests. These two functions reappear later in the chapter to illustrate how to use them and to show how different the two approaches really are.
Optimizing the Database from the Start Relational databases, including Access, rely on tables. Your application’s performance is directly related to the design of a database’s tables and the relationships between those tables. If the tables are not properly normalized, indexed, and related to each other, every query and code routine will be subject to a time-consuming workaround. The greatest thing you can do for the performance of you database is to design the tables correctly.
Application Optimization CHAPTER 14
385
Designing Data Tables for Performance Aside from general rules of normalization, keep the following points in mind when building tables. • When working with linked tables, make the links permanent. • Using a simple subdatasheet in a table, if the subdatasheet is another table and the master/child links use indexed fields, has a negligible negative effect on performance; however, a more complex subdatasheet construct might compromise performance. Use subdatasheets in queries instead of tables. This way, you can use them only when you need them, and not every time you open a table. • Use input masks, lookups, and validation rules only where they are needed: forms. Keep your tables as simple and light as possible. • Do not create data fields larger than necessary. Access provides room for data by that data item’s field size. You can waste a lot of space (and time) by using data types or property settings too large for the data contained in those fields.
Normalizing Data to Enhance Performance This book has already covered normalization, so this chapter simply reiterates a few points. • A normalized database saves disk space because it eliminates repeated data. • A normalized database uses fewer data pages and fewer index pages, making searches less time-consuming. • A normalized database makes it more difficult to have conflicting or confusing data because there’s little repetition.
• The data repetition rule can be broken in some cases, too, when it involves small amounts of incidental data. Often there are extraneous bits of information relating to a customer or a product that you should, if strictly following the rules, store in different tables. An example of this would be day and evening telephone numbers, fax numbers, cellular telephone numbers, pagers, email addresses, and so on.
14 APPLICATION OPTIMIZATION
• When does breaking normalization make sense? It makes sense to base parts of your application on non-normalized tables if those tables are temporary tables, the creation of which is time-consuming. However, the data should be stored permanently in a normalized structure. Keep in mind that you will have to create the routines to update the normalized structure from actions taken against the non-normal structure—this can compromise performance. Break normalization rules only as a last resort.
386
VBA Unleashed PART IV
• Break normalization rules when it makes sense. Breaking normalization can, at times, improve performance for parts of your application. But keep in mind that it carries a high cost in terms of reliability, data integrity, and maintainability. The ideal normalization scheme would have you create a contact table with a customer ID field, a contact type field, and another field to contain the telephone numbers and email addresses. This is probably overkill and the added integrity and flexibility in many situations does not justify the cost. It is reasonable to keep this information in the customer table and take advantage of the performance gain. However, the general rule is to normalize as much as possible. Jet expects this and performs best with normalized data.
Creating Indexes to Speed Queries • Indexes can speed data retrieval performance many times over. • Indexes can also slow updates and data entry, so only create indexes that you need. • Create a primary key that means something to your data and to your users. Allowing Access to create your primary key by inserting an AutoNumber field is nearly useless from a performance standpoint. Unless your users have no other means of identifying their data records, use something more meaningful. Try the telephone number, Social Security Number, account number, vendor code, and so on. For an index to boost performance, you must actually query against it. • Accordingly, index all the fields against which your application exercises criteria. Creating indexes on multiple fields in your tables makes it easier to optimize queries created later in the development process. • Index fields on both sides of an expected join. Because the Order Number fields relate to both the Order Details and Orders tables, both tables need an appropriate index. If you run many reports by order date, index those fields, too.
Establish Relationships Early to Improve Performance Establish the relationships between tables in the Relationships window. When you create a relationship here, you have the ability to define its properties. Doing so also alerts Jet to its existence, so Jet will use that information to create a more effective optimization plan when you query the data. This improves performance. Put simply, normalize your data, create indexes where they are necessary, be economical with data types and sizes, and keep tables simple. They exist to store data, not to present it.
Application Optimization CHAPTER 14
387
Boosting Query Performance Although users might never see a query in your application, the queries do most of the work. However, not all queries are created equal. Even if you have taken all the steps necessary to normalize your data and have created all the indexes you need, you could have queries that do not perform as fast as they could. You could even have two queries with identical resultsets but different performance measurements. To understand how to optimize queries, you need to understand how Jet handles them. Query execution involves four steps: 1. Definition—A SQL statement is created using one of several tools. 2. Compilation—The SQL string is tokenized or broken down into parts. 3. Optimization—Using a cost-based algorithm, Jet formulates and tests different ways to yield a resultset that satisfies the underlying SQL statement, resulting in an execution plan. 4. Execution—Using the optimal plan, Jet delivers a resultset to the user. You can define a query through the QBE grid; a SQL string executed in code; a SQL string in the rowsource property of a form, report, or control; or any means that creates a SQL statement. Jet breaks the SQL into tokens and organizes these tokenized parts into a hierarchical internal structure. The parts are very similar to the keywords of a SQL statement. The base tables used by the query (From) form the foundation. The columns of the result (Select) set are next. The criteria or restrictions (Where) the query must satisfy are next. How the base tables relate (Join) follows. Finally comes how the resultset should be sorted (Order by). The resulting structure is used in the optimization phase.
Jet uses one of three ways to get rows of data from tables: • Scan—This is the most expensive approach. In a scan, Jet must read every row of information without using an index. A query forces Jet to scan a table if: The query is restricted on a nonindexed field. The query’s criteria qualify a large percentage of the rows in the table. • Index—Jet employs the index of the table to read table rows. Although Jet might read a data page more than once, it is still a much faster approach than a scan.
APPLICATION OPTIMIZATION
The optimization phase is the most complex step. Jet evaluates and calculates the cost for each possible method to complete the query. It analyzes the query by accessing the base tables and by exploiting the joins between them.
14
388
VBA Unleashed PART IV
• Rushmore Optimization—Available when restrictions are set against more than one index in the query, Rushmore enables Jet to read far fewer data pages, even none at all in some cases. When Rushmore optimization is used, Jet has to read only index pages, which is extremely efficient. Obviously, avoid scans when possible and try to make the best use of indexes. But how do you make sure that the best choice, Rushmore optimization, is working for your query? There is no way to turn Rushmore on or off, and there’s no obvious indicator to check either. Fortunately, it is always on, but only certain kinds of queries take advantage of it. The following three conditions must exist for queries to benefit from Rushmore optimization. 1. Queries must involve multiple indexes. 2. Criteria restrictions must be used against at least two indexed fields. 3. The criteria statement must use the indexes in one of three ways: • Index intersection—If a criteria expression uses the AND operator, Jet can use Rushmore optimization on the following set of restrictions because both fields are indexed. WHERE CompanyName=’Ernst Handle’ And City=’Graz’
• Index union—If a criteria expression uses the OR operator, Jet can use Rushmore optimization on the following set of restrictions because both fields are indexed. WHERE CompanyName=’Ernst Handle’ Or City=’Graz’
• Index counts—If an aggregate query returns only a record count, Jet can use Rushmore optimization even if there are no restrictions set in the Where clause. SELECT Count(*) FROM Customers;
Be sure to create multiple indexes on all tables that could benefit from them. Try to express queries in ways that cause intersections or unions of those indexes. Those two design tips will take care of the base table operations of the query execution plan. Once Jet figures out how best to access the data in the tables, it then determines how the tables relate to each other. This is the join strategy phase of the optimization. You can anticipate the execution plan’s join strategies by learning what the strategies are, how they work, and how to recognize them. Table 14.2 provides this information.
Application Optimization CHAPTER 14 TABLE 14.2
389
Join Strategies: How They Work and How to Know Them
Join Strategy
How It Works
How to Recognize
Acceptable Use
Index-Merge
Indexes do most of the work.
Indexes used on both Whenever possible. sides of the join. At least one of the indexes does not allow nulls (primary key). All tables must be native Jet format.
Index
The first table is scanned, and then rows in the second table are found by using an index.
Index on the related field(s) of the second table. Nulls allowed in these indexes. Restrictions do not use indexes.
Merge
Both tables scanned at the same time.
Two tables sorted by Both tables are large their joined fields. Data and sorted by their from both tables appears related fields. in resultset.
Lookup
Second table scanned and sorted before join.
No index on the tables’ joined field(s).
When the second table is small and there is no index on the second table’s related field.
Nested iteration
Row-by-row iteration through each table in the relationship.
No indexes on either side of join.
Only on very small tables and when there is no alternative.
If there are few records in second table, if its records do not appear in resultset, or if the first tables criteria is very restrictive.
• The number of records in each base table • The number of data pages used by the base tables • The location and type of table—local ISAM or ODBC • The selectivity of the tables’ indexes—duplicates or nulls allowed • The number of index pages
APPLICATION OPTIMIZATION
Jet selects one plan over another depending on the following factors:
14
390
VBA Unleashed PART IV
Evaluating the Type of Resultset for Optimum Performance Jet next considers the resultset requested. For instance, to present a dynaset, it might employ a plan that quickly presents the first page of data, even if that plan is slow at presenting the remaining records. To create a dynaset, Jet creates a keyset, a set of unique key values pointing to rows in the underlying base tables. Initially, only the keys are retrieved. It retrieves the rest of the record if and when the user needs them. To create a snapshot, however, Jet loads all records and columns for the resultset before presenting it. If the entire snapshot cannot fit in memory, it will overflow to the swap file, negatively affecting performance. In this case, you might get better performance from a large dynaset because complete records are retrieved as needed. To summarize the process, Jet evaluates and scores its base table access options. Next, Jet evaluates and scores its join options available to it and scores them. Finally, Jet evaluates and scores the requested recordset type. After analyzing various combinations of these three scores, Jet chooses an execution plan. Compilation and optimization happens the first time you create or edit a query and run it. It is possible for a query to decompile. If you modify a query and save it but do not run it, it will remain decompiled. Also, if you modify table indexes or the schema of your data, your queries might no longer be optimized. Always open the queries in Design view, save them, and then run them before delivering your applications. This will ensure that your queries compile. Jet performs complex cost assessments in order to optimize queries, including reviewing the database’s statistics. These statistics include information telling Jet how many data pages, index pages, tables rows, and so on, exist in the database. Abnormal database termination, rolled back transactions, or an uncompacted database can skew these statistics, so always compact the database before an optimization session. As explained previously, Jet creates an execution plan for each query. To see the execution plan, create the following registry key: \HKEY_LOCAL_MACHINES\SOFTWARE\MICROSOFT\JET\4.0\ENGINES\
Underneath this key, add a new string value named JETSHOWPLAN (uppercase is mandatory), and set it to ON. Exit and restart Access to reinitialize Jet and cause it to reread its registry entries. The JETSHOWPLAN registry entry causes Jet to append its query execution plans to a text file named showplan.out in the current directory. Many of the topics discussed so far appear in that query optimization plan. You cannot alter the
Application Optimization CHAPTER 14
391
plan, except by altering your data schema, query construction, or query restrictions, but this is the point of examining the execution plan. The more detailed the plan, the better. Listing 14.3 shows the execution plan for a query in the Northwind database, Quarterly Orders. LISTING 14.3 Execution Plan for a Query That Takes Advantage of Rushmore Optimization --- Quarterly Orders --- Inputs to Query Table ‘Customers’ Using index ‘PrimaryKey’ Having Indexes: PrimaryKey 91 entries, 1 page, 91 values which has 1 column, fixed, unique, primary-key, no-nulls PostalCode 91 entries, 1 page, 87 values which has 1 column, fixed CompanyName 91 entries, 3 pages, 91 values which has 1 column, fixed City 91 entries, 1 page, 69 values which has 1 column, fixed Table ‘Orders’ - End inputs to Query 01) Restrict rows of table Orders using rushmore for expression “Orders.OrderDate Between #1/1/98# And #12/31/98#” 02) Outer Join result of ‘01)’ to table ‘Customers’ using index ‘Customers!PrimaryKey’ join expression “Orders.CustomerID=Customers.CustomerID” store result in temporary table
APPLICATION OPTIMIZATION
You can see the base table section, with the indexes analyzed and the number of entries, databases, index pages, and values being assessed. Notice also that Rushmore can optimize this query because of the index over the Order Date field of the Orders table. The plan recognizes the join between the Orders table and the Customers tables as an outer join. The indexes and Rushmore make this a well-optimized query. If you’re not sure how well optimized your query is, you can check its execution plan. The JETSHOWPLAN registry value is barely documented and unsupported. Certain queries will not produce plans, and some plans are wrong, but you can still make cautious use of it. Consider, for example, the poorly written query shown in Listing 14.4.
14
392
VBA Unleashed PART IV LISTING 14.4
A Poorly Optimized Query
SELECT Customers.CustomerID, Customers.CompanyName FROM Customers INNER JOIN Orders ON Customers.CustomerID = Orders.CustomerID WHERE ((Not (Orders.ShipCountry)=”USA”));
Jet’s execution plan makes clear that Jet can do little to optimize this query. --- Customers with Shipping Address Outside USA --- Inputs to Query Table ‘Customers’ Using index ‘PrimaryKey’ Having Indexes: PrimaryKey 91 entries, 1 page, 91 values which has 1 column, fixed, unique, primary-key, no-nulls PostalCode 91 entries, 1 page, 87 values which has 1 column, fixed CompanyName 91 entries, 1 page, 91 values which has 1 column, fixed City 91 entries, 1 page, 69 values which has 1 column, fixed Table ‘Orders’ - End inputs to Query 01) Restrict rows of table Orders by scanning testing expression “Not Orders.ShipCountry=”USA”” 02) Inner Join result of ‘01)’ to table ‘Customers’ using index ‘Customers!PrimaryKey’ join expression “Orders.CustomerID=Customers.CustomerID”
The clue to this query’s weakness is in how it handles the restriction Not Orders.ShipCountry=”USA”. Because the restriction does not use an index, the query must scan the Orders table, testing each record to see if it satisfies the restriction. Table scans are costly. An index would have made this query much more efficient. Jet’s query optimizer is quite sophisticated, but writing smarter queries will help it. Use some of the following tips to improve the quality and performance of queries you write. • If more than one field joined exists between two tables used in a query, use the fields in the query grid in the same order they appear in their tables. • Index fields used for sorting. Only sort on non-indexed fields when necessary. • Do not put calculated fields in a nested query. Instead, perform calculations at the end of the query sequence or on a form or report whenever possible.
Application Optimization CHAPTER 14
393
• Limit the use of Group By fields. If the grouped fields have indexes, try to use them in the same order as they appear in the table. • Totals queries with joins might perform better if broken into two queries. Use the first query to perform the join and the second to group the first query’s resultset. • Usually, queries with criteria on the one side of a one-to-many relationship execute faster, but experimentation might yield a different result. • Forms and reports open faster when based on a table. When possible, create a table from a resultset for your forms and reports, especially the form or report that is frequently reopened. • Crosstab queries run faster if column headings do not use calculations. Though the calculations are sometimes necessary for summation purposes, avoid doing them for strictly cosmetic reasons. • Create indexes for all the field uses in criteria clauses. • Create indexes on both sides of joins. • Use primary keys instead of unique indexes. Because primary keys prohibit null values, a query might be able to take advantage of more join types. • Don’t display any unnecessary columns in the resultset. Each column takes time to process and display. • Refrain from using complex expressions in queries. • Avoid the IIF (immediate IF) function. IIF must evaluate both the true and the false consequences before it can yield a result. Doing this for each record can seriously impede performance. • When using nested queries, move all the calculations to the last query in the series.
• If possible, use the Between operator to reduce the number of rows in the resultset instead of greater than and less than operators by themselves. • Normalized tables can store their data in fewer data pages and fewer index pages. Normalize as a rule and break normalization only if no other alternative exists. • Experiment with subqueries instead of joins or complex OR criteria where appropriate. The optimal choice depends on many discreet factors and only experimentation can help you decide which to use. • Use outer joins only when necessary, because they automatically require a table scan of the dominant table in the join.
14 APPLICATION OPTIMIZATION
• Use Count(*) instead of Count([CustomerID]) because Rushmore can process Count(*) faster—it doesn’t have to check for null values before counting.
394
VBA Unleashed PART IV
• Use saved, parameterized queries instead of SQL statements in code. Jet has already compiled parameterized queries and created an execution plan for them (though they are not available in SHOWPLAN.OUT). Using compiled and saved queries will eliminate Jet’s need to evaluate and optimize your SQL string. Access compiles SQL strings used as record source or row source of forms, reports, or controls, so they can remain as they are. • Always deliver compiled queries. • Use queries instead of DAO for data manipulation whenever possible. Queries (SQL) are almost always faster than DAO for these tasks. • Select your recordset types with care. You need a dynaset if you want to add or edit data. A snapshot or a forward-only snapshot might be suitable if you only want to read data. Snapshots might take longer to open, but they will scroll faster. • If you are opening a recordset simply to add data, open it with the dbAppendOnly option. This keeps it from having to retrieve any rows. • Test pass-through queries when using client/server data. Also, investigate Access 2002’s extensive client/server capabilities. Pass-through queries are not always faster than queries against attached server tables, but they usually result in less network traffic. • Large action queries might perform better if you set the UseTransaction property to False. Access creates temporary tables for transactions, which can grow quite large and hinder query performance. • When querying data on a remote server, use the CacheStart, FillCache, and EndCache methods to handle the data coming from the server. • Avoid local processing of server data. Local processing refers to operations such as a complex Group By using the Distinct keyword, applying the LIKE operator to text or memo fields, multiple aggregate functions in a crosstab query, or crosstab queries with ORDER clauses. Also, avoid complex outer and inner join combinations. These constructs will cause the server to send massive amounts of data to the local PC for processing, bogging down both the PC and the network. • Compact the database regularly and often to ensure the statistics Jet uses to optimize queries are current and accurate. • If at all possible, fill the application with as much test data as possible when it is installed for users. This allows Jet to optimize queries using statistics that accurately reflect the real-world conditions under which the queries will run. • If data is mostly static, consider using the Make Table query to create tables for constantly queried data instead of querying the database repeatedly.
Application Optimization CHAPTER 14
395
• Avoid using domain aggregate functions (Dlookup()) on tables which are not in the query. Queries are Access’s (or any database’s) most complicated, heavily used, and vital component. Luckily, Jet goes a long way to making sure they run efficiently. The information in this section should help you make Jet query operations as fast as possible.
Getting Forms to Run Faster Because users will spend most of their time working with forms, take care to make sure that slow forms negate all optimization efforts. The basic principle behind driving the design of speedy forms is to keep them simple, light, and frugal.
In the Beginning… When an Access application starts, the first thing users see is its startup form. This might be a splash screen with the name of the application and other information, or it could be a switchboard for navigating the application. Regardless, remove the code! Put the form’s startup code into a standard module and run only those routines essential to starting the application. Why? If the startup form loads quickly, it makes a good first impression. Look diligently to identify operations that can be delayed. After removing the module, set the form’s HasModule property to No. This creates a lightweight form that performs better. However, changing the HasModule property erases all the code behind the form and behind the controls, so make sure to place those subroutines and functions in a standard module before changing the property.
Getting Faster Images Pretty graphics on forms also hamper performance. Using images in the interface plays to Access’s graphical strengths, but exercise restraint. Just because Northwind moves along quickly showing employee photographs, it does not mean all applications will follow suit. This section discusses using graphics in your database. Try using the Image control for images. It is much more efficient than bound or unbound object frames for displaying some graphics. Access must paint or render every control on
14 APPLICATION OPTIMIZATION
The startup form should not have ActiveX controls on it. ActiveX controls take longer to load than all the other controls. If an ActiveX control is required on the startup form, you might want to limit the number of other controls on the form. Finally, use the Startup Form option instead of AutoExec macros.
396
VBA Unleashed PART IV
a form when it loads. It also takes longer to render controls that overlap, so use the Format, Vertical and Horizontal Spacing, and Format commands to make sure tightly placed controls do not accidentally overlap. Although users are increasingly less tolerant of flat, 16-color interfaces, strive for the lowest possible color depth for graphic images; black-and-white is best. The more depth you give a bitmap, the more memory and processing time is required to paint it. Keep graphics and other large data types, such as memo fields and OLE objects, off the primary forms and out of the primary tables and queries. Load them using a separate form or query, and only at user request. This prevents the expenditure of time and resources to present something very large and memory-intensive when it might not be necessary. Similarly, memory used by forms containing OLE objects is not released until the form is closed, so be sure to close these forms when they are no longer needed.
The Basic, Fast Form The single most common performance drag on a form is the number and type of controls. All controls require memory and resources, some more than others. Relatively speaking, a bound object frame weighs, or imposes a resource requirement, about 40 times as much as a line. An ActiveX control can be even heavier, depending on the control and what it does. As a rule of thumb, the more functionality a control provides, the more resources it requires. When choosing which controls to place on a form, keep Table 14.3 in mind. It lists the controls found on the form design toolbar and their relative weight. Each control’s relative weight was established by counting their properties and adjusting that number to account for the complexity of the task it accomplishes. Of course, this is only a general guide. What you do with form controls (and how you do it) ultimately determines how they consume system resources. TABLE 14.3
Relative Weights of Form Controls
Control Type
Relative Weight
Rectangle
1
Line
1
Page Break
1
Tab Page (excluding embedded controls)
4
Image (excluding embedded image)
6
Tab Control
6
Application Optimization CHAPTER 14 TABLE 14.3
397
continued
Control Type
Relative Weight
SubForm (at least)
6
Label
8
Option Button
8
Command Button
8
Checkbox
8
Option Group (excluding embedded controls)
8
Toggle Button
9
Text Box
10
List Box (at least)
10
Combo Box (at least)
20
ActiveX Controls
>=20
Object Frame (excluding embedded image)
30
Bound Object Frame (excluding embedded image)
40
Some controls are several orders of magnitude heavier than others, so substitution can be a profitable optimization technique. There are many times when a list box can substitute for a combo box or even a subform. Images can often use an image control instead of an object frame. The native tab control divides your form into faster-loading pieces because it renders only the controls on the visible page. With users becoming more familiar with hypertext links, you might be able to substitute hyperlinks for command buttons. In general, if you have the opportunity to use a lighter control without sacrificing functionality, take it.
• Limit the number of fields displayed in list boxes and combo boxes to the barest minimum. Also, index the found field and the displayed fields. Turn off the AutoExpand feature of combo boxes. Making Access monitor each key stroke drains performance. If you must use the AutoExpand, make sure the column the user is typing is a text field. Access must convert non-text data before displaying it. • If you are hiding a combo box’s bound field, avoid using expressions in that field or in the display fields. Combo boxes display results faster if calculated ahead of time and read from a table. • Populate a combo or list box from a table, even one made on the fly, instead of from a restricted query.
APPLICATION OPTIMIZATION
Here are some additional techniques to help controls perform better:
14
398
VBA Unleashed PART IV
• When refreshing forms or controls that display dynamic or transient data, use the Requery method. It is much faster than a macro’s Requery Action. This is particularly helpful after deletes and updates, especially in a multiuser application. • Instead of having one large and lengthy combo box, see if the form’s functionality accommodates several combo boxes that limit each other. For instance, use one combo box to select states, which limits the contents of a second combo box to cities in the selected state. This would be faster and more efficient than scrolling through one combo box with all the states and cities listed in it. • Be frugal regarding the number of fields presented in a form’s underlying recordset. Although forms will always load data more quickly from a table than a query, there is a caveat. When opening a form based on a table, all the table’s fields are loaded, even if the form does not display them. • Sort a form’s underlying recordset only as a last resort. Re-sorting a recordset while a form loads noticeably slows the form’s opening. • When writing code behind the form (CBF), use the Me keyword. Me always refers to the active form and works faster than other references. • Index the Link Child and Link Master fields of a mainform/subform construction. This greatly improves the lookup speeds. • Use AllowEdits, AllowAppend, and AllowDeletes. If data on subforms will not be edited, set these properties to No. Gain speed by eliminating unused functionality and selecting the correct functionality. • When opening a form for data entry, set its DataEntry property to Yes to keep it from retrieving unneeded records. • Be conservative about setting form and control properties at runtime. Set these properties only when necessary and only when it will not hamper performance. • Do you really need a dynaset? Would a snapshot be faster? • Just as it is possible to gain speed by hiding controls from view with page breaks and tabs, it is possible to gain speed by hiding forms. A good practice is to invisibly load a few of the most common forms in the application when you get a chance. That opportunity might be at startup or when the form is first requested. To load a form invisibly, open it with the WindowMode argument set to acHidden: DoCmd.OpenForm “formname”,,,,,acHidden
• When it comes time for the user to see the form, it can appear by issuing the following command: Forms(“formname”).setfocus
Application Optimization CHAPTER 14
399
• If the user will need the form again, hide it instead of closing it. The Hide method will remove it from view, while keeping it loaded in memory. FormObject.hide
• By avoiding the query loading, form loading, and control rendering steps, you can really boost the performance of the interface. The trade-off is that forms consume memory while hidden, possibly with adverse affects on other parts of the application. When dealing with very large recordsets, try not to present all the records at once. Most users do not work with tens of thousands of records at one time anyway. In a multiuser application, on the other hand, a form opening a recordset against large tables or queries, performance suffers from network bottlenecks, cache limitations, record and page locks, and user overload. In this case, find a way to break the data into logical subsets using a restriction. Better still, show the user one record at a time and request each record by its primary key or an indexed field. In smaller applications, this might yield negative performance results, but in larger, multiuser systems, it is the ideal approach. To do this, rewrite the SQL statement in code, programmatically change the form’s Record Source property, and requery the form.
Get the Reports Printed Faster
Here are several things you can do to speed up report production: • Keep the query behind the report as simple as possible. • Move the calculations onto the report. When you place a calculation in the query, the calculation must resolve for every row in the query. However, if you perform the calculation in the report, it calculates as needed and the user will see a response from the application as soon as Access has calculated one report page of data. • Base the query on as few tables as possible. Because the report will run your query more than once, it might help to create a table for the needed resultset. Reports,
14 APPLICATION OPTIMIZATION
What good is a fast database, if it takes all day to print a report? The single biggest performance-affecting difference between forms and reports is the way they handle their sections, and reports are far more complicated than forms. Forms have a header, detail section, and footer. Reports have a header, footer, page headers, page footers, section headers, section footers, and a detail section. When you open a form, the query behind the form runs only once. When you open a report, it has to create a query (based on the one in the record source) for each section in the report. Given a complex query, a report might run all or parts of it several times.
400
VBA Unleashed PART IV
like forms, run faster through a table. This will be especially helpful for reports based on queries with subqueries. • Avoid subqueries in the source of a report. Reports need a lot of memory, and a query with subqueries consumes more memory than might be necessary. • Do you really need the subreport? Not only do subreports make it more difficult to format output, they also consume memory and hinder performance. Subreports have a place though. If you have numerous domain functions, subreports might be faster than more domain function calls. • Avoid sorting or grouping on expressions. The report will have to calculate each expression more than once to get the sorting and grouping to display correctly. Have these values calculated before they get to the report. • Index all the fields used for sorting or grouping. Because indexes already sort records by default, it is very easy for a report to order and group the data straight from indexed fields. • The record source should not contain aggregate domain functions (DLookup). Aggregate domain functions require repeated trips through the data, delaying the display of the report. • There is no use in showing users an empty report with #Error# all over it. If a report has no data, send a message to that effect to the user and close the report. Use the NoData or HasData properties to test the existence of data in the report. Many of the techniques for enhancing form performance apply to reports. This section addressed those that were specific to reports.
Writing Fast Code When it comes to code, there are a few things you can do to make your functions and subroutines run more quickly. Although the differences between one technique and another might be measured in large fractions of a second, the cumulative effect of many fractions of a second do make a difference. If you have taken the time to explore and implement some of the techniques discussed so far, you will find your code easier to optimize. The greatest impediment code encounters is poor database design. If the database design is poor, almost every function and subroutine must be some type of kludge or workaround. A workaround always takes more time to execute than the standard technique. However, there are a few simple rules to follow, and several alternative techniques to ensure that your application’s functions and subroutines will execute as rapidly as
Application Optimization CHAPTER 14
401
possible. No single tweak or method will have a dramatic effect by itself, but when taken together and executed repeatedly, the cumulative effect can be significant.
How Code Uses Memory Access loads code modules, and all the subroutines and functions contained in them, using a call-tree loading method. This means that if FunctionA in Module1 calls FunctionB in Module2, which in turn calls FunctionC in Module3, Access loads all of Module1, Module2, and Module3. All three modules remain in memory. Therefore, it makes sense to group modules logically. Functions in separate modules that frequently call each other should be placed in the same module to reduce the memory overhead and module load times. Also, delete dead code, functions and subroutines the application never calls. VBA also loads a module if you reference its module public variables. Be sure to keep those public variables grouped in modules in a logical way.
Maintaining Your Modules VBA must compile modules before they run. If a module is not compiled when called, VBA temporarily compiles it at runtime, creating a significant performance drag. So, compile VBA modules before distributing the application.
Compiling and Decompiling Code To compile code, select Debug, then Compile projectname from the VBE menu bar. Be sure to compile your applications before distributing them. Code needs to be recompiled after editing a module, changing a report or form, and, of course, after creating a new report, form, or module. A module decompiles when edited. Reports and forms decompile when changed, even if the underlying code remains unchanged. Creating a new form or report can also decompile your code. During development, you can rely on the Tools, Options, General command to configure Compile on Demand, which causes VBA to compile your applications
14 APPLICATION OPTIMIZATION
When you compile a module, VBA reduces it to a much smaller, faster executing form. Though the original code remains in the .MDB file, Access loads and executes the compiled code. Moreover, compiled VBA code has no white space, comments, or headers, so it requires much less memory than the corresponding source code. If you try to run a decompiled routine, VBA must load the entire source code (white space, comments, and dead code included) into memory and compile it before executing. The same applies to the code behind forms and reports.
402
VBA Unleashed PART IV
modules while you work. Another handy feature is the Background Compile option. By compiling your application in the background, VBA relieves you of some of the downtime for compiling. If you ever take the time to check, you might be surprised to discover that a compiled application needs more disk space than a decompiled application. This is because the source code and the compiled code are stored in a compiled application. The good news is that, beyond the small amount of extra disk space consumed, this odd feature does not affect application performance because only the compiled code loads and executes. Because Access loads modules into memory permanently (at least until it exits), reduce module overhead as much as possible. Eliminate dead code; consolidate modules; reuse code and data as much as possible; and load only the code absolutely necessary. To ensure an Access 2002 application stays compiled, distribute a .MDE file because .MDEs contain no source code and never decompile.
Coding Tips and Hints This section offers a potpourri of tips, hints, and suggestions for improving the overall quality and speed of your code. Alas, there is no magic bullet, no single idea that solves all performance problems. Pick and choose from the following and test the results. Surely, one or two of them, or some combination of many of them, will help you create faster applications.
Use Option Explicit Always use Option Explicit. Option Explicit requires you to dimension all your variables. Otherwise, VBA uses the largest and most flexible data type to accommodate variables. As usual, the largest and most flexible type is also the slowest. DIMming variables is a good practice for reasons of readability and maintainability, too.
Choose Variable Sizes with Care When dimensioning a variable, use the smallest possible variable size. Don’t use a double when an integer will do. Use fixed-length strings instead of variable-length strings when possible.
Save Stack Space with String Variables String variables are one of the most common data types used in code. They can be broken into three different types:
Application Optimization CHAPTER 14
403
• Local fixed-length (of no more than 64 characters in length)—These use two bytes per character and do not use heap space. • Local fixed-length (more than 65 characters in length)—These strings also use two bytes per character, but in heap memory. They also require four bytes on the stack to point to the variable in the heap. • Local variable-length (length doesn’t matter)—The amount of heap space depends upon the length of the string. Four bytes of stack memory are used as a pointer to the variable in the heap. When dealing with strings, the objective should be to reduce the amount of stack memory you are using. Try changing strings to local variable-length strings or make them static fixed-length stings. The following code snippet illustrates a variable length string re-declared as a static fixed-length string in order to conserve stack memory. Dim strString as string Static strString as string * 30
Tip Use a table’s field size to determine the size of fixed-width text strings.
Specific Object Type Declarations When declaring object types, be specific. If your code is going to cycle through a form’s text box controls, declare the object variable as a text box and not simply as a control. This way, VBA does not have to resolve what kind of control you are addressing. For example, instead of
use Sub CycleControls(cntl as TextBox)
to save on execution time.
Toggle True and False When flipping a flag from True to False, there is no need to go through the task of checking the value flag in an If...Then...Else construct. You can save time and lines of code by reversing the value with a NOT operator. By setting a Boolean variable to what that variable is not, you reverse it.
APPLICATION OPTIMIZATION
Sub CycleControls(cntl as control)
14
404
VBA Unleashed PART IV
Instead of If bFlag = False then bFlag=True Else BFlag=False EndIF
use this bFlag=Not bFlag
It takes much less time to execute one line of code than several with an evaluation.
Len() Instead of Empty String To test a string variable to see if it contains any characters, use the Len function instead of comparing it to an empty string (“”). Len evaluates faster than a comparison to a zero length string. Sub CheckString(strString as string) If Len(strString) then MsgBox “Here is the string: “ & strString EndIf End Sub
True and False Evaluations Instead of Zero Because True and False are binary, they are easier to evaluate than the number zero. Use True and False as follows: Function ShowNumber(dblNumber as Double) as string If dblNumber then ShowNumber =”The number is “ & dblNumber EndIf End Function
Fast Object References Use variables instead of repeated object references. It is much faster to reference an existing variable in a form, control, report, or query than it is to refer back to the object again. Instead of referencing a form again Forms![frmMyForm].Height=500 Forms![frmMyForm].Width=500
try declaring a variable and referring to it this way: Dim frm as Form
Application Optimization CHAPTER 14
405
Set frm=Forms![frmMyForm] frm.Height=500 frm.Width=500
When dealing with multiple properties of the same object, reduce object references using With...End With. This is especially useful when the path of the reference is long, and it is much easier to type. With Forms![frmMainForm]![txtCustomerName] .left=200 .top=300 .height=200 .width=100 End With
represents the active form, so it refers directly to the current form, eliminating the need to dimension a variable or declare an object variable. Because it only works in CBF, you will not be able to put it into generic standard modules. ME
With ME .Height=500 .Width=500 End With
Fast Array Uses Use arrays. They reside in memory and, under normal circumstance, are fast to access and manipulate. They take an infinitesimally small amount of time to enlarge, and use memory quite efficiently. If an array’s size is undetermined during development, use a dynamic array, which can be re-dimensioned as necessary at runtime. Do not declare a large static array that wastes memory.
Erase myArray
Conversely, to enlarge an array without destroying its contents use Redim and Preserve. ReDim Preserve myArray(Ubound(myArray+1))
Arrays can store the data from a recordset, groups of controls, data read from a file, user input, and any group of homogenous data. For example, instead of keeping a DAO recordset open and incurring the associated overhead, open the recordset, use the GetRows() method to populate an appropriately specified array, then lose the recordset. This frees memory and can alleviate multiuser issues. GetRows() takes one argument: the number of rows to load into the array.
APPLICATION OPTIMIZATION
To empty an array without destroying its structure, use the Erase keyword. Erase clears an array’s contents but does not completely delete it.
14
406
VBA Unleashed PART IV Dim db as Database Dim rowArray as Variant Dim rs as Recordset Set db=CurrentDB() Set rs=db.openrecordset(“Quarterly Orders”) RowArray=rs.GetRows(rs.RecordCount) rs.close ...
After loading the array, you can manipulate the data as the situation requires or load the records in an unbound form.
Use Constants When Possible In order for VBA to get a variable’s current value, the variable must be dereferenced, requiring several CPU instructions each time, and more if the variable is not in a processor register or the CPU cache. Constants, on the other hand, require fewer instructions and have the added benefit of improving code readability. For example, instead of typing 42 throughout your code, create a symbolic constant, say, TheAnswerToLifeTheUniverseAndEverything and set it equal to 42. VBA reads constants more quickly than variables. In addition, other developers use the constant much easier than a magic number like 42.
The Proper Use of Bookmarks Bookmarks are a convenient way to move among records in a user interface; they are also a toxic and addictive form of syntactic sugar. Bookmarks are convenient because they are fast and are trivial to code. They are toxic because they only represent a record’s temporary position in a series of rows. Keep in mind that there are two different bookmarks: one for forms and one for recordsets. A form bookmark is an array of variants dynamically assigned to each record in the underlying recordset clone. DAO bookmarks are entries in a byte array referring to each record in a recordset. However, regardless of the type of bookmark, bookmarks are created, destroyed, and re-created with recordsets and clones. They do not represent records and have nothing to do with primary keys. Data manipulation should use techniques that adhere to the theory and design of relational databases, not the chance location of a record in a recordset.
Application Optimization CHAPTER 14
407
Close and Destroy Fast code is clean, conscientious code. Close recordsets when you are finished with them. Set objects variables to Nothing when they are no longer needed. Close forms no longer useful. Close database connections that are unused. Keep variables local so they go out of scope and vanish when control passes to another function. Use dynamic rather than static arrays. In general, make every effort to minimize the resources an Access application consumes. rs.Close Set db=Nothing
Use SQL Instead of DAO Cycle through recordsets only when there is no alternative. The Jet database is optimized to use SQL to manipulate data and data structures. Use queries instead of DAO at every opportunity. There are few cases where DAO will be faster than a well-built query. Queries have execution plans and can take advantage of indexes where DAO cannot. If you must address data through an object model, use ADO instead of DAO. ADO is the preferred standard for addressing data manipulation and data definition through the object model. DAO is frozen in time and will have no further extensions or enhancements. This book discusses ADO in several other chapters, including Chapters 6, “Introduction to ActiveX Data Objects,” and 7, “Advanced ADO.”
Use the Index Number of Collections When working with object collections, use their index number if possible. These index numbers are the collection’s internal identification. They are much faster to use than any other property (such as name) of the collection’s object.
Set db=DBEngine(0)(0)
is faster than Set db=Currentdb()
Similarly, this statement Set cntl=Forms!frmMyForm(0)
APPLICATION OPTIMIZATION
For example, the following two statements show two different ways to reference the current database. The reference to Currentdb() will automatically refresh the database’s collections, which takes time, but the first reference, dbEngine(0)(0), does not.
14
408
VBA Unleashed PART IV
is faster than Set cntl =Forms![frmMyForm]![myControl]
Using the index number of a collection member is especially useful in loops, which are discussed next.
Making Faster Loops When looping through a collection, use For...Each instead of For...Next. Consider the controls on a form, which constitute a collection. Use the following syntax to iterate through each control: For Each cntl on frm ... Next
will run faster than a simple For...Next loop. For...Next loops require repeated object references, but For...Each loops do not. If you need to loop through the collection objects, you will want to avoid refreshing the collection unnecessarily. Even on a small database, refreshing the collection can crush the performance of your application. When using a For...Next loop, you can save time by not reciting the variable on the Next line. For i = 1 to 100 ......do what you want Next
The benefit of this is especially noticeable in nested loops. Also, don’t recalculate the limit on the For line. The upper limit should be established before the loop is entered. The first code snippet in the following calculates the loop’s exit criteria, reccount, once. The second snippet calculates it on each iteration. Obviously, the first snippet results in faster code: reccount=rs.recordcount/2 For i=1 to reccount ... Next For i=1 to rs.recordcount/2 ... Next
Application Optimization CHAPTER 14
409
Ban IIF() from Code Do not use the IIF() function in code. This function must evaluate all expressions contained within it before it yields a result. A standard If...Then...Else structure is faster.
Arranging the Select Case When using a Select Case construct, arrange the structure with the most frequently encountered cases at the top. Because a pass through the case options tries each case in order, arranging them with the most common occurrence first will enable the execution to exit the structure as soon as possible.
Use .Execute, Not RunSQL Avoid DoCmd-based code when ever possible. DoCmd is the highest level of VBA code, a holdover from Access’s pre-VBA days when the only “automation” was macros. When there is an alternative, take it. DoCmd RunSQL “...”
will be slower than Querydef.Execute
Use QueryTimer() When timing applications, use the A2KU_Timer provided in this chapter. It’s nearly 1000 percent faster than the alternatives.
Test the Efficiency of Transactions
Control Refreshes Turn off Application.Echo or control the repainting of a screen. It takes time to refresh the screen, and this can slow your application.
14 APPLICATION OPTIMIZATION
Transactions might not always be timesavers. At one time, you could count on a transaction to enhance query speed, but this is no longer true. Although transactions use their caching ability to speed the performance of queries by reducing disk accesses, they also slow down queries because they create temporary disk files in anticipation of a rollback. Moreover, transactions are now implicit by default, so you will only know if transactions will improve performance by testing your particular situations against a realistically sized set of test data.
410
VBA Unleashed PART IV
Reference ActiveX and Use Early Binding Take advantage of early binding. When using ActiveX controls, make sure you have a reference to the underlying OCX file for that control. Check Tools, References. Early binding can greatly enhance performance.
Upsize to Client/Server Keep the design of your application open to the possibility that you might upsize to SQL Server or Oracle. If this happens, you can employ pass-through queries and use stored procedures stored on the server. This will greatly improve performance.
Bread and Circuses If, at the end of the day, you can’t beat ’em, fool ’em. Make it look fast. If a process takes enough time to cause the user to get bored, use a status screen or some other visual device to reassure users that, yes, something is going on and that, no, the application is not frozen. Users often think the application is running quickly just because they have feedback.
Summary This chapter surveyed a range of techniques for optimizing Access 2002 applications. Suggested ideas ranged from simple hardware upgrades to using one VBA procedure instead of another. The simplest way to improve application performance is to upgrade the host system. Other approaches include tweaking Jet’s performance by altering registry settings, designing better database tables and queries, simplifying the user interface, and writing more efficient code. Start from the foundation, work up using the tips and ideas mentioned in this chapter that apply to your particular situation, and be sure to measure the results. You will find the solution that works for you.
Access Client Server
PART
V IN THIS PART 15 Introducing Access Data Projects and the Visual Tools 16 Developing Access Front-Ends to Microsoft SQL Server 17 Access 2002 Front-Ends to Oracle
CHAPTER 15
Introducing Access Data Projects and the Visual Tools IN THIS CHAPTER • Introducing Access Data Projects • Using ADPs
414
415
• Working with ADPs and Existing SQL Server Databases 419 • Creating a Project Based on a New Database 428
414
Access Client Server PART V
With Access 2000 and now with Access 2002, Microsoft has integrated SQL Server with Access using OLE DB as the glue. You can take advantage of this integration when using Access Data Projects or ADPs, a feature first available in Access 2000 and included in Access 2002. The authors recommend that ADPs are best suited for SQL Server 7.0 or SQL Server 2000. If you plan to use something other than SQL Server 7.0 or SQL Server 2000 as your back end, look at Chapter 16, “Developing Access Front-Ends to Microsoft SQL Server,” and Chapter 17, “Access 2000 Front-Ends to Oracle.” This chapter shows you how to use ADPs with SQL Server 2000.
Introducing Access Data Projects As an Access user, you know that Access uses the Microsoft Jet database engine, as of this writing Jet 4.0 SP5, to store its data and process its queries. Many Access applications use an ODBC Database Server as their data store. These applications might consist of linked tables, SQL pass-through queries, and ODBCDirect. In applications like these, you are using the Jet engine and ODBC to communicate with your linked tables, and the application is a regular MDB file. Microsoft has realized that you need more integration with Database Servers. In Access 2002, you have available a database project called Access Data Projects (ADP) to integrate with SQL Server 2000. Because ADPs are hard-wired to the SQL Server OLE DB provider, you can only use them with SQL Server 7.0, SQL Server 2000, or SQL Server 6.5 with Service Pack 5 installed. Caution If you intend to use ADPs with SQL Server 6.5, SP 5, you should also read the Office XP release notes and run a SQL file on your server before ADPs will work with SQL Server 6.5.
The Pros and Cons of ADPs When you are considering the advantages of ADPs versus MDBs, you have to look at the pros and cons. The advantages of using ADPs are • Using ADPs, you can edit table structures in Access, but using Linked tables on a Server, you cannot. • With ADPs, you have one connection for all your database objects, but in MDBs you do not. For example, in an MDB with linked tables, a form with 10 controls
Introducing Access Data Projects and the Visual Tools CHAPTER 15
415
on it has 10 connections to the database. With an ADP, there is only one connection. • ADPs are easy to use. • ADPs enable you to use Access as your development tool. The disadvantages of using ADPs are • ADPs limit you to using only SQL Server, you cannot use them with Oracle or Sybase. • ADPs are new technology and, as with all new technology, might not be perfected yet. • You cannot store any local tables or queries. After looking at the pros and cons of ADPs and reading through this chapter, you should have all the knowledge that you need to determine whether you will want to use ADPs with your applications.
Using ADPs An ADP application is an Access 2002 application with an ADP extension. It uses SQL Server as the database engine. Note Before you can use an ADP, you must either install SQL Server on your machine or have network access to an existing SQL Server. To install SQL Server 2000, Desktop Edition, on your computer, just follow the directions found with the SQL Server 2000 setup instructions. If you do not have SQL Server 2000, you can install the Microsoft Data Engine (MSDE), which is SQL Server desktop without a user interface from your Office XP Setup CD in the /MSDE2000 folder.
Creating an ADP 15 INTRODUCING ACCESS DATA PROJECTS AND THE VISUAL TOOLS
To create an ADP, open Access and select File, New from the main menu. A dialog box will appear, as shown in Figure 15.1. The selections from the right side of the window under New are Blank Database, Blank Data Access Page, Project (Existing Data), and Project (New Data).
416
Access Client Server PART V
FIGURE 15.1 The File, New dialog box.
If you select Blank Database, you will create a normal Access MDB file that uses Microsoft Jet as the database engine. If you select Project (Existing Data), you will create an ADP that will communicate to an already existing SQL Server database. If you select Project (New Data), you will create an ADP and a new SQL Server database on your server. You will now create a sample project based on an existing SQL Server database. Click Project (Existing Data). You will then be prompted for a file location for the ADP as shown in Figure 15.2. FIGURE 15.2 The file location prompt.
Introducing Access Data Projects and the Visual Tools CHAPTER 15
417
After you have created the ADP file, Access will prompt you for the SQL Server database information, as shown in Figure 15.3. Remember that when using Data Links (as you did in Chapter 7, “Advanced ADO”), you must enter the server name, user name, password or Windows NT Integrated security, and database name of the database that you want to use. For the current example, you will be using the SQL Server version of Northwind. After you have connected to the database, Access Project will come up with some new items in the database container, as shown in Figure 15.4. FIGURE 15.3 Creating the data link.
Note If you want to follow along with this example on your computer, make sure that you have SQL Server 2000 installed. You can install the local version of SQL Server with the SQL Server version of NorthwindCS from the Office 2002 CD located in the MSDE2000 folder.
The New Database Container 15 INTRODUCING ACCESS DATA PROJECTS AND THE VISUAL TOOLS
The database container for ADPs (Figure 15.4) is the same as one for MDB files and is shown in Access file format. The database container displays tables, queries, database diagrams, forms, reports, pages, macros, and modules. Table 15.1 summarizes each item in the database container. Views and Stored Procedures are no longer shown in the container. It doesn’t matter whether you’re using the SQL NorthwindCS sample database or connecting to a remote server using another SQL database, the container is the same and is shown in Access file format.
418
Access Client Server PART V
FIGURE 15.4 The new database container for ADPs.
TABLE 15.1
The Database Container Objects for ADPs
Item
Description
Tables
A listing of all the tables in the SQL Server database. As opposed to linked tables, you have access to add, edit, and delete tables directly on the server.
Queries
A listing of all the Queries and Stored Procedures.
Database Diagrams
A listing of all the database diagrams on the server. You can create a diagram with the same tools as in Visual InterDev 6.0.
Forms
Access forms in your application. These forms are not on the server but in your ADP file. They can be bound to tables, views, and stored procedures.
Reports
Access reports in your application. These reports are not on the server but in your ADP file. They can be bound to tables, views, and stored procedures.
Pages
Data Access Pages in your application. These reports are not on the server but exist as HTML files on disk. (See Chapter 25, “Using Data Access Pages” for a full discussion of DAPs.) They can be bound to tables, views, and stored procedures.
Macros
All your Access macros. These macros are not on the server but in your ADP file.
Modules
All your Access modules. These modules are not on the server but in your ADP file.
Introducing Access Data Projects and the Visual Tools CHAPTER 15
419
Working with ADPs and Existing SQL Server Databases As you work with your ADP projects, you might need to add, edit, and delete database objects. This section will show you how to work with SQL Server objects that reside in your ADP.
Working with SQL Server Tables Because ADPs do not link database tables like traditional Access MDB applications do, you have direct access to a SQL Server table in your Access database container. If you delete or rename your table in Access, you will be deleting or renaming it in SQL Server as well. Although this is very convenient, this can also be dangerous. You will want to make sure that you implement SQL Server security in your application to limit your users’ access to the tables. If you want to edit a table, select it, and press the Design button. This will bring up the table designer view as shown in Figure 15.5. FIGURE 15.5 Table designer in Access 2002.
15 INTRODUCING ACCESS DATA PROJECTS AND THE VISUAL TOOLS
The table designer shown in Figure 15.5 is similar to the table designer in Access. Enter the name of the table columns and all the columns’ properties in the designer. Table 15.2 summarizes the column properties that you can set.
420
Access Client Server PART V TABLE 15.2
The Table Designer Properties
Property Name
Description
ColumnName
The name of the column. It must be a valid SQL Server column name.
DataType
The data type of the column. Valid SQL Server data types are listed in Table 15.3.
Length
The length of the column.
Precision
The precision of the column. Used with numeric columns.
Scale
The scale of the column. Used with numeric columns.
Allow Nulls
Indicates whether the column allows Null values.
Default Value
The default value for the column.
Identity
Indicates whether the column is an Identity column. Only one column in a table can be an Identity.
Identity Seed
The starting seed of the Identity column.
Identity Increment
The increment value of the Identity column.
Is RowGuid
Determines whether the Identity column is a GUID.
The SQL Server data types are shown here: • Binary
• nvarchar
• Bit
• Real
• Char
• Smalldate
• Datatime
• Smallint
• Decmil
• Smallmoney
• float
• Text
• Image
• Timestamp
• Int
• Tinyint
• Money
• uniqueidentifier
• nchar
• Varbinary
• ntext
• Varchar
• Numeric
Introducing Access Data Projects and the Visual Tools CHAPTER 15
421
SQL Server Queries Using the Queries section in the database container, you can administer your SQL Queries. Select a Query and then Design. To see the SQL view from the menu bar, select View, Show Panes, SQL; the window should now include the SQL view of the Query. You can also verify the SQL syntax by selecting Query from the main menu and choosing Verify SQL Syntax, as shown in Figure 15.6. If you have the proper server rights you can change and save queries, tables, and other objects in the database. FIGURE 15.6 The SQL Query designer.
When you are in the designer, you can add tables by selecting from the menu bar Query, Select Query, Add Tables. This will bring up the Table/Views list, as shown in Figure 15.7. Select the Table or View you want to add and then click Add; the addition will be shown in the design view. Now you can drag and drop the new table or view as you want in the design view. FIGURE 15.7 The designer Table/Views window.
15 INTRODUCING ACCESS DATA PROJECTS AND THE VISUAL TOOLS
422
Access Client Server PART V
FIGURE 15.8 The SQL Server view designer.
To create a view in designer, select Create view in Designer from the Queries window.
Stored Procedures All the Stored Procedures on your SQL Server can be managed in Access. Enter the Queries section of the database container, and you can add, edit, rename, or delete any stored procedure. To create a stored procedure, select Create stored procedure in designer. You will be brought to the Access stored procedure editor shown in Figure 15.9. To edit a stored procedure, select it from the list and select the Design button on the menu. FIGURE 15.9 A stored procedure.
Although the stored procedure editor might look very basic, what you can do with it is very powerful. You can create any valid Transact SQL stored procedure right in Access and save it on the SQL Server. For more information on Transact SQL, see Sams Teach Yourself TSQL in 21 Days by Sams Publishing.
Introducing Access Data Projects and the Visual Tools CHAPTER 15
423
You can also run stored procedures by double-clicking them. If the stored procedure has a parameter in it, Access prompts you for it as shown in Figure 15.10. FIGURE 15.10 A stored procedure parameter.
Database Diagrams Perhaps the coolest features of ADPs are the database diagrams. For those of you who are new to the Microsoft database tools that are now part of ADPs, database diagrams are a visual way to manage and design your database tables. The SQL Server database version of Northwind, by default, has a relationships database diagram. This diagram shows you the relationships of your database and gives you the capability to edit relationships, edit tables, and add or remove columns right in the window. To work with the database diagram, select Database Diagrams, Relationships and you will see the diagram shown in Figure 15.11. FIGURE 15.11 The database diagram.
15 INTRODUCING ACCESS DATA PROJECTS AND THE VISUAL TOOLS
You can move and resize the tables by dragging and dropping with your mouse. You can also zoom and limit your view to certain tables. And the best feature is that you can print this all out as documentation. By clicking a table, and selecting from the menu bar View, Table, Column Properties, you can choose to view the column properties of a particular table, and you will have access to the table designer as shown in Figure 15.12.
424
Access Client Server PART V
FIGURE 15.12 The SQL Server database diagram with a column properties shown.
Forms, Pages, Reports, Macros, and Modules Using forms, pages, reports, macros, and modules is just like using these objects in Access 2002, with the exception that the current database data objects (tables, views, and stored procedures) are now SQL Server objects and not Access objects.
Administering Your SQL Server via an Access Although there is no room in this book for a complete lesson in SQL Server database administration, it is important to show you that you can administer a SQL Server database via your ADP. To administer your SQL Server, the ADP user must have the correct permissions on the SQL Server. You will want to grant the ADP user full admin permissions. In addition, you will want to administer your SQL Server from a different ADP that you use to distribute your applications. The two areas in which you can administer your SQL Server databases are • Backup/Restore • Security Replication is no longer available using Access. Replication must be done using SQL Server.
Back Up/Restore To back up your current SQL Server database, you can choose Tools, Database Utilities, Backup SQL Database from the main menu. Access prompts you for a backup location as shown in Figure 15.13. This saves your SQL Server database backup in a format that SQL Server can restore via its normal restore feature. In addition, if you choose Tools,
Introducing Access Data Projects and the Visual Tools CHAPTER 15
425
Database, Utilities, Restore from the main menu and select a valid backup file, you can restore your SQL Server database from its backup. FIGURE 15.13 A SQL Server backup via Access.
SQL Server Replication The Database Security and Replication menus, previously available from the Tools menu of Microsoft Data Engine (MSDE), are no longer available on Microsoft SQL Server 2000 Desktop Engine. You can use SQL Server 2000 Enterprise Manager to replicate and secure your database. You can also use the GRANT, REVOKE, and DENY TransactSQL statements to secure your database.
Security You can also manage some aspects of SQL Server database security from Access 2002. With Access 2002, you can manage SQL Server security by selecting Tools, Set Login Password, Workgroup Administrator, or Encrypt/Decrypt Database. You can create Logins for users to actually log in to the database, Users and Roles. A typical user is DBO for Database Owner and Guest for guest. Roles are SQL Server roles that a user can be in, such as “System Administrator.” For more information on SQL Server security, see the SQL Server books online.
Reconnecting to the SQL Server Database 15 INTRODUCING ACCESS DATA PROJECTS AND THE VISUAL TOOLS
What happens when you develop an ADP at your site and then release it to users at another site? You have to resync the ADP so that it connects to the correct SQL Server database. If you remember Chapter 7, you used Microsoft Universal Data Links to manage your OLE DB connection string information in ADO. By setting a reference to the Microsoft OLE DB Service Component 1.0 Type Library, as shown in Figure 15.15, you can use the UDL’s automation interface to resync your ADP with the correct SQL Server database.
426
Access Client Server PART V
FIGURE 15.14 The Tools menu showing the possible selections.
FIGURE 15.15 Setting a reference to the OLE DB Service Component.
After your reference is set, you can assign a string to the PromptNew method of the DataLink object. Then assign this string to the CurrentProject.OpenConnection method to make the ADP connect to a new SQL Server database. The code in Listing 15.1 shows you how to accomplish this.
Introducing Access Data Projects and the Visual Tools CHAPTER 15 LISTING 15.1
427
Updating Your ADP’s Connection
Sub UpdateConnection() ‘Procedure that will use the ‘UDL Automation Interface to ‘Create a connection object ‘And then use that connection ‘to resync the ADP ‘From “Access 2000 Unleashed” (SAMS) ‘By:Forte, Howe, Ralston Dim strConnect As String Dim rst As ADODB.Recordset ‘The Microsoft UDL Reference Dim udl As MSDASC.DataLinks On Error GoTo Proc_Err ‘Create the Data Link Object Set udl = New MSDASC.DataLinks ‘Set the connection object to the ‘Prompt a new Data Link dialog strConnect = udl.PromptNew ‘Take the connection information from ‘The UDL and resync the ADP CurrentProject.OpenConnection strConnect
Proc_Exit: Exit Sub Proc_Err: MsgBox Err.Description Resume Proc_Exit End Sub
Note
15 INTRODUCING ACCESS DATA PROJECTS AND THE VISUAL TOOLS
The CurrentProject.Connection that an ADP uses is the MSDataShape OLE DB Provider, in addition to the SQL Server OLE DB Provider. I have discovered that when using the OLE DB Provider for SQL Server in VB (or Access 2002 with a separate connection), without MSDataShape, some features of ADO behave slightly different. Please visit the Microsoft KnowledgeBase for any updates on limitations to using your Access 2002 code in Visual Basic.
428
Access Client Server PART V
Creating a Project Based on a New Database You can demonstrate how to use the tools in ADPs by creating a SQL Server database from scratch. This example creates a very simple database application; however, it is set up to teach you what potential ADPs have. To begin, open Access 2002 and select File, New from the main menu and then select Project(New Data) from the menu on the right side of your screen. You are prompted for a location for the new project. Next, Access brings you to the Create SQL Server Database Wizard as shown in Figure 15.16. This wizard asks you to provide a server name. Use Trusted Connection or username, password, and database name. When you supply all the information, Access creates the database for you on the server that you specified. Now you are ready to begin creating the database application. FIGURE 15.16 The Create SQL Server Database Wizard.
Creating Tables You are going to create a simple workflow tracking system. To do so, you will have to create a few tables. To create a table, click Create Table in Design View from the Database Container. You will be asked to name the table after it is created. You will need to create two tables in the designer, tblIssue and tlkpTransactionType. The details of tblIssue are in Table 15.3 and the details of tlkpTransactionType are in Table 15.4.
Introducing Access Data Projects and the Visual Tools CHAPTER 15
429
FIGURE 15.17 The Create Table dialog.
TABLE 15.3
Design for Table:tblIssue
Column Name
Data Type
Notes
IssueID
Int (4)
Identity, Primary Key
TransactionTypeID
Int (4)
Will be a Foreign Key
TransactionComments
Text (16)
RecDate
Datetime
Default Value: getdate()
CompleteDate
Datetime
AllowNulls=True
TABLE 15.4
Design for Table:tlkpTransactionType
Column Name
Data Type
Notes
TransactionTypeID
Int(4)
Identity, Primary Key
TransactionDesc
Varchar(50)
Setting a Table’s Properties and Indexes
15 INTRODUCING ACCESS DATA PROJECTS AND THE VISUAL TOOLS
To set table properties and indexes, you can select View, Properties from the main menu. This will bring you to the dialog shown in Figure 15.18. You can select an index and set its attributes such as uniqueness, fill factor, and whether it is a clustered index or not. Because you added a Primary Key via the designer, a unique index is created already.
430
Access Client Server PART V
FIGURE 15.18 The Table Properties dialog.
Creating Relationships via a Diagram Now that you have created two tables (when is an application ever that easy?), you need to define a relationship between tlkpTransactionType and tblIssue. Creating relationships are very easy with ADPs. ADPs leverage the skill that you have in Access for creating relationships. To create a relationship visually with a Database Diagram, select Database Diagrams in the database container and add a new diagram by selecting New. To show the tables to include in the relationships, select View, Show Table from the main menu. Drag the two tables from the Show Table dialog box to the Diagram as shown in Figure 15.19. To create the actual relationship, drag the field from tblkTransactionType to tblIssue. Dragging the field will display a Relationships dialog (shown in Figure 15.20). This dialog box will enable you to set the fields that are to be used in the relationship. After you create the relationship, save the diagram. Remember that you can now use the diagram as documentation for your database.
Creating Cascades via Triggers Because SQL Server 7.0 does not support cascading updates and cascading deletes via its relationships, you would have to specify them via triggers. To create or manage a table’s triggers, right-click the table in the database container and select Triggers from the context menu. This will bring up the dialog shown in Figure 15.21. Select a trigger to edit if the trigger already exists, or click New to create a new trigger.
Introducing Access Data Projects and the Visual Tools CHAPTER 15
431
FIGURE 15.19 The Database Diagram in design mode.
FIGURE 15.20 Relationship properties.
FIGURE 15.21 Managing triggers.
Creating Views
15 INTRODUCING ACCESS DATA PROJECTS AND THE VISUAL TOOLS
You are going to create a SQL Server View with your ADP that will show all the Issues that are completed. To create the view, select Create View in Designer from the Queries window. Then select the tables from the list and click Add, after making sure you have selected SQL from the View, Show Panes from the View main menu. You should see something similar to Figure 15.22. You have created a simple view that produced the following SQL in Listing 15.2.
432
Access Client Server PART V LISTING 15.2
A Simple View
SELECT tlkpTransactionType.TransactionDesc, tblIssue.IssueID, tblIssue.TransactionComments, tblIssue.RecDate, tblIssue.CompleteDate FROM tblIssue INNER JOIN tlkpTransactionType ON tblIssue.TransactionTypeID = tlkpTransactionType.TransactionTypeID WHERE (tblIssue.CompleteDate IS NOT NULL)
FIGURE 15.22 The View designer.
Creating Stored Procedures To create a stored procedure, select New from the container window while the Queries window is open. Then select Create Text Stored Procedure from the New Query popup window. You are going to create a simple stored procedure that will select one Issue based on the issue number. The TSQL code to do so is shown in Listing 15.3. LISTING 15.3
A Simple Stored Procedure
Create Procedure SelectOneIssue @IssueNumber int As SELECT tlkpTransactionType.TransactionDesc, tblIssue.IssueID, tblIssue.TransactionComments, tblIssue.RecDate, tblIssue.CompleteDate FROM tblIssue INNER JOIN tlkpTransactionType ON tblIssue.TransactionTypeID = tlkpTransactionType.TransactionTypeID WHERE tblIssue.IssueID=@IssueNumber
Introducing Access Data Projects and the Visual Tools CHAPTER 15
433
Creating the Access Application To show you just how easy it is to create an Access application with an ADP, see the simple Form shown in Figure 15.23. It is bound to the table tblIssue, and you can go ahead and enter in data, filter, and so on just as you would in a normal MDB file. Creating a report is just as easy in Access 97 against Jet data, just use the wizard to get you started and then create your report. FIGURE 15.23 Your Access form.
Creating—ADE Files Access 97 introduced a new concept, an MDE file. An MDE file was a compiled and locked version of your MDB file. Access 2002 enables you to create MDE files for MDB files and ADE files for ADP files. To create an ADE file, select Tools, Database Utilities, Make ADE File from the main menu as shown in Figure 15.24. Then select a file location and Access will make your ADE file for you. Now all forms, reports, pages, and modules will be locked from the user. FIGURE 15.24 Creating an ADE file.
15 INTRODUCING ACCESS DATA PROJECTS AND THE VISUAL TOOLS
434
Access Client Server PART V
A Final Note: Upsizing an Access 97 Database to SQL Server and ADPs In the past, the Access to SQL Server Upsizing Wizard did not do all that much. It would move your Tables to SQL Server for you and that was about it. Your queries remained in Access running against linked tables, not all that efficient of a result. If you run the Upsizing Wizard from an MDB file in Access 2002, you will get better results. The wizard will convert all your Jet “Views” or select queries to SQL Server views and all your Jet “Procedures” (everything else) to SQL Server Stored Procedures, if possible. Then the wizard will create a new ADP project for you based on the new SQL Server database. Although there are still many limitations of the wizard, it is worth taking a look at; it can at least get you started. The wizard is very straightforward to use, so to get started just choose Select Tools, Database Utilities, Upsizing Wizard.
Summary As you can see, using Access 2002 with a SQL Server database just got a whole lot easier! ADPs are a very powerful tool that enables you to create, edit, and manage remote SQL Server objects while still in Access. SQL Server developers, beware! Access has now entered the client/server market.
CHAPTER 16
Developing Access Front-Ends to Microsoft SQL Server IN THIS CHAPTER • Client/Server Architecture: OLE DB Versus ODBC 436 • Setting Up Your SQL Server Front-End Connection 436 • Stored Procedures and SQL PassThrough Queries 440 • Reporting Against SQL Server in Access 2002 443 • Using Forms in Your Application
448
• Advanced Features of the SQL Server OLE DB Provider 450 • Executing Commands with Parameters 452 • Using a Connection Class
459
436
Access Client Server PART V
Ever since the introduction of Microsoft Access, it has been a great front-end tool for external databases. Access 2002 has some incredible features for working with SQL Server 7.0 in Access Data Projects (ADPs), but these features do not work well with SQL Server 6.5, and they do not work at all with any other ODBC database server. This chapter shows you how to develop applications with SQL Server 6.5 or Sybase System 10 as a back-end. Chapter 17, “Access 2002 Front-Ends to Oracle,” will show you how to develop applications with an Oracle Enterprise Server as a back-end.
Client/Server Architecture: OLE DB Versus ODBC A client/server system is defined as having a database server as a back-end data store (like SQL Server or Oracle) and a client application that talks to the back-end database. (A three-tier system, which you hear about all the time, is a similar architecture, except that all the business logic code is located in a separate logical, and usually physical, tier.) Access MDB front-ends to SQL Server fall into the client category of this architecture, while using ODBC to communicate to the back-end database. Access databases using ODBC load the Jet database engine to broker all communications between the server and your Access client. Access ADP front-ends to SQL Server use the newer OLE DB technology to natively communicate with the server without loading Jet at all. Whereas the OLE DB approach has several significant advantages, ADPs’ largest shortcomings are that they do not support any database servers other than SQL Server. Although ODBC and MDBs might not be as slick as the new ADPs, they are very powerful and useful, as you will see in this chapter.
Setting Up Your SQL Server FrontEnd Connection To start working with a back-end SQL Server database, you have to establish a communications link with the database. Because you will be using ODBC to communicate to the back-end database, you must make sure that you have the proper ODBC drivers installed on the machine that you will be using. A default installation of Access 2002 installs the SQL Server ODBC driver on the machine for you. (If you are using Sybase SQL Server, you need to install the Sybase SQL Server driver, which is not on the Office CD.) After you install Access 2002 with the ODBC drivers, you are ready to start working with the back-end database. You have two data-access methodologies: linked tables or SQL pass-through queries. Most applications use both. In order to work with either
Developing Access Front-Ends to Microsoft SQL Server CHAPTER 16
Setting Up an ODBC Data Source (DSN) A System DSN is a place for you to store all your ODBC connection information on each machine. Each DSN has a unique name so your application can use it. To create a DSN on your machine, follow these steps: 1. Go to Control Panel and select ODBC. This opens the DSN dialog box. Click Add, select SQL Server from the list, and click Finish. This brings you to the Create DSN Wizard, as shown in Figure 16.1. If you are using Windows 2000, go to Control Panel and select Administrative Tools and then Data Sources (ODBC). Then follow the instructions above for the DSN dialog box. FIGURE 16.1 Creating a new DSN in Control Panel.
2. On the first page of the Wizard shown in Figure 16.1, enter a unique name of the DSN (like “Northwind”), an optional description, and the server name on which the SQL Server resides. Click Next when you are done entering this information. 3. On the second page, enter the SQL Server security information. If you want to use standard SQL Server security (or you are not sure), check the With SQL Server Authentication Using a Login ID and Password Entered by the User checkbox, and then enter the username and password and click Next. (If you use the default SQL Server security, the user will be “SA” with a blank password. 4. The third page, shown in Figure 16.2, asks you to what database on the SQL Server you would like to connect. Change it to whatever database you are using. For this demo, I will change it to the SQL Server version of Northwind. 5. The fourth page asks you about ODBC specific items like tracing. Accept the defaults and click Finish.
16 DEVELOPING ACCESS FRONT-ENDS TO MICROSOFT SQL SERVER
linked tables or SQL pass-through queries, you must set up an ODBC System Data Source.
437
438
Access Client Server PART V
6. The last page gives you the opportunity to test your ODBC connection. It is wise to check it here and then go back and make any necessary corrections. If the connection is successful the system tells you the connection was successful. Now you are ready to use the SQL Server database in your Access 2002 application. FIGURE 16.2 Specifying a default database when creating a System DSN.
Linking Tables To begin working with the ODBC data source that you just created, the most commonly used and easiest solution is to link the SQL Server database tables to your .MDB file. This way you can run some simple queries against those tables for combo boxes and list boxes. For really small tables, you might even want to bind a form directly to the table and let users enter data. (For more on bound forms to SQL Server data, see the “Using Forms in Your Application” section later in this chapter.) To link to ODBC database tables, you must follow these steps: 1. Make sure that the ODBC data source is set up on your machine. (See “Setting Up an ODBC Data source (DSN),” earlier in this chapter.) 2. Open your Access database and choose File, Get External Data, Link Tables from the main menu. 3. A list of all the ODBC data sources appears. Select the DSN that you want to work with and click OK. 4. Access displays a dialog of all the tables available to link to (see Figure 16.3). As you select your tables, you can specify to save the database password with the tables so your users will not be given an ODBC login screen when they first launch your application. 5. Access then links the tables to your .MDB file. It prefixes a “dbo_” in front of each table because each SQL Server table is specified as “Database Owner.”
Developing Access Front-Ends to Microsoft SQL Server CHAPTER 16
FIGURE 16.3 Selecting the tables from the ODBC Database that you want to link to.
FIGURE 16.4 Selecting a unique record identifier.
After your table is linked, you can use it as any other linked table in Access. You can create queries, forms, and reports that refer to these linked tables.
Disadvantages of Linked Tables Linking tables from an ODBC data source to Access can be very inefficient and dangerous. The Jet engine must broker all communication to the SQL Server for your linked table. If Jet cannot use a unique index on the Server, the server might download all the records for Jet to process. Imagine this scenario. You link to a table with five million records. You create an Access query asking for about 500 records with query criteria not on any unique index. Depending on your table design, the SQL Server might return all five million records for Jet to sort through! If you are running on a low-end machine, this can take hours. To really harness the power of a back-end SQL Server, you will have to use stored procedures. A stored procedure on a SQL Server is like a saved query in Access. They are compiled SQL statements that execute on the server and can accept parameters. Many developers send SQL statements to the server because it’s faster than using a linked
16 DEVELOPING ACCESS FRONT-ENDS TO MICROSOFT SQL SERVER
6. If your SQL Server table does not have a unique key in its table, Access will not be able to link it as read/write, so it gives you the opportunity to create a local unique index, as shown in Figure 16.4. If you choose to use this technique, the table will be read/write and Jet 4.0 will manage the index for you on the client machine.
439
440
Access Client Server PART V
table. This method is still slower than using a stored procedure. Because a stored procedure is compiled, it will have an execution plan established on the server for the greatest speed. Using stored procedures represents the largest performance gain that you can get for the least amount of effort. In addition to increased speed, stored procedures also offer reduced network traffic. You only send the server a small 1KB execute instruction, not a 100KB or larger uncompiled SQL statement. Also, the server will only send you back the data you need.
The Best of Both Worlds: Filling Tables at Startup Because I tend to use stored procedures as the basis for everything in my Access client/server applications, I usually use linked tables for my lookup data that fills combo and list boxes on forms (and reports too!). To achieve the best performance, you can create exact replicas of the server tables that you are interested in using and fill them at the startup of your application. Doing this requires fewer network connections to the database server in your application. Of course, you can only use this technique with relatively static tables (tables that change infrequently). Note This technique, although extremely useful, is not available in ADPs that were discussed in Chapter 15, “Introducing Access Data Projects and the Visual Tools.” This is because these local tables live in a Jet database, and ADPs do not load the Jet engine.
Stored Procedures and SQL PassThrough Queries It makes sense to base an Access report or reports on stored procedures because they are so fast and powerful. The problem is that you cannot link to a stored procedure the way you can link to a table on the SQL Server. You can use a SQL pass-through, however, to obtain the same functionality. A SQL pass-through query is a SQL statement sent directly to the SQL Server for processing. The Jet engine does not process the query (the back-end database does), and Access displays only the results. SQL pass-through queries take advantage of the processing power of the database server. Creating advanced stored procedures is beyond the scope of this book; however, if you can create a view in an ADP (see Chapter 15 for more information), you can copy and
Developing Access Front-Ends to Microsoft SQL Server CHAPTER 16
LISTING 16.1
A Simple Stored Procedure
CREATE PROCEDURE dbo.sp_OrdersByDate @StartDate datetime,@EndDate datetime AS SELECT Customers.CustomerID, Customers.CompanyName, Orders.OrderDate, [Order Details].Quantity, [Order Details].UnitPrice, Quantity*UnitPrice As TotalAmount FROM Customers INNER JOIN Orders ON Customers.CustomerID = Orders.CustomerID INNER JOIN [Order Details] ON Orders.OrderID = [Order Details].OrderID Where OrderDate Between @StartDate and @EndDate
Running a stored procedure is easy. Log in to the SQL Server and type in an EXECUTE command in the SQL window. If your stored procedure accepts any parameters, you will have to provide them here. To run the sp_OrdersByDate stored procedure you created in Listing 16.1 from the SQL Server T/SQL tool, you will have to call it like this: Execute sp_OrdersByDate @StartDate=’07/01/94’, @EndDate=’09/30/94’
The results of the sp_OrdersByDate stored procedure are shown in Figure 16.5. This stored procedure returns Customer’s Orders sorted by dates with the total amount of each order. FIGURE 16.5 The results of the executed stored procedure.
16 DEVELOPING ACCESS FRONT-ENDS TO MICROSOFT SQL SERVER
paste the outputted SQL into a stored procedure. Listing 16.1 shows an example of a stored procedure from the SQL Server Northwind database that will provide Order Information between a parameterized date range. Listing 16.1 creates a stored procedure called sp_OrdersByDate that accepts two date parameters, @StartDate and @EndDate.
441
442
Access Client Server PART V
Basing Access Reports on Stored Procedures via Pass-Through Queries To execute a stored procedure in a SQL pass-through query, all you have to do is use the Execute syntax discussed earlier. You can set up a SQL pass-through query in Access by selecting Query, SQL Specific, Pass-through from the main menu when in Query Design view as shown in Figure 16.6. FIGURE 16.6 Setting up a SQL pass-through query in Access.
You cannot use the QBE Grid with a SQL pass-through; you will have to type the SQL directly into the SQL window. You have to use SQL syntax that is compliant to your target server. (This chapter uses Microsoft SQL Server 6.5. You will have to change the syntax for another database server. Chapter 17 focuses on Oracle syntax.) After you type in your SQL string, you must set the ODBC connection string. The connect string tells Access what ODBC database to send the SQL to for processing. You must choose Tools, Properties to set the ODBC connect string property. Access provides a nice builder for you if you click on the ellipses next to the property. You can either type in a valid ODBC string or use the builder provided at the end of the property sheet. Use the builder and you will have to choose a valid ODBC data source from those installed on your system as shown in Figure 16.7. Save the pass-through query as qsptOrdersbyDate. Notice that when you save a passthrough query, it has a different icon than a normal query (see Figure 16.8).
Developing Access Front-Ends to Microsoft SQL Server CHAPTER 16
FIGURE 16.7
FIGURE 16.8 A saved passthrough query.
Reporting Against SQL Server in Access 2002 If you ask experienced developers what they like most about Microsoft Access, many will say that it has one of the best report engines around. Access makes it easy to create great reports quickly, even when that data does not come from an Access database. The capability to easily report ODBC data makes Access an excellent choice for all enterprise reporting. There are many ways to report ODBC data. The simplest way is to link to ODBC tables and then create an Access query on which to base your report. Although this is the fastest way to create reports, it is not advisable for the same reasons described earlier in the chapter.
16 DEVELOPING ACCESS FRONT-ENDS TO MICROSOFT SQL SERVER
Valid DSNs on your system.
443
444
Access Client Server PART V
Traditionally, SQL Server database servers have much more processing power and RAM than an average desktop. I am going to look at ways to increase the performance of your Access client/server reports by taking full advantage of the powerful server your on which database resides. For the examples, I will continue to use the database on my SQL Server called “Northwind.” To run a report on a SQL pass-through query, just run the Report Wizard on a SQL passthrough query and customize your report as needed. It’s that easy. A sample report is shown in Figure 16.9. A report in the sample database is also available on this book’s companion CD if you would like to see the results using that report. FIGURE 16.9 A report based on a stored procedure.
Advanced Features: Providing Parameters for the Stored Procedure at Runtime You might be accustomed to using parameter queries in Access for reports that require a user to enter dates or other criteria information at runtime. In Access, if you reference the form field in your query criteria, you can dynamically ask the user for parameters, as shown in Figure 16.10. Using the stored procedure approach, you will have to re-create the Execute statement with the new parameters inside your qsptOrdersbyDate passthrough query every time you run the report. In your parameter collection form shown in Figure 16.10, there is code on the Click event to rewrite the SQL in the pass-through. This code will then execute the stored procedure with the new date range parameters. The code to do this is shown in Listing 16.2; please note that you need a reference to DAO 3.6 in your project to run this code. To set a reference to DAO 3.6 in your application, open a module and then from the main menu in the VBE Editor, choose Tools, References. In this dialog place a check next to “DAO 3.6 Object Library.”
Developing Access Front-Ends to Microsoft SQL Server CHAPTER 16
FIGURE 16.10
16 DEVELOPING ACCESS FRONT-ENDS TO MICROSOFT SQL SERVER
The Collect Parameters form used to run reports.
LISTING 16.2
445
Code to Rewrite the SQL Statement and Run the Report
Private Sub cmdPrint_Click() ‘’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’ ‘Purpose: Reset the SQL Code to call ‘the Stored Procedure with new parameters ‘From “Microsoft Access 2000 Development Unleashed” (SAMS) ‘By: Forte, Howe, Ralston ‘’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’ Dim db As DAO.Database Dim strSQL As String On Error GoTo Click_Err strSQL = “Execute sp_OrdersByDate @StartDate=” & _ Chr(39) & Me.txtStartDate & Chr(39) & _ “,@EndDate=” & Chr(39) & Me.txtEndDate & Chr(39) Set db = CurrentDb db.QueryDefs(“qsptOrdersbyDate”).SQL = strSQL DoCmd.OpenReport “rptOrdersBydate”,acViewPreview Click_Exit: Exit Sub Click_Err: MsgBox Err.Description Resume Click_Exit End Sub
Additional Filtering of a Report at Runtime Sometimes the Where clause in a stored procedure does not fully limit the information that you need. You might return 500 orders and want to filter out more orders based on an amount at runtime. Access provides two methods to filter reports via the DoCmd. OpenReport method: You can supply a filter name as a saved query at runtime, or you
446
Access Client Server PART V
can pass it a Where clause by which to filter the report. Figure 16.9 shows the parameter collection form altered to accept a filter for a report with an Order Amount greater than a dollar amount. If the user decides to use the filter in addition to the date parameters, there is code to call the BuildCriteria method of the Application object. A valid filter should be something like OrderAmount>500. The BuildCriteria method constructs a valid filter for you. After you construct the filter, you use it with the DoCmd.OpenReport method. Listing 16.3 shows the code to both change the SQL pass-through and create and apply the filter. LISTING 16.3
Using the BuildCriteria Method to Provide a Report Filter
Function BuildWhere(curAmt As Currency) As String ‘Build Where Clause with Access’ BuildCriteria Method BuildWhere = _ BuildCriteria(“TotalAmount”, dbCurrency, “>=” & curAmt) End Function
Private Sub cmdPrint_Click() ‘’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’ ‘Purpose: Reset the SQL Code to call ‘the Stored Procedure with new parameters ‘Also Add a Filtering Capability ‘The Filter is set via ‘Access’ Where Clause Property ‘of the DoCmd.OpenReport Method ‘From “Microsoft Access 2000 Development Unleashed” (SAMS) ‘By: Forte, Howe, Ralston ‘’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’’ Dim db As DAO.Database Dim strSQL As String On Error GoTo Click_Err strSQL = “Execute sp_OrdersByDate @StartDate=” & _ Chr(39) & Me.txtStartDate & Chr(39) & _ “,@EndDate=” & Chr(39) & Me.txtEndDate & Chr(39) Set db = CurrentDb db.QueryDefs(“qsptOrdersbyDate”).SQL = strSQL ‘Here we determine if a Filter is Required ‘A filter is required if the Checkbox is clicked If Me.chkFilter Then ‘Apply the Filter DoCmd.OpenReport _
Developing Access Front-Ends to Microsoft SQL Server CHAPTER 16 LISTING 16.3
continued
Else ‘No Filter Selected DoCmd.OpenReport “rptOrdersBydate”, acViewPreview End If Click_Exit: Exit Sub Click_Err: MsgBox Err.Description Resume Click_Exit End Sub
Lastly, it is a good idea to display a label control dynamically to alert the user to the filter and its amount. In addition, you should alert the user to an empty report with the NoData report event. The code to do this follows in Listing 16.4. LISTING 16.4
Report Code to Handle the Filter and NoData
Private Sub Report_NoData(Cancel As Integer) ‘No Data! ‘From “Microsoft Access 2000 Development Unleashed” (SAMS) ‘By: Forte, Howe, Ralston On Error Resume Next ‘Alert User to Empty Report MsgBox “This Report Contains No Data” & _ vbNewLine & “Please Try a Different Selection Criteria”, _ vbCritical, Me.Name ‘Prevent the Report From Opening Cancel = True End Sub Private Sub Report_Open(Cancel As Integer) On Error Resume Next ‘Check to see if the Filter was applied If Forms!frmPrintReport!chkFilter Then ‘Fill the caption with the Filter Criteria Me.lblFilter.Caption = _ “Report Filtered for Orders > $ “ & _ Forms!frmPrintReport!txtFilter
16 DEVELOPING ACCESS FRONT-ENDS TO MICROSOFT SQL SERVER
“rptOrdersBydate”, acViewPreview, , BuildWhere(Me.txtFilter)
447
448
Access Client Server PART V LISTING 16.4
continued
Me.lblFilter.Visible = True End If End Sub
Using Forms in Your Application Forms are usually at the center of your Access applications. It is no different when you have an Access front-end to SQL Server. You can use forms in two types of modes— bound and unbound. The following sections show you how to work with forms in both bound and unbound mode.
Bound Forms It makes sense to base your Access forms on stored procedures as well; however, stored procedures are read-only. If your users are accustomed to bound forms and you want to use them instead of linked tables, there is nothing to do except write a query to limit the number of records that will be displayed on the form. Using a bound form based on a stored procedure/SQL pass-through query will change the parameters at runtime (as shown in Listing 16.2) and open the form. If you have a large amount of data or need to update data, you will have to use ADO code and unbound forms as shown in following sections to optimize performance.
Unbound Forms When you use a bound form, there is a level of overhead involved, especially with linked tables. With a linked table, Jet has to manage the ODBC communication for you and there is an ODBC connection to the server for each bound control on the form. This can add up to a lot of connections and a lot of memory. A great alternative to using bound forms is to use a technique called unbound forms. With unbound forms, you can ask the user to enter in the PrimaryKey or other search value, and then use that value to open an ADO recordset and fill in the text boxes with those values, as shown in Figure 16.11. You can fill the combo box with a SQL pass-through query based on a stored procedure and then use the code in Listing 16.5 on the After Update event.
Developing Access Front-Ends to Microsoft SQL Server CHAPTER 16
FIGURE 16.11
16
Filling a Form with Values from an ADO Recordset
Private Sub cboFind_AfterUpdate() ‘This code will fetch the records ‘based on the primary key ‘and fill in the values in an unbound form ‘From “Microsoft Access 2000 Development Unleashed” (SAMS) ‘By: Forte, Howe, Ralston Dim conn As ADODB.Connection Dim rst As ADODB.Recordset Dim strSQL As String On Error GoTo Proc_Err DoCmd.Hourglass False Set rst = New ADODB.Recordset Set conn = New ADODB.Connection ‘Create SQL String based on what the user chose in ‘the combo box strSQL = “Select * From Categories Where CategoryID=” & Me.cboFind ‘Set up an ADO connection to the SQL Server With conn .Provider = “SQLOLEDB” .ConnectionString = “data source=batgirl;” & _ “user id=sa;initial catalog=Northwind” ‘read only mode .Mode = adModeRead .Open End With ‘Open the recordset rst.Open strSQL, conn ‘Fill the values Me.CategoryID = rst!CategoryID Me.CategoryName = rst!CategoryName
DEVELOPING ACCESS FRONT-ENDS TO MICROSOFT SQL SERVER
An unbound Access form.
LISTING 16.5
449
450
Access Client Server PART V LISTING 16.5
continued
‘clean up rst.Close conn.Close Set conn = Nothing Set rst = Nothing Proc_Exit: DoCmd.Hourglass False Exit Sub Proc_Err: MsgBox Err.Description Resume Proc_Exit End Sub
After you start building unbound forms, you will want to update, delete, edit, and perform other operations in ADO. The following section describes how to use ADO and the OLE DB Provider for SQL Server.
Advanced Features of the SQL Server OLE DB Provider To use the OLE DB Provider for SQL Server, you first need to be sure it is installed on your computer. Luckily, if you install Access 2002, the OLE DB Provider for SQL Server is installed by default. After you have the Provider installed, you need to use a connection object. To set up a connection object, use the following syntax shown in Listing 16.6. LISTING 16.6
Connecting to SQL Server via OLE DB
Sub SQLServer() ‘This procedure will connect to SQL Server ‘From “Microsoft Access 2000 Development Unleashed” (SAMS) ‘By: Forte, Howe, Ralston Dim conn As ADODB.Connection Set conn = New ADODB.Connection With conn .Provider = “SQLOLEDB” .ConnectionString = “data source=BATGIRL;” & _ “user id=sa;initial catalog=Northwind”
Developing Access Front-Ends to Microsoft SQL Server CHAPTER 16 LISTING 16.6
continued
MsgBox “Connected to “ & conn.Provider, vbInformation End Sub
Next Recordset One feature of SQL Server that you will not be allowed to use with Jet is to execute two select statements as one. Using this feature, you can then execute a SQL statement with multiple return recordsets using one recordset object. Listing 16.7 shows you how to use two recordsets with one recordset object. To do this you have to use the NextRecordset method of the recordset object. LISTING 16.7
Using NextRecordset
Sub NextRst(strCustomerID As String) ‘Use ALFKI or ANTON for an example ‘This procedure will use 2 recordsets in one ‘From “Microsoft Access 2000 Development Unleashed” (SAMS) ‘By: Forte, Howe, Ralston ‘This example opens a recordset ‘based on a SQL Statement that will ‘return two recordsets Dim Dim Dim Dim
cmd As ADODB.Command conn As ADODB.Connection rst As ADODB.Recordset strSQL As String
Set conn = New ADODB.Connection ‘Connect to the SQL Server With conn .Provider = “SQLOLEDB” .ConnectionString = “data source=batgirl;” & _ “user id=sa;initial catalog=Northwind” .Open End With ‘A SQL Statement that will produce two recordsets ‘You can only do this against a provider/data engine that ‘supports multiple resultsets
16 DEVELOPING ACCESS FRONT-ENDS TO MICROSOFT SQL SERVER
.Mode = adModeRead .Open End With
451
452
Access Client Server PART V LISTING 16.7
continued
strSQL = “select * From Customers Where CustomerID=” & _ Chr(39) & strCustomerID & Chr(39) strSQL = strSQL & vbNewLine strSQL = strSQL & “Select * From Orders Where CustomerID=” & _ Chr(39) & strCustomerID & Chr(39) Set cmd = New ADODB.Command With cmd .CommandText = strSQL .ActiveConnection = conn .CommandType = adCmdText End With Set rst = cmd.Execute ‘This will open the first recordset Do Until rst.EOF Debug.Print rst!CompanyName rst.MoveNext Loop ‘Grab the next recordset Set rst = rst.NextRecordset Do Until rst.EOF Debug.Print rst!OrderDate rst.MoveNext Loop rst.Close conn.Close Set Set Set End
rst = Nothing cmd = Nothing conn = Nothing Sub
Executing Commands with Parameters Earlier in the chapter, I emphasized the importance of using stored procedures in your application whenever possible to take advantage of the speed and reduction of network traffic they offer. When it comes to writing ADO code, using stored procedures provides the same benefits. In ADO, when you use a command object with the SQL Server
Developing Access Front-Ends to Microsoft SQL Server CHAPTER 16
The Long Way Perhaps the most code-intensive way to execute a stored procedure is the best place to start. Although no one in their right mind wants to code command objects in this way, learning this way first accomplishes a few things. First, you will definitely appreciate the other ways to execute commands; and second, you will fully understand the relationship between the connection, command, and parameter objects that you will be using in all your future code. To execute a command with parameters, you have to create a command object and a parameter object and then append the parameter object to the command, as shown in Listing 16.8. LISTING 16.8
Executing a Command with Parameters
Sub ExecuteCommandwithParms() ‘This procedure will execute a stored procedure w/ parameters ‘From “Microsoft Access 2000 Development Unleashed” (SAMS) ‘By: Forte, Howe, Ralston Dim conn As ADODB.Connection Dim cmd As ADODB.Command Dim prm As ADODB.Parameter Set conn = New ADODB.Connection ‘Establish a connection to the database With conn .Provider = “SQLOLEDB” .ConnectionString = “data source=Batman;” & _ “user id=sa;initial catalog=pubs” .Open End With ‘Set up a command object Set cmd = New ADODB.Command With cmd .ActiveConnection = conn .CommandText = “byroyalty” .CommandType = adCmdStoredProc End With
16 DEVELOPING ACCESS FRONT-ENDS TO MICROSOFT SQL SERVER
OLEDB provider, you can map that command object to a stored procedure on the server and use all its parameters, input, and output with parameter objects. As with most things in ADO, there are many ways to accomplish this task. In the following sections, I explore some of the most common ways that you can use stored procedures and command objects; however, it is up to you to figure out which one will best suit your coding style. Along the way, I throw in my personal experiences and comments to help you decide which methodology to use.
453
454
Access Client Server PART V LISTING 16.8
continued
‘Set up the parameter Set prm = New ADODB.Parameter With prm .Name = “@percentage” .Direction = adParamInput .Type = adInteger .Value = 50 End With cmd.Parameters.Append prm ‘Open a recordset based on the executed command Dim rst As ADODB.Recordset Set rst = cmd.Execute ‘Loop through the results Do Until rst.EOF Debug.Print rst!au_id rst.MoveNext Loop ‘Clean up rst.Close conn.Close Set rst = Nothing Set conn = Nothing Set cmd = Nothing Set prm = Nothing End Sub
Using Create Parameters An easy way to deal with stored procedure parameters without writing a ton of code is with the CreateParameter method of the command object. This method does not force you to create an actual parameter object of each parameter in your stored procedure, saving you perhaps more than a hundred lines of code if you have a lot of parameters. I have found that this is the least code-intensive and most reliable way to execute a command against both SQL Server 6.5 and 7.0. As shown in Listing 16.9, using CreateParameter creates a parameter object for you under the covers, without all that extra code. LISTING 16.9
Executing a Command Without a Parameter Object
Sub ExecuteCommandwithCreateParms() ‘Execute a Parameter Query w/o ‘Having to create Parameter Objects ‘Here we use the CreateParameter method
Developing Access Front-Ends to Microsoft SQL Server CHAPTER 16 LISTING 16.9
continued
Dim conn As ADODB.Connection Dim cmd As ADODB.Command Set conn = New ADODB.Connection ‘Establish a connection to the database With conn .Provider = “SQLOLEDB” .ConnectionString = “data source=batgirl;” & _ “user id=sa;initial catalog=pubs” .Open End With ‘Set up a command object Set cmd = New ADODB.Command With cmd .ActiveConnection = conn .CommandText = “byroyalty” .CommandType = adCmdStoredProc ‘Set up the parameter info ‘Here we use the Create Parameter Method ‘Of the Command Object to simplify your code .Parameters.Append .CreateParameter(“@Percentage”, _ adInteger, adParamInput, , 50) End With ‘Open a recordset based on the executed command Dim rst As ADODB.Recordset Set rst = cmd.Execute ‘Loop through the results Do Until rst.EOF Debug.Print rst!au_id rst.MoveNext Loop ‘Clean up rst.Close conn.Close Set rst = Nothing Set conn = Nothing Set cmd = Nothing End Sub
16 DEVELOPING ACCESS FRONT-ENDS TO MICROSOFT SQL SERVER
‘From “Microsoft Access 2000 Development Unleashed” (SAMS) ‘By: Forte, Howe, Ralston
455
456
Access Client Server PART V
Using Refresh Using the Refresh method of the command object’s parameter collection is similar to the CreateParameter method, except you do not have to create any parameter objects. Instead, you connect to the database, set your command object equal to the stored procedure that you will be using, and then refresh the parameter collection. This gives the collection the parameters you need, and it is up to you to fill them in. As shown in Listing 16.10, this option gives you the advantage of great flexibility in your code (you can create generic command classes); however, this method works 100 percent of the time with SQL Server 7.0 only. Using SQL Server 6.5, I had some issues that led me to wait until a future ADO release to use Refresh. (You can never be too safe with your data!) Even with this limitation, you might want to learn the syntax and usage of the Refresh method for your future programming, or when using SQL Server 7.0. One important thing to note is that you are making an extra roundtrip when you use the refresh method to go to the database and fetch the parameter information. Although it’s the easiest to use, you want to check on the performance before you make it your standard. LISTING 16.10
Using Refresh with a Command
Sub ExecuteCommandwithParmRefresh() ‘Execute a Parameter Query w/o ‘Having to use Parameter Objects ‘Helpful with Stored Procedures that have ‘A whole lot of Parameters ‘This procedure will execute a stored procedure w/ parameters ‘From “Microsoft Access 2000 Development Unleashed” (SAMS) ‘By: Forte, Howe, Ralston Dim conn As ADODB.Connection Dim cmd As ADODB.Command Set conn = New ADODB.Connection ‘Establish a connection to the database With conn .Provider = “SQLOLEDB” .ConnectionString = “data source=batgirl;” & _ “user id=sa;initial catalog=pubs” .Mode = adModeRead .Open End With ‘Set up a command object Set cmd = New ADODB.Command With cmd .ActiveConnection = conn
Developing Access Front-Ends to Microsoft SQL Server CHAPTER 16 LISTING 16.10
continued
16
‘This example uses the Parameter’s Refresh ‘Method to get the parameters from the server ‘And then fill their values before execute ‘Avoids the need to Create/Append Parameters .Parameters.Refresh .Parameters(“@Percentage”).Value = 50 End With ‘Open a recordset based on the executed command Dim rst As ADODB.Recordset Set rst = cmd.Execute ‘Loop through the results Do Until rst.EOF Debug.Print rst!au_id rst.MoveNext Loop ‘Clean up rst.Close conn.Close Set rst = Nothing Set conn = Nothing Set cmd = Nothing End Sub
Handling Return Values Sometimes the stored procedures return a value indicating success or failure of the procedure, or they might return the newly added CustomerID value. In either case, you should know the return value of that parameter. ADO enables you to query the parameter collection after you execute the command to retrieve any return parameters. Listing 16.11 runs a command object against a very simple stored procedure that returns the value of 55. The procedure is shown here: AS
After you execute the command object, you will have to query its parameters collection after using a Refresh (see the preceding section) via its ordinal position as shown here: cmd.Parameters(0).Value
DEVELOPING ACCESS FRONT-ENDS TO MICROSOFT SQL SERVER
.CommandText = “byroyalty” .CommandType = adCmdStoredProc
CREATE PROCEDURE sp_CmdWithPram /*Return Value*/ Return 55
457
458
Access Client Server PART V LISTING 16.11
Querying the Return Values
Sub CommandReturnValues() ‘This procedure will execute a stored procedure ‘and query its return values ‘A copy of the Stored Procedure: ‘CREATE PROCEDURE sp_CmdWithPram AS ‘ ‘/*Return Value*/ ‘Return 55 ‘From “Microsoft Access 2000 Development Unleashed” (SAMS) ‘By: Forte, Howe, Ralston Dim conn As ADODB.Connection Dim cmd As ADODB.Command Set conn = New ADODB.Connection Set cmd = New ADODB.Command ‘Establish a connection to the database With conn .Provider = “SQLOLEDB” .ConnectionString = “data source=batgirl;” & _ “user id=sa;initial catalog=northwind” .Open End With ‘Set up the command object to ‘point to the Stored Procedure With cmd .CommandText = “sp_CmdWithPram” .CommandType = adCmdStoredProc .ActiveConnection = conn .Parameters.Refresh End With cmd.Execute MsgBox “return value: “ & cmd.Parameters(0).Value conn.Close Set conn = Nothing End Sub
Developing Access Front-Ends to Microsoft SQL Server CHAPTER 16
Not Using a Command Object
rst.open ‘exec sp_SimpleProc @OrderID=1’, conn
Using a Connection Class As you begin to rely more on code in your applications, you should get in the habit of trying to write generic, reusable code as often as possible. Although I try to show you as much Class Module programming as possible in this book, creating a connection class is one of the most important examples that I can show you. It is a real-world class module that you can make use of immediately, not only in Access programming, but in any VBA host, including the other Office 2000 applications and Visual Basic 5 and 6. When you program with ADO, you should get into the habit of using a connection class. A connection class is a generic object that creates and returns an ADO Connection object for you. If all your data access code in your application relies on this particular class module to create connections to the database for it, you will only have to maintain this small piece of code when the database connection information changes. (That never happens, right?) In this chapter, you will create a simple class module called DBConnection that will have one public method, Connect. Connect is just a simple VBA function that returns an ADO Connection object. The function, shown in Listing 16.12, creates an ADO Connection object and then sets the function equal to that connection object. (The connection class is very simple. You can enhance the Class Module to use a Microsoft UDL File as described in Chapter 7, “Advanced ADO,” for the most reusability.) LISTING 16.12
A Generic Connection Class
Function Connect() As ADODB.Connection ‘Reusable Component to Connection to a database ‘Can use this component to centralize where the ‘the data connection exists, so you only have to ‘change the database connection info in one place ‘From “Microsoft Access 2000 Development Unleashed” (SAMS) ‘By: Forte, Howe, Ralston Dim conn As ADODB.Connection Set conn = New ADODB.Connection
16 DEVELOPING ACCESS FRONT-ENDS TO MICROSOFT SQL SERVER
For a row returning parameterized stored procedure, you can open one without using a Command object at all. To do this, you have to construct a SQL statement and use the open method of a recordset object as shown here:
459
460
Access Client Server PART V LISTING 16.12
continued
‘SQL Server Connection Information With conn .Provider = “SQLOLEDB” .ConnectionString = “data source=batgirl;” & _ “user id=sa;initial catalog=airline” .Open End With Set Connect = conn End Function
Using the Connection Class in Your Applications Using the connection class in your applications is simple. Wherever you have ADO code to connect to the back-end database, you create a DBConnection object to create the ADO Connection object for you, as shown in Listing 16.13. This enables you to hide all the connection-specific code in one generic reusable class module or COM object for reuse later. LISTING 16.13
Using the Connection Class
Sub UseConnect() ‘Uses the Reusable Component to Connection to a database ‘From “Microsoft Access 2000 Development Unleashed” (SAMS) ‘By: Forte, Howe, Ralston Dim conn as ADODB.Connection Dim oConnect as DBConnection Set oConnect= New DBConnection Set rst=oConnect.Connect End Sub
Summary This chapter showed you the basics of using Access 2002 as a front-end to SQL Server. In this chapter I discussed how to create an Access application using the SQL Server OLE DB Provider. You can use ADO in your unbound forms to display and manipulate SQL Server data efficiently. Once again, if you are planning to use a version of SQL Server earlier than 7.0, you will want to use the techniques in this chapter. If you will be using 7.0 or higher, check out ADPs in Chapter 15.
CHAPTER 17
Access 2002 FrontEnds to Oracle
IN THIS CHAPTER • Accessing Oracle Data with Access 462 • Functions in Oracle Versus Access • Understanding Views and Stored Procedures 482 • Creating an Unbound Interface to Oracle 491
470
462
Access Client Server PART V
Despite the prevalence of Oracle in the world of Access developers, there is very little written on the subject of using Access with it. This chapter attempts to fill that gap. Although new, the Access Data Project (ADP) is available to you for sophisticated client/server development, but there are plenty of times when you will need to retain the rich development environment of a regular Access application. Some of these situations include when you are prototyping an application or when you need to take advantage of the heterogeneous data management capabilities of Access. Oracle is an extremely powerful and sophisticated database, and this chapter in no way reveals all its workings and complexity. However, by following some simple techniques and guidelines, you can harness much of that power without too much trouble. Essentially, good Access/Oracle development requires that you approach Oracle on its own terms and let it do the data management for you because that is what it does so well. Though this sounds obvious, it can be a challenge in practice. Although many Access developers are called upon to use Oracle as part of their design, they often are not invested with as much control over Oracle’s data schema as they are with Access’s. Very often they find that they must negotiate a less-than-optimal data arrangement or data warehouse. Additionally, many Access developers find themselves presented with the task of developing against Oracle with little more than an ODBC driver and a user account. This chapter explores ways of overcoming these challenges, as well as ways to use some of the more basic Oracle tools, which, with a little prodding, should be made available to you.
Accessing Oracle Data with Access There are several ways to use Oracle data with Access. This chapter focuses on two ways: basic ODBC techniques, though not ODBC programming; and ADO programming. During development, these two approaches can be used together, but your final product might benefit from using just one. These two basic approaches manifest themselves in three ways: • Table linking (ODBC) • SQL pass-through queries (SPT) (ODBC) • Direct connections through ADO
Table Linking Because it is so easy for Access to link tables from many data sources, most Oracle data comes to Access through these linked tables. The linked tables use ODBC despite the
Access 2002 Front-Ends to Oracle CHAPTER 17
463
fact that ADO is the default data management model for Access 2002. This means that you must have the ODBC drivers as well as the latest version of ADO installed to appreciate this section. To create a link to a table in Oracle you must have the following: • A compatible ODBC driver for Oracle. • A network connection to the Oracle database using either SQLNet or Net8 (for Oracle 8). Oracle provides and controls its own network connection. Without one of these tools, you will not be able to connect to Oracle over the network. • The ODBC driver included with Access.
Creating an ODBC Connection String The ODBC manager on the client machine communicates between your process and the ODBC driver you are using for your data source. In order to make a connection to Oracle, you must first construct an ODBC connection string. This string is composed of the following optional pieces listed in Table 17.1. TABLE 17.1
ODBC Connection String Arguments
Section
Description
DSN
Data Source Name
UID
User ID
PWD
Password
Database
If the DSN does not name the database, it can be specified here
APP
The application using the connection
An ODBC connection string might look something like this: ODBC;DSN=MSOracleDriver;UID=Scott;SERVER=empexample;;TABLE=SCOTT.DEPT
Every part of the connection string is optional, so if your string is missing necessary information, the user will be prompted for it.
Creating a Database Server Name To prepare to use ODBC with Access, you should create a Database Server Name through ODBC. This is a very simple process. The following steps create a DSN for Oracle.
ACCESS 2002 FRONT-ENDS TO ORACLE
Through ODBC, you establish a connection to the database; manage the tables, views, and procedures; and submit your SQL statements for parsing and execution.
17
464
Access Client Server PART V
1. Start the ODBC Data Sources from the control panel (Administrative Tools if using Windows 2000). 2. In the ODBC Data Source Administrator, click the Add button to create a new data source for Oracle. 3. Select either the Oracle ODBC Driver, Microsoft ODBC Driver for Oracle, or another ODBC Driver for Oracle. Click Finish. At this point, you are prompted for information similar to the following dialog box. The exact dialog box you receive varies depending upon the driver and its version (see Figure 17.1). FIGURE 17.1 Filling out the DSN information.
Once you’ve created the DSN, you can call upon it to link your Oracle data to your database.
Understanding the Cost of an Oracle Connection Creating the connection to an Oracle database is much more expensive in time and resources than creating a connection to a Jet database. When programming through ODBC, RDO, DAO, ODBC Direct, or ADO, you would try to maintain the connection and refer to it repeatedly within your application. Access does this work for you when you link a table from Oracle. It keeps the connection open and you can refer to it by referring to the Table object. Figure 17.2 shows how the linked tables can be used to build a query. FIGURE 17.2 Use linked tables to build queries as if they were local tables.
Access 2002 Front-Ends to Oracle CHAPTER 17
465
SELECT SCOTT_EMP.ENAME, SCOTT_DEPT.DNAME FROM SCOTT_DEPT INNER JOIN SCOTT_EMP ON SCOTT_DEPT.DEPTNO = SCOTT_EMP.DEPTNO WITH OWNERACCESS OPTION;
If you need to browse data or examine the schema of the tables and you have no Oracle tools, linking is an appropriate technique. Linking is also acceptable if you are querying the Oracle data with data that must remain local or comes from more than one source (heterogeneous query). However, if you need to homogeneously query Oracle data, you should consider other approaches.
SQL Pass-Through Queries (ODBC) One such approach is a SQL pass-through query (SPT). By using the same ODBC connection you used for linking, you gain access to the Oracle database and shift the burden of processing your query to the server—where it belongs. Of course in an SPT, all the data sources you reference in the SQL statement must reside on the Oracle database and the SQL statement must conform to Oracle’s dialect, but these are small concerns when weighed against the increased response time and the reduced network traffic. In SPT queries, the only thing you send to the server is the SQL statement, and the only thing it sends back is the result. To create a SPT, follow these steps: 1. Create a new query. 2. From the Query menu in design view, right-click, and choose SQL Specific from the menu. 3. Select SQL pass-through. 4. From the SQL editor view of the new query (the only design view available), rightclick the title bar and select Properties. 5. Enter the ODBC Connection string into the ODBC Connect Str property setting.
17 ACCESS 2002 FRONT-ENDS TO ORACLE
But that convenience is expensive. When you query the linked tables, your SQL statement is written in Access, so Oracle does not know what it is. The ODBC driver and Jet have to work together to resolve the statement. The ODBC driver parses the query and might be able to send a rudimentary fragment of your SQL statement to Oracle, usually referring to one table at a time, for processing. However, this usually results in Oracle sending massive amounts of data over the network to the desktop to be processed by Jet. Because Jet is the only data engine that understands the full SQL statement, all the processing happens at the desktop. This might be unavoidable at times, but it should not be confused with real client/server design.
466
Access Client Server PART V
The ODBC connection string could look like this: ODBC;DSN=EmpExample;UID=SCOTT;PWD=tiger;DBQ=empexample;
Tip To save time, you can copy the ODBC connection string from a linked table.
6. Close the Properties dialog box and type the Oracle SQL statement in the editor. An example of the SQL statement could look like this: Select * from emp
Upon execution, Access passes this SQL statement directly to Oracle through the ODBC connection. Jet is not involved at all in its execution. Oracle receives the statement, responds to it, and sends the result, if any, back to the sender. Some situations where SPTs could be used with Oracle data are • To issue SQL statements to alter the schema of an Oracle database • To retrieve selected fields and particular rows from Oracle • To check the number of rows affected by an update SQL statement • To serve as the recordsource of a simple bound form showing a small number of records • To create, replace, and execute views and stored procedures in Oracle. You should always test the option of using a SPT queries to see if it benefits your application.
Understanding SPT Property Requirements When you create a pass-through query, you must specify whether the query returns records. Queries that insert, update, delete, or alter do not return records, while queries that select usually do. If you do not set this query property correctly, an error results.
Understanding SPT Syntax Requirements You must also write the SQL statement in Oracle’s SQL syntax, which differs in important ways from the Access syntax generated by the QBE grid. Access 2002 supports a SQL dialect that more closely conforms to ANSI-92 standard SQL, but if you have been writing Access SQL since version 1.1, a review of the differences will help you use Oracle effectively.
Access 2002 Front-Ends to Oracle CHAPTER 17
467
Oracle does not require you to end the SQL statement with a semicolon [;] when creating SPT queries, even though it requires them when you run SQL statements from SQL PLUS. A semicolon at the end of a SPT statement causes an error. Square brackets are not required to identify table or field names in Oracle. Using them causes an error. Although Access changes the period [.] of SCOTT.EMP to the underscore [_] in SCOTT_EMP when you link the table, you must use Oracle’s native delimiter when making an SPT statement. Oracle’s only works with the period [.].
Oracle SQL statements are not case sensitive, with the exception of the values you are comparing in the Where clause. The following two statements are identical: Select eName, job FroM eMP SELECT ENAME, JOB FROM EMP
However, only the first of the following two statements returns the correct result Select ename, job form EMP where JOB=’MANAGER’ Select ename, job form EMP where JOB=’manager’
Understanding the Select Statement The Select keyword works exactly as it does in Access. Select tells Oracle which fields (or columns) to return in the result. Column names listed after the Select keyword must exist in the tables mentioned in the following From clause. SELECT ename, job, hiredate....
The tables or views used by the query are listed in the From clause, just as in Access. As in Access, the order of the table name or views does not affect the outcome of the query. Select ename, job, hiredate FROM Emp
Understanding the Where Clause The Where clause gives most Access developers problems when they first develop queries against Oracle data because it encompasses the most differences between Access SQL and Oracle SQL. In Oracle, the Where clause not only limits the records returned from the server as it does in Access; it also explains the relationships between the tables and views used in the query.
17 ACCESS 2002 FRONT-ENDS TO ORACLE
Understanding Case Sensitivity
468
Access Client Server PART V
In its implementation familiar to most Access developers, the Where clause restricts records in the result and looks like some of the following examples: Select * from Emp WHERE MNG=7698 Select * from EMP WHERE HIREDATE between ‘1/1/1990’ and ‘1/1/1995’ Select * from EMP WHERE Ename like ‘M%’
Understanding Relationships When more than one table is involved in the query, the Where clause in Oracle states the relationship SELECT emp.ename,dept.dname FROM emp,dept WHERE (emp.deptno=dept.deptno) AND (emp.ename like ‘M%’)
The SQL statement shown above returns records that are equal on each side of the relationship through their related fields as specified in the Where clause (emp.deptno=dept.deptno). To return records that are not represented on both sides of the relationship, you must construct an outer join. In Oracle, an outer join is created by placing a (+) on the side of the relationship that displays the null value when no match exists. A good way to remember it is to put the (+) sign on the side that shows the extra, though blank, entries. The SQL statement below represents a left outer join that shows all employees whether or not they have a matching department code. SELECT emp.ename,dept.dname FROM emp,dept WHERE emp.deptno(+)=dept.deptno
Relationships in Oracle can also be written as expressions. This is not possible in Access. Although using expressions in a relationship is not common, it can be a powerful tool when dealing with less than optimal data arrangements and the peculiar things you can encounter when dealing with legacy systems and data warehouses. The SQL statement below shows how you can use an expression to represent a relationship. Select emp.ename, dept.dname From emp, dept Where (emp.deptno=dept.deptno+10)
Using Wildcards/Position Markers Access takes either the * or the ? as wildcards. The * stands in for any number of text characters when used in the Where clause and the ? holds just one place. In Oracle, you have the same capabilities, but the % substitutes for the * and _ holds a single space. These wildcards can be used as follows: Select * from emp where ename like ‘M%’ Select * from emp where ename like ‘J___S’
Access 2002 Front-Ends to Oracle CHAPTER 17
469
Using Null/Not Null Oracle handles null evaluations the same way that Access does. SELECT * FROM emp WHERE comm IS NULL SELECT * FROM emp WHERE comm IS NOT NULL
Constructing Insert Statements An Access query that inserts new values into an existing table would generate a SQL statement that looks something like this:
However, Access also accepts a “Values” clause like the one shown below for Oracle, but you would have to type it yourself. In Oracle you can simplify that SQL statement as follows: INSERT INTO EMP ( EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO) VALUES( 9998, ‘JP’, ‘SALESMAN’, 7698, _ TO_DATE(‘7/27/1997’,’MM/DD/YYYY’), 2380,0,10)
Notice how Oracle uses the To_Date() to verify that the value entering a date type field is really a date. There is more on this and other Oracle functions later in the chapter.
Constructing Update Statements Issuing an Update SQL in Oracle is not very different from doing so in Access. Following is the Access QBE generated SQL against an attached table followed by the SPT SQL to do the same thing: UPDATE SCOTT_EMP SET SCOTT_EMP.SAL = [7000] WHERE ((([SCOTT_EMP].[EMPNO])=9999));
Oracle’s version is simpler than that of Access with fewer parenthetical clauses and no square brackets. Also, because the ODBC connection string directs the SQL statement directly to the Scott table space, there is no need to preface the table name situations. UPDATE emp SET sal=7000 WHERE empno = 9999
Using Group By/Having Access’s QBE grid takes care of the syntax when you create a grouping query. In Oracle, you have to take care of these issues yourself. As in Access, Group By distills your data down to a representation of each value in the designated columns. Any other columns of
17 ACCESS 2002 FRONT-ENDS TO ORACLE
INSERT INTO SCOTT_EMP ( EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, DEPTNO ) SELECT ‘9999’ AS Expr1, ‘JP’ AS Expr2, ‘SALESMAN’ AS Expr3, 7698 AS Expr4, _ #7/27/1997# AS Expr5, 2380 AS Expr6, 10 AS Expr7;
470
Access Client Server PART V
data displayed in the result must be viewed through an aggregate function (for example, Sum(), Avg(), and so on). In the following SQL statement: SELECT deptno, count(*) FROM emp WHERE deptno 10 GROUP BY deptno HAVING count(*) SELECT MONTHS_BETWEEN(‘27-JUL-2001’,’27-JUL-1997’) FROM SYS.DUAL MONTHS_BETWEEN(‘27-JUL-2001’,’27-JUL-1997’) ------------------------------------------48 SQL> SELECT MONTHS_BETWEEN(‘27-JUL-01’,’27-JUL-97’) FROM SYS.DUAL MONTHS_BETWEEN(‘27-JUL-01’,’27-JUL-97’) ---------------------------------------1152
It is vital that you address this issue in your development efforts. When dealing with dates, you can use the format mask RR to window the century according to the following scheme. If the year is 0–49, Oracle assigns the date to the twenty-first century. If the year is between 50 and 99, it places the date in the twentieth century. SQL> SELECT TO_CHAR(TO_DATE(‘000727’,’RRMMDD’),’DD-MON-YYYY’) RRMMDD, TO_CHAR(TO_DATE(‘000727’,’YYMMDD’),’DD-MON-YYYY’) YYMMDD FROM SYS.DUAL RRMMDD YYMMDD ----------- ----------27-JUL-2000 27-JUL-1900 CONVERSION
Understanding Views and Stored Procedures Passing a SQL statement to Oracle for processing is a great way to improve performance and reduce network traffic, but keeping the SQL statements is not the very best solution possible. To really tap the power of Oracle, benefit from work already done on the database, reduce your maintenance chores, and secure your work and preserve the integrity of your data, you will want to use Oracle views and stored procedures. A view in Oracle is simply a query saved in Oracle. Like a Select query saved in Access, it is compiled and has an execution plan. This means it runs faster than the ad hoc query you send through the SPT. The benefits of using views are many. You can call it by its name, which is usually much shorter than the average SQL statement, thereby further reducing network traffic.
Access 2002 Front-Ends to Oracle CHAPTER 17
483
Because the view resides in Oracle, it has an added level of security, especially beneficial in those applications which remain open or only lightly secured. Because your application is probably not the only one using the Oracle data, you can take advantage of views that have already been built, thereby saving time and assuring consistent results across different applications. You can create a view right from Access or with other Oracle development tools. In order to make a view, or make any schema change for that matter, your Oracle account must have a Resource permission. Check with your database administrator to have this level of permission granted to your account.
Creating a View A view is merely a SQL statement that returns records. Because a view is part of the database schema, it can be created, altered, used, and dropped with SQL. Consider the following simple SQL statement in Oracle’s SQL dialect: Select * from emp where SAL>3000
You could create this in Access and execute it as a pass-through query, or you could create a view in Oracle and simply call that view. Creating a view from Access requires that you create a SQL pass-through to the server and issue a data definition SQL statement from it. These statements begin with the keywords Create, Alter, Drop, and so on. To create a view, your statement should follow this syntax: Create [or Replace] View [ViewName] AS [record returning SQL Statement]
To create a view of the preceding SQL statement, the following data definition SQL could be issued from a SPT: Create or Replace View MyView As Select * from emp where SAL>3000
The keywords “or Replace” would allow you to avert an error should MyView already exist on the server. You might not want to include “or Replace” all the time because you might inadvertently overwrite an existing view, so use it with care.
17 ACCESS 2002 FRONT-ENDS TO ORACLE
Because this chapter is taking advantage of Oracle from Access, everything I do uses only Access as its tool. If you have the option of using SQL Plus or other Oracle administrative tools, you should consult the Oracle documentation on how to use them.
484
Access Client Server PART V
Executing the query now creates an Oracle view that returns all the columns from the emp table with salaries greater than 3000. You can verify the view’s creation by attempting to link it as if it were a table. You can use it as a linked table, or in any valid SQL statement in an SPT. Select * from MyView
or Select MyView.ename, dept.deptno from MyView, Dept Where MyView.deptno=Dept.deptno
Connections to Oracle Through ADO Creating SPTs is a great way to interact with Oracle because it allows the server to do the work instead of Jet. As a matter of fact, Jet’s not involved at all. You can get all the benefits of the SPTs, and much more, by making a connection to Oracle through ADO. This approach enables you to effectively validate data, dynamically construct SQL statements, and execute stored procedures after manipulating and validating variables to use in those procedures. Also, your code can respond to your users in ways that your queries cannot. Creating a connection to Oracle used to involve a lot of complex ODBC API calls or RDO, which was a DAO-like wrapper around ODBC. Now, we have ADO and OLEDB to present a fast, uniform, and flexible interface to a wide variety of different data sources and types, including Oracle. For a detailed explanation of ADO and OLEDB see Chapter 6, “Introduction to ActiveX Data Objects,” and Chapter 7, “Advanced ADO.” This section looks at ways of using ADO/OLEDB to connect to Oracle, issue SQL statements for data manipulation, and define data. When using SPTs, Oracle does the work of resolving the SQL statement and rendering a result set, but the query is ad hoc. Oracle has no plan for it, so performance might be less than optimal. Furthermore, if several applications need the same query or procedure performed, it would have to be developed over and over again and distributed to all the different applications. A better approach might be to create a view or a stored procedure on the server that can be shared by a variety of applications, ensuring consistent results. Because the query or stored procedure has an execution plan, it might perform better than an ad hoc submission. We’ve already seen how to create a view or a stored procedure by using an SPT. The SQL statement is the same, but using ADO code to make the connection and create the view or stored procedure is quite different.
Access 2002 Front-Ends to Oracle CHAPTER 17
485
The examples in this chapter use the Oracle Native OLEDB Provider from Microsoft. This provider interfaces with Oracle’s Call Interface (OCI). However, at this time, not everything that can be done through the OCI can be done through the OLEDB provider, or any other object interface for that matter. By far, most of the things that will need to do with Oracle can be performed through the existing functionality. The code in Listing 17.1 creates a view in Oracle, and then opens it and displays the results in the debug window. LISTING 17.1
Creating a View in Oracle from Access
Dim conn As New ADODB.Connection Dim cmd As New ADODB.Command On Error GoTo CreateOracleView_Err With conn .Provider = “MSDAORA” .ConnectionString = “Data Source=empexample; _ User ID=Scott;password=Tiger” .Open End With cmd.ActiveConnection = conn cmd.CommandType = adCmdText ‘ The Create or Replace SQL statement saves us some error handling cmd.CommandText = “Create or Replace View MyView As _ Select * from emp where SAL>2000” cmd.Execute CreateOracleView_Exit: conn.Close Set conn = Nothing Set cmd = Nothing Exit Function CreateOracleView_Err: ‘ Failure CreateOracleView = False ‘ Find out what errors occured by looping through the Errors With conn For i = 0 To .Errors.Count - 1 errDesc = errDesc & .Errors(i).Description & Chr(13) Next End With
17 ACCESS 2002 FRONT-ENDS TO ORACLE
Function CreateOracleView() As Boolean
486
Access Client Server PART V LISTING 17.1
continued
‘ Notify the user MsgBox “The following error(s) occured:
“ & errDesc
Resume CreateOracleView_Exit End Function
Parameters In Access, a developer can create parameter queries so that the same basic execution plan is used with different criteria values. Unfortunately, Oracle does not expose its views’ parameters through OLEDB providers. An alternative is to replace existing views, as seen previously, or to use the OLEDB provider’s command object to append and feed parameters for SQL statements that are then passed to Oracle. The code in Listing 17.2 demonstrates how this is done. LISTING 17.2
Using Oracle Parameters from Access VBA
Function OracleParams(SalAmnt As Long) As Boolean Dim Dim Dim Dim
conn As New ADODB.Connection rs As New ADODB.Recordset rsfield as New Field cmd As New ADODB.Command
‘ Make the connection With conn .Provider = “MSDAORA” .ConnectionString = “Data Source=empexample;_ User ID=Scott;password=Tiger” .Open End With ‘ Use server side cursors in this case With rs .CursorLocation = adUseServer End With ‘ Build the command object With cmd cmd.ActiveConnection = conn ‘ Set the CommandType to accept a SQL statement cmd.CommandType = adCmdText ‘ Set the CommandText cmd.CommandText = “Select * from emp where sal > ?”
Access 2002 Front-Ends to Oracle CHAPTER 17 LISTING 17.2
487
continued
‘ Create the parameter and Append it to the cmd object’s ‘ Parameter collection cmd.Parameters.Append _ cmd.CreateParameter(“SAL”, adNumeric, adParamInput) ‘ Assign the SalAmnt’s value to the command object’s first parameter cmd(0) = SalAmnt End With ‘ Execute the command and assign the result set ‘ to the ADO recordset object Set rs = cmd.Execute
rs.MoveFirst While Not rs.EOF strfield = “” For Each rsfield In rs.Fields strfield = strfield & “ “ & rsfield Next Debug.Print strfield & Chr(9) rs.MoveNext Wend End Function
Creating Stored Procedures A stored procedure in Oracle is similar to a code module in Access. It executes a set of steps on the server. The main benefits of stored procedures are their speed, reliability, and security. They can be used to enforce business rules, validation, and transactions. The main drawbacks of Oracle’s stored procedures is in the making and debugging of them. Oracle does not provide a development environment that would be familiar to an Access/VB developer. This makes building, maintaining, and replacing stored procedures difficult. Though Oracle8 provides a schema interface that validates procedures when you create them, it is still tough going. Additionally, stored procedures are specific to the server you are using, so if you have to migrate to another server, you’ll have to rewrite the stored procedures.
ACCESS 2002 FRONT-ENDS TO ORACLE
If conn.Errors.Count = 0 Then OraclParams = True End If
17
488
Access Client Server PART V
N-tier architectures were developed partly in response to these drawbacks. There is no compelling reason to place all complex business logic on the server (just as there is no compelling reason to place it all on the client), so stored procedures themselves are becoming much simpler as other objects are handling business rules and other tasks. This section creates and calls a simple stored procedure from Access. To create procedures in your own schema you must have the Create Procedure system privilege that is part of the Resource role. To create procedures in someone else’s schema, you must have Create Any Procedure privileges. Consult your Oracle Database Administrator if you are not sure of your permissions. Procedures break down into the following pieces: • Declaration of arguments • Beginning of processes • Commitment of processes • Exception handling • Rollback of commitment • Error handling To table with two fields would look like Listing 17.3. LISTING 17.3
An Oracle Procedure to Add a New Record
(Ename CHAR, EJob CHAR) IS BEGIN INSERT INTO SCOTT.TESTTABLE VALUES(Ename, EJob); COMMIT; EXCEPTION WHEN OTHERS THEN ROLLBACK; RAISE; END;
In this case the stored procedure takes two arguments, a name and a job title, and supplies them to the SQL statement. Access can create this procedure by way of a SPT as follows: CREATE OR REPLACE PROCEDURE MyStoredProcedure (Ename CHAR, EJob CHAR) IS BEGIN INSERT INTO SCOTT.TESTTABLE VALUES(Ename, EJob);
Access 2002 Front-Ends to Oracle CHAPTER 17
489
COMMIT; EXCEPTION WHEN OTHERS THEN ROLLBACK; RAISE; END;
Once this procedure is created, it can be executed through a SPT or through VBA.
Calling a Stored Procedure Calling it from a SPT is a bit different than calling a view:
The curly brackets mimic the syntax Oracle expects when addressed through its call interface, which is the interface used by OLEDB provider. However, running the stored procedures from VBA through OLEDB provider for Oracle looks a bit different. The following function in Listing 17.4 takes advantage of the fact that stored procedures expose their parameters in a way that can be used in VBA. By supplying a stored procedure name, the name of the table into which you are going to insert a new record, and filling out the required arguments in the paramArray of the function, most stored procedures could be executed with the function in Listing 17.4, or a variation of it. LISTING 17.4
Executing a Stored Procedure with Parameters from Access VBA
Function RunOracleStoredProcedures(ProcName As String, DestTable as String, _ ParamArray ParamArgs() As Variant) As Boolean Dim Dim Dim Dim Dim Dim
conn As New ADODB.Connection rs As New ADODB.Recordset i As Integer cmd As New ADODB.Command rsfield As Field strfield As String
With conn .Provider = “MSDAORA” .ConnectionString = “Data Source=empexample;_ User ID=Scott;password=Tiger” .Open End With
ACCESS 2002 FRONT-ENDS TO ORACLE
{call MyStoredProcedure }
17
490
Access Client Server PART V LISTING 17.4
continued
cmd.ActiveConnection = conn cmd.CommandType = adCmdStoredProc cmd.CommandText = ProcName For i = 0 To UBound(ParamArgs()) With cmd.Parameters(i) .Type = adChar .Direction = adParamInput .Value = ParamArgs(i) End With Next cmd.Execute ‘ Now we’ll review the result in the debug window ‘ You would not need this code in reality rs.Open “Select * from “ & destTable, conn, _ adOpenForwardOnly, adLockReadOnly rs.MoveFirst While Not rs.EOF strfield = “” For Each rsfield In rs.Fields strfield = strfield & “ “ & rsfield Next Debug.Print strfield & Chr(9) rs.MoveNext Wend Set rs = Nothing conn.Close RunOracleStoredProcedures = True End Function
Calling many simple stored procedures from code gives you the ability to create complex procedural logic from within the familiar surroundings of VBA while taking advantage of the rich debugging and error-handling capabilities.
Access 2002 Front-Ends to Oracle CHAPTER 17
491
Creating an Unbound Interface to Oracle Given the performance limitations and heavy network load of linked tables, Access applications using Oracle should consider using an unbound user interface whenever possible.
The following exercise creates a simple, unbound interface to Oracle that looks and feels like a bound form. In order to show ADO’s flexibility, there are SQL statements as well as recordset methods being employed. The form displays records from the EMP table. You will be able to insert and delete records from the Oracle database. You will also be able to edit the records without having to consciously save each changed record.
Building an Unbound Interface To start, build a form like the one in Figure 17.3. FIGURE 17.3 Data form in Design view.
17 ACCESS 2002 FRONT-ENDS TO ORACLE
Creating an unbound interface frees you to employ n-tier architecture in your application, increasing your application’s scalability and maintainability. It also allows you to mix your tools more freely. You can use ADO, ADOX, DAO, RDO, and so on where you think one might offer an advantage over another, or where it might simply be more familiar to you. An unbound interface makes future migrations to another development platform, like VB, easier than it would be from a bound interface. And by ensuring that the data processing work is performed on the server, you might gain some performance benefits as well.
492
Access Client Server PART V
For simplicity, the control names correspond to the field names of the EMP table: •
Empno
•
Ename
•
Job
•
Mgr
•
Hiredate
•
Sal
•
Comm
•
Deptno
The navigation and manipulation buttons are named as follows: •
BtnGotoFirst
•
BtnGotoPrevious
•
BtnGotoNext
•
BtnGotoLast
•
BtnEdit
•
BtnNew
•
BtnDelete
•
BtnSave
•
BtnClose
Creating Global Variables The basic tasks of this form can be handled in many ways. For simplicity and focus, the connection to the database, the recordset used by the form, and a flag to indicate if the data is being edited will be handled by three global variables declared in standard module: Global conn As ADODB.Connection Global rs As ADODB.Recordset Global EditRec As Boolean
Loading Data and Initializing the Form The code listed in Listing 17.5 triggers the data loading and initialization of the application from the OnOpen event of the form.
Access 2002 Front-Ends to Oracle CHAPTER 17 LISTING 17.5
493
Create an Open Connection to Oracle and Fill the Form
Private Sub Form_Open(Cancel As Integer) Set conn = New ADODB.Connection Set rs = New ADODB.Recordset With conn .Provider = “MSDAORA” .ConnectionString = “Data Source=empexample;_ User ID=Scott;password=Tiger” .Open End With
FillForm EditRec = True End Sub
Oracle does not return recordsets, but the OLEDB provider you are using allows you to capture the results of a SQL statement as a recordset. The OLEDB provider masks Oracle’s call interface so it appears to you and behaves like any other data handled by OLEDB. By using a global recordset, you can quickly and easily fill the form’s controls with the first record of data. It then sets the EditRec flag to true to indicate that the form’s data is now ready to be edited. The OnOpen event calls the FillForm function listed in Listing 17.6. LISTING 17.6
Filling the Form with Data
Function FillForm() As Boolean On Error GoTo FillForm_Err [Empno] = rs(0) [ENAME] = rs(1) [Job] = rs(2) [MGR] = rs(3) [HIREDATE] = rs(4) [SAL] = rs(5) [COMM] = rs(6) [DEPTNO] = rs(7) FillForm = True
ACCESS 2002 FRONT-ENDS TO ORACLE
rs.CursorLocation = adUseClient rs.Open “Select * from emp”, conn, adOpenStatic, _ adLockOptimistic, adAsyncFetch
17
494
Access Client Server PART V LISTING 17.6
continued
FillForm_Exit: Exit Function FillForm_Err: FillForm = False Exit Function End Function
Programming the Navigation Buttons In order to move through the recordset, you need to program the navigation buttons as shown in Listings 17.7 through 17.10. LISTING 17.7
Go to First Record
Private Sub btnGotoFirst_Click() On Error Resume Next rs.MoveFirst If Not rs.BOF Then FillForm End If CurrentContext Me End Sub
LISTING 17.8
Go to Previous Record
Private Sub btnGotoPrevious_Click() On Error Resume Next rs.MovePrevious If Not rs.BOF Then FillForm End If CurrentContext Me End Sub
Access 2002 Front-Ends to Oracle CHAPTER 17 LISTING 17.9
495
Go to Next Record
Private Sub btnGotoNext_Click() On Error Resume Next rs.MoveNext If Not rs.BOF Then FillForm End If CurrentContext Me
LISTING 17.10
Go to Last Record
Private Sub btnGotoLast_Click() On Error Resume Next rs.MoveLast If Not rs.BOF Then FillForm End If On Error GoTo 0 CurrentContext Me End Sub
The current state of the navigation buttons depends on your position within the record set. The CurrentContext function, which takes the form as an argument, controls when to activate or deactivate the different navigation buttons, as shown in Listing 17.11. LISTING 17.11
Activating and Deactivating the Navigation Buttons
Sub CurrentContext(frm As Form) Dim bFirstRec As Boolean Dim bLastRec As Boolean bFirstRec = (rs.AbsolutePosition = 1) bLastRec = (rs.AbsolutePosition = rs.RecordCount) With frm
17 ACCESS 2002 FRONT-ENDS TO ORACLE
End Sub
496
Access Client Server PART V LISTING 17.11
continued
If Not bFirstRec And Not bLastRec Then !btnGotoFirst.Enabled = True !btnGotoPrevious.Enabled = True !btnGotoLast.Enabled = True !btnGotoNext.Enabled = True GoTo CurrentContext_Exit End If If bFirstRec And bLastRec Then !btnEdit.SetFocus !btnGotoFirst.Enabled = False !btnGotoPrevious.Enabled = False !btnGotoLast.Enabled = False !btnGotoNext.Enabled = False GoTo CurrentContext_Exit End If If bFirstRec Then !btnGotoLast.Enabled = bFirstRec !btnGotoNext.Enabled = bFirstRec !btnGotoLast.SetFocus !btnGotoFirst.Enabled = Not bFirstRec !btnGotoPrevious.Enabled = Not bFirstRec GoTo CurrentContext_Exit End If If bLastRec Then !btnGotoFirst.Enabled = bLastRec !btnGotoPrevious.Enabled = bLastRec !btnGotoFirst.SetFocus !btnGotoLast.Enabled = Not bLastRec !btnGotoNext.Enabled = Not bLastRec GoTo CurrentContext_Exit End If End With End Sub
Updating Oracle Data Using the Form So far, you can view data only, but none of the changes you make will be saved. The function in Listing 17.12 uses the Update method of the ADODB recordset to pass your changes along to Oracle as soon as you make them. This gives your interface behavior similar to a hyperactive bound form, when in fact it is not bound at all. This function, shown in Listing 17.12, must be called out of the AfterUpdate event of each text box.
Access 2002 Front-Ends to Oracle CHAPTER 17 LISTING 17.12
497
The UpdateField Function
Function UpdateField(FieldName As Variant, NewValue As Variant) As Boolean If EditRec = False Or IsEmpty(EditRec) Then Exit Function Dim rsOrigSource As String Dim errDesc As String Dim i As Integer On Error GoTo UpdateField_Err
‘ Attempt to update the field using the update method of the recordset rs.Update FieldName, NewValue ‘ Success UpdateField = True UpdateField_Exit: Exit Function UpdateField_Err: ‘ Failure UpdateField = False ‘ Find out what errors occured by looping through the Errors With conn For i = 0 To .Errors.Count - 1 errDesc = errDesc & .Errors(i).Description & Chr(13) Next End With ‘ Notify the user MsgBox “The following error(s) occured:
“ & errDesc
‘ Must counter the Update method with the CancelUpdate method ‘ otherwise you won’t be able to manipulate the recordset object rs.CancelUpdate ‘ Close the existing recordset rs.Close ‘ Open the recordset with the old source, thereby setting ‘ the form back to its original condition rs.Open rsOrigSource, conn, adOpenStatic, adLockOptimistic, adAsyncFetch ‘ Call FillForm Forms![form1].FillForm
17 ACCESS 2002 FRONT-ENDS TO ORACLE
‘ Keep the original recordset’s source around in the event of a failure rsOrigSource = rs.Source
498
Access Client Server PART V LISTING 17.12
continued
Resume UpdateField_Exit End Function
The UpdateField function can be called from each AfterUpdate event by selecting all the text box controls at the same time and pasting in the expression in Listing 17.13. An alternative to saving changes immediately would be to buffer the changes and then save them when you move to a new record. You could also take advantage of ADO’s ability to call a stored procedure in Oracle and pass the new values to its parameters. LISTING 17.13
Expression to Save Changes to Fields After Update
=UpdateField(screen.activecontrol.[Name],screen.activecontrol)
Now when you cycle through the records and change a data item for an employee, the change is saved to Oracle as soon as you move to another field.
Adding Oracle Data Using the Form To add a new record, you need to clear all the controls on the form and set the EditRec flag to false. The code in Listing 17.14 could be written into the btnNew OnClick event. LISTING 17.14
Preparing the Form for a New Record
Private Sub btnNew_Click() ClearForm EditRec = False End Sub
A Utility function needs to be written to clear the form for edits and inserts. For our purposes, this can be located in a standard module behind the form. The function is exceedingly simple and is shown in Listing 17.15. LISTING 17.15
Clear the Form
Sub ClearForm() Me![Empno] = “” Me![ENAME] = “” Me![Job] = “” Me![MGR] = “” Me![HIREDATE] = “”
Access 2002 Front-Ends to Oracle CHAPTER 17 LISTING 17.15
499
continued
Me![SAL] = “” Me![COMM] = “” Me![DEPTNO] = “” End Sub
Saving Oracle Data Using the Form
LISTING 17.16
Addressing Oracle Through SQL and ADO
Private Sub btnSave_Click() Dim cmd As New ADODB.Command Dim strCmdtxt as string ‘ If we are not editing a record then..+. If EditRec = False Then ‘ make sure the command object is properly configured and then ‘ construct and execute the Insert SQL statement With cmd .ActiveConnection = conn .CommandType = adCmdText with Me strCmdtxt = strCmdtxt & “insert into emp values (“ strCmdtxt = strCmdtxt & “‘![Empno] ‘,” strCmdtxt = strCmdtxt & “‘![ENAME] ‘,” strCmdtxt = strCmdtxt & “‘![Job] ‘,” strCmdtxt = strCmdtxt & “‘![MGR] ‘,” strCmdtxt = strCmdtxt & “‘ to_date(‘“ &![HIREDATE] & “‘,’MM/DD/YY’) ‘,” strCmdtxt = strCmdtxt & “‘![SAL] ‘,” strCmdtxt = strCmdtxt & “‘![COMM] ‘,” end with .Execute End With End If ‘ Close, configure and reopen the recordset With rs .Close
17 ACCESS 2002 FRONT-ENDS TO ORACLE
When it comes time to save the record, you can execute the following code from the btnSave Onclick event. It checks to see that you are entering a new record by checking the EditRec flag and then fills out a SQL statement and executes it in a command object. This technique is shown in Listing 17.16 as a way to address Oracle as it is customarily.
500
Access Client Server PART V LISTING 17.16
continued
.CursorLocation = adUseClient .Open “Select * from emp”, conn, adOpenStatic, adLockOptimistic, adAsyncFetch End With ‘ Fill the form, you might want to handle this differently. FillForm ‘ Put the application back in edit mode EditRec = True End Sub
Deleting Oracle Data Using the Form The same basic approach is taken to delete a record. The code in Listing 17.17 deletes the current record from Oracle when executed from the OnClick event of btnDelete. LISTING 17.17
Delete the Current Record
Private Sub btnDelete_Click() Dim cmd As New ADODB.Command ‘ If we are not editing a record then... If EditRec = True Then ‘ make sure the command object is properly configured and then ‘ construct and execute the Delete SQL statement With cmd .ActiveConnection = conn .CommandType = adCmdText .CommandText = “Delete from emp where empno= “ & Me![Empno] .Execute End With End If ‘ Close, configure and reopen the recordset With rs .Close .CursorLocation = adUseClient .Open “Select * from emp”, conn, adOpenStatic, adLockOptimistic, adAsyncFetch End With ‘ Fill the form, you might want to handle this differently. FillForm End Sub
Access 2002 Front-Ends to Oracle CHAPTER 17
501
To make the navigation buttons reflect the context of your form’s navigation through the available records, the code in Listing 17.18 can be written into the form’s OnCurrent event. LISTING 17.18
Populate Fields with Data, Then Check Current Context of the Form
Private Sub Form_Current() On Error Resume Next Dim bFirstRec As Boolean Dim bLastRec As Boolean
CurrentContext Me End Sub
Closing the Connection to Oracle When closing the form, close the connection to Oracle and set the connection and recordset objects to nothing. In other applications, you might want to keep the objects around. The function in Listing 17.19 handles these housekeeping chores. LISTING 17.19
Cleaning Up on Close
Private Sub btnClose_Click() On Error GoTo Err_btnClose_Click_Err DoCmd.Close Exit_btnClose_Click_Exit: conn.Close Set conn = Nothing Set rs = Nothing Exit Sub
ACCESS 2002 FRONT-ENDS TO ORACLE
If Not rs.BOF Then With Me ![Empno] = rs(0) ![ENAME] = rs(1) ![Job] = rs(2) ![MGR] = rs(3) ![HIREDATE] = rs(4) ![SAL] = rs(5) ![COMM] = rs(6) ![DEPTNO] = rs(7) End With End If
17
502
Access Client Server PART V LISTING 17.19
continued
Err_btnClose_Click_Err: MsgBox err.Description Resume Exit_btnClose_Click_Exit End Sub
By managing other recordset objects, persistent recordset objects, and by using the OLEDB provider’s simplification of the Oracle call interface, you can create an entire unbound interface to Oracle, thereby taking full advantage of the speed, scalability, and robustness of Oracle in your Access application.
Summary In this chapter I explored some of the different ways to use Oracle in your Access solution. You saw the convenience of Linked tables, but you also learned of their drawbacks. SQL pass-through queries overcome the problems caused by local processing of linked tables. You saw how to create and use them, with a review of the SQL and function differences you must negotiate with Oracle to get the results you want. Giving Oracle ad hoc instructions in a SPT is not a good way to use Oracle to the fullest in most cases. To learn to use Oracle fully, you created, modified, and ran views and stored procedures using only Access tools. This makes Oracle put its query executing plans to work, while at the same time drastically cutting network traffic when compared to using linked tables. This is Oracle development more than Access development. Finally, to demystify the use of Oracle, you created a simple unbound interface that almost anyone could build—even if they do not have permissions to create and modify views and procedures. By using the OLEDB provider for Oracle, the techniques used to build that interface look almost identical to any other unbound interface using OLEDB provider.
Interoperability
PART
VI IN THIS PART 18 Using ActiveX Automation 19 Integrating with Microsoft Office 20 Using Visual Basic with Access
CHAPTER 18
Using ActiveX Automation
IN THIS CHAPTER • What Is ActiveX Automation? • Why Use Automation?
506
506
• Distinguishing Automation Server Versus Automation Client 506 • Determining Automation Resource Requirements 507 • Understanding the Big Picture
507
• Creating and Setting a Reference to Another Application 507 • Assigning an Object Variable to an Application 512 • Creating an Instance of the Application 513 • Using the Automation Object’s Properties and Methods 517 • Releasing the Automation Object • Putting It All Together
518
• Closing the Automation Server Application 519
517
506
Interoperability PART VI
Today, more than ever, developers are asked to build more sophisticated applications faster and integrate them with other software. Fortunately, with ActiveX Automation (formerly called OLE Automation), applications can work together to provide a comprehensive software solution. This can substantially speed up development time, because instead of creating a feature, developers can integrate and use other software that has the feature. This chapter covers the principles and techniques for ActiveX Automation. Chapter 19, “Integrating with Microssoft Office,” specifically explains how to use ActiveX Automation to integrate Access with the rest of Microsoft Office.
What Is ActiveX Automation? ActiveX technology is based on Microsoft’s Component Object Model (COM). ActiveX Automation enables programs to interact with each other. Automation specifically enables one program to access and manipulate another program’s objects from outside that application. An example of Automation is printing letters or reports in Microsoft Word from Microsoft Access.
Why Use Automation? Using Automation is an effective way to build and extend applications. Automation can substantially speed up development time because rather than developing and testing a feature from scratch, you can “borrow” the functionality from another application. There is no reason today to build your own word processor or spreadsheet functionality within your Access application. It is much easier to use Automation and some of the techniques discussed in this chapter to automate Word or Excel for that functionality. This enables a developer to focus on features not provided by other programs that can be of benefit to users.
Distinguishing Automation Server Versus Automation Client When two applications are interacting with one another, it is important to distinguish which application is exposing its objects and which application is using those objects. The Automation Server is the application that exposes the Automation objects. The Automation Client (or sometimes referred to as Automation Controller) is the application that decides which objects to use and when to use them.
Using ActiveX Automation CHAPTER 18
507
For example, if a command button on an Access form is clicked and prints a letter in Word, the Automation Server is Word, and the Automation Client is Access.
Determining Automation Resource Requirements Because Automation involves one application controlling another, at a minimum, two programs are open. Whenever multiple applications are running, additional processing power and memory is required. What requirements are necessary to automate from Access to Word, for example, is a subjective decision. What one user might find satisfactory, another might find unacceptable. The minimum acceptable performance would be a Pentium II processor with 64MB of RAM. Certainly, a Pentium processor would substantially improve performance; however, RAM is more beneficial than processing power. If Automation performance is not acceptable, you should increase the amount of RAM first and upgrade the processor speed second.
Understanding the Big Picture 1. Get the Automation object. Either create a new one or reference an existing one. 2. Use the Automation object as needed in your application. 3. Release the Automation object. As an example, if you would like to print letters or reports in Word from Access, the first step would be to create or get a reference to the Word application. Next, use Word’s objects, properties, and methods (such as open, modify, or print a document). Finally, release the Word application.
Creating and Setting a Reference to Another Application To automate to another application, it is necessary to set a reference to the other application by creating an object variable and assigning it to an instance of the application.
Setting a Reference to Another Application With Automation, you are interacting or using an application outside of Microsoft Access. Whenever an application or component is used outside of Access, you must set a
USING ACTIVEX AUTOMATION
There are three steps to Automation:
18
508
Interoperability PART VI
reference to that application. To do so, go to the Visual Basic Editor and choose Tools, References. A Reference dialog box will display a list of type libraries, programs, DLLs, and ActiveX controls (see Figure 18.1). Set a reference to the application you would like to automate. For example, to set a reference to Word 2002, check the box next to Microsoft Word 10.0 Object Library. FIGURE 18.1 The References dialog box.
Tip If you cannot find the item that you would like to set a reference to in the References dialog box, click the Browse button to locate the application in the file system.
After you have set a reference to another application, you can review and use its objects, properties, and methods.
Reviewing Objects, Properties, and Methods Before discussing how to use another application’s objects, let’s review some basics. It is essential to understand the terms object, properties, and methods, or the rest of the Automation chapter will make no sense. An object is an item that can be programmed, manipulated, or controlled. In Access, objects include forms, text boxes, command buttons, and more. With Automation, other applications and their objects can be programmed and manipulated from Access. Microsoft Office has more than 500 objects that can be programmed, manipulated, or controlled. For example, Word has a Document object and Excel has a Workbook object.
Using ActiveX Automation CHAPTER 18
509
A property is a characteristic of an object. A property can be thought of as an adjective because it describes or characterizes an object. In Access, properties of a text box include Name, Visible, Forecolor, and more. Likewise, when using objects in other applications, those objects have properties that can be manipulated via Automation. For example, similar to an Access form, Word and Excel applications can be set to visible or invisible. Most properties of an object can be both set and retrieved. A method is an action that can be taken on an object. A method can be thought of as a verb. For example, a method of an Access form is Close. An example of a method in Excel is Quit which closes the Excel application.
Understanding Object Models When automating to another application, it is helpful to review its Object Model to understand what objects are available to work with. The Object Model is a representation (in outline form) of an application’s objects. This is a quick and easy way to determine what objects in the application are available to program, manipulate, or control. Object Models for various applications can be found in the Help files. Object Models for the Microsoft Office programs are shown in Chapter 19.
Another way to quickly determine the objects available in an application is to use the Object Browser. The advantages of the Object Browser are that it includes properties, methods, and events, and it has links to online help. The Object Browser is a helpful tool to quickly search, find, and learn about the various objects of an application. To use the Object Browser in the Visual Basic Editor, under the View menu, choose Object Browser or press F2 (see Figure 18.2). When first opening the Object Browser, the combo box in the top-left corner will be set to All Libraries. With this setting, the Object Browser is showing all objects for all referenced libraries. The first step might be to select the application in the combo box for the objects that you want to review. For example, if Word is selected, only Word objects will be shown. The other combo box can be used to search for a particular item. For example, if you want to search for a message box in all libraries, set the top combo box to All Libraries, and in the combo box beneath it type MsgBox and click on the binoculars icon (see Figure 18.3).
USING ACTIVEX AUTOMATION
Using the Object Browser
18
510
Interoperability PART VI
FIGURE 18.2 Object Browser showing Microsoft Word objects.
FIGURE 18.3 Search for MsgBox in the Object Browser.
The lower-left pane of the Object Browser is entitled Classes. The classes list shows the available types of objects in the selected type library. When an item is selected in the classes list, the properties, methods, and events are shown in the Members Of list. The lower-right pane, entitled Members, shows the items related to the particular class chosen on the left side. For example, if the application object is selected, the right pane
Using ActiveX Automation CHAPTER 18
511
Members will show all the application’s objects properties, methods, and events. The Members Of list displays the properties, methods, events, and constants that are associated with the selected object in the classes list. Where does the Object Browser get information about the objects, properties, and methods? Most applications that ship today contain a type library file which has information about the application’s objects, properties, methods, and constants. The Object Browser shows information from the type library. Type library files usually have a .TLB or .OLB file extension. Tip By default, the items listed in the Classes and Members panes are in alphabetical order. It is useful to right-click the panes in the shortcut menu and choose Group Members. This will re-sort the items in the list by putting together all the properties, methods, and events. The items within each property, method, and event are sorted in alphabetical order.
18
Creating an Object Variable A variable is a chunk of memory set aside to store or retrieve information. Undoubtedly, you have had a great deal of experience with simple variables, such as strings and integers. The following are examples of declaring and using two simple variables: Dim strName as String Dim I as integer StrName = “Russ” I = 10
In the preceding examples, the variables include a specific type of data and the information is stored and can be retrieved as needed. Object
variables are declared with the Dim statement just like simple variables:
Dim objWord as Word.application Dim objExcel as Excel.application
USING ACTIVEX AUTOMATION
At the very bottom of the Object Browser, the syntax is shown for the item selected. For example, with the Access Application object selected and by choosing the Dlookup, the correct syntax is shown at the very bottom of the Object Browser. For further information, after an item is selected, press F1 to review the online help for the item.
512
Interoperability PART VI
Tip Fully qualify all objects used in Automation. Instead of Dim objWord as Application
use Dim objWord as Word.Application
Several referenced programs might have an application object. Fully qualifying the object variable assures that the code will run properly.
Referencing an Application That Is Already Running After creating an object variable, the next step is to actually reference the application you want to automate. However, remember, you have no idea whether the application you want to automate is already open on the user’s computer. If you want to automate Word, the user might already have Word open. If this is the case, you might want to use the current instance of Word for your Automation session. Otherwise, you will be opening a separate instance of Word and rapidly consuming additional resources on the computer. To determine whether an instance of the application is running, use the GetObject function. The GetObject function will reference an application that is already running. ‘ Declare objWord as an Object Variable. Dim objWord As Word.Application ‘ Use “GetObject” function to use an application that is already running. Set objWord = GetObject(, “Word.Application”)
Assigning an Object Variable to an Application The Set keyword is used to assign the reference of the object variable to the object. For example: Set objExcel = Excel.Application
Object variables hold a pointer to an object. When an object variable is assigned to the Excel application, the object variable contains a pointer to the Excel application; it does not hold the entire Excel application within the variable. When objects are passed from one procedure to another, they are always passed by “reference,” never by “value.”
Using ActiveX Automation CHAPTER 18
513
Creating an Instance of the Application If the application is not open or you want to create a new instance of the application, use the New keyword. The following example would create a new instance of Microsoft Word: ‘ Declare objWord as an Object Variable. Dim objWord As Word.Application ‘ Create a new instance of Microsoft Word. Set objWord = New Word.Application
Caution Always declare the Dim statement and Set statement on separate lines. Do not combine the Dim and Set statements on a single line such as Dim objWord = New Word.application.
Using GetObject and the New Keyword Together If an application is already open, use it; do not open another version of the application. If the application is not running, create a new instance of the application. To accomplish this, use the GetObject function to see if the application is running, and the New keyword to start a new instance of the application (if necessary). ‘ If Word is running, use it. If Word is not running, create an instance ‘ of Word._ ‘ Declare objWord as an Object Variable. Dim objWord As Word.Application ‘ An error will occur with GetObject if Word is not running. Ignore the ‘ error. On Error Resume Next ‘ Attempt to reference Word which is already running.
18 USING ACTIVEX AUTOMATION
If you combine the Dim and Set statement on one line, the code will execute more slowly and you will not know exactly when the object is instantiated (comes into memory).
514
Interoperability PART VI Set objWord = GetObject(, “Word.Application”) ‘ If objWord is Nothing (an uninstantiated Object Variable), Word is ‘ not running. If objWord Is Nothing Then ‘ Create a new instance of the Word application. Set objWord = New Word.Application ‘ If objWord is still “Nothing” assume MS Word 10.0 is not ‘ installed on the computer. If objWord Is Nothing Then MsgBox “MS Word 2002 is not installed on your computer” End If End If ‘ On Error GoTo ErrorHandler (Put Error Handler Code Here!)
Note The New keyword can only be used if you can create a reference to the application’s type library.
Using Early Versus Late Binding Notice that the object variable in the Dim and Set statements specifically references the Word.Application. This is referred to as early binding. At compile time, the object variable is bound to the Word application’s type library. This will result in the code executing much faster because much of the work is done at compile time rather than at runtime. On the other hand, if objWord were declared as an object, the binding to the Word application’s type library would not occur until runtime. This would slow the execution of the code substantially. The following example summarizes the point: ‘ Use early binding, it’s faster. Dim objWord as Word.Application ‘ Late binding, this code is slower. Dim objWord as Object
Using ActiveX Automation CHAPTER 18
515
Tip In the Automation.mdb included in Chapter 19, there are speed test examples that show that early binding is substantially faster than late binding.
Using the CreateObject Function The most efficient Automation code will declare object variables with a specific class (early binding), use the GetObject function to determine if the application is already running, and use the New keyword to create an instance of the application if it is not running. Unfortunately, in some cases, it is not possible to use Early Binding or the New keyword. The CreateObject function is useful in these circumstances.
When Other Applications Cannot Be Referenced
Note You can still use the GetObject function to determine whether Word is currently running. The CreateObject function creates a new instance of the application.
‘ Declare objWord as an Object Variable. Dim objWord As Word.Application ‘ An error will occur with GetObject if Word is not running. Ignore the ‘ error. On Error Resume Next ‘ Attempt to reference Word which is already running. Set objWord = GetObject(, “Word.Application”) ‘ Error 429 occurs if Word is NOT running. If Err.Number = 429 Then Err.Number = 0
18 USING ACTIVEX AUTOMATION
When using Automation from some applications (fortunately not Access), you cannot create a reference to another application (there is no references dialog box). Examples include Outlook 97/98 and Internet development. In these cases, you are forced to use late binding and the CreateObject function. The following is an example of Automation code you can write within Outlook to automate to Word.
516
Interoperability PART VI ‘ Create a new instance of the Word application. Set objWord = CreateObject(“Word.Application”) ‘ If error 429 occurs again, assume MS Word 10.0 is not installed ‘ on the computer. If Err.Number = 429 Then MsgBox “MS Word 2002 is not installed on your computer” End If End If
‘ On Error GoTo ErrorHandler (Put Error Handler Code Here!)
When Users Have Different Versions of the Application In some companies, different users can be running different versions of an application. Some users might be using Word 95, others Word 97, whereas still others are using Word 2000 or Word 2002. A few users might even have multiple versions of Word on the same computer! With the CreateObject function, you can pass as a parameter which particular version of Word to use. The following example uses CreateObject to open Word 97: ‘ Declare objWord as an Object Variable. Dim objWord As Word.Application ‘ Create a new instance of a Word 97 application. Set objWord = CreateObject(“Word.Application.8”)
Note The version number for Office 95 programs is 7.0; Office 97 is 8.0; Office 2000 is 9.0; and Office XP is 10.0. You can also pass a parameter with the version number when using the New keyword.
Using ActiveX Automation CHAPTER 18
517
Using the Automation Object’s Properties and Methods After you have an instance of the object, you can program, manipulate, and control the object’s properties and methods.
Setting an Object’s Properties This is where Automation really pays off. You can now work with other applications to use their features and functionality. In the same way that you work with an object’s properties in Access, you can work with the Automation object’s properties. The following examples use properties in Word and Excel: ‘ Setting an Automation Object’s Properties objWord.Visible = True objExcel.Cells(1,1).Value = “Yearly Sales”
Setting an Object’s Methods ‘ Executing an Object’s “Add” Method objWord.Documents.Add objExcel.Workbooks.Add
Tip Chapter 19 includes numerous examples of automating to Microsoft Office applications. In that chapter, objects, properties, and methods of various applications will be discussed in detail.
Releasing the Automation Object When you are done with the object variable, release it by setting the object equal to Nothing. In this way, valuable resources can be reclaimed. The proper syntax is as follows: ‘ Release the Object Variable Set objWord = Nothing
18 USING ACTIVEX AUTOMATION
With Automation, you can execute an object’s method. The following are a few examples:
518
Interoperability PART VI
Tip When you dimension an object variable, immediately go to the bottom of the procedure and Set the object variable equal to Nothing. This prevents you from forgetting to release object variables after writing a long procedure.
Putting It All Together I have now reviewed all the steps to make Automation work. Let’s now look at a code sample that puts it all together. This example will print a Word document with Automation. All the steps previously discussed are included in the code: creating or getting a reference to Word, using Word’s objects (specifically the PrintOut method), and releasing the Automation object. ‘ Declare objWord as an Object Variable. Dim objWord As Word.Application ‘ An error will occur with GetObject if Word is not running. Ignore the ‘ error. On Error Resume Next ‘ Attempt to reference Word which is already running. Set objWord = GetObject(, “Word.Application”) ‘ If objWord is Nothing (an object variable that does not refer to an ‘ object), Word is not running. If objWord Is Nothing Then ‘ Create a new instance of the Word application. Set objWord = New Word.Application ‘ If objWord is still “Nothing” assume MS Word 10.0 is not ‘ installed on the computer. If objWord Is Nothing Then MsgBox “MS Word 2002 is not installed on your computer” End If End If ‘ On Error GoTo ErrorHandler (Put Error Handler Code Here!) ‘ Open a Word document. objWord.Documents.Open (“C:\Automation\Northwind Magazine Ad.doc”)
Using ActiveX Automation CHAPTER 18
519
‘ Print the Word document objWord.PrintOut Background:=False ‘ Close Word objWord.Quit ‘ Release the object variable. Set objWord = Nothing
Tip Admittedly, this is a very simple example. Chapter 19 covers more complex Automation code and the Automation.MDB file for that chapter has thousands of lines of Automation code for your use.
Closing the Automation Server Application If the application is already running when you start the Automation session, and you use that instance of the application, do not close the application at the end of the Automation session. Users will become exceedingly frustrated if applications they use are closed by your code. If, on the other hand, you create a new instance of the application, you should close the application at the end of the Automation session. If not, the application will remain open unused by the user, but taking up valuable computer resources. To close an application, use the appropriate method for the application. For example, with Excel, call the Quit method as follows: ‘ Close the Excel application by calling the “Quit” method. objExcel.Quit
USING ACTIVEX AUTOMATION
During the course of an Automation session, you can use a current instance of the application that is running, or open a new instance of the application. It is important that you close the Automation Server application under the appropriate circumstances.
18
520
Interoperability PART VI
Using the UserControl Property to Determine How an Application Was Opened The UserControl property enables you to determine whether an application or document was created or opened by the user or programmatically. If the UserControl property (a Boolean) is true, the application or document was opened by the user; if false, the application or document was opened programmatically. The following example opens Word programmatically and then tests how Word was opened using the UserControl property. ‘ Declare objWord as an Object Variable. Dim objWord As Word.Application ‘ Open Word programmatically. Set objWord = New Word.Application ‘ Check the UserControl property to see if Word was opened ‘ by the user or opened programmatically. If objWord.Application.UserControl Then MsgBox “The Word application was opened by the user”. Else MsgBox “The Word application was opened programmatically”. End If Set objWord = Nothing
Using WithEvents to Expose Events of the Automation Server Using Automation as discussed so far in this chapter is analogous to a one-way telephone call. Access, for example, has controlled and directed Word to do certain things. Using the WithEvents keyword, you can actually create a two-way communication between Access and Word. Access can direct Word to print a letter or report and Word can respond back to Access when certain events occur. First, it is important to determine what events can be reported back by the calling application. The easiest way to determine this is to use the Object Browser. Click on the
Using ActiveX Automation CHAPTER 18
521
application object and see if there are any events listed. Word 97 had only two events exposed by the Application object (DocumentChange and Quit). Word 2002 now has 12 events exposed by the Application object. See Figure 18.4, which shows the Object Browser. When referring to an object, different icons indicate properties (a finger pointing to a sheet of paper), methods (green flying erasers), and Events (lightning bolts). FIGURE 18.4 The Object Browser shows Microsoft Word 2002 exposed events.
18
FIGURE 18.5 The Object Browser shows Microsoft Excel 2002 exposed events.
USING ACTIVEX AUTOMATION
Microsoft Excel exposes even more events than Word. Excel 2002’s application object exposes more than 21 events (see Figure 18.5).
522
Interoperability PART VI
Making WithEvents Work Normally, when using Automation within a procedure, you dimension the object variable in a procedure as follows: Dim objWord as Word.application
To use WithEvents, move the Dim statement out of the procedure and create a modulelevel variable instead. Because a module-level variable will be used, the Dim keyword should be changed to Private or Public. You can also use the WithEvents keyword to expose the events of the application. ‘ Declare module level variable using “WithEvents” keyword. Private objWord WithEvents as Word.application
Tip WithEvents can only be used within class modules (form modules are class modules). WithEvents cannot be used in standard modules.
See Figure 18.6, which shows how to declare a module-level variable using the WithEvents keyword. FIGURE 18.6 Declare modulelevel variable using the WithEvents
keyword.
Using ActiveX Automation CHAPTER 18
523
After declaring the module-level variable using the WithEvents keyword, examine the combo boxes at the top of the module window. In the left column box, choose the application that is the Automation Server. Now, in the combo box on the right side, you will see the events that are exposed by the application. Choose any of these events and the procedure will appear in the code window where you can write code to respond to the event. Figure 18.7 shows procedures using the exposed Word events Document Quit.
Change
and
FIGURE 18.7 Procedures using exposed events of Microsoft Word.
18 USING ACTIVEX AUTOMATION
Using the Automation Tips and Techniques Traditionally, Automation has received a lot of bad press because of poor performance. In reality, with reasonable hardware and proper coding techniques, Automation runs efficiently today. Please keep in mind the following concepts when using Automation.
524
Interoperability PART VI
Set References, Use Early Binding, and the New Keyword To start an Automation session, set a reference to the application with the references dialog box. Use a specific class when dimensioning the object variable and use the New keyword to instantiate a new instance of the object. All these issues have been discussed previously in this chapter.
Use an Existing Application If It Is Already Open Do not open a new instance of an application in every Automation session. If an application is already open on the user’s machine, use the existing instance of the application for the Automation session.
Turn ScreenUpdating Off During a typical Automation session with Word or Excel, a tremendous amount of screen updating and paints are occurring. As far as performance is concerned, if ScreenUpdating is turned off, the performance improves because processing will be less intensive. The best technique is to turn ScreenUpdating off, complete all the Automation work, and then turn ScreenUpdating back on at the end. The user then sees the completed document at one time when it is completed. The following code illustrates this: ‘ Turn Off Screen Updating. Application.ScreenUpdating = False ‘ Automation Code Runs Here! ‘ Turn On Screen Updating. Application.ScreenUpdating = True
Tip A friend of mine once mistakenly distributed an application with ScreenUpdating left on, which slowed down the application’s performance. In the next version, ScreenUpdating was turned off to improve performance. He received so many complaints from users that he had to revise and redistribute the application with ScreenUpdating turned back on. The users explained that while the screen was being refreshed, they had an opportunity to read the document, to start reviewing it, or to make sure it was the correct document to print. By forcing the user to wait until all the automation work was complete
Using ActiveX Automation CHAPTER 18
525
before making the document visible, they lost three to five seconds of review time. They also thought it was somewhat cool to watch the documents being “magically” constructed piece by piece on the screen. Therefore, the lesson was to consider the “processing time” of human beings in reviewing the documents, not just the processing of the computer.
Give the Users Feedback If an Automation session will take some time, definitely give the users feedback to indicate that progress is being made. This might mean showing a progress meter, an hourglass cursor, or some other visual cue. If not, the user might turn off the computer thinking it is locked up. Remember, with Automation, more than one application is open. If the computer is abruptly shut off, devastating consequences can result.
Tip A friend of mine created an application for an aerospace company that involved very sophisticated Excel Automation code. While the process was occurring, a small icon of an airplane flew across the screen. The users were more excited about the little airplane flying across the screen than the complex Automation code doing the work.
Have the Automation Server Run the Code It is always faster to let the Automation Server run its own code. For example, if an Access application automates to Word, first create the instance of the Word document object. Then, place all the VBA code, which controls and manipulates Word (such as changing fonts or styles of the text, or printing the document), in Word. In other words, have an application run its own code. If Word’s objects, properties, or methods are being used, put the VBA code in Word. Otherwise, cross-process communication is occurring between Access and Word for each statement of code. By following this technique, you will substantially improve performance.
18 USING ACTIVEX AUTOMATION
You know as a developer that providing visual feedback, such as a progress meter, can in fact slow down the time it takes to complete the process. Nevertheless, what is important is not the real time it takes to complete a process, but the “perceived” time. Users report that a process seems to take less time when visual cues are provided even though, in fact, the process actually took longer.
526
Interoperability PART VI
Tip VBA code in applications such as Word and Excel is placed in templates. To make it easier to distribute templates, place the templates in a shared folder on the network server. Under the Tools menu in Word, for example, under File Locations, workgroup templates can be set to that network drive location. When changes need to be made to the code or new templates added, they can simply be copied to the network drive where the workgroup templates are stored.
Use the With/End With Construct There are two good reasons to use the With/End With construct. First, the code runs more efficiently because the object is referenced once instead of with each statement of code. The second reason is readability. Think of every dot as a speed bump on a racetrack. The fewer dots the better! Consider the following examples: Example 1: The object is referenced on each line of code, which will cause it to run more slowly. It is also harder to read. objExcel.Range(“F6”).Select objExcel.ActiveCell.FormulaR1C1 = “Yearly Sales” objExcel.Range(“G6”).Select objExcel.ActiveCell.FormulaR1C1 = “Sales Summary” objExcel.Range(“F7”).Select
Example 2: The object is referenced only once, which will cause it to run more quickly. The code is also easier to read. With objExcel .Range(“F6”).Select .ActiveCell.FormulaR1C1 = “Northwind Traders” .Range(“G6”).Select .ActiveCell.FormulaR1C1 = “Sales Summary” .Range(“F7”).Select End With
Release Object Variables As discussed earlier, always release the object variable at the end of the Automation session by setting it equal to Nothing to reclaim valuable resources.
Using ActiveX Automation CHAPTER 18
527
Do Not Display Dialog Boxes and Message Boxes Generally, avoid actions that cause the Automation session to stop, such as dialog boxes and message boxes. If a stoppage does occur, it might not be seen by the user because another application might have focus at the time.
Use Error Handling Automation adds a great deal of complexity to an application. The benefit of using features from other applications has the downside that more can go wrong. It is essential that an Error Handler be in place to handle any errors that might occur during an Automation session. With Microsoft Office applications, the server application can return error information. See Chapter 13, “Professional Error Handling,” for additional information about Error Handling.
Summary
18 USING ACTIVEX AUTOMATION
In this chapter, the principles and techniques of Automation were discussed. Automation enables applications to work together affording the developer an opportunity to create complete software solutions. It is certainly easier to learn these principles than to create a word processor, spreadsheet, or other feature that is already included in another application. Creating an instance of another application, using the application, and closing the automation session were discussed. Many automation tips were covered to improve the performance of Automation. Chapter 19, “Integrating with Microsoft Office,” discusses integrating Access applications with other Microsoft Office XP applications.
CHAPTER 19
Integrating with Microsoft Office
IN THIS CHAPTER • Why Integrate with Microsoft Office? 533 • Use the Right Tool
533
• Using the Macro Recorder to Write Code 533 • Using Auto Macros • Microsoft Forms • Object Browser
535
536 537
• Class Arguments for Office Applications 537 • Automation Example
538
• Automating Word
540
• Automating Excel
555
• Automating PowerPoint • Automating Outlook • Automating Graph
559
562 567
530
Interoperability PART VI
As the title suggests, this book is about Access development. The very phrase Access development (unintentionally) intimidates many competent, capable users of Microsoft Office who are not developers or programmers. This chapter is for those who want to learn how to make the components of Microsoft Office interoperate, how to automate repetitive computing tasks, and how to use Microsoft Office, especially Access 2002, more efficiently and productively. In particular, you will learn how to use Access 2002 to automate other members of the Microsoft Office suite, including Word, Excel, PowerPoint, Outlook, and Graph. Tip See the chapter code examples. There are thousands of lines of automation code for Access, Word, Excel, PowerPoint, Outlook, Graph, FrontPage, and MapPoint (see Figure 19.1).
FIGURE 19.1 The Automation.MDB file.
This chapter shows you how to automate Microsoft Office applications. Automation in this chapter refers to replacing manual tasks that are tedious or repetitive—such as updating a Word document containing the most current list of newsletter subscribers by typing new or updated entries from a hard copy of an Access report—with a process that requires just a few clicks and that requires little or no user involvement. Automation also refers to the services, or features, Microsoft Office applications provide which make such an automatic process possible. The following list shows the services each Office application offers via automation:
Integrating with Microsoft Office CHAPTER 19
531
• Access—database services • Word—word processing and desktop publishing services • Excel—calculation and financial services • PowerPoint—presentation services • Outlook—email, calendar, contacts, tasks, and other services • Graph—graphing and chart services
Why Integrate with Microsoft Office? Microsoft Office is an entire suite of products meeting the customer needs and demands for feature-rich office productivity applications. The most well-known applications are Microsoft Word, Microsoft Excel, and, of course, Microsoft Access. Each application offers a tremendous number of features making them more powerful and easier to use, but, understandably, each application lacks features that are properly implemented in other applications. For example, Word can perform basic calculations in tables, but Excel is vastly more competent in this regard than Word.
WITH
Word is a terrific report writer. You could use Access 2002’s very capable report-writing features to create fancy documents, but Word’s support for templates, styles, formatting, indexing, and other word processing functionality is more complete, so why go through the effort to re-create the wheel? Word is an excellent choice to create invoices, letters, interoffice memos, forms, newsletters, legal documents, faxes, and business reports.
INTEGRATING
Using Word
19 MICROSOFT OFFICE
More importantly, for this chapter’s purposes, Office applications can be manipulated using a common scripting language, VBA, Visual Basic for Applications, allowing end users to use the already comprehensive capabilities of Microsoft Office to create custom solutions for common office tasks. For example, using VBA, you can import the latest sales figures contained in an Excel spreadsheet into a Word report, and then use a list of email addresses maintained in an Access database to perform a mail merge to distribute the updated Word report via email. No other office suite today provides the power and integration that Microsoft Office offers. The following sections illustrate some of the features each Office component offers that you can use to create your own, full-featured applications.
532
Interoperability PART VI
Using Excel When you need to crunch numbers, Excel is the perfect choice. After using it to create impressive graphs and charts for office meetings, detailed quotes for customer proposals, or sophisticated financial projections for board of directors meetings, you can use VBA to automate these tasks within Excel or from other Office applications, or automatically to incorporate the graphs, charts, quotes, and projections into other Office projects.
Using PowerPoint When it is show time and your presentation to senior management must make a powerful impression, choose PowerPoint. PowerPoint makes it easy to create impressive, high quality presentations that are dynamic, compelling, and visually appealing. Create effective demonstrations for product sales, staff meetings, Board of Director meetings, investor presentations, and more. VBA makes it possible automatically to update an existing presentation with current information, to use features from other Office applications in a PowerPoint presentation, or even to create new presentations.
Using Outlook Outlook provides numerous features of which you can take advantage, using VBA, in other Office applications and in your own custom solutions. For example, you could use Outlook’s email features in an Excel spreadsheet to email the spreadsheet to certain users. An Access application can use Outlook’s calendaring support to create the weekly order for office supplies. Outlook’s Journal feature is ideal for tracking changes to Office documents. Similarly, you can use Outlook’s task management features to update other people’s tasks lists.
Using Graph Microsoft Graph is the tool most Office applications use to work with graphs (bar charts, Pareto charts, and so on). Graph is not separately executable, though. That is, you cannot invoke it directly by double-clicking an icon. Rather, Office applications use Graph behind the scenes when they need to create or modify graphs. But, even though Graph is not a standalone application, VBA allows you to use Graph’s functionality in your own applications in the same way you would use features in Access or other Microsoft Office applications.
Integrating with Microsoft Office CHAPTER 19
533
Use the Right Tool An old programmer’s quip sagely observes that if the only tool you have is a hammer, every problem looks like a nail. Hammers hardly exhaust the list of available tools, and nails constitute a small subset of the possible problems to solve. The point is to use the tool best suited to solve a given problem. Accordingly, before writing any code, creating any forms, or designing any queries, ask yourself the following question, “What tool is right for feature X?” Most people understandably use the tools with which they are familiar, even if the tools are not always entirely appropriate. So, resist the urge to use only Access to create solutions just because you are familiar with that application. VBA works across all Office products now. Moreover, except for the application specific functionality (such as creating pivot charts in Excel or tables of contents in Word), VBA’s interface is consistent across all Office products. As a result, it is easy to apply what you know from Access VBA to Word’s or Excel’s VBA.
Using the Macro Recorder to Write Code
WITH
The Record Macro dialog box.
INTEGRATING
FIGURE 19.2
19 MICROSOFT OFFICE
Before the, um, “programming challenged” run screaming in the other direction at the prospect of writing code, Microsoft Office’s Macro Recorder is a great tool for getting started. Why? Macro Recorder converts keystrokes and mouse clicks into VBA code, providing both an example of the code required to perform a certain task and a starting point for accomplishing related tasks. Macro Recorder is a terrific code writer and can be used for rapid development in Word, Excel, and PowerPoint. To access Macro Recorder in these applications, select Tools, Macro, Record New Macro. Figure 19.2, for example, shows the initial Macro Recorder dialog box invoked from Microsoft Word.
534
Interoperability PART VI
In the Record Macro dialog box, name the macro and choose OK to start the Macro Recorder. On the document, you will see a small toolbar with two buttons. By moving the mouse to hover over each button, you can see there is a Stop Recording and Pause Recording button. In addition, the mouse pointer becomes a small icon of a cassette tape to indicate that the Macro Recorder is running (see Figure 19.3). FIGURE 19.3 A Word document with the Macro Recorder running.
While Macro Recorder is running, it converts most actions you take within the application to VBA code. Recorded actions include selecting menu commands, keystrokes, typing text in the document, formatting the text, even saving and printing the document. After finishing the tasks you want to record, click the Stop Recording button. To review the VBA code Macro Recorder wrote, select Tools, Macro, Macros, highlight the macro you recorded, and then click the Edit button. This procedure opens the Visual Basic Editor and shows the VBA code written for that macro (see Figure 19.4). Tip Macro Recorder does not always write the most optimized code, so, once you become familiar with VBA, you might want to review the code before deploying it in a production application.
Integrating with Microsoft Office CHAPTER 19
535
FIGURE 19.4 VBA code (slightly edited for brevity) written by Macro Recorder.
Using Auto Macros Word, Excel, and PowerPoint include auto macros that can be used to run code under various circumstances. Auto macros are specially named macros that execute automatically in specific situations, such as when an application starts or opens a file. For example, Word has five auto macros: AutoExecute—runs
•
AutoNew—runs
•
AutoOpen—runs
•
AutoClose—runs
•
AutoExit—runs
when you start Word
when you create a new file
19
when you open a file when you close Word
Public Sub AutoOpen () ‘ Code to run when the document opens. End Sub
WITH
To use an auto macro in Visual Basic for Applications, create a procedure or function in a VBA module named after one the auto macros in the previous list. For example, to create an AutoOpen macro, create a procedure (or function) resembling one of the following skeletons:
MICROSOFT OFFICE
when you close a file
INTEGRATING
•
536
Interoperability PART VI Private Sub AutoOpen () ‘ Code to run when the document opens. End Sub Public Function AutoOpen () ‘ Code to run when the document opens. End Function Private Function AutoOpen () ‘ Code to run when the document opens. End Function
Microsoft Forms Microsoft Office applications, including Word, Excel, and PowerPoint, feature a separate forms package called Microsoft Forms. When automating other applications or obtaining user input, these forms may prove useful. Developers experienced with Access forms will have no difficulty with Microsoft Forms. To illustrate, try the following procedure. 1. Start Microsoft Word. 2. Select Tools, Macro, Visual Basic Editor. 3. In the Visual Basic Editor, select Insert, UserForm. 4. Use the control toolbox to add controls to a form. Figure 19.5 illustrates a Microsoft Forms design session. FIGURE 19.5 Excel, Word, and PowerPoint use Microsoft Forms to create forms.
Integrating with Microsoft Office CHAPTER 19
537
Although not as fully featured as Access 2002’s forms designer, the Microsoft Forms designer provides a familiar interface for creating and modifying forms and, as you would expect, you can write VBA code to respond to control and form events, and also can use ActiveX controls. Tip Microsoft Forms are distinct from forms within Access, Visual Basic, and Outlook. These are all separate forms packages. You cannot, for example, convert an Access form to a Microsoft Form, or vice versa.
Object Browser As you work with the various applications covered in this chapter, keep in mind what you learned in the previous chapter about using the Object Browser to get more information about the objects available in Microsoft Office applications.
Class Arguments for Office Applications To automate with other applications, you need to know the class arguments: Class Argument Access.Application Excel.Application
Excel.Chart Graph.Application Outlook.Application PowerPoint.Application Word.Application Word.Document
WITH
Graph Outlook PowerPoint Word
MICROSOFT OFFICE
Excel.Sheet
19 INTEGRATING
Application Access Excel
538
Interoperability PART VI
Automation Example The following short example illustrates the general procedure and overall flow of automating Microsoft Office. In order for this example to work, you will need to set a reference to the Microsoft Word object library (select Tools, References while working in a code module, then check the box next to Microsoft Word 10.0 Object Library). The previous chapter covered the basics of automation, such as creating or getting a reference to Word, using Word’s objects, and releasing the automation object. This example prints Access information in a Word report. Option Compare Database Private Sub AccessAppReport() Dim objWord As Word.Application Dim objTable As Word.Table DoCmd.Hourglass True ‘ Resume to the next line following the error. On Error Resume Next ‘ Use a running Word instance if possible Set objWord = GetObject(, Word.Application) ‘ Otherwise, use a new instance If objWord Is Nothing Then ‘ Create a new instance of the Word application. Set objWord = New Word.Application ‘ If true, MS Word is not installed. If objWord Is Nothing Then MsgBox “Word is not installed on your computer” End If End If ‘ Open a new document objWord.Documents.Add ‘ Activates Word objWord.Activate ‘ Show Word to the user objWord.Visible = True ‘ Create a heading for the report. With objWord.Selection .TypeText vbTab & vbTab & “Access Application Report” .StartOf Unit:=wdStory, Extend:=wdExtend .Font.Bold = True .Font.Size = 20 .EndKey Unit:=wdLine
Integrating with Microsoft Office CHAPTER 19
539
.TypeParagraph .TypeParagraph .Font.Bold = False .Font.Size = 16 End With ‘ Put information about this Access app into the document With objWord.Selection .TypeText “Application Item” & vbTab & “Value” & vbCrLf .TypeText “Database Name and Path” & vbTab & _ Application.CurrentDb.Name & vbCrLf .TypeText “Current Form Name” & vbTab & _ Forms.Item(0).Name & vbCrLf .TypeText “Current Active Object” & vbTab & _ Application.CurrentObjectName & vbCrLf .TypeText “Current Jet Version” & _ vbTab & Application.DBEngine.Version & vbCrLf .TypeText “Application Compiled” & vbTab & _ Application.IsCompiled & vbCrLf .TypeText “Number of References” & vbTab & _ Application.References.Count & vbCrLf .TypeText “Current Mouse Pointer” & vbTab & _ Application.Screen.MousePointer & vbCrLf .TypeText “Application Visible” & vbTab & _ Application.Visible & vbCrLf .StartOf Unit:=wdStory .MoveDown Unit:=wdLine, Count:=2 .StartOf Unit:=wdLine .EndOf Unit:=wdStory, Extend:=wdExtend End With
19 INTEGRATING WITH
MICROSOFT OFFICE
Set objTable = objWord.Selection.ConvertToTable( _ Separator:=wdSeparateByTabs, _ NumColumns:=2, _ NumRows:=11, _ Format:=wdTableFormatColorful2, _ ApplyBorders:=True, _ ApplyShading:=True, _ ApplyFont:=True, _ ApplyColor:=True, _ ApplyHeadingRows:=True, _ ApplyLastRow:=False, _ ApplyFirstColumn:=False, _ ApplyLastColumn:=False, _ AutoFit:=True, _ AutoFitBehavior:=wdAutoFitFixed, _ DefaultTableBehavior:=wdWord10TableBehavior)
540
Interoperability PART VI ‘ Turn paragraph markers off. objWord.ActiveWindow.ActivePane.View.ShowAll = False DoCmd.Hourglass False ‘ Release the object variable. Set objWord = Nothing End Sub
Automating Word This section covers specific issues and examples for automating Microsoft Word. The example in the previous section showed code containing all of the steps to make automation work. Although the example used Word, the intent was to illustrate automation in general. The basic approach would be the same for controlling any Microsoft Office application from Access 2002.
The Word Object Model This section focuses on the objects, properties, and methods available in Word, that is, on Word’s object model. An object model is a representation (in outline form) of an application’s objects. This is a quick and easy way to determine which objects in an application are available to program, manipulate, or control. Tip Refer to “Microsoft Word Objects” in the help file for a diagram of the Word object model.
The Word object model is quite comprehensive. It includes more than 180 Word objects that can be manipulated with VBA. Frequently used Word objects are summarized in Table 19.1. The objects that also have collections end with (s). TABLE 19.1
Word Objects
Word Object
Description
Application
The entire Word application. You do not usually need to reference the application object from within Word. If using Word from another application, you will use this object.
AutoCaption(s)
A caption that can be added automatically.
AutoCorrect
AutoCorrect feature in Word.
Integrating with Microsoft Office CHAPTER 19 TABLE 19.1
541
continued
Description
Characters
Characters within an opened document.
CommandBar(s)
A particular command bar referred to by index number or name.
DefaultWebOptions
Default settings for publishing or saving as a Web page.
Dialog(s)
A built-in dialog box.
Dictionary
A dictionary in Word.
Dictionaries
Collection of Dictionary objects.
Document(s)
An opened document referred to by index number or name.
Email
An email message for a document.
EmailSignature
Email signature information.
Find
An object used to find a word, selection, range, and more.
Paragraph(s)
A collection of paragraphs.
Range
A defined starting and ending point in a document. A document might have more than one range.
Replacement
An object specifying a replacement criteria.
Selection
The current selection in Word. There can only be one selection in Word at a time.
Table(s)
A single table in a document.
Template(s)
A document template.
WebOptions
Settings to override DefaultWebOptions.
Window(s)
A particular window referred to by index number or name.
Words
A collection of words.
Application—Refers
•
Count—The
number of words in a sentence, selection, or range
•
Creator—A
numeric identifier of the application that created the document
•
First—The
first word in a sentence, selection, or range
•
Item—Refers
•
Last—The
•
Parent—Refers
to the Microsoft Word application
to a specific word in the Word.Words collection
last word in a sentence, selection, or range to the parent object
WITH
•
MICROSOFT OFFICE
Using the Object Browser from within the Visual Basic Editor, you can obtain additional information about the members of each class. For example, the Words class has the following members:
19 INTEGRATING
Word Object
542
Interoperability PART VI
Using Word Templates As discussed in the previous chapter, automation code runs fastest in the automation server. That is, when automating Word’s objects, properties, and methods from Access, the VBA code should be placed in Word templates, rather than copied into Access. To create a Word template, open a new Word document. After all code has been added, choose Save As under the File menu to save the document as a document template (.DOT extension). To add code to the template, use the Macro Recorder as discussed earlier in this chapter. Simply turn on the Macro Recorder, perform the actions you want to automate, and the Macro Recorder will write the VBA code. So, how do you call the VBA code in Word from Access? Actually, it is very easy. Use the Run method. The code is as follows: ‘ Run Macro in Word that formats the document. objWord.Run “FormatDocument”
Again, running the code in Word will be much faster because cross-process communication need not occur for each automation instruction. With the code in Word, the VBA code runs in a single process. Tip There are examples in the chapter code showing the same automation code in Access versus in a Word template. A timer demonstrates the speed difference. Placing the automation code in Word is substantially faster.
Although code runs faster when put in a Word template, does it create a distribution headache? No, not if managed correctly. Templates can be saved either on each workstation or as workgroup templates on the server. The advantage of saving templates on a server as workgroup templates is that changing a template or adding a new template only requires updating shared workgroup directory on the server. The disadvantage, however, is that template usage results in network traffic. To minimize this traffic, some developers write simple applications that copy the templates from the server to the workstations when the application starts up. To provide users access, from their workstations, to the workgroup templates on the server, select Tools, Options, File Locations on Word’s menu bar, then select the Workgroup templates item, as shown in Figure 19.6, click the Modify button, set the path to the workgroup templates, and then click OK to save your changes.
Integrating with Microsoft Office CHAPTER 19
543
FIGURE 19.6 Use the File Locations tab to set the path to workgroup templates.
Figure 19.6 shows the path to workgroup templates as C:\My Documents, but the path could just as well be a shared folder on a network server.
Inserting Data in Word Documents Word, obviously, is a terrific document processing tool. Corporate data presented in Word documents, such as reports or proposals, are easy for other users to modify and adapt to their own needs. Figure 19.7 shows an example of a magazine created using corporate data. FIGURE 19.7 Creating reports in Word.
19 INTEGRATING WITH
MICROSOFT OFFICE
544
Interoperability PART VI
There are three features that can be used to send data from an Access 2002 database to a Word document: Mail Merge, Bookmarks, and Find and Replace. The sample code in this chapter demonstrates all these techniques.
Mail Merge End users often use Word’s Mail Merge to assemble documents. A mail merge combines a form letter or some other type of boilerplate with data from, in this case, an Access database. Mail merges create customized communications, such as sales flyers or newsletters, quickly easily, and with minimal effort once the initial setup has been completed. Using automation, you can further refine the mail merge process. To create the mail merge automation code, turn on the Macro Recorder and complete the steps for a Mail Merge just as an end user would: Select Tools, Letters and Mailings, Mail Merge Wizard from the Word menu, and complete each step in the Mail Merge Helper dialog box. When choosing a data source for the merge document, you can select an Access table or query as the data source. You will find that if you output the data to an RTF file first and use the RTF file as your data source, the automation code will run significantly faster. Several examples of this are included in the chapter code. A key advantage of the Mail Merge technique is that end users themselves can insert fields of data into the document. In the mail merge document, users can insert fields from the Mail Merge toolbar. This empowers users to create their own documents and relieves developers from the necessity of creating reports. When properly designed by developers, templates can provide data fields that users might need to create Word reports of their own (see Figure 19.8). The code in the template to run the mail merge in the automation.mdb chapter code sample is Private Sub RunMailMerge() With ActiveDocument.MailMerge .MainDocumentType = wdFormLetters .OpenDataSource Name:=”automation.mdb”, _ Format:=wdOpenFormatAuto, _ ConfirmConversions:=False, _ ReadOnly:=False, _ LinkToSource:=True, _ AddToRecentFiles:=False, _ PasswordDocument:=””, _ PasswordTemplate:=””, _ Revert:=False, _ WritePasswordDocument:=””, _ WritePasswordTemplate:=””, _
Integrating with Microsoft Office CHAPTER 19
545
Connection:=”QUERY qryEmployeeLetters”, _ SQLStatement:=”SELECT * FROM [qryEmployeeLetters]”, _ SQLStatement1:=”” .Destination = wdSendToNewDocument .Execute End With End Sub
FIGURE 19.8 Users can create customized Word reports.
Bookmarks
WITH
To use this method, create a Word template with all the standard document text. Insert bookmarks where you want to insert data by choosing Bookmark under the Insert menu. Give the Bookmark a name and choose the Add button.
INTEGRATING
One disadvantage of Bookmarks is that developers themselves must insert the bookmarks in the template and write the code (or use the macro recorder to create it) to search for the bookmarks and insert the data. End users cannot create their own documents using this technique as they can using mail merges. Another disadvantage of the Bookmark method is that Bookmarks are not readily visible when Word templates are open, making Bookmarks slightly more difficult to find and identify.
19 MICROSOFT OFFICE
Another way to insert data in Word documents is to use Bookmarks. A Bookmark is a placeholder for data that will be inserted later. You can use automation to search for each Bookmark and then to insert the applicable data in the appropriate place.
546
Interoperability PART VI
Use the Macro Recorder to write the VBA code as you go to bookmarks and insert text. Code in this chapter includes an example of automation using Bookmarks. The VBA code is as follows: Selection.GoTo What:=wdGoToBookmark, Name:=”MyBookmark” Selection.TypeText “New Text”
Find and Replace The find and replace technique is a more powerful variation of the Bookmark technique. Find and Replace inserts text in the document as a placeholder for the actual data to be inserted later, for example, replacing {First Name} with Stephen. The code searches for the placeholder text with a Find and replaces it with the appropriate data. In other words, {First Name} could be found and replaced with Stephen. One of the advantages of using this method instead of Bookmarks is that placeholder text can be seen more easily and is more descriptive than bookmarks. Again, as with Bookmarks, use the macro recorder while doing a search and replace in order to create the VBA code. An example of the Find and Replace code is With Selection.Find .ClearFormatting .Text = “{Find Text}” .Replacement.Text = “New Text” .Forward = True .Wrap = wdFindContinue .Format = False .MatchCase = False .MatchWholeWord = False .MatchWildcards = False .MatchSoundsLike = False .MatchAllWordForms = False .Execute Replace:=wdReplaceAll End With
Word Automation Code Examples The examples in this section demonstrate how to use VBA to interrogate and change Word object properties, and how to invoke frequently used Word object methods.
Formatting Word Documents Word has extensive formatting capabilities. To automate document creation, VBA code can be used to format a Word document: ‘ Formatting Text With Selection.Font .Size = 14
Integrating with Microsoft Office CHAPTER 19
547
.Bold = True End With ‘ Formatting Paragraphs With Selection.ParagraphFormat .LeftIndent = InchesToPoints(0) .RightIndent = InchesToPoints(0) .SpaceBefore = 0 .SpaceAfter = 0 .LineSpacingRule = wdLineSpaceSingle .Alignment = wdAlignParagraphLeft .KeepWithNext = False .KeepTogether = False .PageBreakBefore = False .NoLineNumber = False .Hyphenation = True .FirstLineIndent = InchesToPoints(0) .OutlineLevel = wdOutlineLevelBodyText End With ‘ Setting Line Spacing to Double Space Selection.ParagraphFormat.LineSpacingRule = wdLineSpaceDouble ‘ Adding a Page Break Selection.InsertBreak Type:=wdPageBreak ‘ Adding a Section Break Selection.InsertBreak Type:=wdSectionBreakNextPage
Using Document Styles
The next two code blocks create a new style and then customize it. The first With section creates a new style, named MyNewStyle, in the current document, and defines its base style, the starting point for any other formatting modifications, to be Heading 5. The second, longer With statement, sets many of the Font properties for MyNewStyle, such as the name (Times New Roman) and its face (underlined). All other values are set to False or are otherwise disabled.
WITH
‘ Applying the Heading Style to Text Selection.Style = ActiveDocument.Styles(“Heading 1”)
MICROSOFT OFFICE
The following line applies the style named Heading 1 to the currently selected text in the active document.
19 INTEGRATING
A style is a group of formatting instructions that can be named and assigned to text. Styles enable formatting of several blocks of text or paragraphs with a single command. In addition, styles help provide consistent formatting throughout a document.
548
Interoperability PART VI ‘ Creating a New Style ActiveDocument.Styles.Add Name:=”MyNewStyle”, Type:=wdStyleTypeParagraph With ActiveDocument.Styles(“MyNewStyle”) .AutomaticallyUpdate = False .BaseStyle = “Heading 5” End With With ActiveDocument.Styles(“MyNewStyle”).Font .Name = “Times New Roman” .Size = 12 .Bold = False .Italic = False .Underline = wdUnderlineSingle .StrikeThrough = False .DoubleStrikeThrough = False .Outline = False .Emboss = False .Shadow = False .Hidden = False .SmallCaps = False .AllCaps = False .ColorIndex = wdAuto .Engrave = False .Superscript = False .Subscript = False .Scaling = 100 .Kerning = 0 .Animation = wdAnimationNone End With
AutoCorrect AutoCorrect fixes easily misspelled words (for example, teh can be changed to the). You also can use AutoCorrect to save typing (for example, convert MS to Microsoft).
AutoText AutoText is a database that stores frequently used text and graphic objects that can be inserted quickly and easily into documents. The chapter code has examples of inserting text and photographs to documents. ‘ Create an AutoText Entry. NormalTemplate.AutoTextEntries.Add Name:=”Microsoft”, _ Range:=Selection.Range ‘ Insert an AutoText Entry. NormalTemplate.AutoTextEntries(“Microsoft”).Insert _ Where:= Selection.Range
Integrating with Microsoft Office CHAPTER 19
549
AutoSummarize AutoSummarize analyzes each sentence of a document to create a document summary. The summary can be displayed several ways: as highlighted text within the document, as an abstract in the beginning of the document, in a new document, or by itself with no other document showing. AutoSummarize can even be used to create a summary of Web pages. ‘ AutoSummarize a Document (summary put in a new document). ActiveDocument.AutoSummarize Length:=25, Mode:=wdSummaryModeCreateNew, _ UpdateProperties:=True
Document Views After creating a document for the user, present the document in the appropriate view: Normal, Online Layout, Page Layout, Outline, or Master Document. ‘ Normal View ActiveWindow.View.Type = wdNormalView ‘ Online Layout ActiveWindow.View.Type = wdOnlineView ‘ Page Layout ActiveWindow.View.Type = wdPageView ‘ Outline View ActiveWindow.ActivePane.View.Type =wdOutlineView ‘ Master Document ActiveWindow.ActivePane.View.Type = wdMasterView
Table of Contents
INTEGRATING WITH
‘ Create a table of contents With ActiveDocument .TablesOfContents.Add _ Range:=Selection.Range, _ UseHeadingStyles:=True, _ UpperHeadingLevel:=1, _ LowerHeadingLevel:=3, _ UseFields:=True, _ TableID:=1, _
MICROSOFT OFFICE
In the past, creating a table of contents was a laborious task. Now, Word makes generating a table of contents a largely automated process. The trick is to use styles in your document and then to use the styles to define the table of contents headings. When you set up styles to format Heading 1, Heading 2, and so on, Word can use this information automatically to create an accurate table of contents.
19
550
Interoperability PART VI RightAlignPageNumbers:=True, _ IncludePageNumbers:=True, _ AddedStyles:=””, _ UseHyperlinks:=True, _ UseOutlineLevels:=True .TablesOfContents(1).TabLeader = wdTabLeaderDots End With
Footnotes Footnotes usually are used to cite the source of information, such as a quote, used in a document and also to help the reader locate and evaluate information supporting a point made in the primary document. They also are used to provide additional information that might detract from the main text. The Footnotes object is used to work with footnotes in Word VBA code. ‘ Create a Footnote. ActiveDocument.Footnotes.Add Range:=Selection.Range, _ Reference:=Selection, Text:=”My New Footnote”
Headers Headers appear at the top of each page of a document. They frequently consist of the document title, but also are often used to enhance the appearance of the document using a small, attractive graphic or a thin line. This book uses headers throughout the text. ‘ Create a Header. ActiveWindow.ActivePane.View.SeekView = wdSeekCurrentPageHeader Selection.TypeText Text:=”My Header”
Footers Footers appear at the bottom of each page of a document. They are often used to show a chapter title, section head, or a page number. ‘ Create a Footer. ActiveWindow.ActivePane.View.SeekView = wdSeekCurrentPageHeader If Selection.HeaderFooter.IsHeader = True Then ActiveWindow.ActivePane.View.SeekView = wdSeekCurrentPageFooter Else ActiveWindow.ActivePane.View.SeekView = wdSeekCurrentPageHeader End If Selection.TypeText Text:=”My Footer”
Creating Tables A Word table contains information organized into rows and columns, just like an Access 2002 database table. Word makes it easy to create tables with a wide range of flexibility, formatting, and styles.
Integrating with Microsoft Office CHAPTER 19
551
The following code snippet creates a 3×3 table and automatically formats it using one of Word’s predefined table formats (the Classic 3 format). The last two lines then insert a new row and a new column into the table. Sub CreateTable() Dim tblNewTable As Table ‘ Create a Table Set tblNewTable = ActiveDocument.Tables.Add( _ Range:=Selection.Range, _ NumRows:=3, _ NumColumns:=3) tblNewTable.AutoFormat _ Format:=wdTableFormatClassic3, _ ApplyBorders:=True, _ ApplyShading:=True, _ ApplyFont:=True, _ ApplyColor:=True, _ ApplyHeadingRows:=True, _ ApplyLastRow:=False, _ ApplyFirstColumn:=True, _ ApplyLastColumn:=False, _ AutoFit:=False End Sub
Page Setup The Page Setup dialog box controls margins, paper size, orientation, paper source, and layout. All these settings can be adjusted with code, such as the following, which sets the top, bottom, left, and right margins to 1/2” and changes the page orientation to landscape:
To present a Print Preview of the current Word document, use the following code: ‘ Print Preview ActiveDocument.PrintPreview
WITH
Previewing and Printing
INTEGRATING
‘ Set Page Layout to Landscape. ActiveDocument.PageSetup.Orientation = wdOrientLandscape
19 MICROSOFT OFFICE
‘ Setting Margins With ActiveDocument.PageSetup .TopMargin = InchesToPoints(0.5) .BottomMargin = InchesToPoints(0.5) .LeftMargin = InchesToPoints(0.5) .RightMargin = InchesToPoints(0.5) End With
552
Interoperability PART VI
The following examples show how to print documents, envelopes, and labels with code: Sub PrintMe() ‘ Printing Entire Document ActiveDocument.PrintOut Range:=wdPrintAllDocument, _ Item:=wdPrintDocumentContent, _ Copies:=1, _ Pages:=””, _ PageType:=wdPrintAllPages, _ Collate:=True, _ Background:=True, _ PrintToFile:=False ‘ Printing Envelopes ActiveDocument.Envelope.PrintOut _ ExtractAddress:=False, _ OmitReturnAddress:=False, _ PrintBarCode:=False, _ PrintFIMA:=False, _ Height:=InchesToPoints(4.13), _ Width:=InchesToPoints(9.5), _ Address:=”Tom Howe”, _ AutoText:=”ToolsCreateLabels1”, _ ReturnAddress:=”1001 SW Fifth Avenue, Suite 1100”, _ ReturnAutoText:=”ToolsCreateLabels2”, _ AddressFromLeft:=wdAutoPosition, _ AddressFromTop:=wdAutoPosition, _ ReturnAddressFromLeft:=wdAutoPosition, _ ReturnAddressFromTop:=wdAutoPosition ‘ Printing Labels ActiveDocument.MailingLabel.DefaultPrintBarCode = False ActiveDocument.MailingLabel.PrintOut _ Name:=”2160 Mini”, _ Address:=”Tom Howe” End Sub
Tip When printing long documents or reports printed on a daily basis, consider using a timer routine to print the reports in the middle of the night when printers are available and network traffic is minimal.
Word Fields A Word field is a special code that is used to insert particular information, such as page numbers, dates, author information, formulas, and more. To insert a field in a document,
Integrating with Microsoft Office CHAPTER 19
553
select Insert, Field from the menu, select the field you want to insert, then click OK. Some fields can be customized. Others, like date and time fields, can be configured always to show the current date or time. When working with documents containing Word fields, you can toggle between the text and field code in the document by using the shortcut key Alt+F9. The fields can be updated manually using the F9 shortcut key automatically when the document is opened or printed. Table 19.2 lists some commonly used Word fields. TABLE 19.2
Word Fields
Word Field
Description
Ask
Prompts user for text to attach to bookmark
CreateDate
Date document was created
Fill-in
Prompts user for text to insert in the document
Hyperlink
Opens and jumps to specified file
If
Evaluates arguments conditionally
MergeField
Inserts a Mail Merge field
Next
Goes to next record in the Mail Merge
SectionPages
Inserts total number of pages in a section
The following shortcuts will make working with field codes at little easier and faster:
When a new document is created and saved, information must be saved for later searches and retrieval. Information to identify a document is contained in the Word Properties dialog box (see Figure 19.9).
WITH
Document Summary Information
19 INTEGRATING
Description Switches between field codes and data in the field Creates field characters Unlinks the field codes Deletes a field code Manually updates the field codes
MICROSOFT OFFICE
Shortcut Key Alt+F9 Ctrl+F9 Ctrl+Shift+F9 Delete F9
554
Interoperability PART VI
FIGURE 19.9 Word document summary information.
The following information can be associated with a document: • Document name • Document size • Date and time of creation • Last modified date and time • Template document based on • Keywords to identify document • Document location • Author name • Manager name • Company • Category • Comments • Document statistics • Contents information It is even possible to create custom properties to meet the specific needs of an organization. See the Custom tab in the Properties dialog box.
Integrating with Microsoft Office CHAPTER 19
555
Other Word Features Word contains many advanced desktop publishing features, such as • Multicolumn layouts • The capability to import graphics from other applications • Embedded TrueType fonts • Borders and shading • Backgrounds and textures • Word Art • Extensive drawing tools
Automating Excel This section covers specific issues and examples for automating Microsoft Excel. In particular, it discusses the Excel object model and shows you how to use VBA to format Excel spreadsheets, create charts, and how to use the Parent property.
Excel Object Model Like Microsoft Word, Microsoft Excel has a rich, comprehensive object model. It consists of more than 140 objects that give your Access 2002 application almost complete control over Excel using VBA. Tip
INTEGRATING WITH
Table 19.3 summarizes commonly used Excel objects. Many of them behave like their similarly named Word counterparts, except that Excel’s objects, when necessary, exhibit Excel-specific behavior rather than Word-specific behavior, as you would expect. The objects in Table 19.3 that also have collections end with (s).
19 MICROSOFT OFFICE
Refer to “Microsoft Excel Objects” in the help file for a diagram of the Excel object model.
556
Interoperability PART VI TABLE 19.3
Excel Objects
Excel Object
Description
Application
The entire Excel application
Characters
Characters in an object that contains text
Chart(s)
A chart in a workbook
CubeField(s)
A hierarchy or measure field from an OLAP cube
DefaultWebOptions
Default settings for publishing or saving as a Web page
Dialog(s)
A built-in dialog box
DocumentProperty(s)
Built-in or custom document properties
Name(s)
A defined name for a range of cells
PivotCache(s)
A memory cache for a PivotTable report
PivotTable(s)
A PivotTable report on a worksheet
PublishObject(s)
An item in a workbook that has been saved to a Web page
Range
A cell, row, column, or selection of cells containing contiguous blocks of cells, or a 3D range
Shape(s)
An object, such as a picture, in the drawing layer
ShapeRange
A subset of shapes on a document
Style(s)
A style description for a range
WebOptions
Settings to override DefaultWebOptions
Workbook(s)
An opened workbook referred to by index number or name
Worksheet(s)
An opened worksheet referred to by index number or name
Excel Automation Code Examples The examples in the next few sections show VBA code for manipulating certain Excel object properties and activating a few key Excel object methods.
Formatting Excel Documents Excel has extensive formatting capabilities. The following examples demonstrate how to format column headings and column data and how to call the AutoFit method: ‘ Format column headings. With objWorkSheet ‘Bold column headings. .Cells(1, 1).Font.Bold = True .Cells(1, 2).Font.Bold = True End With
Integrating with Microsoft Office CHAPTER 19
557
The VBA code in the first snippet changes makes the font face bold in the first two cells of the worksheet to which objWorkSheet refers. The next snippet first applies a number format of 0.00 to the data in one column, then uses the AutoFit method to adjust the spreadsheet so that data are visible and properly formatted. ‘ Format the data in the worksheet. With objWorkSheet.Columns With .Item(2) .NumberFormat = “0.00” .AutoFit End With ‘ Use the “AutoFit” method to format the data. .Item(1).AutoFit End With
Creating Charts As you read in previous sections, you can use the Macro Recorder to create automation code quickly and with minimal aggravation. The next few code snippets show the code that the Macro Recorder wrote as the Chart Wizard created various types of charts. ‘ Create a Pie chart. objChart.ChartWizard Source:=objSheet.Cells(1, 1).CurrentRegion, _ Gallery:=xlPie, Format:=4, PlotBy:=xlColumns, _ CategoryLabels:=1, SeriesLabels:=1, HasLegend:=2, _ Title:=”Product Sales (Pie Chart)” ‘ Create a Column chart. With ObjChart.ActiveChart .ChartType = xlColumnClustered .SetSourceData Source:=Sheets(“Top 5 Product Sales”).Range(“A1:B6”), _ PlotBy:=xlColumns End With
19
WITH
MICROSOFT OFFICE
‘ Create a Doughnut chart. With ObjChart.ActiveChart .ChartType = xlDoughnut .SetSourceData Source:= _Sheets(“Top 5 Product Sales”).Range(“A1:B6”), _ PlotBy:=xlColumns .Location Where:=xlLocationAsNewSheet, Name:=”Doughnut Graph” .HasLegend = True .ChartTitle.Characters.Text = “Product Sales (Doughnut Chart)” End With
INTEGRATING
Figure 19.10 shows the column chart the preceding code snippet created.
558
Interoperability PART VI
FIGURE 19.10 Using VBA code to create an Excel column chart.
Using the Parent Property Excel includes an application object and a workbook object. You can access all other objects by using the properties of these two top-level objects. Generally, automation code starts with top-level objects and works down the hierarchy. In Excel, code may start with the application object, move to the workbook object, and then travel down the hierarchy to the worksheet and cell levels. When you open an object in the application’s object hierarchy, you can traverse the hierarchy in both directions. Use the Parent property to travel up the hierarchy. For example, if you are working with a particular worksheet, you can access the workbook that contains the worksheet by using the parent property. The code to do so appears in the following: Private Sub ParentPropertyDemo() Dim objExcel As Excel.Application Dim objWorkBook as Excel.WorkBook ‘ Create a new instance of the Excel application. Set objExcel = New Excel.Application ‘ Open a new Excel workbook. ‘ Make sure the spreadsheet from the chapter code ‘ is in the following path. objExcel.Workbooks.Open (“C:\Automation\Sales by Country Data.xls”)
Integrating with Microsoft Office CHAPTER 19
559
‘ Set objWorkBook to Active WorkBook Set objWorkBook = objExcel.ActiveWorkBook ‘ Get the name of objWorkBook (“Sales by Country”) MsgBox objWorkBook.Name ‘ Get the name of the application object using the ‘ parent property (“Microsoft Excel”). MsgBox objWorkBook.Parent.Name ‘ Maximize window. objExcel.WindowState = xlMaximized ‘ Show Excel to the user. objExcel.Visible = True ‘ Use the Quit method to terminate Excel. objExcel.Quit ‘ Release the object variable. Set objExcel = Nothing End Sub
In this example, a workbook is opened. Using the Parent property of the workbook object, it refers up the object hierarchy to the Excel application object.
Automating PowerPoint
Tip Refer to “Microsoft PowerPoint Objects” in the help file for a diagram of the PowerPoint object model.
WITH
There are more than 85 PowerPoint objects that can be manipulated with VBA.
MICROSOFT OFFICE
The PowerPoint Object Model
19 INTEGRATING
This section covers specific issues and examples for automating Microsoft PowerPoint. Although PowerPoint’s object model is smaller than Word’s or Excel’s, it still permits the same level of control, simplifying the process of updating information in a presentation with the latest information.
560
Interoperability PART VI
Table 19.4 summarizes commonly used PowerPoint objects. The objects that also have collections end with (s). TABLE 19.4
PowerPoint Objects
PowerPoint Object
Description
ActionSetting(s)
Setting that defines how a shape or text range reacts to mouse actions during a slide show
AnimationSetting(s)
Animation setting for a shape during a slide show
Application
The entire PowerPoint application
Cell
A cell in a table
CellRange
A collection of cells in a table column or row
ColorScheme(s)
The color scheme for a slide, handout, and more
Column(s)
A column in a table
DefaultWebOptions
Default settings for publishing or saving as a Web page
DocumentWindow(s)
A document window
Pane(s)
A portion of a document window
Presentation(s)
An opened presentation referred to by index number or name
Row(s)
A row in a table
Selection
A selection in a document
ShapeRange
A set of shapes on a document
SlideRange
A set of slides
SlideShowWindow(s)
The window in which a slide show runs
Table
A table on a slide
TextRange
Text attached to a shape
WebOptions
Settings to override DefaultWebOptions
PowerPoint Automation Code Examples The following examples demonstrate code to automate specific properties and methods of PowerPoint objects (see Figure 19.11).
Integrating with Microsoft Office CHAPTER 19
561
FIGURE 19.11 A PowerPoint presentation created with automation.
Adding PowerPoint Slides The Add method is used to add PowerPoint slides to a presentation: ‘ Add a slide. Dim objPP As PowerPoint.Application Dim ppPres As PowerPoint.Presentation Set objPP as New PowerPoint.Application Set ppPres = objPP.Presentations.Add
19
Adding Data and Formatting Slides The following code adds data and formats a slide: ‘ Insert Text in the first slide. With ppPres.Slides(1) .SlideShowTransition.EntryEffect = ppEffectFade
WITH
‘ Add a transition effect to a slide. objPP.Slides(5).SlideShowTransition.EntryEffect = _ ppEffectFade
INTEGRATING
The following code adds a transition effect to a slide:
MICROSOFT OFFICE
Adding Transition Effects
562
Interoperability PART VI .Shapes(1).TextFrame.TextRange.Text = “Northwind Traders” .Shapes(1).TextFrame.TextRange.Characters.Font.Size = 80 With .Shapes(2).TextFrame.TextRange .Text = Chr$(CharCode:=13) + “Board Of Director Meeting” _ + Chr$(CharCode:=13) + “Sales Summary Information” .Characters.Font.Color.RGB = RGB(0, 0, 255) .Characters.Font.Shadow = True End With With .Shapes(“Rectangle 3”).TextFrame.TextRange.Characters(1, 53).Font .Size = 36 .Color.SchemeColor = ppFill End With End With
Adding a Photo to a Shape on a Slide The following code adds a photo and formats a slide: Dim strFileName as String strFileName = ADOrs!Photos objPP.Shapes.AddPicture _ FileName:=strFileName, _ LinkToFile:=msoFalse, _ SaveWithDocument:=msoTrue, _ Left:=110, _ Top:=260, _ Width:=250, _ Height:=250
Running a PowerPoint Presentation The following code starts a PowerPoint presentation: objPP.SlideShowSettings.Run
Automating Outlook This section covers specific issues and examples for automating Microsoft Outlook.
The Outlook Object Model There are more than 55 Outlook objects that can be manipulated with VBA.
Integrating with Microsoft Office CHAPTER 19
563
Tip Refer to “Microsoft Outlook Objects” in the help file for a diagram of the Outlook object model.
Table 19.5 lists commonly used Outlook objects. The objects that also have collections end with (s). TABLE 19.5
Outlook Objects
Outlook Object
Description
Action(s)
A specialized action of an item
AddressEntries(s)
Address information for delivery of a message
Application
The entire Outlook application
AppointmentItem
An appointment in the Calendar folder
Attachment(s)
An attached document to an Outlook item
ContactItem
A contact in the Contact folder
DistListItem
Distribution list in a Contact folder
DocumentItem
A document in an Outlook folder
Explorer(s)
The window that displays the contents of a folder
Folders
A collection of MAPIFolder objects
JournalItem
An entry in a Journal folder
NameSpace
The root object to access data
NoteItem
A note in the Notes folder
Pages
A collection of pages of an Inspector
PostItem
A post in a Public folder
PropertyPage(s)
A custom property page
Recipient(s)
A user or resource in Outlook
SyncObject(s)
A synchronization profile for a user
TaskItem
A task in the Tasks folder
The following examples demonstrate code to automate specific properties and methods of Outlook objects.
19
WITH
A collection of Outlook items in a MAPIFolder
INTEGRATING
The window that displays an Outlook item
Items
MICROSOFT OFFICE
Inspector(s)
564
Interoperability PART VI
Adding and Displaying Outlook Folders The following code adds a subfolder and displays a Public folder in Outlook: ‘ Add a subfolder to the default calendar folder Dim objOutloog as Outlook.Application Dim objSubFolder as Outlook.Folders Set objSubFolder = objOutlook.GetNamespace(“MAPI”).GetDefaultFolder _ (olFolderCalendar).Folders.Add(“New Calendar”)
Adding a New Task and Displaying Task Items The following code adds a new task and displays task items: Dim objOutlook As Outlook.Application Dim objTaskFolder As Object Dim objTaskItem As Object Set objTaskItem = objOutlook.CreateItem(olTaskItem) With objTaskItem .Subject = “This is the subject of a task” .Body = “This is the body of a task” ‘ You can also add a reminder, time, and date. .Save End With ‘ Go to tasks folder. Set objTaskFolder = objOutlook.GetNamespace(“MAPI”) _ GetDefaultFolder(13) ‘ Show the items in the Task folder. objTaskFolder.Display
Creating an Email Message with an Attachment It is easy to send email from Access applications using Outlook with automation (see Figure 19.12). The following code creates a new email message and attaches an Excel spreadsheet: Dim Dim Dim Dim
objOutlook As Outlook.Application objRecipient As Recipient objAttachment As Attachment objMailItem As MailItem
‘ Create a Mail Item. Set objMailItem = objOutlook.CreateItem(olMailItem) With objMailItem
Integrating with Microsoft Office CHAPTER 19
565
‘ Create a recipient for the email message. Set objRecipient = .Recipients.Add(“Steven Johnson”) objRecipient.Type = olTo ‘ Set the Subject, Body, and Importance of the mail item. .Subject = “Here is my Sales Report for your review” .Body = “If you need any other information, please contact me.” _ & vbCrLf & vbCrLf .Importance = olImportanceHigh ‘ Attach an Excel spreadsheet and graph called “Sales by Country.” Set objAttachment = .Attachments.Add(”Sales by Country.xls”) ‘ Resolve each recipient’s name. For Each objRecipient In .Recipients objRecipient.Resolve Next ‘ Display the email message. .Display ‘ Send the email message. .Send End With
FIGURE 19.12 Send Outlook email with an attachment from Access.
19
WITH
Table 19.6 lists the Outlook items, such as appointments or mail messages, you can create and the VBA code snippets necessary to create them.
INTEGRATING
MICROSOFT OFFICE
Creating Outlook Items
566
Interoperability PART VI TABLE 19.6
VBA Code for Creating Outlook Items
Outlook Item
Code to Create the Item
Appointment
Set objItem = objOutlook. CreateItem(olAppointmentItem)
Contact
Set objItem = objOutlook. CreateItem(olContactItem)
Journal
Set objItem = objOutlook. CreateItem(olJournalItem)
Mail Message
Set objItem = objOutlook. CreateItem(olMailItem)
Note
Set objItem = objOutlook. CreateItem(olNoteItem)
Displaying Default Outlook Folders Table 19.7 shows the constant to use with the GetDefaultFolder method to display a default Outlook folder. TABLE 19.7
Outlook Folder Constants
Outlook Folder
Constant for GetDefaultFolder Method
Calendar
GetDefaultFolder(olFolderCalendar)
Contacts
GetDefaultFolder(olFolderContacts)
Inbox
GetDefaultFolder(olFolderInbox)
Journal
GetDefaultFolder(olFolderJournal)
Notes
GetDefaultFolder(olFolderNotes)
Tasks
GetDefaultFolder(olFolderTasks)
Display an Outlook Public Folder The following code displays an Outlook Public folder: Dim objOutlook As Outlook.Application Dim objNameSpace As Outlook.NameSpace Dim objPublicFolders As Outlook.Folders Dim objAllPublicFolders As Object Dim objOfficeContacts As Object
Integrating with Microsoft Office CHAPTER 19 Set Set Set Set
567
objNameSpace = objOutlook.GetNamespace(“MAPI”) objPublicFolders = objNameSpace.Folders(“Public Folders”) objAllPublicFolders = objPublicFolders.Folders(“Favorites”) objOfficeContacts = objAllPublicFolders.Folders(“Office Contacts”)
‘ Display the public folder “Office Contacts.” objOfficeContacts.DisplayThe
Finding an Outlook Item The following code demonstrates how to find a contact in Outlook: Set objItem = objContactItems.Find(“[File As] = ‘Zelko, John’”) objItem.Display
Filtering Outlook Items The Restrict method is used to filter Outlook items. The following code returns only the contact items with the First Name of “Steve”: Set objFilter = objContactItems.Restrict(“[First Name] = ‘Steve’”)
Tip The code for this chapter contains an entire Internet email application. Email can be sent from Outlook to groups of individuals in an Access database. The chapter code also includes examples of letters created in Word that can be sent automatically to contacts in Outlook.
There are more than 10 Graph objects that can be manipulated with VBA. Table 19.8 summarizes commonly used Graph objects. The objects that also have collections end with (s).
WITH
Graph Object Model
INTEGRATING
This section covers specific issues and an example for automating Microsoft Graph.
MICROSOFT OFFICE
Automating Graph
19
568
Interoperability PART VI TABLE 19.8
Graph Objects
Graph Object
Description
Application
The entire Microsoft Graph application
AutoCorrect
Graph AutoCorrect feature
Axis(s)
A single axis in a chart
Chart
A Graph chart
ChartArea
The area of a specified chart
ChartGroup(s)
One or more series of points plotted in a chart
DataSheet
A Graph datasheet
DataTable
A data table in a specified chart
Legend
A legend in a specified chart
PlotArea
The plot area of a specified chart
Series(s)
A series in a specified chart
Creating Graphs Sophisticated documents often include graphs and charts. Word and other Office products share Microsoft Graph, which can be used to create and format graphs. In Access, it is very easy to create graphs on forms using a wizard. Often, however, you might want to show different types of graphs of specific data. Rather than create six forms with different graphs, you can manipulate the graph on one form with automation code to give the user various graphic views of the data (see Figure 19.13). FIGURE 19.13 One form can show many types of graphs created with Microsoft Graph.
Integrating with Microsoft Office CHAPTER 19
569
Tip When automating to an application for charting capabilities, you can use Microsoft Excel or Microsoft Graph. Generally, you should use Excel because of its more extensive object model.
Summary This chapter showed you how to use the Access and VBA to automate Microsoft Office applications. After a short introduction to the subject of automation and a short example illustrating the general idea, the chapter introduced the key features of Word, Excel, PowerPoint, Outlook, and Graph that you can manipulate using VBA code, and provided examples of using VBA to control each of these applications from Microsoft Access.
19 INTEGRATING WITH
MICROSOFT OFFICE
CHAPTER 20
Using Visual Basic with Access
IN THIS CHAPTER • Creating ActiveX Code Components 572 • Creating ActiveX Controls
593
572
Interoperability PART VI
Access is a powerful development tool, but it does not do everything. Visual Basic (VB) is a powerful and easy-to-use adjunct to Access. VB can be used to create standalone ActiveX code components and ActiveX controls. This chapter assumes that you have no experience developing with Visual Basic. Using step-by-step instructions, you will learn how to create ActiveX controls that you can use in your Access applications. You will also learn how to create other types of code components in Visual Basic that can be used in Access, Visual Basic, and Office applications. Tip To work through the examples in this book you can use any version of Visual Basic 6. If you do not have Visual Basic 6, the Learning Edition is the least expensive version.
You might ask, “What is the difference between Visual Basic and Visual Basic for Applications (VBA)?” VBA is a programming language used in Visual Basic, Access, and other Office applications. The VBA used in Office 2000 and Visual Basic 6 is bit by bit identical. Visual Basic 6 is the development environment you will use in this chapter to create components and ActiveX controls.
Creating ActiveX Code Components With Visual Basic, you can create ActiveX code components and use them in Access applications. As an example in this chapter, you will create an ActiveX code component to play a sound file in Access applications. Tip The ActiveX components and ActiveX controls discussed in this chapter are included on the book’s CD-ROM. These components must be registered before you can use them. To register the components, open the project file and compile it. Later in this chapter, you will find additional information about registering components.
Using Visual Basic with Access CHAPTER 20
573
What Is an ActiveX Code Component? An ActiveX code component is code in a class module that is compiled into an executable (EXE) or a dynamic link library (DLL). The main advantage of creating and using ActiveX code components is that you can use them in any COM-compliant application. That is, ActiveX code components are not restricted to use in a particular Access application. Rather, they can be reused in multiple Access applications. Moreover, ActiveX code components can also be used in Visual Basic applications, Office applications, and in any application that supports ActiveX. In Chapter 11, “Creating Objects with Class Modules,” you created a sound object using the cSound class module. The class module passed a sound file to a Windows API call that plays the sound on the computer. The relevant code snippet from that class module appears in Listing 20.1. LISTING 20.1
cSound Class Module
Option Explicit ‘ Windows API call. Private Declare Function sndPlaySound Lib “winmm.dll” Alias _ “sndPlaySoundA” (ByVal lpszSoundName As String, _ ByVal uFlags As Long) As Long Public Sub PlaySound(SoundFile As String) ‘ Play the Sound File. sndPlaySound SoundFile, 1 End Sub
The problem with this class module is that it can be used only in the Access application in which the module exists. If another Access application is created, the class module must be imported into the new application. However, if you decide to make a change to the class module in the future, you will have to go to every class module in every Access application and make the change to each one individually. By creating an ActiveX code component for the sound object, all Access applications (as well as other applications) can use the ActiveX code component. If a change must be made, it must be made in only one place.
20 USING VISUAL BASIC WITH ACCESS
574
Interoperability PART VI
What Is the Difference Between an ActiveX EXE and an ActiveX DLL? When you create an ActiveX code component in Visual Basic, you can create either an ActiveX EXE or an ActiveX DLL. What’s the difference? An ActiveX EXE is an out-of-process component. This means that it runs in its own process space, separate from the calling application. If an ActiveX sound component is compiled as an EXE, it runs in its own process space, separate from the Access application’s process space. An ActiveX DLL, on the other hand, is an in-process component, meaning it runs in the same process space as the calling application. Why does this matter? Simply stated, calls from an application to an out-of-process (EXE) component are slower than calls to an in-process (DLL) component. As always, though, the increased speed of using a DLL involves a tradeoff. If the DLL crashes, the calling application will likely crash as well because the DLL crash corrupts the calling application’s address space. If an ActiveX EXE crashes, the calling application continues to function because its process space is separate from the ActiveX component’s process space. Tip An ActiveX DLL runs in the same process as the Access application that calls it. Therefore, an ActiveX DLL is substantially faster than an ActiveX EXE.
Creating an ActiveX Code Component To get started using Visual Basic, you will create an ActiveX code component to play sounds. First, open Visual Basic. In the New Project dialog box, shown in Figure 20.1, choose ActiveX DLL. You will use an ActiveX DLL project because the component will run in the same process, as will the Access application that calls it. When the project opens in the IDE, you will notice one class module named Class1. In the Project Properties window, rename this class module cSound. In Access, open the Chapter 11 Access application Creating Objects with Class Modules.mdb. Open the cSound class module in Design view, and copy the contents of the module into the Clipboard. Return to the Visual Basic project, and paste all the contents into the code window for the cSound class module, as shown in Figure 20.2.
Using Visual Basic with Access CHAPTER 20
575
FIGURE 20.1 Choose ActiveX DLL from the New Project dialog box.
FIGURE 20.2 Copy the cSound code into the class module.
20 USING VISUAL BASIC WITH ACCESS
In the Project Properties window, there is an Instancing property. An instance is an actual object whose properties and methods you can use. A class simply defines an object’s methods and properties. The default value for ActiveX DLL’s is MultiUse, meaning that applications can create, or instantiate, objects from the class and that one instance of the class can provide multiple objects. This reduces memory requirements, but when a second request for the object is made, it cannot run until the first request is completed. Most of the time, you will use the MultiUse default settings.
576
Interoperability PART VI
Tip There are six settings for the Instancing property. Because this chapter is an overview of Visual Basic for Access developers, these property settings are not discussed in detail. See the Visual Basic Help for additional information.
Next, modify the project’s properties. To do so, select Project, Project1 Properties. On the General tab of the Project Properties dialog box (see Figure 20.3), enter the project name cSoundObject. Tip The project name cannot include spaces.
In Project Description, enter cSound Object. The project description is the name used when referencing the object in the References dialog box. FIGURE 20.3 The Project Properties General tab.
On the Make tab in Project Properties, shown in Figure 20.4, you can specify a version number. It is a good idea to check the Auto Increment check box so that each time you compile the component, a new version number is provided. In the Version Information section, you can enter a company name, copyright, product name, and other information. The rest of the settings do not have to be changed for this example.
Using Visual Basic with Access CHAPTER 20
577
FIGURE 20.4 The Project Properties Make tab.
On the Compile tab, shown in Figure 20.5, the default is Compile to Native Code. Most of the time, you will not want to change this setting. If you choose Compile to P-Code, the code will run more slowly, but the component will be smaller in size. For information about other settings under the Compile tab. See the Visual Basic Help file. FIGURE 20.5 The Project Properties Compile tab.
On the Component tab, shown in Figure 20.6, the default is set to Project Compatibility. This is the setting you should use as a developer when creating the component on your computer. You will read about the other options, No Compatibility and Binary Compatibility, in the section, “Distributing ActiveX Components,” later in the chapter. For this example, do not make any changes under the Debugging tab. Click OK to close the Project Properties dialog box.
20
Save the project and all the cSound class module by choosing File, Save Project and use the name cSound Object. It is best to save the project (.vbp file) and class module (.cls file) in the same directory (for example, Sound Object).
USING VISUAL BASIC WITH ACCESS
578
Interoperability PART VI
FIGURE 20.6 The Project Properties Component tab.
Compiling the DLL Naturally, to use the DLL, it must be compiled. Compiling the DLL is easy to do. If you saved the project as cSound Object, choose File, Make cSound Object.dll. If you used another name, select it instead. You will likely be prompted to save this DLL in your Visual Basic directory. Instead, save the resulting DLL in the same directory as the project file. Tip Saving the compiled DLL in the same directory as your project file makes it easy to locate.
When the process completes, which should take only a few seconds, the DLL will have been created and registered on your computer. Visual Basic automatically registers a DLL it compiles, allowing its immediate use in a development environment. Before distributing the finished DLL to others, however, you must perform additional steps, described in the section titled “Distributing ActiveX Components.” Note If you look at the directory in which you saved the project files (see Figure 20.7), you will see numerous files. Visual Basic saves forms, modules, and other project components as separate files on disk. In contrast, Microsoft Access includes objects within one MDB file.
Using Visual Basic with Access CHAPTER 20
579
FIGURE 20.7 Unlike Access, VB saves project components as separate files.
Tip This example shows how to create an ActiveX DLL. The process to create an ActiveX EXE is very similar. To begin, open Visual Basic and start with an ActiveX EXE in the New Project dialog box.
Using the cSound ActiveX Component Using your newly created cSound component in an Access 2002 application is straightforward. The general procedure is to set a reference to the DLL, instantiate the object, and then to use a Sound object. The following steps show the procedure: 1. Create new Access database and save it with the name Sound. 2. Create a blank form and name it frmSound. 3. Add a command button to the form and name it cmdSound. 4. Open the event procedure for cmdSound’s On
Click
event.
6. In the code window, enter the following code:
USING VISUAL BASIC WITH ACCESS
5. With the code window active, select Tools, References, check the cSound Object checkbox, then click OK.
20
580
Interoperability PART VI Private Sub cmdSound_Click() Dim objSound As cSound Set objSound = New cSound ‘ If using Windows NT, use the path “C:\WINNT” objSound.PlaySound “C:\Windows\chimes.wav” Set objSound = Nothing End Sub
7. Save the code, close the code window and the form, then open the form in normal view and click the command button you created to play the sound. It was necessary to set a reference to the cSound object, because it is a component external to Microsoft Access. Note that you will use the cSound object without any class module in your Access application. Did you notice as you typed the code that IntelliSense appeared for the object (see Figure 20.8)? This is a terrific benefit for writing code faster and better. FIGURE 20.8 IntelliSense appears for the cSound object.
Although this example was somewhat contrived, you can see how easy it is to create an ActiveX code component and then use its properties, methods, and events. What might not be immediately clear, though, is that you can now use the cSound component (or any other ActiveX code component) from other Access applications. If you modify and
Using Visual Basic with Access CHAPTER 20
581
rebuild cSound, you have to make changes in only one place, in the component itself, rather than in each application using the code. As noted earlier, you can use the cSound component in any COM-compliant application, such as VB, Office, Outlook, or Web applications. The next section explains how to distribute ActiveX components so applications can use them.
Distributing ActiveX Components When distributing a completed ActiveX component to end users, several issues arise: • Installing the component • Component Registry settings • Component compatibility This section discusses how to install the component properly, how to register it correctly, and how to ensure component compatibility.
Installing the Component The easiest way to install an ActiveX component on an end user’s computer is to use VB’s Package and Deployment Wizard. The Package and Deployment Wizard creates a setup program that installs the component or modifies the necessary system registry settings. Third-party installation programs exist that provide the same functionality.
Creating Setup Files with the Package and Deployment Wizard To create a setup program using the Package and Deployment Wizard, start the wizard from Visual Basic by selecting Add-Ins, Package and Deployment Wizard. If Package and Deployment Wizard is not available on the Add-Ins menu, load it by selecting AddIns, Add-In Manager, highlighting Package and Deployment Wizard, placing a checkmark in the Loaded/Unload checkbox in the lower-right corner of the screen, and then clicking the OK button. Note
20 USING VISUAL BASIC WITH ACCESS
This example invokes the Package and Deployment Wizard from within Visual Basic, but it can also be executed as a standalone application. Refer to the documentation or the help files for the development tool you are using for instructions on using the Package and Deployment Wizard in standalone mode.
582
Interoperability PART VI
Once the wizard loads, your current project should be listed in the Active Project text box at the top of the first screen, as shown in Figure 20.9. FIGURE 20.9 Loading a project in the Package and Deployment Wizard.
Next, click the Package button to specify a package type, as shown in Figure 20.10. Note If you have modified the source code for an ActiveX component without recompiling it, the Package and Deployment Wizard might prompt you to recompile it before continuing the packaging process. After you make the appropriate selection, the process will continue as described in this section.
There are three package types: • Standard Setup Package: Creates a standard Windows setup. • Internet Setup: Creates an Internet setup. • Dependency File: Setup includes only dependency files. For this example, use the Standard Setup Package. Click the Next button to continue. On the next screen, specify where you want your setup files located (see Figure 20.11). The wizard might prompt you for permission to create the specified folder if it does not exist. Click the Next button to continue. The next screen shows the files to include in the setup (see Figure 20.12). Ordinarily, the Package and Deployment Wizard accurately chooses the necessary files, but not always. For example, notice that the VB runtime file appears in the list shown in Figure 20.12. In some cases, it might not be necessary to include the runtime or other files, so review
Using Visual Basic with Access CHAPTER 20
583
the default list carefully before continuing. If you want to add other files to the setup, click the Add button and select the files to include. Click the Next button to continue. FIGURE 20.10 Choose the type of package you would like to create.
FIGURE 20.11 Specify the package folder.
Tip The VB runtime file must be installed on the end user’s computer for the component to work. After initially installing the VB runtime file, you might want to uncheck the VB runtime file’s boxes to keep the setup file small. For example, simply adding a form to an existing application does not require reinstalling VB’s runtime file.
20 USING VISUAL BASIC WITH ACCESS
On the next screen, specify cab options (see Figure 20.13). A single cab (compressed) file can be created for a network or CD setup, or multiple cab files can be created for a floppy disk setup.
584
Interoperability PART VI
FIGURE 20.12 Specify included files.
FIGURE 20.13 Specify cab options.
On the next screen, indicate the installation title (see Figure 20.14). Choose a friendly, short, and descriptive name because the name defined on this screen is the one users see when they run the setup program. FIGURE 20.14 Specify the installation title.
Using Visual Basic with Access CHAPTER 20
585
On the Start Menu Items screen, indicate the Start menu items (see Figure 20.15) for your component. You can create a program group that the end user can use to start an application with the Start button. Because this is a component, rather than a standalone application, you should not specify a program group. FIGURE 20.15 Specify Start menu items.
The next screen, the Install Locations screen, allows you to set the default installation path for the component. You might want to install the component in your application directory for easy maintenance, rather than in the Windows/System directory. Figure 20.16 illustrates the Install Locations screen. Note that it uses the $(WinSysPath) variable, which causes the component to be installed into the Windows system directory, which is usually C:\Windows\System on Windows 9x and Me systems or C:\WINNT\ SYSTEM on Windows NT systems. FIGURE 20.16 Specify install locations.
20 USING VISUAL BASIC WITH ACCESS
On the last screen, a script name can be specified to save a setup script so that all the options need not be selected the next time setup files are created (see Figure 20.17). Click the Finish button to create the package.
586
Interoperability PART VI
FIGURE 20.17 Specify a script name and create the package.
The cab file(s) will then be created, and a Packaging Report will open with additional information. You can save the report if you like. All the setup files are now located in the package folder you specified (see Figure 20.18). These files can be distributed to the user so that the component can be installed. FIGURE 20.18 Setup files created.
End users simply double-click the setup icon or use Add/Remove Programs in the Control Panel to install the component and other necessary files. Figure 20.19 shows the beginning of the setup program just created.
Deploying Files with the Package and Deployment Wizard Creating the setup program completes half of the deployment. The other half is actually deploying the files. The Package and Deployment Wizard includes a Deploy component to simplify this step. To get started, open the Package and Deployment Wizard, then click the Deploy button. The wizard will ask you to select the package to deploy, using the script name provided at the end of the packaging process. Figure 20.20 shows this screen. Click the Next button to continue.
Using Visual Basic with Access CHAPTER 20
587
FIGURE 20.19 The setup created for the user.
FIGURE 20.20 Specify the package to deploy.
The next screen asks you to specify the deployment method. You can deploy to a local or network folder or to a Web server. Figure 20.21 shows deploying the package to a local folder. On the Folder screen, name the folder to which to deploy the package (see Figure 20.22). This can be a folder on your network server or a local folder.
Review, and, if you wish, save the Deployment Report.
USING VISUAL BASIC WITH ACCESS
Use the final screen to provide a script name that allows you to save the deploy script just created and avoid reselecting all the options just chosen the next time the Deploy wizard runs (see Figure 20.23). Click the Finish button to deploy the package.
20
588
Interoperability PART VI
FIGURE 20.21 Specify the deployment method.
FIGURE 20.22 Specify the deployment folder.
FIGURE 20.23 Specify a script name and deploy the package.
Component Registry Settings For components to work, information must be stored in the Windows Registry. Specifically, each component must have its own GUID, or Global Unique Identifier. A GUID allows Windows and Windows applications precisely and unambiguously to
Using Visual Basic with Access CHAPTER 20
589
identify class objects and interfaces, even in situations in which components or applications use identically named objects. The Package and Deployment Wizard creates setup programs that automatically and invisibly (to both developer and end user) create and register GUIDs and set other registry values. However, if you do not use the Package and Deployment Wizard, you must manually register your component. This section explains how to do so. An ActiveX EXE component registers itself automatically when invoked (executed) from a shell command line. An ActiveX EXE also registers itself when double-clicked in Windows Explorer or another file manager interface. However, an ActiveX DLL component must be registered using the REGSVR32.EXE program. REGSVR32.EXE is located on your computer in the Windows\System32 directory (or Winnt\System32 in Windows NT). For example, if the cSound DLL is named cSound Object.dll and is stored in C:\, execute the following command at a shell prompt or using the Run command to register the newly installed component. Figure 20.24 demonstrates using the Run command to register this chapter’s example ActiveX component using the Run command: regsvr32 “c:\csound object.dll”
FIGURE 20.24 Manually register an ActiveX DLL with REGSVR32.
You can also manually unregister components. For ActiveX EXEs, run the executable from the command line, and append /UNREGSERVER between the command name and the EXE name. Similarly, for ActiveX DLLs, run REGSVR32, and append the /U parameter. Tip If you frequently use REGSVR32 to register and unregister components, add REGSVR32.EXE to the Send To directory in the Windows directory. Then, to register and unregister the component, you need only to right-click the DLL and select REGSVR32 from the popup shortcut menu.
20 USING VISUAL BASIC WITH ACCESS
590
Interoperability PART VI
Figure 20.26 shows the command to unregister this chapter’s sample ActiveX component at the shell command prompt. FIGURE 20.26 Unregistering an ActiveX DLL from a shell prompt.
The command includes the full path (c:\windows\system\regsvr) because the directory c:\windows\system is not in the default path.
Component Compatibility As mentioned earlier in the chapter, an ActiveX code component will be assigned a GUID in the Windows Registry. What happens to an installed component if it is later modified? When the modified component and its GUID are installed, does it have to be reregistered and/or assigned a new GUID? To answer these questions, go back to Visual Basic’s Component tab (in Project Properties). As you might recall, the Component tab offered three choices for the Version Compatibility option: No Compatibility, Project Compatibility, and Binary Compatibility. Project Compatibility manages the Registry settings as you develop, compile, debug, and recompile the component. When it is ready to ship, however, choose Binary Compatibility (see Figure 20.27). In the textbox under that option, a DLL must be referenced to maintain compatibility. Navigate to the DLL using the ellipsis button. Whenever a new DLL is compiled, it will be compatible with the prior version of the component because it will always refer to the specified DLL. Caution Even with Binary Compatibility, if you change the interface of the component, the calling application will fail when it uses the component. For example, if the
Using Visual Basic with Access CHAPTER 20
591
method called PlaySound is changed to PlayMusic in the cSound component, the component will no longer be compatible. In that case, choose the No Compatibility option and redistribute the component with your Access application.
FIGURE 20.27 Choose Binary Compatibility and reference a DLL.
Tip Take care not to delete the DLL referenced when defining Binary Compatibility for a DLL. Create a DLL with a special name, and save it in a secure location. A naming example is Binary–cSoundObject.dll to alert you that this DLL is used for binary compatibility.
When do you use No Compatibility? Let’s assume that you distribute the cSound component, and later decide to eliminate the PlaySound method, replacing it with another method. All applications using the component will fail because they rely on the PlaySound method. In such a case, choose No Compatibility so that a new GUID is assigned. It is best in this situation to recreate the setup and distribute it as if it were the first time.
The Error Handling Component
20 USING VISUAL BASIC WITH ACCESS
Chapter 13, “Professional Error Handling,” includes a class module that provides extensive error handling capabilities, such as logging errors to an Access table, logging errors to a text file, e-mailing errors, and recording errors on an Outlook calendar. This chapter includes a more comprehensive error handler using a cError component
592
Interoperability PART VI
(cErrorObject.dll). The entire ActiveX DLL project is included so that you can utilize a global error handler. As with all ActiveX DLLs, you can use the enhanced error handler for program errors in all Access, VB, and Office applications. Tip In the chapter code there is an Access application and a VB application that use the cError component to log all errors to the global error handler.
Global Error Handling Data The data for the cError global error handler is contained in an Access database named Global Error Handler Data.mdb. The database contains two tables. One table, tblErrorLog, stores all error information. The other table, tblErrorOptions, contains settings determining how the global error handler runs. A form in the database, frmErrorOptions, provides an easy interface to modify the error handler’s configuration options (see Figure 20.28). FIGURE 20.28 The Error Options form in the global error handler.
The error database, Global Error Handler Data.mdb, also includes a report for listing errors.
Using Visual Basic with Access CHAPTER 20
593
The cError Component The bulk of the work is done by the error handling component. The component contains six class modules: •
cComputerName—Gets
•
cDBConnection—Creates
•
cError—Processes
•
cSound—Plays
•
cSystemInformation—Gets
•
cUserName—Gets
the name of the computer. an ADO connection.
the error information.
a sound file.
computer information such as operating system, processor, and total and available memory. the Windows login username.
Creating ActiveX Controls Visual Basic is exciting because it allows you to create your own ActiveX controls, giving you enormous power to extend your applications.
Types of ActiveX Controls ActiveX controls fall into two categories: runtime controls and design-time controls. Runtime controls have an interface for user interaction. Examples include the TreeView and ListView controls. Design-time controls, on the other hand, have no end user interface; they are strictly developer tools. Examples include the ImageList and CommonDialog controls. Chapter 9, “Enhancing Forms with ActiveX Controls,” discusses 23 Microsoft ActiveX controls and the chapter code includes examples of each control.
ActiveX Control Attributes ActiveX controls cannot exist independently. They must be instantiated in a host application, typically a form. A compiled ActiveX control is saved in a file, typically with an OCX extension. A single OCX file might contain several ActiveX controls. For example, the MSCOMCTL.OCX that Microsoft ships contains the following ActiveX controls: ImageCombo, ImageList, ListView, ProgressBar, Slider, StatusBar, TabStrip, ToolBar, and TreeView.
USING VISUAL BASIC WITH ACCESS
ActiveX controls are in-process automation servers. Therefore, an ActiveX control you create and use on an Access form runs in the same process in which Access runs, which results in excellent performance.
20
594
Interoperability PART VI
Creating a Design-Time ActiveX Control In Access, when a timer is necessary, developers generally use the On Timer property and Timer event of a form. One problem with this approach is that only one timer can be used. What if you need multiple timers, each working independently? Visual Basic provides the solution because it permits you to use more than one Timer control on a single form. Visual Basic includes a built-in Timer control not available in Access. The VB Timer control is a design-time control that can execute code at certain intervals. To use the VB Timer control, place it on the form, and set the Interval property in milliseconds (1,000 milliseconds equals one second; see Figure 20.29). Code in the Timer event executes whenever the interval occurs. Multiple VB timers can be placed on a form, each operating independently. FIGURE 20.29 Set the Timer control’s Interval property.
In this section, you will create your own Timer ActiveX control that can be used in Access and other applications. Tip The VB ActiveX Control project and Access database that use the Timer control are both available on the CD-ROM accompanying this book. Remember, the ActiveX control must be registered (compiled) on your computer in order to use it.
Using Visual Basic with Access CHAPTER 20
595
Starting an ActiveX Control Project To create your ActiveX control, open Visual Basic. In the New Project dialog box, choose ActiveX Control (see Figure 20.30). FIGURE 20.30 Choose ActiveX Control from the New Project dialog box.
Creating the Interface When the project opens, you will see what appears to be a form. Actually, it is not a form, but is called a UserControl object. The UserControl is used to create the interface for the ActiveX control. In the Properties window for the UserControl, change the name from UserControl1 to ctlTimer. ActiveX controls might include other controls. In your Timer control, you will insert a control from the toolbox. Name the PictureBox control picClock and choose for its Picture property, select CirClock.bmp from the Graphics\Bitmaps\Gauge directory where you installed Visual Basic. This picture of a clock is what the developer will see when using the design-time control (see Figure 20.31). PictureBox
Next, insert a VB Timer control from the toolbox, and name it ctlVBTimer (see Figure 20.32). Notice in the Properties window of the control that there is an Interval property. You will be using this property in your ActiveX control.
20 USING VISUAL BASIC WITH ACCESS
Now, you will resize the UserControl object. First, move the VB Timer control, and place it directly on top of the PictureBox control. Then, choose Send to Back under the Format menu. This will hide the control. Next, resize the UserControl so that it fits around the PictureBox control, as shown in Figure 20.33.
596
Interoperability PART VI
FIGURE 20.31 Insert a PictureBox
control on the UserControl.
FIGURE 20.32 Insert a VB Timer control on the UserControl.
Because this is a design-time control, the end user will never see it. In the UserControl Properties window, set the InvisibleAtRuntime property to True.
Using Visual Basic with Access CHAPTER 20
597
FIGURE 20.33 Resize the UserControl.
Setting Project Properties Open Project1 Properties under the Project menu. In the Project Properties dialog box, enter the project name (cTimerControl), and project description (cTimer Control) as shown in Figure 20.34. Click OK to close the Project Properties dialog box. FIGURE 20.34 Specify the project name and description in Project Properties.
Saving the Project Save the project and UserControl by choosing Save Project under the File menu. It is best to save the project (.vbp file) and UserControl (.ctl file) in the same directory (for example, Timer Control).
Adding a Method and Event
20 USING VISUAL BASIC WITH ACCESS
Click the UserControl (not the PictureBox), and review the list of properties in the Properties window. Notice that there is no Interval property. You will have to add this property. Also, a Timer event must be added to enter code to execute when the Timer interval occurs. To add properties, methods, and events to an ActiveX control, use the ActiveX Control Interface Wizard.
598
Interoperability PART VI
Using the ActiveX Control Interface Wizard Open the ActiveX Control Interface Wizard by choosing ActiveX Control Interface Wizard under the Add-Ins menu. If you do not see the item under the Add-Ins menu, choose Add-In Manager, select VB 6 ActiveX Control Interface Wizard, click the Load on Startup check box, and then click the OK button to close the dialog box. The first screen of the wizard is the Introduction screen (see Figure 20.35). Click the Next button. FIGURE 20.35 The ActiveX Control Interface Wizard: Introduction screen.
On the left side of the Selected Interface Members screen is a list of standard properties, methods, and events (see Figure 20.36). Remove all the items on the rightmost side. On the left side, choose the Interval property and Timer event, and move them to the right side so that they are included in the control. Click Next. FIGURE 20.36 The Selected Interface Members screen.
On the Create Custom Interface Members screen, click Next because you have already added the property and event you wanted (see Figure 20.37).
Using Visual Basic with Access CHAPTER 20
599
FIGURE 20.37 The Create Custom Interface Members screen.
On the Set Mapping screen, select the Interval property and the Timer event, and map them to the UserControl (see Figure 20.38). Then, click Next. FIGURE 20.38 The Set Mapping screen.
On the Set Attributes screen, choose the Interval property, and make sure that the data type is set to Long (see Figure 20.39). Then, click Next. FIGURE 20.39 The Set Attributes screen.
20 USING VISUAL BASIC WITH ACCESS
600
Interoperability PART VI
On the Finished screen, click the Finish button (see Figure 20.40). The wizard will write code for you to add the Interval property and the Timer event to the ActiveX control. Also, a Summary Report is created that can be saved to disk. FIGURE 20.40 The Finished screen.
Open the code window for the UserControl, and examine the code (see Figure 20.41). The wizard created a Property Get and Property Let for the Interval property. Also, code was written to add the Timer event. FIGURE 20.41 Code created by the ActiveX Control Interface Wizard.
Adding Code to the ActiveX Control The ActiveX Control Interface Wizard did a great deal of work for you, but you have to add a little code to complete your Timer control.
Using Visual Basic with Access CHAPTER 20
601
Raising an Event In your ActiveX control, there is a VB Timer control with a Timer event. Your ActiveX control also has a Timer event, which was added with the ActiveX Control Interface Wizard. You have to synchronize these Timer events so that when the VB Timer event fires, it triggers your ActiveX control’s Timer event. To do this, you will “raise” an event in your UserControl. In the code window, choose from the object list on the top-left drop-down list. Then, choose Timer from the event list on the top-right drop-down list. A Timer event procedure will then be created (see Figure 20.42). Enter the code shown in Figure 20.42 to “raise” the Timer event. ctlVBTimer
FIGURE 20.42 The RaiseEvent Timer.
What is the effect of this? When the VB Timer executes, its Timer event fires, which then triggers the Timer event of the ActiveX control. This will run the code you create for the Timer control.
Using the ReadProperties Event
20 USING VISUAL BASIC WITH ACCESS
When a developer inserts your Timer control on a form, the Interval property will be set to determine when the Timer event fires. The VB Timer control will take care of the time keeping. Therefore, the value of the Interval property of your Timer control must be synchronized with the Interval property of the VB Timer control.
602
Interoperability PART VI
ActiveX controls are created and destroyed often, as they are added to forms by developers and when forms that contain the control are opened. To synchronize the Interval properties, the ReadProperties event is a good choice because it will execute when the form hosting the control opens. The following code will set the value of the Interval property in the VB Timer control to be the same as your Timer control: ‘ When the interval on your control is changed on the form. ‘ change the interval on the VBTimer control in your control. UserControl.ctlVBTimer.Interval = Me.Interval
Make sure that you save the project.
Testing the ActiveX Control in Visual Basic Before using your Timer control in Access and other applications, let’s test it in Visual Basic. With the ActiveX Control project open, you can add a standard EXE project and test your control on a VB form. To add a project, choose Add Project under the File Menu. In the New Project dialog box, select Standard EXE. Notice that two projects are now displayed in the Project Explorer window (see Figure 20.43). FIGURE 20.43 Add a project to test the ActiveX control.
Because there are now two projects, set the Startup Project to the Standard EXE (Project1). In the Project Explorer window, right-mouse click Project1. On the menu, choose Set as Startup. Now, when you run the project, the Project1 will start. Next, close all the ActiveX Control project windows. Under the Windows menu, select any open windows (except Form1), and close each window. While in the Design view of Form1, open the toolbox. Notice that on the toolbox is a new control named ctlTimer. Insert the control on the VB form (see Figure 20.44). Your Timer control on the form can be used just like any other control (for example, TextBox). Its properties can be set in the Properties window, and code can be written to respond to events.
Using Visual Basic with Access CHAPTER 20
603
FIGURE 20.44 Insert the Timer control on a VB test form.
In the Properties window, set the Interval property to 5000. Because the control uses milliseconds, the Timer event will execute every five seconds. Next, choose Code under the View menu to open the code window. Choose ctlTimer1 from the object list and Timer from the event list. In the Timer event procedure, enter a simple message box as shown in Figure 20.45. FIGURE 20.45 The Timer event procedure.
20 USING VISUAL BASIC WITH ACCESS
604
Interoperability PART VI
Run the project by choosing Start under the Run menu (or press F5). Every five seconds, the message box will appear (see Figure 20.46). Notice that the control cannot be seen at runtime. Any type code can now be written utilizing your Timer control. FIGURE 20.46 The ActiveX Timer control fires every five seconds.
Now that you have tested your Timer control in Visual Basic and know it works, you can use it on an Access form.
Using the Timer Control in an Access Form Testing your control was very easy in Visual Basic. To use the control in Access, you must compile the control. Open the ActiveX Control project, and choose Make cTimerControl.ocx from the File menu. Save the OCX file in the same directory as your ActiveX Control project. The ActiveX control is now compiled, and Registry settings have been made on your computer. Close Visual Basic. Open a form in design view in an Access database. Choose ActiveX Control from the Insert menu. Choose your Timer control (cTimerControl) from the list, and then click OK to close the dialog box (see Figure 20.47). FIGURE 20.47 Choose cTimerControl
from the Insert ActiveX Control dialog box.
As you did in the VB test form, set the Interval property of the control to 5000, and create a message box in the Timer event. The cTimerControl will now work on an Access form.
Using Visual Basic with Access CHAPTER 20
605
Using Multiple Timer Controls The beginning of this section mentions that the OnTimer event of Access forms is often used as a timer. However, that is only one timer that can be used. You can place several Timer controls on a form, and they will all work independently (see Figure 20.48). FIGURE 20.48 Multiple Timer controls working independently.
Distributing the Timer ActiveX Control Distributing an ActiveX control is essentially the same as distributing ActiveX components. See the section earlier in the chapter titled “Distributing ActiveX Components.”
Creating a Property Page Chapter 9 explains how to use property pages to easily enter property values for ActiveX controls. Figure 20.49 shows the property page for the ImageList control. FIGURE 20.49 The ImageList control property page.
20
On the Introduction screen, click Next.
USING VISUAL BASIC WITH ACCESS
You can create a property page for your Timer control as well! In the ActiveX Control project, choose Add Property Page under the Project menu. In the Add Property Page dialog box, choose the VB Property Page Wizard. Click Open to start the wizard.
606
Interoperability PART VI
On the Select the Property Pages screen, click Add and add a General page. Use the up arrow on the right side of the screen to list the General page first (see Figure 20.50). Click Next. FIGURE 20.50 The VB Property Page Wizard: Select the Property Pages screen.
On the Add Properties screen, click the General tab. Move the Interval property from the left list box to the right list box (see Figure 20.51). Click Next. FIGURE 20.51 The VB Property Page Wizard: Add Properties screen.
On the Finished screen, click Finish. The wizard will create the property page and a summary report that can be saved to disk. Notice that the property page now exists in the Project Explorer window (see Figure 20.52).
Using the Property Page To use the property page, save and recompile the ActiveX control. In design view of an Access form, insert the Timer control, and right-mouse click the control. On the menu, choose cTimerControl properties. The property page can be used to enter the Interval property (see Figure 20.53).
Using Visual Basic with Access CHAPTER 20
607
FIGURE 20.52 The property page in the Project Explorer window.
FIGURE 20.53 Using the property page.
Using the Timer Control in Other Applications Your Timer control is not restricted to just VB and Access applications. This same control can be used on a form in Word, Excel, Outlook, and other applications.
Creating a Runtime ActiveX Control Most ActiveX controls have an interface with which users interact. In this section, you will create a runtime ActiveX control. The sample control will be named cSlider and will include a Slider control and UpDown control. As users increment and decrement the UpDown control, the Slider control will move. Tip
20 USING VISUAL BASIC WITH ACCESS
The VB ActiveX Control project and Access database that use the cSlider control are both available on the CD-ROM accompanying this book. Remember, the ActiveX control must be registered (compiled) on your computer in order to use it.
608
Interoperability PART VI
Creating the Interface As with the design-time control, open an ActiveX Control project. On the UserControl, add a Slider control and UpDown control as shown in Figure 20.54. Notice that ActiveX controls you build can include many other controls. FIGURE 20.54 Add a Slider and UpDown control to the UserControl.
Name the objects as follows: UserControl: cSlider
control: ctlSlider UpDown control: ctlUpDown
Slider
Then, resize the UserControl. Right-mouse click ctlUpDown, and choose Properties from the menu to open the property page. On the property page, you will associate the UpDown control with the Slider control using the Buddy property. The Buddy property allows the controls to work together without writing any code. Click the Buddy tab on the property page. In the Buddy Control text field, enter This will associate the ctlSlider control with the ctlUpDown control. Choose Value from the Buddy Property list so that when the value of ctlUpDown changes, the value of ctlSlider will change. Enable the check box for Auto Buddy, click the Apply button to activate the change, and then click OK to close the property page (see Figure 20.55). ctlSlider.
FIGURE 20.55 Set the Buddy properties in the property page.
Using Visual Basic with Access CHAPTER 20
609
Setting Project Properties Open the Project1 Properties under the Project menu. In the Project Properties dialog box, enter the project name (cSliderControl) and project description (cSlider Control). Click OK to close the Project Properties dialog box (see Figure 20.56). FIGURE 20.56 Specify the project name and description in Project Properties.
Saving the Project Save the project and UserControl by choosing Save Project under the File menu. It is best to save the project (.vbp file) and UserControl (.ctl file) in the same folder (for example, cSlider Control).
Testing the Control on a Visual Basic Form You test the cSlider control on a Visual Basic form by following the same steps used to test the design-time control discussed earlier.
Using the cSlider Control in an Access Form Open the ActiveX Control project, and choose Make cSliderControl.ocx from the File menu. Save the OCX file in the same directory as your ActiveX Control project. The ActiveX control is now compiled, and Registry settings have been made on your computer. Open a form in design view in an Access database. Choose ActiveX Control from the Insert menu. Then, choose cSliderControl from the list, and click OK to close the dialog box.
USING VISUAL BASIC WITH ACCESS
The cSlider control now works on the Access form (see Figure 20.57). As the UpDown control changes, the slider changes as well.
20
610
Interoperability PART VI
FIGURE 20.57 The ActiveX control on an Access form.
Distributing the cSlider ActiveX Control Distributing an ActiveX control is essentially the same as distributing ActiveX components. See the section earlier in the chapter, titled “Distributing ActiveX Components.”
Using the cSlider Control in Other Applications Your cSlider control is not restricted to just VB and Access applications. This same control can be used on a form in Word, Excel, Outlook, and other applications.
Creating an Internet Package (Setup) Use the Internet Package in the Package and Deployment Wizard to use the cSlider ActiveX controls on Web pages. Start the Package and Deployment Wizard, and choose the cSlider project file (.vbp file). Click the Package button to go to the Package Type screen. Choose Internet Package (see Figure 20.58) and then click Next. FIGURE 20.58 Choose Internet Package.
On the Package Folder screen, specify a folder for the package files, and click Next (see Figure 20.59). The default creates a subfolder named Package in your project directory. The next screen, the Included Files screen, shows all the files necessary to use the ActiveX control. This includes the VB runtime file and any other necessary files (see Figure 20.60). Click the Next button to continue.
Using Visual Basic with Access CHAPTER 20
611
FIGURE 20.59 Specify the package folder.
FIGURE 20.60 Files to include in the package.
On the File Source screen, specify the location of the files to install, as shown in Figure 20.61. The control you create (the OCX file) will generally be included in the cab (compressed) file. The default setting for the VB runtime files and other Microsoft files is the Microsoft Web Site. You might want to leave this default setting as is because users will then be able to download these files as needed from Microsoft’s Web servers. Click Next to continue. Tip Specify the Microsoft Web site for the location of the VB runtime and other Microsoft files. Microsoft offers more bandwidth than most companies, which ensures that the most updated files are installed.
20 USING VISUAL BASIC WITH ACCESS
612
Interoperability PART VI
FIGURE 20.61 Specify the file source.
On the Safety Setting screen, shown in Figure 20.62, select Yes for Safe for Scripting and Yes for Safe for Initialization. This is your declaration that the control is safe. Click the Next button to continue. FIGURE 20.62 Specify safety settings.
On the last screen, you can specify a script name to save a deploy script so that all the options need not be selected the next time the Deploy wizard is run (see Figure 20.63). Click Finish to deploy the package. FIGURE 20.63 Specify a script name and create the package.
Using Visual Basic with Access CHAPTER 20
613
After creating the package, the wizard displays a Packaging Report that can be saved for later use.
Using the cSlider Control on Web Pages Open the folder where the Internet package was saved. Notice that a cSliderControl.HTM file was created (see Figure 20.64). FIGURE 20.64 Files in the Internet Package folder.
Open the cSliderControl.HTM file in your Web browser. You will notice that the cSlider control works in your Web browser just as it did in VB and Access forms (see Figure 20.65). FIGURE 20.65 The cSlider control on a Web page.
Caution
USING VISUAL BASIC WITH ACCESS
Not all Web browsers support ActiveX controls. Of course, Microsoft Internet Explorer does support ActiveX controls.
20
614
Interoperability PART VI
To view the HTML for a Web page, open Microsoft Internet Explorer, and choose Source under the View menu. Notice that choosing this option will also reveal the cSlider ActiveX control’s class ID. Tip If you want to use the cSlider ActiveX control on another Web page, copy the HTML object code from one Web page to another.
Summary Using Visual Basic to create ActiveX code components and ActiveX controls can enhance and extend Access applications. With ActiveX code components, the same code can be used in Access and many other applications. This makes maintaining the code much easier because changes to the code have to be made in only one place—the code component. Visual Basic allows you to create ActiveX controls to use in Access, in other applications, and even in Web pages. You can create runtime controls with an interface users interact with, as well as design-time controls exclusively for developer use.
Multiuser Issues
PART
VII IN THIS PART 21 Multiple Users and Database Locking 22 Security
CHAPTER 21
Multiple Users and Database Locking
IN THIS CHAPTER • Understanding Multiuser Issues • Reviewing Jet’s Multiuser Design
618 619
• Understanding Jet’s Multiuser Locking 619 • Selecting the Proper Architecture • Working With Locks
623
• Optimizing Multiuser Applications 631 • Oracle/SQL Server Locking
632
623
618
Multiuser Issues PART VII
Access 2002 databases and applications destined for deployment in a multiuser environment must be configured and coded to handle concurrent use. Concurrent usage even can be an issue during development, especially if the project is being developed by a programming team rather than a single individual. This chapter explores the issues associated with multiuser databases, explains how Access and the Jet engine handle concurrent data access, describes how to configure a database and write code for multiple users, and offers tips for managing and optimizing multiuser Access databases and applications. The last section highlights multiuser considerations when working with Microsoft SQL Server and Oracle databases.
Understanding Multiuser Issues Access database applications supporting multiple concurrent users must be designed, configured, and implemented to handle situations single user applications rarely encounter. Key issues include: • Resolving conflicts when two or more users update the same record simultaneously—Which update should be applied first, what happens to other updates, and should users be notified that the conflict exists? • Ensuring consistent data views—What happens if one user updates the recordset underlying another user’s view, say, in a report, of that data? • Applying changes to data efficiently—Should ten records be updated in a batch or individually? • Controlling database access—Who is permitted access to the data, who is currently connected, who is permitted to update the database, and who is only allowed to view data? • Monitoring data integrity—How should potential data corruption be handled? Data locking addresses the first issue, simultaneous updates of the same data. Locking can be handled at both the database level—that is, using Jet’s native abilities—and at the application level, using both code and features available through Access 2002’s interface. You also can use code to detect changes to the recordset beneath a report or query and update views based on that recordset, which takes care of the second issue, making sure users are looking at the most current information. Jet supports transactions and batch updates, so you can apply data changes en masse or atomically, depending on the application’s needs. Jet and Access also support programmatic access control and detection/repair of corrupt data. These features enable applications to monitor security and maintain data integrity at the engine and code level, reducing administrative overhead.
Multiple Users and Database Locking CHAPTER 21
Reviewing Jet’s Multiuser Design
Jet’s multiuser design consists of four components: 1. A database residing on a network server 2. A database locking information file residing on a network server 3. Copies of Jet running on client workstations 4. A workgroup information file residing on the server or on each client workstation The database can consist of one file or multiple files. The locking information file is a special temporary file created when Jet opens a database. It has the same name as the database it serves but uses the .ldb filename extension. It always resides in the same directory as the database. The locking information file contains up to 255 entries, each entry containing the computer name, such as Bubba’s PC, and the user’s security account name, such as Guest or Admin. Jet uses the locking information file to track data locks. Jet deletes this file after the last user closes or disconnects from the database. Tip The locking information file only permits 255 entries because Microsoft Jet’s user limit is 255.
The workgroup information file is primarily used for access control and security. It is a Jet database, usually named system.mda, storing information about the users, groups, and passwords for a secured database. It can be stored locally or on the network.
Understanding Jet’s Multiuser Locking Jet’s locking behavior is defined by the mode in which a database has been opened (shared, exclusive, read-only), the default locking scheme (no locking, all records, edited record), and whether locks are applied at the page level or the row or record level.
21 MULTIPLE USERS AND DATABASE LOCKING
If you understand how Microsoft Jet supports multiuser use, you can take advantage of its features to create applications that function smoothly under concurrent use. This section explains Jet’s multiuser model and how to take advantage of it in your applications. The discussion focuses on applications using a file server model, in which local clients running a suitable application access a centrally located database.
619
620
Multiuser Issues PART VII
The Database Mode Multiuser applications must open the database in shared mode in order to support multiple users. A database opened in exclusive mode effectively locks the entire database because it prevents all other users from opening the database at all. Read-only mode applies only to the user opening the database, not to other users. That is, if Bubba opens a database in read-only mode, Bubba cannot alter its data or structure, but other users can. To configure a database to open in shared mode, Select Tools, Options, Advanced, then select the Shared radio button, as shown in Figure 21.1. The default open mode is Shared. FIGURE 21.1 By default, Access opens databases in shared mode.
In a multiuser setting, users should not be permitted to open databases in exclusive mode. However, maintenance tasks such as compacting and repairing the database must be performed on a database opened exclusively. Large scale updates, deletions, or insertions happen more rapidly on a database opened exclusively because Jet does not have to check locks before performing operations. You can use the following command line options to control a database’s default open mode. These and other options are accessible from the command line when starting an Access application. Table 21.1 lists some of these switches. TABLE 21.1
Command Line Options for Opening Access
Switch
Description
dbname
Open the database named dbname.
/EXCL dbname
Opens dbname exclusively. Can be used even if the database’s option is set to Shared.
/RO dbname
Open dbname in read-only mode.
Multiple Users and Database Locking CHAPTER 21
You also can open a database in shared mode using VBA’s OpenDatabase method. The following function, for example, opens a database in shared mode and returns a reference to the database to the calling function: Function OpenSharedDB (strDbPath As String) As Database Dim dbs As Database ‘ If open in Exclusive mode, give up. If IsDbOpenedExclusively(strDbPath) Then MsgBox “Database is already open exclusively, cannot open in shared mode.” Set OpenDatabaseShared = Nothing Else ‘ Open database and return reference. Set dbs = OpenDatabase(strDbPath, False) Set OpenDatabaseShared = dbs End If End Function
An Options argument of False opens the database in shared mode; if Options is True, OpenDatabase opens the database in exclusive mode.
The Locking Scheme As you can see in Figure 21.1, you can set Access’s default locking scheme to No locks, All records, or Edited record. The default scheme is No locks, which corresponds to an optimistic locking strategy. Optimistic locking earns its name because it assumes that multiple users will not need simultaneously to update the same record. Based on this assumption, no locks are placed until the data is actually updated. Optimistic locking allows for a high degree of data availability because no user makes long-lasting or exclusive claims on the data. Accordingly, when a user opens a record for editing, other users are still able to open that record for editing and the first user to save his changes has the advantage. Though optimistic locking is easy to implement and usually causes few problems for users getting their data, it does create potential data contention conflicts because it allows multiple users to update the same record simultaneously. For example, suppose two users are editing the same record. The first user to save changes to that record “wins,” in a manner of speaking. The second user will receive a message reading: “The Microsoft Jet database engine stopped the process because you and another user are attempting to change the same data at the same time.”
21 MULTIPLE USERS AND DATABASE LOCKING
The default open mode if neither /EXCL nor /RO is used is to open a database in shared mode, unless the Default open mode is set to Exclusive in the Options window (see Figure 21.1).
621
622
Multiuser Issues PART VII
Earlier versions of Access presented a confusing dialog box prompting the user to choose between overwriting the other user’s changes, not saving his own changes, or copying the information to the clipboard. This write conflict dialog box simply did not present enough information to make a reasonable choice between its options. The current approach method, though harsh in that it offers you no options, is unambiguous. Other methods for handling the conflict exist, however, as you will see later in the chapter. The other two options, All records or Edited record, correspond to a pessimistic locking scheme. Pessimistic locking is so named because it assumes that conflicts will occur and places a lock at the beginning of an update process, rather than waiting until the last moment like optimistic locking. The lock can be placed on all records the user is viewing or just on the single record being updated. Many other databases use this type of locking, so it should be familiar to most developers and the consequences of it will be familiar to users. Although pessimistic locking can eliminate the write conflicts encountered with optimistic locking, it has its own problems. Pessimistic locking increases the likelihood of conflicts because more data is locked at any given time than in an optimistic locking scheme. If the typical editing process is a time-consuming one and there are many concurrent users, using pessimistic locking requires careful evaluation. Some applications, such as sales and inventory processing, will probably benefit more from the pessimistic lock because control over a record is so important, whereas a time tracking system probably will find pessimistic locking a hindrance to performance.
Lock Granularity Finally, you can control the granularity of record locks. By default, Access and Jet use row or record level locks, so that only a single record is locked. The alternative is page level locking, in which locking any given record locks all other records on the same 4096-byte page. To use record-level locking, select the Open databases using record-level locking checkbox on the Advanced tab of the Options dialog box. Page-level locking is especially problematic when using a pessimistic locking strategy. In addition to locking records when first accessed, an entire page of records are locked, whether they will be updated or not. The principal benefit of row-level locking is increased data availability. More users can access the same data without encountering locking or write conflicts. Record-level locking also allows developers to employ pessimistic locking in more applications. Users will see behavior with which they are more familiar and which seems more intuitive. Users expect to simply open a record, edit that record, and have their changes saved.
Multiple Users and Database Locking CHAPTER 21
Selecting the Proper Architecture
On the other hand, if concurrent usage is high and the impact of data contention and contention resolution imposes unacceptable delays or performance overhead, row-level locking may solve the problem. Row level locking is especially advantageous in environments in which data is actively and frequently updated. A database that records deposits and withdrawals from customer accounts must have a high level of availability. It would have to lock some records exclusively while they are being edited; otherwise, a user runs the risk of having his changes overwritten by a concurrent user. Moreover, when one account record is being edited, that edit cannot prevent anyone else from editing a neighboring record. Record-level locking might also be appropriate in situations where a record needs to be kept open for a period of time while preventing others from editing it. When reviewing a customer’s account history for credit worthiness, for example, it is probably preferable to lock that information until the review is complete and a judgment rendered. Locking other, unrelated records, however, is unnecessary, so a row-level lock is the proper solution. As a matter of good practice, you should avoid locking even a single record for any long period of time unless it is absolutely necessary.
Working With Locks This section describes the programmatic facilities available for working with record locks. You can identify the type of lock applied, if any, apply your own lock, check the status of a lock, and determine how to proceed if a lock is already in place.
Testing the Existence and Type of a Lock As discussed previously, locks can be placed on a single record, or a page of records, and when a lock is applied depends on whether optimistic or pessimistic locking is used. In
21 MULTIPLE USERS AND DATABASE LOCKING
Access’ default locking method is optimistic, row-level locking. This default setting is not the best solution for all situations. In situations where performance is extremely important, yet record conflicts are infrequent or manageable, the slight overhead of rowlevel locking might prove to be too much of a compromise. Consider a system like a bank’s foreign exchange trading system where records are entered far more often than they are edited. Because such a system would be found in a fast-paced environment and performance is important, the argument could reasonably be made that a performance hit should come only when a conflict occurs, but all other database activities should run as fast as possible. In such a case, a page locking scheme might be in order.
623
624
Multiuser Issues PART VII
addition, in a multiuser application, or if different applications use the same data, different locking approaches may be used on the same data at the same time. As a result, an attempt to access data will generate different errors at different times. The error received depends upon the type and status of the lock. To deal with this programmatically, use ADO’s LockType property. A recordset’s LockType property indicates the type of lock placed on records during editing. It is read/write before a recordset is opened and read-only while it is open. Table 21.2 lists the LockType constants for Microsoft.Jet.OLEDB.4.0. Other providers might provide different options. Tip To determine what options are supported by a provider, use the .Supports method with adUpdate or adUpdateBatch.
TABLE 21.2
ADO LockType Error Constants
Constant
Description
adLockReadOnly
The recordset is opened read-only, and no locks are placed on data.
adLockPessimistic
Pessimistic record level locks placed upon editing.
adLockOptimistic
Optimistic record-level locks are placed when the Update method is invoked.
adLockBatchOptimistic
Optimistic locking for batch update mode.
adLockUnspecified
Knowing the status of a record lock enables you to code the application to respond appropriately, although precisely how to respond depends on the application’s requirements. Figure 21.2 shows an application stopped in the debugger. The selected property shows that the underlying recordset’s LockType property is adLockOptimistic. Keep in mind two caveats when using the LockType property. First, adLockPessimistic is not supported if the CursorLocation property is adUseClient because adUseClient prevents the server from keeping track of the current record. Using adLockPessimistic in this case does not generate an error because Jet substitutes a similar LockType. Secondly, the ADOR object, a subset of the ADO object model providing only the Recordset and Field objects, only supports the adLockOptimisticBatch LockType.
Multiple Users and Database Locking CHAPTER 21
FIGURE 21.2
21 MULTIPLE USERS AND DATABASE LOCKING
A recordset’s LockType property reveals its lock status.
To determine if a recordset is being edited, use the EditMode property. The most important values of EditMode are adEditNone and adEditInProgress. The value is adEditNone before and after an edit, and adEditInProgress during an edit. Table 21.3 lists the possible values for the EditMode property. TABLE 21.3
625
Values for the EditMode Property of an ADO Recordset
Constant
Description
adEditNone
No editing is in progress.
adEditInProgress
Data in the current record has been modified but not saved.
adEditAdd
The copy buffer contains a new record created using AddNew that has not been saved to the data.
adEditDelete
The current record has been deleted.
The value of the EditMode property represents the status of Jet’s buffer for editing and creating records. adEditNone is the ideal status, allowing you to proceed with your own edit operation. If the value is adEditInProgress, you can recheck its status or notify the user the record is being edited and provide alternatives. Use a similar approach if EditMode is adEditAdd or adEditDelete. Typically, application code would use the Update or CancelUpdate methods when EditMode is not adEditNone. In some cases, the only way to test locks is to actually cause a contention error. Jet’s OLEDB provider gives some information about another user’s lock when an error occurs.
626
Multiuser Issues PART VII
When an error (usually indicating a conflict) occurs, check the value of the Connection.Errors(index).SQLState property to identify the error. Table 21.4 describes some of the contention error constants returned by .SQLState. TABLE 21.4
Jet 4.0 OLEDB Locking Error Codes
Code
Message
3006
Database dbname is exclusively locked.
3008
Table tblname is already open or has been opened using the Access interface and cannot be manipulated programmatically.
3009
Table tblname can be opened but not locked because it is currently in use.
3027
The object cannot be updated because it is read-only.
3046
Changes could not be saved because the object is currently locked by another user.
3158
The record could not be saved because it is currently locked by another user.
3164
The field could not be updated because another user or process has locked the corresponding record or table.
3167
Record has been deleted.
3186
Could not save because object is currently locked by user username on machine machinename.
3187
Could not read because object is currently locked by user username on machine machinename.
3188
Could not update because currently locked by another session on this machine.
3189
Table tblname is exclusively locked by user username on machine machinename.
3196
The database dbname is already in use by another person or process.
3197
Jet stopped the process because two users are attempting to change the same data at the same time.
3202
Could not save because object is currently locked by another user.
3211
Could not lock table tblname because it is already in use by another person or process.
Multiple Users and Database Locking CHAPTER 21 TABLE 21.4
continued
21 Message
3212
Could not lock table tblname because it is in use by user username on machine machinename.
3218
Could not update the object because it is currently locked.
3260
Could not update the object because it is currently locked by user username on machine machinename.
3261
Table tblname is exclusively locked by user username on machine machinename.
Responding to Locking Errors How any given multiuser application should respond to locking errors or conflicts depends heavily on application requirements. Regardless of local necessities, however, every multiuser Access application must be designed and coded to anticipate and respond appropriately, that is, without abruptly exiting. This section suggests methods for responding smoothly to the most common situations involving locking conflicts and how, in some cases, to avoid them altogether. The best way to handle multiuser errors is to avoid them. Access can be configured to reduce the likelihood and frequency of contention issues using settings available on the Advanced tab of the Options dialog box. They are located in the lower-left corner of the Advanced tab, as shown in Figure 21.3.
Advanced Access configuration options reduce frequency of data contention problems.
MULTIPLE USERS AND DATABASE LOCKING
Code
FIGURE 21.3
627
628
Multiuser Issues PART VII
These settings, their default values, and their function are described in Table 21.5. TABLE 21.5
Access Options That Affect Data Contention
Option
Default Value
Description
OLE/DDE Timeout
30
The time, in seconds, between attempts to retry an OLE or DDE operation that failed. Valid settings are 0–300.
Refresh Interval
60
How long, in seconds, between refreshes or records in Datasheet or Form view. Valid settings are 1–32,766.
Number of Update Retries
2
How many times Access attempts to save or update a record against a lock before giving up. Valid settings are 0–10.
ODBC Refresh Interval
1500
The time, in seconds, between refreshes when using an ODBC database. Valid settings are 1–32,766.
Update Retry Interval
250
The time, in milliseconds (1/1000 of a second), between attempts to save or update a record against a lock. Valid settings are 1–1000.
The values in Table 21.5 are applied automatically, at the engine or application level. The default values work in most cases, but use other values if experimentation and testing suggest the defaults are suboptimal. Application code can implement a number of solutions or workarounds if conflicts occur, such as pausing before retrying a failed operation, attempting the operation later on, allowing the user to decide how to proceed, abandoning the change completely, or some combination of one or more of these options. The precise approach depends on the application. As explained earlier, if two or more users edit the same record simultaneously, a write conflict occurs. Write conflicts of this sort usually occur when an optimistic locking strategy is used. The first user to save any changes wins. Jet returns the error value 3197 (see Table 21.4) to “losing” applications, ignores the changes made by other users, and displays an error message to this effect. Application code should trap this error in order to display a friendlier or more informative message or perhaps to retry the update after a short pause. When a pessimistic locking strategy is used, if User2 tries to change a record that User1 is editing, User2 will receive a locked record error (error number 3260 in Table 21.4).
Multiple Users and Database Locking CHAPTER 21
Using Transactions In a heavily used database with highly transient data, wrapping updates, inserts, and deletes in a transaction may represent the best solution. A transaction groups multiple data changes—say, an insertion, a deletion, and two updates—into a single logical change, called a transaction, and treats them as a single change. As a result, either all of the changes succeed and the transaction is committed (applied), or none of them do and the transaction is rolled back. If a transaction fails, the database is restored to the state it was in before the transaction process began. Transactions both guarantee data integrity and allow greater concurrent data access. Transactions ensure data integrity because they ensure that all related operations complete successfully. Consider an application linking a sales system to an inventory system. In such a system, selling one widget requires subtracting one widget from the inventory database. Imagine the problems that might result if the sale of an item completed, but the related inventory database update failed. In a transaction-based system, one transaction manages updates for both the sale and the inventory adjustment. Transactions also can facilitate concurrent access because they allow multiple edits to be stored for later execution. For example, if a busy warehouse receives hundreds of orders for products during the day, rather than constantly updating the database during the day, all orders can be stored in one or more temporary tables until the close of business. During less busy off hours, all updates can be grouped into transactions and applied to the primary table, avoiding record-locking conflicts entirely. Without transactions, highly dynamic multiuser applications executing these kinds of insertions, deletions, and updates would likely encounter locked-record errors during some part of the process, leaving accounts out of balance, inventories artificially inflated, or changes incompletely recorded. However, transactions are not a universal solution. Transactions set all the necessary locks required, or specified, by their individual constituent operations and do not release them until all the operations have succeeded (or failed, as the case may be). So, because a transaction sets more locks, simultaneously, they last longer than they would if they were set as part of a single process, the application concurrency is actually reduced for the duration of the transaction. Again, if or how transactions can be used depends on the application and local requirements, but a database enabling high concurrent usage offers little else than its speed if it fails to maintain data integrity.
21 MULTIPLE USERS AND DATABASE LOCKING
The standard approach in this case is to create an error-handling routine that makes a fixed number of attempts to save User2’s record before giving up and telling User2 to try again or give up. In many cases, one of the retries will succeed because updates complete very quickly.
629
630
Multiuser Issues PART VII
Transactions are methods of the Connection object in ADO. Table 21.6 describes the three methods and their associated events. TABLE 21.6
Defining a Transaction
Method/Event
Description
BeginTrans
All processes between BeginTrans and CommitTrans or RollbackTrans will be treated as a single entity.
BeginTransComplete
Called after a BeginTrans operation completes.
CommitTrans
Write all changes to the database, provided no errors occur.
CommitTransComplete
Called after a CommitTrans operations completes.
RollbackTrans
Cancels the current transaction and sets the database back to its state before BeginTrans was called.
RollbackTransComplete
Called after a RollbackTrans operation completes.
You can use the three events, BeginTransComplete, CommitTransComplete, and RollbackTransComplete, to monitor a transaction’s status, to inform the user of the success or failure of an operation, or to enable other application-specific functionality. The basic form of a transaction process looks like Listing 21.1. LISTING 21.1
Using a Transaction in VBA
Function TestTrans() As Boolean Dim conn As ADODB.Connection Dim rst As ADODB.Recordset On error resume Err_TestTrans Set conn = New ADODB.Connection Conn.BeginTrans ‘ ‘ Your code goes here ‘ ‘If everything worked, then commit the transaction. Conn.CommitTrans Exit Function Err_TestTrans: ‘Something went wrong, so cancel the transaction ‘undo any changes, and perform other necessary processing Conn.RollbackTrans ‘ ‘ Additional error handling code goes here ‘ End Function
Multiple Users and Database Locking CHAPTER 21
Optimizing Multiuser Applications Multiuser applications perform better when data contention and locking have been applied and implemented properly, but other measures can be taken to improve a multiuser Access application’s overall performance. A primary concern is to minimize network traffic. Performance enhancement is discussed in detail in Chapter 14, “Application Optimization,” so this section only highlights the general ideas.
Indexing Tables If queries or reports use tables that are not indexed, Access must scan the table sequentially and examine each record. Moreover, then entire tables must be sent from the server to the local Jet instance. So, make sure tables that are queried have indexes on the queried fields. This step reduces both network traffic and the time to execute the query.
Partitioning the Application Application partitioning means separating variable or dynamic objects from constant or static objects. A common tactic for improving application performance is separating a database application’s data, its tables, from the rest of the application, storing the tables on a server and installing everything else, such as forms, reports, queries, and code, on client workstations. This technique can be refined. Suppose one of the tables contains nothing but product codes that rarely change. Moving this table to client workstations will reduce network traffic and speed up queries using those product codes. In fact, any database object that changes infrequently or irregularly can be updated from the server using application startup code that updates locally stored objects, such as forms, reports, or even queries. The approach here is simple. The database on the server includes a table listing the current versions of relevant objects. When client workstations start, code compares local version numbers against server version numbers and updates,
21 MULTIPLE USERS AND DATABASE LOCKING
Transactions also can be nested, so higher level CommitTrans calls require successful completion of any nested CommitTrans calls. That is, nested transactions are resolved from the innermost or lowest level, to the outermost or highest level. Thus, commit or rollback the current transaction before committing or rolling back a transaction at a higher level. You can have up to five levels of nested transactions for a single Connection object. Be aware, however, that not all data sources support transactions, so if an application connects to unknown data sources, check the Connection.Properties collection for the Transaction DDL property. If it is not present, the transaction methods listed in Table 21.5 will cause an error.
631
632
Multiuser Issues PART VII
adds, or deletes local objects based on the master table on the server. Not only does this type of scheme reduce network traffic, overall, it reduces the amount of administrative time spent upgrading workstations with the latest and greatest version of the application.
Oracle/SQL Server Locking When an Access application works with SQL Server, Informix, Oracle, or any other remote data server engine, it no longer controls the locking. Nevertheless, the basic concept remains the same—control data access to permit as many users as possible to use the database at the same time. By design, data servers handle heavy concurrent usage extremely well. The server, rather than Access, manages the record locking and concurrency management quickly, transparently, and reliably. The server keeps locking information in a memory resident table and can decide on the most effective lock to employ, place the lock, execute the operation, and release the lock in a matter of microseconds. Microsoft SQL Server uses one of three types of locks: • Shared locks—Used in read-only operations, a shared lock enables other users to read, but not update, a record or a page. A record or page can have multiple shared locks imposed on it at one time. Shared locks are released as soon as the data is not needed. • Exclusive locks—UPDATE, DELETE, and INSERT statements result in exclusive locks, meaning no other operation can acquire a lock on the affected data until SQL Server commits the transaction and releases the lock. • Live locks—A live lock is a request for an exclusive lock made after four attempts to place an exclusive lock on records locked with a shared lock fail. Live locks exist to prevent shared locks (from read operations) from monopolizing a table or page and preventing UPDATEs, DELETEs, or INSERTs, a condition known as lock starvation. SQL Server uses other strategies to handle concurrency issues, including dynamic rowlevel locking (SQL Server 7.0), deadlock avoidance, detection and correction, optimistic concurrency control, and scalable lock escalation. In dynamic row-level locking, SQL Server’s lock manager dynamically adjusts the server’s lock configuration based on the size and usage of the database. Using deadlock avoidance, detection, and correction, SQL Server recognizes when two transactions conflict and attempts to resolve the conflict. A deadlock occurs when one transaction has exclusively locked data needed by a second transaction and the second transaction has
Multiple Users and Database Locking CHAPTER 21
SQL Server employs an optimistic concurrency control approach to managing its multiuser environment. With an optimistic approach, users can use a server-side cursor to browse the data forward and backward without locking any data. Instead, SQL Server detects if rows have been modified since they were retrieved and then acts accordingly (usually rejecting the change unless accompanied with an explicit locking instruction). This approach makes a large amount of data available without extensive lock management and the overhead that entails.
Summary This chapter explored how Access applications function in a multiuser environment. It discussed database configuration settings, recordset locking, locking schemes, and overall design issues. It provided detailed information about different locking strategies, such as optimistic and pessimistic locking, and the advantages of row level versus page level locking. You learned how to determine what type of locks have been placed and how to decipher cryptic error messages from the OLEDB provider. You are now well-equipped to asses the value of record locking in your own applications and apply it prudently.
21 MULTIPLE USERS AND DATABASE LOCKING
exclusively locked records needed by the first; neither transaction will release its lock. Without intervention by the engine, they would remain in deadlock. SQL Server detects this condition, rolls back one transaction, completes the other, and runs the first one again to break the deadlock.
633
CHAPTER 22
Security
IN THIS CHAPTER • Elements of Security
636
• Workgroup Creation
638
• Users and Groups
641
• Implementing Security Using Startup Options 649 • Security Concerns with Replication 650 • Security for Split Databases • Security for Client/Server
651
636
• Securing a Database Step by Step 653 • Common Security Errors
664
636
Multiuser Issues PART VII
No application, especially a multiuser application, is truly finished until it has been secured. Improperly implemented security, or, worse, no security at all, leaves an application vulnerable to the worst intentions of malicious crackers and the innocent curiosity of inexperienced users. Unfortunately, despite Jet’s formidable security capabilities, security is usually neglected or implemented improperly in Access databases. Part of this is undoubtedly because figuring out what the security structure of the database should be is often a daunting task. The instructions for implementing security are confusing, and the way Jet handles security is different from many other database management systems, past and present. This chapter explores Jet’s workgroup security model in detail and looks at many of the practical matters you must consider when securing a database. It also demonstrates ActiveX Data Object Extensions for Database Definition Language and Security (ADOX) techniques for handling security functionality. Because ADOX only handles data source and data-bearing or data-handling objects such as tables, queries, views, and procedures, I will also review DAO and user interface security techniques. These methods will enable you to apply security to the forms, reports, and macros in an Access database client.
Elements of Security ADOX succeeds DAO as the programming interface for data definition and security management. With a simpler object model and, when used with an OLEDB provider, an interface unburdened by native syntactical differences, ADOX frees you from many of the burdens of securing a database, especially those using nontraditional or heterogeneous data sources. There are two options for securing an Access 2002 database application: • Setting a database password • Implementing workgroup-based security
Database Password Security Setting a database password is a quick and easy way to secure the MDB file. When employed (by selecting Tools, Security, Set Database Password in a database opened in exclusive mode), all users are assigned the same password. When opening the MDB file, Access will prompt each user for the database password specified. Because everyone has the same password and anyone who knows that password has access to the database, it is easy to see how the database can be compromised. Moreover, because all users have the same password and privileges, users are indistinguishable from
Security CHAPTER 22
637
each other, making security audits impossible. Further aggravating the situation, should everyone forget the database password, there is no way to recover it. Finally, using a database password precludes the use of replication. The point is that a database password, alone, is not the best way to secure a serious, multiuser application. Rather it should be used in conjunction with workgroup security. Workgroup security is more complicated than the database password. This chapter focuses on this method.
Workgroup-Based Security
This user-based system is not administered by Access or Jet. It is administered by the workgroup file (system.mdw). The workgroup file contains the security information on users and groups of users and is consulted by Access/Jet on security matters. In Access, security is always on, it just is not always visible. That is, the workgroup file is always in use, whether you can see it working or not. Because of this, it is very important to know to which workgroup you are connected before setting up a security scheme. Separate administration of database security means that groups of users can have several database applications secured and administered through one centralized security file. The MDW file used for a database defines the workgroup. The workgroup is a collection of users, their groups, and the databases they can use. As illustrated in Figure 22.1, the database engine relies on its workspace (the workgroup as determined by the MDW file) to supply information on the users for the database in question. FIGURE 22.1 Notice that there are no database objects in this schematic.
DBEngine Workspaces Users
Groups
Groups
Users
22 SECURITY
If the database password is database-based (that is, each database is secured as a file or as a set of files, without regard to the user’s identity), Access/Jet is secured based on how users are identified within a particular workgroup. It is that identity that determines what users are allowed to do. In this sophisticated and flexible system, the identity of the user within a workgroup is central to the functioning of the security in the workgroup security scheme. Users are uniquely identified and can be assigned specific permissions to databases and the objects within them. It could be said that the coming discussion of workgroup files, workspaces, and groups is really about enhancing, organizing, and easing the basic tasks of creating and assigning permissions to users.
638
Multiuser Issues PART VII
Although users are the center of the security system, it is impossible to create users or have a meaningful discussion about them without putting them in the context of a workgroup. A workgroup must exist for security to function. It can have many users or only one, but a workgroup file must be established. The workgroup file is a special encrypted database containing the following information: • Usernames • User passwords • User personal identifiers (PIDs) • User preferences (toolbar settings and some defaults) • The last four databases opened by each user (for the file menu) • Group names • Group (PIDs) • Group members (users belonging to that group)
Workgroup Creation Out of the box, Access is joined to a default workgroup defined by system.mdw. At installation, system.mdw was created for convenience only. That is, information critical to its security capability was left out, so it is permanently and inherently unsecured. Anyone willing to check Access help files can defeat the security plan of the default system.mdw. You should leave this workgroup file alone and create new ones as needed. At the time you create a new workgroup file, you do not have to have a detailed plan for the security structure of your users; however, it is a good idea to create the workgroup file as early in the development process as possible to avoid confusion later. To create a new workgroup information file, start Microsoft Access and select Tools, Security, Workgroup Administrator. Workgroup Administrator enables you to create new workgroups and join existing ones. When creating a workgroup, the resulting MDW file should be placed in a network location accessible to all users expected to use the application. Then, click the Create button to create a new workgroup information file. The resulting dialog box should resemble Figure 22.2. The next step is to create a new information file, so click the Create button, which results in the dialog box shown in Figure 22.3.
Security CHAPTER 22
639
FIGURE 22.2 Workgroup Administrator allows you to join an existing workgroup or create a new one.
FIGURE 22.3
There are three fields to fill out, as Figure 22.3 illustrates. The Workgroup Administrator might have completed one or two of these fields for you using information provided during the installation of Microsoft Office or Access 2002. You can change this information or leave it alone, but you must provide a Workgroup ID. A Workgroup ID is a case-sensitive string up to 20 alphanumeric characters in length that Workgroup Administrator uses to create a seed for encrypting the workgroup file. It is this piece of information that is missing from the default workgroup information file and that makes the default installation insecure as a result. Workgroup Administrator will allow you to leave this field (after nagging you with a warning first), but doing so defeats the whole purpose of creating a workgroup. The Workgroup ID you enter ensures that this workgroup is unique by generating a binary ID called the Workgroup Security ID (SID). With the workgroup ID properly secured and hidden, your workgroup cannot easily or quickly be compromised. The security system is so particular about this that you will have only one more opportunity to see or change its value ever again. Write this string down, keep it secret, and store it in a separate location from the group of users. Caution Write down the workgroup ID value and store it in a safe and separate location! You might need it again to re-create the workgroup; however, Jet will never provide it for you—ever.
22 SECURITY
Workgroup Administrator provides default values, but you can change them.
640
Multiuser Issues PART VII
The next step is to find a location for the workgroup file. The workgroup file should be placed in a location where all the intended users have read/write directory access. Although, if your users are very static, it is possible to place a copy of the MDW file on their local machines and replace that copy each time there are changes, the performance gains might not outweigh the administrative overhead. Placing the file on a network drive also improves your chances that the MDW file will be backed up at regular intervals, making disaster recovery much easier. Tip Resist the temptation to name the MDW file with the same name as the application or data source if they are located in the same directory. The MDW file will create an LDB file to track its own locking activity, just as the data source or executable will. If the MDB and the MDW share a name, they will not conflict with each other, but their LDB files will. One approach is to prefix the name of the workgroup information file with “SYS,” “SYS_,” “INF,” or “INF_.”
After you select the file location and click OK, you will see a dialog box resembling Figure 22.4, the Confirm Workgroup Information dialog box. This is your last chance to change or view the information you have provided. Make sure you accurately recorded all this information, especially the workgroup ID, because you might need it later to recreate the MDW file. After confirming the information, Workgroup Administrator will confirm that your new workgroup was created and you return to the first dialog box. Notice that you have automatically joined the new workgroup. You can now create databases, create users, create groups, assign users to groups, and grant and revoke permissions for that workgroup. If you choose to join a different workgroup, you will be able to work in that group to the extent that your security settings permit. To avoid wrecking any existing databases, create a new database to use while exploring the rest of the security features. First, close and restart Access in order to start Access in the workgroup you just created. The newly created workgroup is the default workgroup, but, unless you are the workgroup administrator, there is no way to tell. In any event, before creating or changing any security features, it is imperative to make certain you are joined to the appropriate workgroup.
Security CHAPTER 22
641
You can set workgroup membership to a database through the command line shortcut, using the general format shown in the following: msaccess.exe some.mdb /WRKGRP some.mdw /USER username /PWD password
Replace some.mdb with the full path to the database file, some.mdw with the full path to the workgroup information file, username with your user name, and password with your password. Note
Users and Groups Users are the basis of Jet’s workgroup security system. Jet recognizes users and knows what they are allowed to do. What they are allowed to do, or the permissions they have, depends on the permissions the users have been assigned directly, and what they have inherited through the groups they belong to. A user’s permission set is the sum of their group permissions (implicit permissions) and any individual permissions (explicit permissions) granted. A user always belongs to at least one group, the Users group. Though users are the basic element of the security system, it is difficult to discuss them without also discussing groups because users always belong to at least one group, and groups exist to organize and manage users. When you log on to a Jet database, you always log on as a user. You cannot log on as a group. The security system knows to which groups you belong and the permissions you have been assigned. Take the case of a hypothetical Clerk group. Although the group might have no rights to create or modify objects, there might be one user who is designated to revise and create reports. That user, and no one else, could be given the explicit rights to create and modify report objects. Although this approach works, it creates an administrative headache because the security administrator must now track an individual as well as a group. Chances are, if the security administrator makes one exception, she will make others. A better alternative for a report writing clerk would be to join her to a group with permissions to create and modify reports. This way, the security administrator can track individuals by their functional capabilities rather than by unique security profiles.
SECURITY
The /WRKGRP, /USER, and /PWD command line switches only work for Access databases and not Access projects.
22
642
Multiuser Issues PART VII
Caution Because the Users group has all permissions by default, and every user is always a member of it, you must remove all permissions from the Users group in order to successfully secure a database. Otherwise, every user of the database will inherit administrative rights.
Keep in mind, too, that because reports can be created with the wizards, and the query builder helps users to precisely define recordsources without adding query objects to the query container, it is possible to give extensive permissions for report building—even if the database is secured. This can save you a lot of tedious development efforts.
Understanding Default User and Group Settings Before you do anything with security, it is important to know what the default settings are. Jet starts with two groups and one default user. The two groups are Admins and Users. The default user is Admin. Admin is a member of both groups and both groups have all permissions in the database. When creating other users and groups, the names of groups and the names of users must not conflict. Access uses a naming convention in which groups are plural and users are singular. For consistency’s sake, this seems to be a good convention to follow. The most important group is the Admins group because it has special permissions that cannot be granted to groups you create. You must be a member of the Admins group to perform any of the following tasks: • Create, change, or delete a user • Create or delete a group • Change a group’s membership • Clear the users’ passwords A user who is not a member of the Admins group can view the accounts of other users and can see who belongs to a group, but cannot change any of these values, even for her own username. She can only change her own password. For obvious reasons, every database’s Admins group must have at least one member. Groups are more than mere collections of users. Although you are not able to log on as a group and groups cannot possess a password or own a database, groups can own
Security CHAPTER 22
643
Jet/Access objects and have distinct permissions assigned to them. These capabilities make administration of the database and its users much easier. As a general rule, you should only assign permissions to groups; assign permissions to users by making them members of the appropriate group. This makes administering security much simpler because it is easier to change permissions for one group than a dozen users.
Creating Users Although users should have few details attached to them that distinguish them from other members of the same group, they are still necessary to a proper security structure of your database.
1. From the Tools menu, choose Security, and then select User and Group Accounts. 2. Select the User tab and click New. 3. Enter the username and a PID (Personal Identifier), as shown in Figure 22.4. The PID must be between 4 and 20 characters in length. It is case-sensitive and serves as the seed for the creation of the SID or security Identifier. You must make a record of the PID because you will never be able to see it again. 4. Click the OK button to create the new account. FIGURE 22.4 Creating a new user and assigning a personal ID.
Setting/Changing User Passwords After a user has been created, you have the option of assigning a password for that user. You are not prompted to do this, but it should be standard practice. Click the Change Logon Password tab in the User and Group Accounts window to set or change a password. The password is case-sensitive, between 1 and 14 characters, and optional. However, because the password is designed to verify that users are who they
SECURITY
To create a user, follow these steps:
22
644
Multiuser Issues PART VII
say they are when they log on, all users should have a password. There is no way to see a password after it has been assigned, so write it down and keep it safe. In a worst case, an administrator can always clear another user’s password if the user should forget it. Figure 22.5 shows how the dialog box conceals the passwords from view as you change them. FIGURE 22.5 Changing a user’s password through Access’s user interface.
Caution Remember to use a password that is easy for you to remember but hard for others to guess. If you have trouble remembering the password, write it down and store it in a separate and secure location.
Creating Groups The lynchpins of your security scheme should be your groups. As discussed earlier, groups ease your administrative burden by passing on their security profiles to all their members. To create a group, follow these steps: 1. From the Tools menu, choose Security, and then select User and Group Accounts. 2. Select the Groups tab and click New. 3. Enter the group’s Name and a PID (Personal ID). The PID must be between 4 and 20 characters in length, case-sensitive. The PID serves as the seed for the creation of the SID or Security Identifier. You should make a record of the PID, because you will never be able to see it again. Figure 22.6 shows what the group creation dialog box looks like.
Security CHAPTER 22
645
FIGURE 22.6 Create a group by assigning a name and PID.
22
The PID of a group is the seed that will be used to uniquely identify the group; if you ever need to re-create the group, you will need that PID, so write it down and store it in a secure location.
Assigning Users to Groups Because users and groups are so tightly entwined, and because the Jet’s workgroup security model is user-based, you control group membership from the Users tab, not from the Groups tab. In fact, creating users and groups is done through the same interface. Use the same tab of the User and Group Accounts dialog box where you create users and groups to join them together. Follow these steps to assign a user to a group: 1. Choose the user you are interested in from the top dialog box. 2. At the bottom of the dialog box, choose the group to which you want to assign this user from the Available Groups list. 3. Double-click this group or click the Add button to join this user to this group. You can also remove groups from a user by double-clicking a group name in the Member Of list or by using the Remove button. You can never revoke the membership in the Users group. To see all the assignments made in the database, use the Print Users and Groups button.
SECURITY
Caution
646
Multiuser Issues PART VII
Distinguishing Between Default and Special Users and Groups Access’s security structure creates default users and groups when you create a new database. In fact, these default groups and users must be present in order for security to function. Since Access has to know who is logged in, everyone is identified as the default Admin user. That is, by default and until changed, all users are members of the default Admins and Users groups, but only when logging in before security is implemented. Although these groups should not have any role in your final security configuration, it helps to know what they are and what they do, so these users and groups are described in this section. Admins—Unlike the default accounts, the Admins group is unique to any given workgroup because the SID of the Admins group is generated from the SID of the workgroup. The Admins group must always have at least one member and Jet will enforce this rule. Though membership in the Admins group can be revoked, the group imparts certain special and irrevocable privileges. A member of the Admins group can grant permissions to any object in the workgroup. With DAO and ADOX, it is possible to deny these privileges to Admins’ members over a database object so that only the object owner has special privileges, but neither DAO nor ADOX can revoke Admins’ privileges over groups and user account administration. Admin—The only default user in Jet. Though invested with no special privileges, it inherits rights from the Admins group. You are logged on to a database as Admin if you do not specifically log on as some other user. As long as the Admins group has another member, you can remove the Admin user from the Admins group. This is a necessary step for properly securing a database and is discussed in the section “Securing a Database Step by Step.” Users—An empty group at creation whose members have full rights by default. Removing all permissions from the Users group is a necessary step for properly securing a database and is discussed in the section “Securing a Database Step by Step.” Owner—The owner is the creator of the object or the user to whom ownership has been transferred. The owner of an object is the most powerful user and has extensive, irrevocable rights. The database object must be owned by a user, but other objects can be owned by groups.
Understanding Permissions After the users and the groups have been created, you are ready to implement your security plan. However, just as it is difficult to discuss users without groups, it is difficult to cover permissions without talking about objects. Permissions determine what a user or
Security CHAPTER 22
647
group can or cannot do with an object. The objects range from the database to tables, queries, forms, reports, and macros (module security is no longer addressed through the user interface) in the database. The types of permissions available vary from object to object and some permissions depend on other permissions.
TABLE 22.1
Permissions and What They Can Do for You
Permission
Grants
Object
Open Exclusive
Open while barring others from opening.
Database
Open/Run
View in runtime mode.
Database
Read Design
View in design-time mode.
All except Modules
Modify Design
View, change, or delete objects’ structure and properties.
All
Administer (Database)
Set database password, create replication master, modify startup options. No security administration.
Database
Administer (Other)
Full rights and privileges including security administration.
All except Database
object
Read Data
View data only.
Tables and Queries
Update Data append or delete.
View and change data, no
Tables and Queries
Insert Data
View and append, no change or delete.
Tables and Queries
Delete Data
View and delete, no change or append.
Tables and Queries
22 SECURITY
For instance, if you have the permission to alter the design of an object, you must have the ability to open it (maybe even exclusively), read it, and write to it. There is no need to memorize the independent and dependent permissions sets. The Access interface will take care of that for you—you just need to know what they all mean. Table 22.1 details the objects, their permissions, and their implications. Permissions for objects are not stored in the MDW file. In fact, the MDW file does not know anything about object permissions. Rather, this sort of information is stored in the database and the user is verified with the MDW file.
648
Multiuser Issues PART VII
Remember, to compact or repair a database, you must have the database opened exclusively. You must also open the database exclusively if you want to make changes to a multiuser application that has already been deployed. Query permissions are a common source of confusion, so I will try to resolve it once and for all. Suppose you develop a query for your users, but do not want them to have access to the underlying tables. There is a way you can enable users to open or run queries without giving them access to the supporting tables. Creatively employing a query’s run permissions gives you row-level and column-level security on the database because you can restrict users to records by criteria statements (for example, department codes, employee number, current dates, field values, and so on), and they can be prohibited from changing the queries you provided. To work with a query’s run permissions, select Tools, Options, Tables/Queries tab. Next, select Owner’s (as shown in Figure 22.7) to give all users the same permissions for running or opening the query as the query’s owner possesses. Note, however, that only the query’s owner can modify the query to transfer its ownership to another user. Selecting Owner’s overrides existing permissions for users. This option can also be exercised from the query’s property sheet and by using the With OwnerAccess option in a SQL statement. (See the section “Implications When Using SQL” for more on this.) FIGURE 22.7 Setting the Run Permissions property for a query through the user interface.
In all my years of developing database applications, I have seen few that could really be called “finished.” If the database is being used and its users like it, it is bound to be extended. To accommodate the eventuality that new objects will be created in the future, Access provides a way to handle the security settings for objects that do not yet exist when you implement your security plan. But the name of this feature, , is misleading regarding what it does for your security scheme.
Security CHAPTER 22
649
Choosing the in the Users and Group Permissions dialog does not prevent users from creating database objects, it only determines what permissions the object will have when it is created. Even if you remove all the permissions for , users will still be able to create them. Because these users are the owners of these objects, they are free to assign whatever permissions they want to them. The only way to prevent users from creating objects is by intervening in the security assignments using DAO or ADOX.
The user who created the database is the database’s permanent owner. The database owner’s rights to create objects in the database are irrevocable, regardless of what the security user interface says about those privileges. Other users can create objects within the database. They become the owners of those objects. Objects’ ownership can be transferred to groups to ease administrative burdens—this way, any member of a group can administer an object or set of objects. Ownership of objects can be transferred by the database owner or by the object owner. To change the ownership of a database, a user must have Read Design and Read Data privileges to the original database, create a new database in the workgroup of the original, import all the objects into the new database, delete the original database, and rename the new database to the exact same name as the deleted original. The Access Security Wizard does the exact same set of steps for you.
Implementing Security Using Startup Options In addition to a rigorous, properly implemented security scheme, there are a few other things you can do using startup options to augment Access security. These measures should never be mistaken for, or used to substitute for, a real security plan, but they can add a little panache to your applications and might even plug an unanticipated hole. By themselves, they might serve to lock down an application just enough for a temporary solution.
22 SECURITY
The owner of an object is the most powerful user of the object and can do anything with it. An owner’s rights cannot be revoked or removed. The security user interface might make it appear that permissions for an owner have been revoked, but they really have not. The only way to disfranchise an owner of an object is for the owner to transfer ownership.
650
Multiuser Issues PART VII
Under the Startup menu there are a number of options to examine. They are Display Database Window—The place where users can do the most harm is in the database window. By hiding it at startup, less experienced (and usually the most curious) users might not realize it is there to begin with. And, of course, if it is hidden, it cannot be used to damage the database or its data. Menu Options—Again, giving users access to native functionality turns a small security hole into a gaping crater. Be careful about letting a user close a form in a way other than the one you prescribe, or about letting a user save a record without verifying that user’s identity and rights to do so. Disallowing full menus, default shortcut menus, and built-in toolbars, and prohibiting toolbar and menu customization can prevent such a breach. Carefully select which, if any, of these features remain active. Allow Viewing Code After Error—Without proper security (and good error-handling) users will see either a disturbing and cryptic error message or the code itself, which is an extremely bad and embarrassing thing. Turn this feature off. AllowSpecialKeys—F11, Control-Break, and Control-G could ruin an application. Disable these features when your application is distributed. Application Icon—Depending on the environment your application will run in, it might serve to substitute the Access icon with something else. If nothing else, this might throw troublesome users off the scent that they are dealing with an Access application. Substitute it with a BMP or ICO file.
Security Concerns with Replication Using replication in the database application poses no insurmountable problems to security. The replicas can be secured using user-level security. Any changes made to the Design Master’s objects’ security bits will propagate to the replicas during synchronization because those permissions reside in the objects themselves. Unfortunately, the workgroup information file does not replicate. If the users of the replicated database cannot share a single workgroup file (this is the case in most replication arrangements), the workgroup file must be distributed when new objects are created, or when changes in the security scheme require it. Note When replication is used, the Database Password feature is unavailable.
Security CHAPTER 22
651
Another consideration when distributing replicas and MDW files is Admins membership. Because members of this group can add and delete users, as well as reset or clear passwords, it is important to carefully select the administrators and have as few of them as possible when they are far afield.
Security for Split Databases Access databases should always be split. That is, their data should reside in one MDB file and the executable parts such as its forms, reports, and modules, should reside in another. How do you secure such an arrangement?
Database=C:\Program Files\Microsoft Office\Office\Samples\Northwind.mdb
With the understanding that there is no data from the linked table residing in the client database, provide full permissions to the linked table. Doing so also enables the client’s user to re-create the table definition (TableDef) if needed. With the links unrestricted, you can secure the back-end database’s tables in the normal way. This way, unrecognized users will not be able to access the data with other methods. When it comes time to refresh the links to the source tables, you have three options: • RefreshLink—Read data permissions on the back-end data table • RefreshLink—Read design permissions on the back-end data table • Connect property—None or no permissions on back-end data If all the permissions have been removed from the back-end data, the third option is the natural choice. These options must be run from DAO or ADOX. The user interface does not permit you to manage the link’s security unless you have read data permissions on the back-end data source. The following code shows how this might look: Set tbldef=db.CreateTableDef(strTableName) With tbldef .SourceTableName=strTableName .Connect = “;Database=” & strTableName .Append tdf ‘attach single table - better yet, loop through some End WithI
SECURITY
When using linked tables, you must deal with two sets of permissions: one set for the source database and another set for the client. At the source, or back-end, there are tables. In the client, there are links to those tables. It is possible to set restrictive permissions on the links, but doing so makes little sense because the link is little more than a set of properties, a string if you will, such as the following:
22
652
Multiuser Issues PART VII
Another way to deal with the link issue is to drop the link entirely and access the backend tables through a remote database object and SQL statements. You can also take advantage of Access 2002’s capability to bind a form to a detached recordset object. Listing 22.1 shows how to bind a form to a detached recordset object. LISTING 22.1 Dim Dim Dim Dim Dim
Binding a Form to a Detached Recordset Object
wrk As Workspace db As Database rdb As Database rs As Recordset Connstr as String
Set wrk = DBEngine.Workspaces(0) Set db = CurrentDb() Connstr = “C:\program files\microsoft office\” Connstr = Connstr & “office\samples\northwind.mdb” Set rdb = wrk.OpenDatabase(Connstr, , , “admin”) Set rs = rdb.OpenRecordset(“Select * from Customers”) ...
ADOX is not affected by the table linking because it disregards objects located in the current database—currentdb(). As a result, ADOX cannot determine the security status of the link. Rather, with ADOX, data would be opened directly in the back-end database. The code in Listing 22.2 opens an ADO connection to the datasource and then assigns permissions to the user named “JP”. LISTING 22.2
Opening a Database and Assigning Permissions to It Through ADOX
Dim cnn As New ADODB.Connection Dim cat As New ADOX.Catalog With cnn .Provider = “Microsoft.Jet.OLEDB.4.0” .ConnectionString = “data source=F:\NorthWind_Traders\& _ NorthWind.mdb;jet oledb:system database=” & _ “F:\NorthWind_Traders\sysnwind.mdw; “ & _ “user id=Chuck;Password=opensesame” .Open End With Set cat.ActiveConnection = cnn cat.Users(“JP”).SetPermissions objName, adPermObjTable, _ adAccessGrant, adRightFull ....
Security CHAPTER 22
653
With OwnerAccess Option Another possibility for implementing security in split databases is to remove all permissions from the tables on the back-end. You can then run queries against the links using the With OwnerAccess option at the end of the SQL statement or as the property setting of the query. This way the query will be able to run or return rows, but users from other applications will not be able to alter data or table structure. A code fragment showing such a SQL statement is listed here: SELECT Customers.* FROM Customers WITH OWNERACCESS OPTION; OwnerAccess
option, follow these steps:
1. Remove all permissions from the table(s) in question for the group that will use that query. 2. Using an account that has Read Data and Update Data permissions on those tables, create a query and include the column and set the criteria that return the desired result. 3. Change the query’s permissions options to “Owner’s.” This will enable the query to run as if it were being run by the owner, but only the owner can make changes to it. Setting query permissions such that it can only be changed by its owner might hinder development of a complex database application on which there is more than one developer. In such multi-developer projects, it is best to assign ownership to a group so that there is more than one person who can change or maintain the query when needed. Bear in mind that you will need to reset the query’s ownership back to User before transferring it to a group or deploying the application.
Security for Client/Server When working with data from a database server such as Microsoft SQL Server, Oracle, or Informix, you usually have very little control over security because these enterprise class database servers implement their own security models. Nevertheless, if your application is attached to tables on a server and you want to restrict the write and insert permissions to these tables, you can do so using Access, either through ADOX (use the CurrentProject.Connection property) or through the user’s interface.
SECURITY
To create a query that runs the With
22
654
Multiuser Issues PART VII
Managing Users Jet security is user-centric, but that does not mean you should dwell on the user and get involved in lots of details about users. All any database user needs is a username, a password, and a PID. To be useful, a user needs permission to work with objects in the database. Users should belong to at least one group in order to be manageable. Beyond these three elements, you should do little with database users. If you get into the habit of customizing users’ security profiles, you will create a maintenance nightmare for yourself. After you have created a name, a PID, and a password, you should modify only those settings. Avoid granting object permissions to users. Instead, grant object permissions to groups and then have your users join the appropriate group. Listing 22.3 shows a simple function that demonstrates how to check for the existence of a user, create a user, delete a user, assign group membership, and check a user’s membership in a group. To create or revamp the user, the subroutine takes a username argument, a PID, a password, and, in a parameter array argument, a list of the groups to which the user should belong. The subroutine checks to see if the user’s name is in the .Users collection. If it is, the subroutine deletes and then re-creates the user with the new PID. Next, the function loops through the parameter array passed in and joins the user to each group. To make the subroutine consistent with the Workgroup Administrator user interface, it also checks to make sure that the user belongs to at least the default, permissionless Users group. If that group name was not passed in, the sub assigns him to it. LISTING 22.3 Them
Code Example to Check the Existence of Users, Delete Them, and Add
Sub ADOXManageUser(strUserName, strPID, strPWD, _ ParamArray JoinGroups() As Variant) Dim cnn As New ADODB.Connection Dim cat As New ADOX.Catalog Dim Group As Variant With cnn .Provider = “Microsoft.Jet.OLEDB.4.0” .ConnectionString = “data source=C:\Program Files\ & _ Microsoft Office\Office\Samples\Northwind.mdb; & _ jet oledb:system database=C:\Program Files\ & _ Microsoft Office\Office\Samples\sysnwind.mdw; & _ user id=NWindAdmin;Password=opensesame” .Open End With
Security CHAPTER 22 LISTING 22.3
655
continued
Set cat.ActiveConnection = cnn With cat ‘ We don’t anticipate any errors, so On Error Resume Next ‘ Check to see if the name already exists ‘ If so, delete him so his groups can be redefined ‘ Deleting the user also removes any group references If strUserName = .Users(strUserName).Name Then .Users.Delete strUserName End If
‘ Even if the user exists, we can add him to ‘ a new group if appropriate For Each Group In JoinGroups() .Groups(Group).Users.Append strUserName Next ‘ Before finishing, so we are consistent with the UI, ‘ check to see that he at least belongs to the Users Group ‘ There’s no need to loop through the group’s members ‘ to determine this, just check the name against the group’s ‘ users collection. If Not strUserName = .Groups(“Users”).Users(strUserName).Name Then .Groups(“Users”).Users.Append strUserName End If End With End Sub
Enumerating Groups and Users and List Membership It would be nearly impossible to administer your database if you could not programmatically find out who can use the database and in what capacity they can use it. Access VBA provides a way, through ADOX, to list all the users in your database, as well as their group memberships and other information. An example of this code is shown in Listing 22.4.
SECURITY
‘ Create the new user .Users.Append strUserName, strPWD
22
656
Multiuser Issues PART VII LISTING 22.4
Using ADOX to List User Information
Public Sub ADOXGroupsandUsers() Dim cnn As New ADODB.Connection Dim cat As New ADOX.Catalog Dim i As Integer, k As Integer With cnn .Provider = “Microsoft.Jet.OLEDB.4.0” .ConnectionString = “data source=h:\books and articles\ & _ unleashed\securityexample.mdb; & _ jet oledb:system database=h:\books and articles\ & _ unleashed\sysnwind.mdw;user id=Admin;Password=letmein” .Open End With Set cat.ActiveConnection = cnn With cat Debug.Print ‘Enumerate and list the Groups Debug.Print “GROUPS count = “ & .Groups.Count For i = 0 To .Groups.Count - 1 Debug.Print “ “ & .Groups(i).name Next Debug.Print ‘Enumerate and list the Users Debug.Print “USERS = “ & .Users.Count For i = 0 To .Users.Count - 1 ‘Suppress the ‘Engine’ and ‘Creator’ Users If .Users(i).Groups.Count > 0 Then Debug.Print “ “ & .Users(i).name End If Next Debug.Print ‘List the Groups collection of the catalog and their members Debug.Print “GROUP MEMBERSHIP” For i = 0 To .Groups.Count - 1 Debug.Print “ “ & .Groups(i).name For k = 0 To .Groups(i).Users.Count - 1 Debug.Print “ “ & .Groups(i).Users(k).name Next Next
Security CHAPTER 22 LISTING 22.4
657
continued
Debug.Print
End With End Sub
Identifying Current Users with ADOX The question “Who’s logged in right now?” usually comes up, and, before ADOX, no elegant way to answer the question existed. Happily, ADOX lets you use the roster to find out who is using the database at any particular moment. See Listing 22.5 for an example of this feature. LISTING 22.5
ADOX Can Let You See Who Is Currently Using a Database
Sub UserRoster() ‘Procedure that will use the ‘User Roster feature or Jet 4.0 ‘to connect to a database and check ‘to see who is in the database ‘From “Microsoft Access 2000 Development Unleashed” (SAMS) ‘By: Forte, Howe, Ralston Dim conn As ADODB.Connection Dim rst As ADODB.Recordset On Error GoTo Proc_Err Set conn = New ADODB.Connection ‘Open the back-end database With conn .Provider = “Microsoft.Jet.OLEDB.4.0”
22 SECURITY
‘ List the Users collection of the catalog ‘ and their group memberships Debug.Print “USER MEMBERSHIP” For i = 0 To .Users.Count - 1 ‘Suppress the ‘Engine’ and ‘Creator’ Users If .Users(i).Groups.Count > 0 Then Debug.Print “ “ & .Users(i).name For k = 0 To .Users(i).Groups.Count - 1 Debug.Print “ “ & .Users(i).Groups(k).name Next End If Next
658
Multiuser Issues PART VII LISTING 22.5
continued
.ConnectionString = “data source=h:\books and articles\ & _ unleashed\securityexample.mdb; & _ jet oledb:system database=h:\books and articles\ & _ unleashed\sysnwind.mdw;user id=Admin;Password=letmein” .Open End With ‘Create the recordset based on the ‘Number of users in the database Set rst = conn.OpenSchema(adSchemaProviderSpecific, , _ “{947bb102-5d43-11d1-bdbf-00c04fb92675}”) ‘For each user print out the computer name ‘and other information Do Until rst.EOF Debug.Print rst!COMPUTER_NAME Debug.Print rst!LOGIN_NAME Debug.Print rst!CONNECTED Debug.Print rst!SUSPECTED_STATE rst.MoveNext Loop Proc_Exit: Exit Sub Proc_Err: MsgBox Err.Description Resume Proc_Exit End Sub
Identifying Users With Blank Passwords One of the biggest holes any secured database can have is a user with a blank or unset password. One of Jet’s security shortcomings is that it does not require a password, so the password must either be forced, in a custom security interface, or someone must manually test each user name to identify any user able to open the database with a username and no password. The function in Listing 22.6 shows how to use ADOX to perform this type of test. LISTING 22.6
Avoiding the Blank Password Security Problem
Public Sub ADOXPasswordCheck() Dim strcnn1 As String Dim strcnn2 As String
Security CHAPTER 22 LISTING 22.6 Dim Dim Dim Dim Dim
659
continued
cnn As New ADODB.Connection pwrdtestcnn As New ADODB.Connection cat As New ADOX.Catalog pwdCat As New ADOX.Catalog i As Integer, k As Integer
Set cat.ActiveConnection = cnn With cat For i = 0 To .Users.Count - 1 ‘Suppress the ‘Engine’ and ‘Creator’ Users If .Users(i).Groups.Count > 0 Then ‘ Try to open another connection with a username ‘ and a blank password With pwrdtestcnn .Provider = cnn.Provider strcnn2=”data source=C:\Program Files\” strcnn2= strcnn2 & “Microsoft Office\Office\Samples\” strcnn2= strcnn2 & “NorthWind.mdb; “ strcnn2= strcnn2 & “jet oledb:system database=” strcnn2= strcnn2 & “C:\Program Files\” strcnn2= strcnn2 & “Microsoft Office\Office\” strcnn2= strcnn2 & “Samples\sysnwind.mdw; “ strcnn2= strcnn2 & “user id=NWindAdmin;Password=’’” .ConnectionString = strcnn2 ‘ Since we’ll usually get an error, ‘ we don’t want that to stop us. On Error Resume Next .Open ‘ No error means the database opened with no password If Err = 0 Then
22 SECURITY
strcnn1 = “data source=C:\Program Files\Microsoft Office\Office\” strcnn1 = strcnn1 & “Samples\NorthWind.mdb;” strcnn1 = strcnn1 & “jet oledb:system database=” strcnn1 = strcnn1 & “C:\Program Files\Microsoft Office\Office\” strcnn1 = strcnn1 & “Samples\sysnwind.mdw; “ strcnn1 = strcnn1 & “user id=NWindAdmin;Password=OpenSaysaMe” With cnn .Provider = “Microsoft.Jet.OLEDB.4.0” .ConnectionString = strcnn1 .Open End With
660
Multiuser Issues PART VII LISTING 22.6
continued ‘ Use the catalog from the current connection Set cat.ActiveConnection = pwrdtestcnn Debug.Print cat.Users(i).name & _ “ has a blank password”
‘ Close the new connection pwrdtestcnn.Close End If End With End If Next End With End Sub
Setting/Clearing Passwords Setting and clearing a password can be done from the interface or from ADOX. The following line of code will change a password: cat.Users(“admin”).changepassword “oldpassword”,”newpassword”
Setting both arguments of the .changepassword method to zero length string will clear a password. Though members of the Admins group cannot see another user’s password, they can reset another user’s password.
Managing Group Object Ownership Since you have the most flexibility available to you when you own an object, you should know how to assess and modify object ownership through code. Listing 22.7 demonstrates how to find out to what group an object belongs and then to change that ownership using VBA code. The function assigns the object’s ownership to a group so that it will be easier to administer. LISTING 22.7
Assigning Object Ownership
Public Sub ADOXGroupOwnership(strObjectName As String, strGroupName As String) Dim cat As New ADOX.Catalog Dim n As String
‘ Here we are working with the client, but if you wanted to ‘ work with another MDB file, just create and open a connection to it Set cat.ActiveConnection = CurrentProject.Connection
Security CHAPTER 22 LISTING 22.7
661
continued
With cat Debug.Print .GetObjectOwner(strObjectName, adPermObjTable) .SetObjectOwner strObjectName, adPermObjTable, strGroupName .Tables.Refresh Debug.Print .GetObjectOwner(strObjectName, adPermObjTable) cat.Tables.Refresh End With End Sub
22
Managing Multiple Apps
ADOX allows you to administer and coordinate all visible workgroups from a central location. In and of itself, this feature is not new, but when you consider the possibility of coordinating Oracle or SQL Server security and data definition with your local workgroup using a single common interface, such centralized administration is genuinely impressive. Currently, you can only perform DDL functionality against Oracle when using the MSDAORA provider and ADOX. When facing the security and data definition tasks of multiple applications, evaluate ADOX and see if it can serve your needs. Common ownership of objects is another tactic you should employ for handling multiple applications. Just as ADOX gives you one-stop-shopping for DDL and security, you should try to unify the ownership of objects as much as possible. The fact that only one entity can own an object in the database at one time should give any serious developer pause. What should happen if the owner of the object leaves and changes must be made to it, but you have found it impossible? Rather than find yourself in that situation, when you have one central point of massive failure, let groups own objects. That way, several individuals will have the rights to change and reassign the object as needed. Separate applications can share a workgroup, as you saw in Figure 22.1. Take advantage of this as much as possible. Chances are that you will be able to overlap many groups from one application to another, thereby reducing your administrative overhead.
SECURITY
With ADOX, your project does not need to load DDL (data definition language) or security functionality if it does not require it. Conversely, if your project does require DDL or security, ADOX comes in a lightweight package with a clear and, hopefully, universal programming interface accessed through msadox.dll. Though ADOX is not yet mature, it shows great promise. If it lives up to the promise it holds—and, judging from ADO, it probably will—ADOX soon will be the DDL and security programming interface of choice.
662
Multiuser Issues PART VII
If at all possible, strive for a common file location on the network rather than adopting a replication scheme. Though replication is a fine tool for sharing data and has opened up a world of possibilities for Access applications, its use comes at a high administrative cost. The key to managing multiple applications is to share objects, users, groups, workgroups, and locations as much as possible. Reducing the number of variables in the maintenance of these applications will make them more reliable and robust.
Implications When Using SQL Access 2002 provides another way to handle basic security functionality in a database, including the client. New keywords have been added to the SQL dialect that enable you to perform a number of administrative functions using SQL. These functions include the following: • Creating users and groups • Adding users to and removing users from groups • Dropping users or groups • Granting and revoking permissions on objects to users and groups These SQL statements are executed like any other SQL statement, but instead of returning recordsets or altering the data definition of the database, they affect the security settings of the objects at which they are aimed.
Create Create User or Group does exactly what it implies. The user and group collections in the system file of the database are appended with the new user and group entities. One difference between this technique and ADOX is the absence of a PID. PIDs cannot be included with the SQL statements. The following code shows how you would create users and groups through SQL statements. CREATE USER user password[, user password,...] CREATE GROUP group password[, group password,...]
After the user or group is created, it can be added to an existing group with the Add statement. Users are always added to groups in SQL, never the other way around.
User
Add User Add User also affects the group collection in the workgroup file. Wherever the workgroup file is affected, it is always the current workgroup. You cannot, using SQL, affect any workgroup file other than the one you are logged in to. ADD USER user[, user,...] TO group
Security CHAPTER 22
663
Grant/Revoke After users have been created and added to groups, they can be granted permissions. The Grant keyword enables you to give object permissions to users and groups. GRANT {privilege[, privilege...}] ON {TABLE table | INDEX index | QUERY query | CONTAINER} TO {user/group[, usergroup,...]}
The permissions that can be granted are more limited in SQL than you will find in ADOX. They are described in the following list: Dbpassword
Delete
Updateidentity
Insert
Create
Update
Selectschema
Drop
Schema
Selectsecurity
Updateowner
Updatesecurity
With SQL, you can grant permissions on tables, indexes, queries, and containers. After granting privileges, you can revoke them with Grant’s opposite, Revoke. The syntax and options are the same as they are in a Grant statement, but the result is exactly opposite.
Drop After the permissions are removed, you can Drop the user or group. The syntax and options are exactly like Create, except that Drop is the keyword and the result of the query is the removal of a user or group from the workgroup file.
Securing a Database Step by Step Despite the potentially bewildering array of options and features available in Access’ security model, the basic procedure for securing a database has not changed very much. As a refresher and reference point, this section walks through the steps necessary to secure a database. By following these steps closely while you implement your security plan, you will have full advantage of Access’s security capabilities. Failing to follow these steps could compromise the strength of your security.
SECURITY
Select
22
664
Multiuser Issues PART VII
1. Create a new workgroup—Be sure to use the Workgroup Administrator as discussed earlier in the chapter. Select the option to create a new workgroup file. 2. Open Access using the workgroup file created especially for your new application. 3. Open or create the database you intend to secure. 4. Create a new user and add him to the Admins group. 5. Change the Admin user’s password from its default blank to something you will remember, but that few, if any, other people know. 6. Remove all permissions from all objects for the Users group in the database. 7. Close the database. 8. Reopen Access and log in as the new administrator. 9. Open the database you are securing. 10. Run the Security Wizard on the new database. This will save you a great deal of tedious work changing object ownership. 11. Remove the Admin user from the Admins group. 12. Create the users and groups your application needs. 13. Join your users to their respective groups. 14. Assign the appropriate permissions to the groups. 15. Add or remove group permissions as needed.
Common Security Errors One common problem occurs when users logged in to one workgroup are able to get into another database with their admin accounts. Most likely this has happened because you created your security in the default system.mdw. The default system MDW is not secure and can never be made secure because it was not seeded with a PID when it was created. Because the admin account is universal, others will be able to enter this vulnerable system. It is also possible that the Admin user was not removed from the Admin group on your system. Because the Admin user is not unique across workgroups, every workgroup will see Admin as its own Admin. If your workgroup still has Admin as a member of the Admins group, people from other workgroups will be able to log on to your workgroup as administrators. To solve this problem, you will have to secure the database using a new workgroup file. Sometimes a database seems to be secured, everyone logs in, but the permissions are not working.
Security CHAPTER 22
665
A common problem with secured databases is the failure to strip the users group of all permissions. At birth, all workgroups grant users full rights to everything in the database. All users are, by default, members of the Users group. Even if you grant no explicit administrative rights, all users in the Users group will inherit them by virtue of being in that group. Make sure to remove all permissions from the Users group. There is nothing stopping you from creating as many groups as you could ever want. Because the Users group is worthless as a permission-less group, and the Admin groups is super-powerful, you really have no choice but to create a security plan and create groups to satisfy it.
As vital as security is—even to the simplest databases—it is often neglected or improperly configured. This chapter has explored the ever-growing number of ways to secure a Jet database to protect it from inadvertent misuse and malevolent harm. Planning security from the start of the application and studying these topics to find an implementation that suits your needs will enhance the experience users have with your application, and will mean fewer maintenance headaches for you.
SECURITY
Summary
22
Web Publishing with Access 2002
PART
VIII IN THIS PART 23 Configuring a Web Site for Web Publishing 24 Web Enabling Access 2002 with Office Web Components 25 Using Data Access Pages 26 Web Publishing with Access 2002 and Active Server Pages
CHAPTER 23
Configuring a Web Site for Web Publishing IN THIS CHAPTER • Development Versus Production Environment 670 • Choosing Your Platform • What Is the Option Pack?
671 673
• Setting Up Your Web Server
675
• Managing and Configuring Your Web Server 682 • Securing Your Web Applications
692
• What Is the Difference Between a Site and a Virtual Directory? 693
670
Web Publishing with Access 2002 PART VIII
Most Access developers get extremely intimidated when their boss or client starts talking to them about Web publishing with Access 2002 because most of them are experts in database management and VBA, but do not know much about setting up and maintaining a Web site. This chapter explains how to set up a machine for Web site development and Web site deployment, and how to work with Microsoft Web server technology. By no means will you be a Web server administrator after this chapter, but you will be able to set up a Web site when you are through. This chapter discusses Windows 95/98 and NT4 because most readers are still using this technology. Windows 2000 offers some improvements and differences, but this section is devoted mostly to the technology being currently used in production. Some information about Windows 2000 and IIS5 has been included later in the chapter.
Development Versus Production Environment Let’s clarify some terminology that we will use in this section of the book. There are two different environments with which you will probably be dealing during the life cycle of your development project: the development environment and the production environment. Your development environment is exactly what it sounds like—the place you develop your application. The production environment is the place your application will ultimately reside when it is finished and ready to be viewed by your users or clients. If you create your pages on a Windows 95 machine, but place them on a Windows NT Server machine for testing and debugging, the Windows 95 machine is the development machine, and the Windows NT Server machine is your development environment. Ideally, your development and production environments would have identical hardware and software configurations, but would be physically located on two different machines. In the real world, this is not always feasible. Many people (including some of the authors of this book) use Windows 95/98/2000 Professional or Windows NT Workstation as their development environment, and then place the files on a Windows NT or Windows 2000 Server machine for their final resting place (production environment). The great thing about Microsoft Web technology is that you can develop and test your Access 2002 Web applications on a Windows 95/98 machine and deploy it to a Windows 2000/NT 4.0 machine without changing any code. Microsoft has provided one Web server technology that works on any of these operating systems. The way to harness this technology is by installing a Web server on your development and deployment machines.
Configuring a Web Site for Web Publishing CHAPTER 23
671
Choosing Your Platform Microsoft has released four different Web server applications—one for each level of its Windows platforms. The first, called Personal Web Server 4.0 (PWS4), runs on Windows 95/98 and provides the most basic Web server functionality. The second, called Peer Web Services 4 (again PWS4), is also a limited-functionality Web server, almost identical to the Windows 95/98 version, but with modifications made so it will run properly under Windows NT Workstation. Another server application, called Internet Information Server 4.0 (IIS4), can only be run on Windows NT Server 4.0. The latest version of Internet Information Server, IIS5, runs on Windows 2000 machines. A limited version of IIS5 is installed automatically when you install Windows 2000 Professional; it does not, however, include the MMC snap-in that is included with the Server version. Because the versions that run under Windows 95/98 and Windows NT Workstation are so similar in capabilities and functions, I refer to them together through the rest of this chapter as PWS, noting differences where applicable. Some of the features that both PWS and IIS provide are • The ability to serve basic HTML Web pages • The ability to implement transactional Web applications through the use of Microsoft Transaction Server • ODBC connection pooling • The ability to utilize Microsoft’s Data Access Components (MDAC) • Microsoft Message Queue (MSMQ) Note Version 1.5 of the Microsoft Data Access Components is installed with a standard installation of the Option Pack, but it is recommended you acquire and install the newest version. At the time of this writing, the newest version is MDAC 2.5, shipped with Access 2002. You can obtain the latest versions and service packs from Microsoft at http://www.microsoft.com/data/. It should also be noted that there is a difference in the version of the MSMQ that is installed with the Option Pack depending on which operating system you are installing to. The version of the MSMQ installed on Windows NT/2000 Servers is a fully functional message queue service, whereas the NT Workstation and Windows 95/98 versions are merely clients that can utilize the MSMQ service on a remote server.
CONFIGURING A WEB SITE FOR WEB PUBLISHING
• The ability to implement Active Server Pages applications
23
672
Web Publishing with Access 2002 PART VIII
Although there are many similarities, there are also some differences, mostly related to performance and scalability, that you should be aware of before choosing which platform to run.
Personal Web Server and Peer Web Services Personal Web Server and Peer Web Services are great tools for developing applications locally on your workstation without the requirement of running NT Server. They enable you to develop and test your applications prior to moving them to the production Web server. If an application runs properly on PWS, you are assured that it will also run properly when deployed to IIS4 or IIS5. Because PWS was not designed to host high-volume sites, some of the features that are included with IIS4 and IIS5 are missing, such as Index Server, SMTP Server, and Certificate Server. You are also limited to the number of concurrent client connections with this platform—only one on Windows 95/98 and a maximum of just ten on Windows NT Workstation.
Internet Information Server Internet Information Server 4.0 is a much more robust Web server application than the Personal Web Server is. It is the recommended platform for all production Web servers, because it will hold up better under high volume. You will also notice performance gains with IIS4 over PWS4. IIS4 has no limitation on the number of concurrent users. Depending on the design of your application, it can potentially handle thousands of concurrent users. The installation of both is very simple and also quite similar. It is recommended to install only the services that you plan to take advantage of. Loading unused components will create unnecessary load on the workstation/server. I do, however, recommend you install the latest service pack for IIS4 available from the Microsoft Web site. IIS5 for Windows 2000 has a similar installation. Information about installation and an overview are available at Microsoft’s Web site at http://www.microsoft.com/ windows2000/techinfo/howitworks/iis/iis5techoverview.asp. Now you know some basics about each of your choices for Microsoft Web servers— which one do you use? Well, you should definitely use IIS4 and Windows NT 4 Server, or IIS5 and Windows 2000 Server for your production environment. You would also be better served to develop on an environment as close as possible to the production environment, so I also recommend IIS4 on NT Server 4 or IIS5 on Windows 2000 Server as a development environment. Almost every Active Server Pages application that you write will run on these Web platforms IIS4/IIS5 or PWS4—ASP is ASP.
Configuring a Web Site for Web Publishing CHAPTER 23
673
Note There is a difference between the ASP that can run on IIS3, IIS4 and IIS5. IIS3 uses VBScript 2 but IIS4 uses VBScript 3 and IIS5 uses VBScript 5. All versions of the VBScript engine are backward-compatible, but each version has added functionality. You need to be careful not to require the newer features unless you are sure that your Web server supports them. If you have installed Internet Explorer 5 on your server, you are running VBScript 5, which contains all the previously included functionality, plus some additional benefits. If you do not have Internet Explorer 5 installed on your system but would still like to take advantage of the newest scripting engine, you can download the most recent release from http://msdn.microsoft.com/ scripting/. At the time of this writing, a beta version of VBScript 5.6 is available from that site. Two new functions introduced in version 5 are the Eval and Execute functions, which provide the ability to evaluate code at runtime. There have also been changes made that have resulted in performance gains for any application that uses VBScript (including Active Server Pages).
23
What Is the Option Pack? Only Windows 2000’s standard installation includes the files necessary to develop Web applications, but the Web services for NT 4.0 and Windows 95/98 are a free add-on. The installation package that can be used to provide Web development and Web deployment capabilities is the Windows NT Option Pack. Even though it is named the “Windows NT” Option Pack, the same installation package can be used to install Personal Web Server 4 and other Web enhancements on Windows 95/98. During the installation of Windows NT Server 4.0, you are given the option to install Internet Information Server (version 2), but that does not provide the needed functionality to implement Active Server Pages, Microsoft’s key Web development technology and the topic of Chapter 26, “Web Publishing with Access 2002 and Active Server Pages.” Note I recommend either not installing IIS during a Windows NT4 Server installation, or uninstalling it before you move forward. It is always better to start with a fresh install rather than an upgrade.
CONFIGURING A WEB SITE FOR WEB PUBLISHING
The way to install a Microsoft Web server on Windows NT4 Server is by installing the NT 4.0 Option Pack.
674
Web Publishing with Access 2002 PART VIII
Microsoft’s most recent release of Web server software is included as part of the Option Pack, which can be obtained from a variety of sources. It will automatically be sent if you are an MSDN subscriber. You can also buy the CD or download it from Microsoft’s Web site at http://www.microsoft.com/ntserver/nts/downloads/recommended/ NT4OptPk/default.asp. The option pack can be installed and run on Windows 95/98, Windows NT Workstation, and Windows NT Server 4.0. Prior to installing the Option Pack on a platform, you are required to install Microsoft’s Internet Explorer 4.0 or higher. I recommend downloading and installing the newest release of the browser to have all the most recent patches and bug fixes. (At the time of this writing, it is Internet Explorer 5.0 included with the Office XP disks.) Additionally, if you are installing the Option Pack on Windows NT 4.0, you will be required to install Microsoft’s Windows NT Service Pack 3. I would recommend you install the latest Service Pack (6a) to take advantage of the latest update. Note Service Pack 6a is the most recent Windows NT4 service pack released from Microsoft, but should be installed after installing the Option Pack.
Note If you need to reinstall Service Pack 3,or higher, for any reason after you have installed the Option Pack, be sure not to overwrite any of the newer files that Option Pack installed.
Before installing the Microsoft Option Pack, you should be sure that your computer meets the minimum system requirements noted in Table 23.1. TABLE 23.1 Minimum Hardware Requirements for the Installation of the Windows NT Option Pack
Hardware Component
Required
Recommended
Processor
66MHz 486
90MHz Pentium
RAM
32MB
64MB
Free hard disk space
30MB
100MB
Monitor
VGA
Super VGA
CD-ROM drive (optional)
3x
6x
Configuring a Web Site for Web Publishing CHAPTER 23
675
Setting Up Your Web Server Web servers can be either simple or complicated to install and configure, depending on your application requirements. The reason for the complexity is the extreme amount of control you can maintain with Microsoft’s Web servers.
Installation There are some basic requirements that need to be met before installing the NT Option Pack, such as the installation of Internet Explorer 4.01 or higher and Service Pack 3 or higher (NT only), mentioned earlier. When you begin installation of the NT Option Pack, there are some further requirements that need to be met for successful application development with Office XP. There are also a lot of components that can and may be installed unnecessarily if you accept the defaults. In this next section, I will tell you what the different options are, which are required, and which should only be installed if you are planning to use them.
NT Option Pack for Windows 95/98 Installable Components Below is a list of the components available to you during installation if you were to choose “Custom Installation” during setup of the NT Option Pack. Not all these components are for each system. The ones that require a certain operating system have been noted. Also noted are the ones that are required to develop Office 2000 Web applications using Active Server Pages or COM objects. • Certificate Server (Windows NT Server only)—Enables you to create and send digital certificates. This ability allows for an additional layer of security in your Web application. You can create a personalized security certificate for a client that they can download and install to their client software (Web browser). In the future, you are assured that the client is who you think it is. This is not required for Office XP Web development. • Option Pack Common Program Files—Contains files common to most of the other components. This is required for Office XP Web development.
CONFIGURING A WEB SITE FOR WEB PUBLISHING
The components available to you during the installation of the NT Option Pack differ depending on the platform for which you are installing.
23
676
Web Publishing with Access 2002 PART VIII
• FrontPage 98 or FrontPage 2000 Server Extensions—Enables you to author sites and perform basic administration through Microsoft FrontPage and Microsoft Visual InterDev. This is not required for basic development of Office XP Web applications unless you decide to use FrontPage or Visual InterDev as your development tool. Note You can perform some level of development in these tools without having the FrontPage server extensions installed, but you will not be able to take advantage of the built-in wizards, Web-bots, or publishing features. Because the extensions add some overhead to a server (and FrontPage has a habit of occasionally rewriting ASP code), many developers only use these tools to create “shell” code that they later modify in another tool and then transfer to the server using standard FTP software.
• Internet Connection Services for RAS—Manages multiple dial-up connections and phone books. This is not required for Office XP Web development. • Microsoft Data Access Components 1.5—This installs ActiveX Data Components (ADO) 1.5 and Remote Data Services components (RDS). It also places necessary drivers and providers on your system that will enable you to connect to databases. This is a required component for Office XP Web development. After installing this component, you will also need to acquire the latest version of the MDAC components (version 2.6sp1). Since the Option Pack was released, there have been several enhanced revisions that contain bug fixes and additional functionality required for successful Office XP development (Web or otherwise). • Microsoft Message Queue (MSMQ)—Allows applications to pass transaction information to other components without having to wait for a reply. This is a feature that should be investigated if you are developing transactional applications, but the interfaces that you are dealing with are sometimes unreliable. For example, if you are using the MSMQ, and there is a momentary disturbance in network connectivity, the MSMQ will continue to attempt completion of the transaction. When the network comes back online, the transaction can be committed. This is not required unless you have specific requirements for it and plan to incorporate its services into your application. • Personal Web Server (Windows 95/98 and Windows NT Workstation only)—This component contains the core Web services.
Configuring a Web Site for Web Publishing CHAPTER 23
677
It is required if you plan to develop, test, or deploy applications on your workstation. • Internet Information Server (Windows NT/2000 Servers only) Includes: File Transfer Protocol (FTP) services NNTP services (for publishing newsgroups) Internet Service Manager (ISM)(a snap-in for the MMC): This is a required component that enables you to administer and configure your Web services through the Microsoft Management Console. Simple Mail Transfer Protocol (SMTP) service: This enables you to generate e-mail messages from your application and send it to the destination of your choice. Note
World Wide Web Server (HTTP Web services): This is required if you plan to develop, test, or deploy Web applications on your server. Sample Web site: If you are new to Web programming, you might want to install this sample Web site and look at the coding conventions and techniques used. HTML version of the ISM (Internet Service Manager): This enables you to manage your Web server remotely via an HTML interface. • Microsoft Transaction Server (MTS) 2.0—This installs the COM and DCOM manager. It is required for Office XP Web development.
23 CONFIGURING A WEB SITE FOR WEB PUBLISHING
This component enables you to send and receive e-mail, but it does not have any method for securing or controlling access to mail. After mail is received, it is placed in a shared folder and could potentially be accessed by anyone. This component is best used for outgoing mail only (like feedback forms). If you need to send and receive e-mail, you should install e-mail server software (such as Microsoft Exchange Server) that supports both the SMTP and POP protocols and is better able to handle the security involved with multiple users.
678
Web Publishing with Access 2002 PART VIII
• Microsoft Index Server (Windows NT Server only)—Enables you to perform fulltext search and retrieval of documents residing on your Web server. This is an excellent search tool with many advanced capabilities. If you install it, be sure you use it—having it sit on your system and index all your Web pages can be extremely resource-intensive and a waste if not used. This is not required for Office XP development. • Microsoft Script Debugger (Windows NT Server only)—Allows you to perform real-time ASP script debugging. It can be used to enhance development and testing cycles by automatically opening the active ASP script once an error is detected. Because of security and performance considerations, this should not be installed on a production Web server. This is not required for Office XP development. • Windows Scripting Host—Provides the ability to write and run local script files to perform certain functions like certain system maintenance features. This is not required for Office XP development. • Microsoft Management Console (Windows NT only)—The main GUI interface in Windows NT to allow you to manage and configure each of the services installed in the NT Option Pack. This is a required component if you are installing Windows NT Server. It is not required on NT Workstation (you could use the Personal Web Manager), but it is highly recommended. • Visual InterDev RAD Support—This enables Visual InterDev applications to be deployed remotely to your server. This is not required for Office XP Web development. Note Installing the Visual InterDev RAD Support poses a potential security risk on your system that you should be aware of. This component allows for remote deployment of applications, possibly without your knowledge. As with any development platform, it is possible that the component(s) deployed without your knowledge could have adverse effects on your system.
As you can see, there is quite a lot included with the NT Option Pack. Each of these components adds enhanced functionality, but they are not required for basic Office XP Web development and can add unnecessary overhead to your system if installed without reason.
Configuring a Web Site for Web Publishing CHAPTER 23
679
To make it easier to install the NT Option Pack, Microsoft has included three preconfigured install options with the setup package. The components installed with them are noted below.
Minimum, Typical, Custom Installation Options If you choose the Custom installation, you will be presented with the list of components mentioned previously and be able to pick-and-choose the ones you want installed. This is not usually necessary or desired, so Microsoft has made two other options available to you. A minimum installation includes everything necessary to develop basic Office XP Web applications. The components installed during a Minimum install are • Microsoft Data Access Components 1.5 (update to the latest version after installation. • Personal Web Server (Windows 95/98 and Windows NT Workstation only) • Internet Information Server (Windows NT/2000 Servers and Windows 2000 Professional only) • SMTP Service (Windows NT/2000 Servers only) A Typical installation includes all the components in the Minimal installation plus the following components: • FrontPage Server Extensions (update to the extensions that match the version of FrontPage you are using) • Personal Web Server documentation (Windows 95/98 and Windows NT Workstation) • Internet Information Server documentation (Windows NT/2000 Servers only) • Additional documentation: Active Server Pages, SMTP services, ADO, and Index Server (Windows NT/2000 Servers only) • Microsoft Index Server (Windows NT/2000 Servers only) • Microsoft Management Console (Windows NT/2000 Servers only) • Index Server (Windows NT/2000 Servers only) • Microsoft Script Debugger • Windows Scripting Host
23 CONFIGURING A WEB SITE FOR WEB PUBLISHING
• Transaction Server
680
Web Publishing with Access 2002 PART VIII
Note To get a full install of the Active Server Pages documentation for Personal Web Server, you will be required to perform a Custom install and specifically include that option. For some reason, Microsoft decided not to include them with either of these two preconfigured installation options.
If you want any of the advanced components that are not installed automatically, you should perform a custom installation. One of the components discussed previously, Microsoft Transaction Server, is such a major advancement and integral part of Microsoft’s Web strategy that I have dedicated the next section to explaining it in more detail.
Microsoft Transaction Server 2.0 Microsoft Transaction Server (MTS) is a major component of the Option Pack and required for many of the other services to work, including IIS/PWS. MTS is an object request broker and transaction broker. In past years, critical applications had to run on big beefy systems with special software installed in order to perform this functionality. Note MTS is so crucial to the operation of IIS that in Windows 2000 Server, Microsoft has integrated it into the operating system itself and renamed it COM+. MTS is required to install Microsoft’s Web services because, even if you do not develop towards it, MTS controls all Active Server Pages applications. Every ASP page that is processed is run through MTS (controlling the ASP.DLL)—this is one reason IIS4 has seen such great performance gains over IIS3. A wealth of information about the changes in ASP can be viewed here: http://windows. microsoft.com/windows2000/en/server/iis/. ASP 3 is installed by default when you install Windows 2000. ASP.NET (formerly called ASP+) is the next generation of ASP. Information concerning ASP.NET can be found at http://msdn. microsoft.com/library/default.asp?url=/nhp/Default.asp?contentid= 28000440
What Is a Transaction Broker? Good question. A transaction broker manages transactions, but this still doesn’t tell you much. Most current database systems, like Microsoft’s SQL Server, provide transaction
Configuring a Web Site for Web Publishing CHAPTER 23
681
management. A database server is told that a transaction is starting, and then one or more SQL statements are performed on one or more tables in the database. The database server does not commit (make permanent) these changes until it is sent a command to tell it that everything worked correctly. This helps maintain referential integrity through the database and consistency within the application. This ensures that SQL1 will not execute unless SQL2 and SQL3 execute also. If the transaction is not told to commit, it is rolled back, meaning all the SQL statements are cancelled and the database is restored to the state it was in before starting the transaction. MTS does for COM objects what database servers do for multiple SQL statements. With good development practice, you will break the application into the smallest possible chunks, then isolate that code so that it can be reused. For a truly scalable application, this code can be migrated to COM and then registered with MTS. Once properly registered, MTS knows which COM objects are a required part of a transaction and tracks the success or failure of each object. Note
In addition to brokering transactions, MTS controls object creation and destruction. This enables COM objects to be created only when needed, speeding application execution and preventing unnecessary overhead on the server. MTS also handles connection pooling into databases. This enables users to share connections instead of each one having a unique connection. Without connection pooling, 500 users would require 500 connections to a database. MTS allows you to use about 1/10 of those connections. If a connection is not in use, it is borrowed and used for another user, and then released back to the “pool” of available connections. The number of connections is only increased when all available connections are in use. This is a great advancement and has incredible performance gains for applications. There are a number of good books on the market that give more detail on the operation, configuration, and development considerations for Microsoft Transaction Server. Digging deep into this topic is beyond the scope of this book, but if you will be developing transaction-based applications or applications that need inherent scalability, you should definitely investigate MTS.
23 CONFIGURING A WEB SITE FOR WEB PUBLISHING
Access 2002 (and all previous versions) is limited in its ability to be supported by Microsoft Transaction Server. Although you can still develop towards MTS to gain speed, scalability, and connection pooling, you will have to use an advanced database platform, such as Microsoft’s SQL Server, to gain the full level of transactional support available.
682
Web Publishing with Access 2002 PART VIII
Now that you know what to install, how to install it, and what basic functions are provided, let’s discuss configuration of your Web services.
Managing and Configuring Your Web Server There are two different administration interfaces used to configure the Web services depending on whether you are running Windows 95/98 or Windows NT/2000 (Workstation or Server). You will need to be comfortable with the interface that relates to the environment and platform you are working with.
Personal Web Manager The interface that runs on Windows 95/98 is called the Personal Web Manager and has a very simple (if limited) interface to control your Web site. There are five main options from within the interface—Main, Publish, Web Site, Tour, and Advanced. You can get to any of these areas by choosing either the item from the left-hand navigation bar or from the View item on the menu bar. • The Main screen, shown in Figure 23.1, is where you go to start and stop your local Web services and to view some basic information about your site. FIGURE 23.1 The Main page of the Personal Web Manager is mostly used to start and stop your Web services.
Configuring a Web Site for Web Publishing CHAPTER 23
683
Note You can also start and stop the Web services by right-clicking on the PWS icon in your system tray (bottom-right corner next to your clock) and choosing the desired action.
In addition to starting and stopping the Web service, you can view the physical location of your Web root (your home directory), the Web location of your Web root (just above the start/stop button), and some basic traffic statistics about your site (Monitoring). • The next page is an interface to the Web Publishing Wizard. Microsoft’s documentation states that this wizard will “automatically place a copy of the file in the Webpub directory and add a descriptive link to your home page.” However, the wizard is temperamental, so it is usually better to modify the files yourself. Choosing Web Site gives you the option of creating a default home page or editing it if you have already created one. This is a very basic tool and probably best left alone if you are a serious Web developer.
• Most of the options that are required to perform your function as a Web developer are located in the Advanced area, as shown in Figure 23.2. From here, you will create your virtual directories, set default documents, and control folder properties. FIGURE 23.2 You will need to create and manage your virtual directories and Web settings on the Advanced page of the Personal Web Manager.
CONFIGURING A WEB SITE FOR WEB PUBLISHING
• The Tour provides some basic information—probably nothing you do not already know. If you are curious, you can step through the ten informational screens that are available.
23
684
Web Publishing with Access 2002 PART VIII
Note The term virtual directory has caused confusion for some Web developers. This term is clarified in the section “What Is the Difference Between a Site and a Virtual Directory?” later in this chapter.
Virtual directories will be covered later in the chapter, but it should be noted that there are three access levels allowed when you create a virtual directory: Read, Execute, and Scripts (see Figure 23.3). Always place a check next to Read; if you don’t, no one will be able to view your site. If you intend to view Active Server Pages within the virtual directory you are setting up, you need to check the box that allows scripts to run (ASP is server-side VBScript). Take care when enabling the Execute option. With this option enabled for a virtual directory, any executable file placed within can be executed, including script engines (such as Perl) or Windows binary files (such as .exe and .dll files). FIGURE 23.3 Access permissions for a new virtual directory within the Personal Web Manager.
The interface used to manage the Web services on a Windows NT or Windows 2000 system is called the Microsoft Management Console and is quite a bit more involved. If you develop on Windows 95/98 but have someone else who will manage the Windows NT/2000 production servers, you can skip the next section.
Microsoft Management Console The Microsoft Management Console (MMC), shown in Figure 23.4, is the main tool for controlling all services that are part of the Option Pack on Windows NT and is a very large and important part of Windows 2000 Server (Microsoft NT’s successor). It is a good idea to get comfortable with it now if you plan to use future versions of Microsoft Windows NT.
Configuring a Web Site for Web Publishing CHAPTER 23
685
FIGURE 23.4 The Microsoft Management Console (MMC) is the main interface for managing Windows NT’s Web services.
Internet Information Server Configuration The first snap-in you will see (and the only one I will discuss in this book) is the Internet Information Server control. If you expand this branch one level, you will see the netbios name of your local computer listed below. Expanding this one level further will reveal different options depending on the services you have installed. Usually you will see • Default FTP Site • Default Web Site • Administration Web Site • Default SMTP Site Covering each of these is beyond the scope of this book, but I will dig a bit into the Web site configurations because they will affect the operation and performance of your Web application.
23 CONFIGURING A WEB SITE FOR WEB PUBLISHING
When you open the MMC, you will notice that the left pane has two options and expanding branches under each. The right pane is the detail pane that will show specifics of the item chosen on the left. The very first item in the list on the left is Console Root—basically the MMC itself. Each item under that is called a snap-in and enables you to perform specific tasks—usually isolated to one service. Windows 2000 has many snap-ins available in the MMC, so get used to them now.
686
Web Publishing with Access 2002 PART VIII
Okay, let’s step through the different options and screens now. I will walk you through some changes to explain and demonstrate the effect. First, choose the name of your local server and right-click. As with many Windows application, choosing an item and right-clicking will display a context menu of frequently accessed options (see Figure 23.5). Then choose Properties from the option list. This presents the default settings for the entire server. You could get these same options by choosing properties of an individual site, but then you would have to make these changes for each site. FIGURE 23.5 Right-clicking an item gives a quick menu list of actions that can be performed.
The first thing you should change is the location of the log files. Option Pack defaults to placing these files at C:\winnt\system32\logfiles\. This looks okay at first, but let’s think about it… The log files can grow very large on an active site. Do you really want the potential of choking required disk space from your operating system? I didn’t think so. At the bottom of the Web Site tab, choose the logging properties. Then on the general tab, change the default location of the Web log files. This can be a folder that already exists on your system. I like d:\logfiles—it’s off the system partition and easy to find. If you choose the Extended Properties tab, you can change exactly what information is stored in the log files when a client requests a page.
Configuring a Web Site for Web Publishing CHAPTER 23
687
Note Do not simply choose everything. Each item you record causes additional disk activity and system overhead. For best performance, choose only the items you really want and need.
Click OK a few times to get back to the main WWW Properties box. Now look at the Documents tab. This is where you can specify one or more default documents (see Figure 23.6). This is the information that IIS uses to tell which file to load first. If someone types www.YourSiteName.com without specifying a file, IIS starts at the top of this list and looks for each file until one is found, or until the end of the list is reached. If the file is not present on the site, or you have not specified at least one default document, the user will receive an error. Because my company, ORCS Web, Inc hosts many clients, I always add index.html to each Web server—that was the UNIX standard and many people are familiar with it.
Sometimes you will want to change the name of the default document or add more default documents to the list for a particular site.
Click back to the WWW Properties again and choose the Directory Security tab. The Microsoft Management Console allows you to manage additional security settings that work in conjunction with Windows NT File System (see Figure 23.7). There are three sections to the directory security that IIS controls. • Anonymous Access and Authentication Control • Secure Communications • IP Address and Domain Name Restriction
23 CONFIGURING A WEB SITE FOR WEB PUBLISHING
FIGURE 23.6
688
Web Publishing with Access 2002 PART VIII
First, choose Anonymous Access and Authentication Control. Here again you have three choices. • Allow Anonymous Access • Basic Authentication (Password is sent in clear text) • Windows NT Challenge/Response FIGURE 23.7 Security is almost always a concern on any system that is connected to the Internet.
If you want to allow anonymous access to your site, be sure that the first box (Allow Anonymous Access) is checked. This option tells IIS to first check permissions on that file and determine if the IUSER account (explained later in the section titled “What Is IUSER?”) has access whenever someone requests a page or file. If so, assume the client is IUSER and process the request. If this option is not checked or the IUSER account does not have access to the file in question, a process is initiated based on the other two settings on this page. If Challenge and Response is enabled and the client is using Internet Explorer, the username/password combination that was used to log on to the client will be passed to the Web server and authentication will be attempted again. Using Challenge and Response, the transmission of the username/password combination is encrypted and totally secure. If that username/password combination can be matched to an account on the server (either local or domain accounts will work), the user is allowed or denied based on that user’s access. If the account cannot be matched, access is denied. If the client is not using Internet Explorer as the browser, authentication can only be done through clear text authentication.
Configuring a Web Site for Web Publishing CHAPTER 23
689
To validate a user on the Web server when the client is not using Internet Explorer, IIS must be set to allow clear text authentication. This method is not secure, but is necessary if you must protect areas on your site and cannot know for sure what browser the client will be running. Usually, enabling both of these options is the best choice. First, IIS will try to authenticate as IUSER (if enabled), and then it will try Challenge and Response. If still unsuccessful, it will attempt basic authentication. Choosing the Secure Communications option on this screen will open the Key Manager if you have not already installed a secure key. When, and if, you decide that you want to implement Secure Socket Layer (SSL), this is the place to start. From here, you generate the necessary key request to be sent to a Certificate Authority such as VeriSign. After it processes the request, you will need to enter the certificate into the Key Manager to complete the process. After you have installed your SSL key, this button changes from Key Manager to Edit. Choosing Edit will enable you to require SSL to access this resource. URLs will need to be formatted https:// instead of http:// to view pages at this location (site, folder, virtual directory, and so on).
E-commerce is becoming more and more popular. To safely transmit sensitive data (such as credit card numbers) over the Internet, you must implement SSL.
You can also limit access to your site based on the client’s IP address. This is not very convenient unless your server is going to be used for intranet use only. Most client IP addresses on the Internet change every time they log on because of dynamic allocation from their ISPs. If you decide to restrict based on IP address, you can specify that all clients are granted access except a specified list, or just the opposite—all clients are denied access except the ones specified. Click back to the main Web Site Properties page and let’s move to the Home Directory tab shown in Figure 23.8. This is where you specify your application settings.
CONFIGURING A WEB SITE FOR WEB PUBLISHING
Note
23
690
Web Publishing with Access 2002 PART VIII
FIGURE 23.8 The Home Directory tab enables you to manage the lowest level of settings for the Web site— such as the physical path on the server.
The “local path” is the physical location of the Web site that you are viewing properties for. Access Permissions enable you to specify whether this site allows read, write, both, or no access. I have not run into a situation yet where write access was required, so leave Read checked. Content Control has four options: • Log Access—If this is checked and you also enabled logging on the Web Site tab, all HTTP requests will generate an entry in the Web server log files. • Directory Browsing Allowed—If this is checked and no default home page is present (or specified on the Documents tab), a list of the files in that folder will be displayed. • Index This Directory—Tell Index Server to monitor and index the files at this location for possible search. • FrontPage Web—Exactly as it sounds. Specifies that this location is a FrontPage (or Visual InterDev) Web site and that the FrontPage Server Extensions are supported. The bottom third of this dialog box deals with Application Settings. By application, Microsoft means an independent set of Active Server Pages that work together to perform some function. (That’s my definition—not Microsoft’s.) On this part of the screen, be sure that the script permissions are selected. This enables Active Server Pages to execute on the server and send their results back to the browser. I have seen quite a few people select execute permissions (because that was the old setting
Configuring a Web Site for Web Publishing CHAPTER 23
691
in IIS3), but this is a no-no. Allowing execute is necessary on folders that contain CGI or Perl, but should not be selected otherwise. Placing an executable in a folder marked as execute will potentially enable that program to run on the server and cause undesirable effects. Click the Configuration button and let’s look at some of the details that are specific to the performance and functionality of your ASP pages. Under configuration, there are three tabs: App Mappings, App Options, and App Debugging. App Mappings is where the file types are specified (by extension) and the executable that should be used to process the file types is listed. At a minimum, you should leave .asa and .asp specified in this list and related to the ASP.DLL. You will also notice quite a few other mappings listed—if you are sure that you are not using any files of this type, you can remove these mappings and improve the performance of your server.
FIGURE 23.9 Application settings will become very important to your site as you get deeper and deeper into Microsoft Web development.
On this page, you can also specify whether to enable buffering (yes, this also improves performance), enable parent paths (allows ASP to reference relative paths to the parent directory), and specify your default language (usually VBScript, but can be JScript if you desire). You can also specify the script timeout. Increasing this enables longer scripts to
23 CONFIGURING A WEB SITE FOR WEB PUBLISHING
You can specify whether you want to allow your application to maintain session state and how long each session should last on the App Options tab shown in Figure 23.9. Be careful when modifying any of the settings located on this tab because they can have drastic effects on the operation and performance on the Web site. Most developers take advantage of session state—it’s one of the great things about ASP. However, if you can work around it, you will see great improvements in performance by turning off session state.
692
Web Publishing with Access 2002 PART VIII
run without error, but this can also tie up your server resources unnecessarily. It is preferable to leave this at 60 seconds and specify a longer script timeout in the individual ASP page if required (). The final tab, App Debugging, is where you specify whether you want to allow debugging on the server side, client side, both, or neither. You can also specify a generic error message to send to clients if your ASP page runs afoul. This is a nice feature because it looks cleaner to the client if you do encounter an error. However, don’t do this while debugging, or you won’t know what went wrong.
Securing Your Web Applications One of the problems that users face after installation of IIS or PWS is security—especially if you are developing an e-commerce application or an application that contains sensitive data. There are some basic steps that can be taken, such as the ones noted later in the “ASP/HTML—Placement and Permissions” and the “Databases—Placement and Permissions” sections. These security settings are required just to allow the basic functionality required by a Web application, but there are some additional measures that can be taken to protect sensitive data and prevent unauthorized people from viewing information on your site. Note Many companies implement a firewall between their servers and the rest of the Internet. A firewall allows the company to place certain restrictions on who can access the server and how it can be accessed. Quite often, firewalls are told to exclude external access to the server from any port other than port 80 (the default HTTP port). This allows HTTP requests to come through and view the Web application, but it restricts a user from gaining access on any unauthorized ports.
The options that I just discussed and the changes they create were all site-wide. In addition to changing options for an entire site, you can also set specific configurations on a virtual directory.
Configuring a Web Site for Web Publishing CHAPTER 23
693
What Is the Difference Between a Site and a Virtual Directory? A site is a distinct entity on a Web server signified by a unique domain name. Internet Information Server on Windows NT/2000 Servers are very good at handling multiple sites on a single server. Only hardware resources limit the number of sites you can run on a single NT Server. A virtual directory is always contained within a site and is restricted to that site and that site only. A virtual directory can have its own Web settings that are different from the main Web sites, allowing a great level of control over the content on your site. For example, if you are planning on running CGI on your site (not recommended for performance reasons, but certainly possible), you will want to place all your CGI files into a single virtual directory and mark that directory as allowing execute permissions. Note
Probably the most exciting thing about a virtual directory is that it can be physically located anywhere on the server. It can even be located on a share drive mapped to another server (this requires special security configurations that are beyond the scope of this book). So if your site is www.YourSite.com, and you map a virtual directory called “user1” to d:\user1\, this will be accessible as http://www.yoursite.com/user1/, even though the folder is in a physically isolated area on the server. Note Although a virtual directory can be placed anywhere, it can also be located under the physical path for the Web root. The directory structures for your Web application should be thought out in advance. Having many virtual folders that are mapped to obscure areas of the server can cause a maintenance nightmare.
23 CONFIGURING A WEB SITE FOR WEB PUBLISHING
Microsoft FrontPage and Visual InterDev are very particular about the way their virtual directories are configured. If you are using either of these tools in your development, it is best to allow that product to create the virtual directories for you. Also, after the program has created the virtual directory, do not manually change any of the settings. Many developers have “broken” their sites by modifying either Web settings or file permissions on a FrontPage or Visual InterDev Web site.
694
Web Publishing with Access 2002 PART VIII
One of the most useful benefits of using virtual directories is control access to Web content. Let’s say you have your Web root located at d:\wwwroot\, in which you control the content. You also have two co-workers who want their own Web areas where only they control the content. Regardless of whether they map a shared folder or use FTP, it is very hard to keep them from accessing the Web root’s files if you simply create folders at d:\wwwroot\user1\ and d:\wwwroot\user2\. One way of tightening security and restricting users’ access to their own content only, is to create folders at d:\users\user1\ and d:\users\user2\, and then map virtual directories to these areas. The second most exciting thing about virtual directories is only available on Windows NT/2000: the ability to specify an “application” starting point. This allows isolation of session/application variables and total isolation between Web applications—even if they exist on the same server. Although you can create Web applications on Windows 95/98, there is no real way to create multiple applications and take advantage of the application isolation such as you can on Windows NT/2000. On a Windows 95/98 Web server, all the content running under PWS is considered to be the same Web application. Session-level variables created in a virtual directory on Windows 95/98 are available from all other virtual directories on that site. If you plan to host multiple Web applications on the same server, this is yet another reason to use Windows NT. When viewing the properties of a virtual folder on Windows NT, you can set specific settings different from those of the main site. All the options that were mentioned earlier in the chapter for site-level control are also available for virtual directory-level control. This provides more flexibility and control over individual Web applications, and allows you to make specific settings that might be needed on one application but not on another. On the Home Directory tab, there is an option to create an application out of the virtual directory whose properties you are viewing. After you specify this folder as an application, it will operate independently of the rest of the Web site. When I say “independently,” I mean that this newly created application can have its own global.asa file and unique configurations. Also, any session or application-level variables that are defined are available only from within that application. Trying to access these variables from outside the current application—even from a folder on the same server—will be unsuccessful. This allows for isolation and an increased level of security. A wealth of information about IIS5 can be found here: http://windows.microsoft. com/windows2000/en/server/iis/. This information can be found at the above URL.
Configuring a Web Site for Web Publishing CHAPTER 23
695
One new feature of IIS5 is the ”BROWSER CAPABILITIES COMPONENT”. ASP has a new feature for determining the exact capabilities of a browser. When a browser sends a cookie describing its capabilities (such a cookie can be installed by using a simple client-side script), you as a developer can create an instance of the Browser Capabilities Component that retrieves the browser’s properties as returned by the cookie. Developers can use this feature to discover a browser’s capabilities and adjust an application accordingly. Another very useful feature is MMC TASK PAD. The MMC task pad simplifies the administration of an IIS5 server. For example, if a user selects a server under the IIS5 MMC snap-in, the task pad will display wizards for creating new Web and FTP sites. Administrators simply select the task they want to complete, and a wizard walks them through the steps. Another is “MULTIPLE USER DOMAINS”. The integration between the Web servers and directory services (the Active Directory) in Windows 2000 Server lets organizations host multiple Web sites with independent user domains—that is, each Web site on a single server has its own user database. Note
If you want even further isolation, you can specify that the application should run in its own memory space. Setting this feature requires context switching by the Web server and might have a negative impact on server performance. The benefit is that all processes of this specific application are isolated, and a fault or failure of this one application will not affect other applications running on the same server. Now that you have your Web service installed and you know how to configure it, are you done? Not quite yet. There are a few other items that you should be aware of and take into consideration for Web application development and deployment. Although you set some basic options when you set up your application, you also need to consider the environment outside of the Web settings. One of the more important considerations is security, and how to properly set up your application to allow access to your files and data sources.
What Is IUSER? During the installation of IIS/PWS on Windows NT, a new user account is created and named IUSER_<machine_name> where <machine_name> is the netbios name associated with the server (in network properties).
23 CONFIGURING A WEB SITE FOR WEB PUBLISHING
Global.asa files should always reside at an application’s starting point. If you have declared a virtual directory to be an application, the global.asa file should be at the same physical path that you defined as that virtual directory’s starting folder.
696
Web Publishing with Access 2002 PART VIII
When someone accesses a Web page on an IIS server and the server is set to allow anonymous access, IIS first checks the NTFS permissions of the file to see if the IUSER account has access. If IUSER has the appropriate rights to view the page, it is returned to the browser. If the IUSER account does not have rights assigned to this file, or if IIS is set to not allow anonymous access, IIS will try to authenticate the user based on the settings within the MMC.
File System Type Although IIS has some options buried within it to allow for a small level of security for a site, it is insufficient for most needs without the complement of NT Security. I strongly recommend using Windows NT File System (NTFS) on the partition where your Web files and database will reside. This allows you to implement security at a file and folder level and is really the only safe way to configure a production Web server. Note NTFS is only available on Windows NT or Windows 2000 systems. If you are developing on a Windows 95/98 workstation, you will not be able to maintain as tight of a security model as you would on NT. This is one more good reason to deploy your application to Windows NT4 or Windows 2000 in production.
Directory Structures and Required Permissions A fairly standard configuration for a Web server is to have two disk partitions—one small partition (perhaps 1GB) for the operating system and another for all Web-related files and log files. What file system type is used for the OS partition is a matter of preference, but almost always the Web partition is converted to NTFS for security reasons. During installation of IIS, the path to the Web root was specified. Assuming the root was placed on the second partition, the default path would be D:\Inetpub\wwwroot\. This is the area that IIS will route all Web requests, unless the configuration is modified to send them elsewhere. If you were to look at the properties of this folder within the MMC, you would notice that the IP allocation is set to “all unassigned,” so even if you have multiple IP addresses on the server, all requests will be directed to this area. For sake of simplicity, I will assume there is only one site on your server and that the Web path is D:\Inetpub\wwwroot.
Configuring a Web Site for Web Publishing CHAPTER 23
697
ASP/HTML—Placement and Permissions I’ll assume you have a general knowledge of NT permissions for this section and quickly run through the requirements for IIS. To view an HTML document, the IUSER account (or related NT account if using security) must have at least READ permissions to the file. In order to view an Active Server Pages document, the user must have at least EXECUTE permissions to the file. If you assigned a user general READ permissions (see Figure 23.10), the related NT permissions assigned are READ/EXECUTE (RX). When I say READ and EXECUTE, I am referring to the lowest level of NT permissions—found under “special file permissions” (or “special folder permissions”) as shown in Figure 23.11. Although this will allow access to both HTML and ASP pages, it is not totally necessary or the most secure solution (if security is a concern). FIGURE 23.10
Databases—Placement and Permissions If you are using Active Server Pages to connect to an Access database, the database does not need to be under the Web root. Because the path is specified in the connection string, you can place it anywhere. For security reasons, it is recommended you place the database in a directory structure that is totally isolated from the Web files. D:\databases\ would work just fine. This prevents a mischievous client from typing http://www. yoursite.com/database/database.mdb and being able to download the database file. Permissions can also be set for the database directory such as read only.
23 CONFIGURING A WEB SITE FOR WEB PUBLISHING
NTFS on Windows NT or Windows 2000 have some default permission settings that can satisfy the security needs in most situations.
698
Web Publishing with Access 2002 PART VIII
FIGURE 23.11 If you need even greater control over file security permissions, you can get very specific with the special file permissions.
Security on an Access database can be tricky without knowing some of the “quirks.” There are two ways to implement security on an Access database—either through Access’s built-in security measures, or through the use of NTFS file permissions. If you have a read-only application that will never insert information into your database, things are straightforward. Simply assign the IUSER account READ access to the database file. If you currently have nothing but read-only data in your application, you probably won’t for long. Sure, one of the great benefits of Web applications is to display dynamic information to your users, but that’s only half the game. There is a lot of value added when you start storing information in the database. You can collect user feedback, mailing-list information, and online orders—anything you want. This allows the user to interact more with the site, and helps you provide better service to the client. Let’s say you have a database you will use to store online orders in. Okay, you assign permissions to the database file and give it a try—but it won’t work. When an Access database is in use by a user who has write permissions, it creates a lock file. Because the IUSER account has READ/WRITE permissions, Access attempts to create this file but gets an access-denied error. To prevent this error, the entire folder where the database is stored must have CHANGE access for the IUSER account. CHANGE access is necessary because the lock file must be updated as SQL commands are being executed. Just ADD and READ permissions will not suffice.
READ/WRITE
This really gets ugly if you didn’t take my earlier advice and move your database to its own folder. With the database in your Web root, you will need to give the IUSER
Configuring a Web Site for Web Publishing CHAPTER 23
699
account the ability to READ/WRITE/DELETE files in that folder—BAD IDEA! In the early stages of experimenting with Access databases, Web applications, and security requirements, one of the other authors and I did just this. It didn’t take long for a hacker to realize that the site was wide open and make changes to our files for us—lesson learned. If you are using SQL Server, all the security is handled from within the database server itself so you can avoid the potential nightmare of working out your file-level security requirements.
Summary Developing applications for the Web is very exciting. Many developers and their clients alike are just now realizing the great benefits that can be reaped by Web-enabling their applications. With the information just presented to you, you should be able to install, configure, and perform some basic maintenance on a Microsoft Web server. Combining this knowledge with the other information provided in this book, you should be well on your way to either developing Office XP Web applications or converting existing Office XP applications to a Web format.
23 CONFIGURING A WEB SITE FOR WEB PUBLISHING
CHAPTER 24
Web Enabling Access 2002 with Office Web Components IN THIS CHAPTER • What Are the Office XP Web Components? 702 • Using the Office Spreadsheet Control 703 • Using the Office Chart Control • Using the Office PivotTable Control 711
707
702
Web Publishing with Access 2002 PART VIII
As you know, there is no ignoring the Internet. With Office XP, Microsoft wanted to find a simple way to move as much core Office functionality to the Internet as possible. The result was the Microsoft Office XP Web components and Data Access Pages (see Chapter 25, “Using Data Access Pages”). This chapter introduces you to the Office Web components.
What Are the Office XP Web Components? The Office Web components are a group of COM-based ActiveX Controls that provide some of the same basic spreadsheet, chart, and pivot table functionality that you would find in Office proper to an ActiveX-based Web browser. The Office XP Web components are composed of • Office Spreadsheet Control • Office Chart Control • Office PivotTable Control These three graphical controls (Spreadsheet, Chart, and PivotTable) and the DataSource control are based on COM, so they can be controlled from VBScript, JScript, Visual Basic, C++, Java, and of course, VBA. Just because these controls are named Web components, do not think that they are only available on Web pages. They will also play a great role in your Access applications. Figure 24.1 shows a simple Spreadsheet control on an Access form. FIGURE 24.1 An Office Web component inside an Access form.
What Do the Office Web Components Do? Each Web component has a distinct functionality that you will want to incorporate into your applications. The Spreadsheet control is like a mini-Excel in a Web browser or form. Most basic Excel functionality is provided, such as recalculation, filtering, and
Web-Enabling Access 2002 with Office Web Components CHAPTER 24
703
sorting. You can also use the Spreadsheet control as an invisible calculation engine in your Access, VB, or Web applications. The Chart control provides 2D charting and has the capability to be bound to other HTML elements like a Spreadsheet control or recordsets. Chart can also export GIFs for Web browsers that do not support ActiveX Controls. The PivotTable control provides the pivot table functionality found in Excel via an ActiveX control. PivotTable can also export GIFs like the Chart control. Finally, there is the nongraphical DataSource control. DataSource works like the Data control in Visual Basic by connecting to a database and enabling another control to bind to it. Each of these controls is discussed more in depth in the next chapter.
What Are the License Requirements for Using Web Components? Unlike most ActiveX controls, Web components require the purchase of an Office XP user license for the destination machine in order to use them. Your users do not need to have Office XP installed, but they do need to have a license for it. This way, if you are in a large organization and want to deploy some of the Web components (and Data Access Pages shown in the next chapter), you can get a site license and begin deployment of the Web components before you deploy Office XP. That being said, it is obvious that Microsoft intends Web components to be used as intranet tools.
Using the Office Spreadsheet Control
Getting Started What makes the Spreadsheet control so easy to use is that you can transfer your Excel spreadsheets to a Web page automatically. In Excel 2002, create a simple spreadsheet. Then click File, Save As Web Page from the main menu. A dialog box will be presented to you, as shown in Figure 24.2. This dialog box lets you select a particular range or an entire sheet to export. When you want to save data to an interactive Web page, you must define your data as a range in Microsoft Excel. To export a selected range to an Excel Spreadsheet control on your Web Page, choose the Add Interactivity check box as shown in Figure 24.2.
24 WEB-ENABLING ACCESS 2002 WITH OFFICE WEB COMPONENTS
Using the Spreadsheet control is a lot like using Microsoft Excel. There are two ways to use the Spreadsheet control, in either bound or unbound form. Bound involves using the DataSource control, which you will learn about in the next chapter. Let’s take a look at the control in an unbound state here.
704
Web Publishing with Access 2002 PART VIII
FIGURE 24.2 The Save As Web Page dialog box.
After you save your spreadsheet as a Web page via Excel, you can open the Web page in Internet Explorer, as show in Figure 24.3. FIGURE 24.3 The result of saving the Excel 2002 spreadsheet as a Web page.
When you have the Web page open, you can right-click the control and then bring up the Spreadsheet Property Toolbox dialog box, as shown in Figure 24.4. This dialog box, created with Dynamic HTML, will allow you to format and control the layout of your control. (For more information on using the control on the Web, see the information on Data Access Pages later in the next chapter.)
Web-Enabling Access 2002 with Office Web Components CHAPTER 24
705
FIGURE 24.4 The Spreadsheet Property Toolbox dialog box.
Using the Control in Access To use the Spreadsheet Web component on an Access form, choose Tools, ActiveX Controls from the main menu. This will bring up the available ActiveX Controls dialog box as shown in Figure 24.5. Scroll down to Microsoft Office Spreadsheet 10.0 and click Register. To insert the ActiveX Control onto your form in Design mode, use the more controls icon on the Toolbox toolbar and then choose Microsoft Office Spreadsheet 10.0 from the list. The result is shown in Figure 24.6. Once inserted, rename the control to SpreadSheet2 in the Property dialog box.
The ActiveX Control dialog box.
WEB-ENABLING ACCESS 2002 WITH OFFICE WEB COMPONENTS
FIGURE 24.5
24
706
Web Publishing with Access 2002 PART VIII
FIGURE 24.6 The Spreadsheet control at design time in an Access 2000 form.
Now that the control is on your form, write some code to get data inside the Spreadsheet control when the form loads. Listing 26.1 works on a form’s load event and opens an ADO recordset based on the “Ten Most Expensive Products” query in the Northwind database. Now iterate through the recordset. At each row in the recordset, you use the ActiveCell’s default property to insert the values of the data in the recordset into the cell of the Spreadsheet control. As you move through the recordset, you must increment the ActiveCell property of the spreadsheet to move the data to the next line of the spreadsheet. As you can see, this is much like programming Microsoft Excel. LISTING 24.1
Loading Data into the Spreadsheet Control at Runtime
Private Sub Form_Load() ‘Loads the Spreadsheet ActiveX Control ‘With the data from a query in Access ‘From “Microsoft Access 2002 Development Unleashed” (SAMS)
Dim rst As ADODB.Recordset Dim intCount As Integer On Error GoTo Proc_Err Set rst = New ADODB.Recordset rst.Open “[Ten Most Expensive Products]”, CurrentProject.Connection
Web-Enabling Access 2002 with Office Web Components CHAPTER 24 LISTING 24.1
707
continued
‘Hardcode the captions Spreadsheet2.ActiveCell(1, 1) = “Product” Spreadsheet2.ActiveCell(1, 2) = “Price” ‘Start the counter for the loop intCount = 2 Do Until rst.EOF ‘Fill in the current cell with the ‘the current record from the recordset Spreadsheet2.ActiveCell(intCount, 1) = “” & _ rst!TenMostExpensiveProducts Spreadsheet2.ActiveCell(intCount, 2) = “” & _ rst!UnitPrice ‘Move to the next record and increase the counter rst.MoveNext intCount = intCount + 1 Loop rst.Close Set rst = Nothing Proc_Exit: Exit Sub Proc_Err: MsgBox Err.Description Resume Proc_Exit End Sub
You might think you’ve wasted too much time playing with Microsoft Graph, in the past and are not willing to use another Graph control from Microsoft. Chart is easy to use and very powerful. Like the other controls, it can be used in both bound and unbound mode. I will look at using the control in unbound mode here and in bound mode in the next chapter. In addition, in Chapter 26, “Web Publishing with Access 2002 and Active Server Pages,” I will look at how you can use the component to export GIFs for use in Active Server Pages.
WEB-ENABLING ACCESS 2002 WITH OFFICE WEB COMPONENTS
Using the Office Chart Control
24
708
Web Publishing with Access 2002 PART VIII
Getting Started As with the other controls, start by creating a simple chart in Microsoft Excel 2002, as shown in Figure 24.7. Choose File, Save As Web Page, and then click the “Selection: Chart” option. Check the Add Interactivity box. The result is shown in Figure 24.8. You can now manipulate the chart via client-side scripting code such as VBScript or JScript. In order to use the control in your scripts, you will have to know the properties and methods that it exposes, which you will learn about in the next section. FIGURE 24.7 A chart in Microsoft Excel 2002.
Using the Control in Access Insert the control via the Insert, ActiveX Control method on an Access form. You must choose “Microsoft Office Chart Control 10.0” from the list of ActiveX controls. After you have created the control, rename it Chart1 inside the property box. When using the Chart control in unbound mode, there are two important things to remember. First, the Chart control can contain many objects, so each chart that you create will be part of a Charts collection. Second, you can fill arrays of values for the chart series and an array of values for the chart’s series’ data.
Web-Enabling Access 2002 with Office Web Components CHAPTER 24
709
FIGURE 24.8 A Chart control exported as a Web page.
To demonstrate this, I created a simple crosstab query in the Access 2000 format version of Northwind that comes with Access 2002, with the following SQL:
The SQL in the previous example will break out the total sales by quarter and produce an output as shown in Figure 24.9. FIGURE 24.9 A simple crosstab query in Access 2002.
24 WEB-ENABLING ACCESS 2002 WITH OFFICE WEB COMPONENTS
TRANSFORM Sum(CCur([Order Details].[UnitPrice]* [Quantity]*(1-[Discount])/100)*100) AS ProductAmount SELECT Year([OrderDate]) AS OrderYear FROM Products INNER JOIN (Orders INNER JOIN [Order Details] ON Orders.OrderID = [Order Details].OrderID) ON Products.ProductID = [Order Details].ProductID WHERE (((Orders.OrderDate) Between #1/1/1997# And #12/31/1997#)) GROUP BY Year([OrderDate]) PIVOT “Qtr “ & DatePart(“q”,[OrderDate],1,0) In (“Qtr 1”,”Qtr 2”,”Qtr 3”,”Qtr 4”);
710
Web Publishing with Access 2002 PART VIII
You will now use the simple crosstab query created previously to create an unbound chart on your form’s Open event. I previously created two array aHeaders in which I fill in the values of my series headers (in this case the four quarters of a fiscal year). Then you will open up an ADO recordset based on the saved crosstab query called qryQuarterlySales, and then fill the aValues array with the values of the fields of the recordset. The full code listing is shown in Listing 24.2. LISTING 24.2
Creating an Unbound Chart
Private Sub Form_Open(Cancel As Integer) ‘Loads the Chart ActiveX Control ‘With the data from a query in Access ‘From “Microsoft Access 2000 Development Unleashed” (SAMS) ‘By: Forte, Howe, Ralston Dim ChartSpace1, aHeaders(3), aValues(3) Dim rst As ADODB.Recordset On Error GoTo Proc_Err Set rst = New ADODB.Recordset ‘Create an array of items for the chart aHeaders(0) = “Q1” aHeaders(1) = “Q2” aHeaders(2) = “Q3” aHeaders(3) = “Q4” ‘Open a recordset based on a XTAB query ‘and fill in the values for the chart bars rst.Open “qryQuarterlySales”, CurrentProject.Connection aValues(0) aValues(1) aValues(2) aValues(3)
= = = =
rst![Qtr rst![Qtr rst![Qtr rst![Qtr
1] 2] 3] 4]
Set ChartSpace1 = Me.Chart1 With ChartSpace1 .Border.Color = chColorAutomatic ‘Can have more than one chart in a ‘chartspace, so you have to refer to ‘the collection .Charts.Add With .Charts(0) .Type = chChartTypeColumnClustered
Web-Enabling Access 2002 with Office Web Components CHAPTER 24 LISTING 24.2
711
continued
.SeriesCollection.Add .SeriesCollection(0).Caption = “1997 Sales” ‘This sets the data to the arrays we ‘created above .SeriesCollection(0).SetData _ chDimCategories, chDataLiteral, aHeaders .SeriesCollection(0).SetData _ chDimValues, chDataLiteral, aValues .HasLegend = True End With End With rst.Close Set rst = Nothing Proc_Exit: Exit Sub Proc_Err: MsgBox Err.Description Resume Proc_Exit End Sub
Using the Office PivotTable Control
Getting Started Pivot tables are one of the most popular features of Microsoft Excel; they give you the ability to run ad hoc reports while in Excel. Many end users are big fans of pivot tables after they are trained to use them. The ability to create pivot tables and incorporate them as either a part of a Web site or Access form is pretty cool. To start, create a pivot table in Microsoft Excel 2002 as shown in Figure 24.10.
24 WEB-ENABLING ACCESS 2002 WITH OFFICE WEB COMPONENTS
As with the other two controls that I have explored in this chapter, the PivotTable control is available in both bound and unbound modes. The next two sections will explore how to use the controls in unbound mode, whereas in the next chapter, you will see how to bind the control to data in a Data Access Page.
712
Web Publishing with Access 2002 PART VIII
FIGURE 24.10 A simple Excel 2002 pivot table.
After your pivot table is created, select File, Save As Web Page from the main menu; however, do not click Save on the dialog box, click Publish. This brings up the advanced Save As Web Page dialog box as shown in Figure 24.11. FIGURE 24.11 The Publish as Web page dialog box.
This dialog box, shown in Figure 24.11, gives you the opportunity to select the items of the pivot table to export and set the viewing options. By setting the viewing options, you can select the Pivot Table option for the output, as shown in Figure 24.11. Excel will produce a Web page, as shown in Figure 24.12.
Web-Enabling Access 2002 with Office Web Components CHAPTER 24
713
FIGURE 24.12 Excel pivot table exported to Internet Explorer.
Summary The Office XP Web components might represent the future of Office computing. As users hear more and more about computers selling for less than $1000 in the future and the infamous NetPC, Office Web components offer a nice neat package for Office functionality in a Web browser. As business moves to cheaper alternatives to PCs, the Office Web components might play a major role in the evolution of Office and business computing in general. With the ability to use native functions of a Spreadsheet PivotTable and Charting in an ActiveX Control both in Access 2002 and on a Web page, this chapter gave you a great introduction to the Web components. Several new events have been added to the Access 10.0 Object Library, giving you more control over PivotTable and PivotChart views. You can find details and examples at http://msdn.microsoft.com/office/xp/access.asp.
24 WEB-ENABLING ACCESS 2002 WITH OFFICE WEB COMPONENTS
CHAPTER 25
Using Data Access Pages
IN THIS CHAPTER • Creating Your First Data Access Page 717 • Implementing Interactive Drill-Downs 722 • Incorporating the Office Web Components with DAPs 724 • Scripting Data Access Pages
726
716
Web Publishing with Access 2002 PART VIII
As you know, there is no ignoring the Internet. With Office XP, Microsoft wanted to find a simple way to move as much core Office functionality to the Internet as possible, starting with Office 2000. The result was Data Access Pages and now the Microsoft Office XP Web components. This chapter introduces you to Data Access Pages and gets you started using them. Chances are that you have explored with the Internet or your corporate intranet already. Now that you have shown your users some basic Web pages, they ask you if they can have drill-downs, charts and graphs, and more dynamic pages. You reply that you are a database programmer, not a Web programmer, and that is too difficult to do quickly. Microsoft realized your users would want more power and online reporting and created something called Data Access Pages in Access 2000 and included them with Access 2002.
What Is a Data Access Page? You might have noticed the database container tab called Pages. This tab is for the database object called Data Access Pages (DAPs), as shown in Figure 25.1. DAPs are subsets of Access forms and reports designed for availability on the Web, but more appropriately, the intranet. These pages are Dynamic HTML pages that can be viewed in Internet Explorer 5.0. They connect to a live database that provides the user with the ability to do form data entry or drill down into a report. FIGURE 25.1 The database container tab for Data Access Pages.
Data Access Pages Architecture and Requirements Data Access Pages use ActiveX Controls, Dynamic HTML, ADO, and client-side scripting. With client-side scripting, you need ADO and the OLEDB providers for Access and SQL Server installed on your machine. Only Internet Explorer 4.0 or 5.0 or higher
Using Data Access Pages CHAPTER 25
717
supports ActiveX Controls, so you are limited to Microsoft’s browser. In addition, Microsoft has made an Office XP license required for use of Data Access Pages. An Office XP installation is the only way to guarantee that the Web components, ADO 2.1, and the proper drivers and OLE DB Providers are installed on your users’ hard drive.
Creating Your First Data Access Page Let’s get started creating your first Data Access Page. Open the Northwind database and Click on New while in the database container for DAPs. Click Design view, or select Create data access page in Design view from the list in the Pages container. You will be brought to a blank DAP in Design view, as shown in Figure 25.2. FIGURE 25.2 A blank Data Access Page in Design view.
25 USING DATA ACCESS PAGES
Give your page a title by clicking the top part of the page and typing “Categories.” Bring up the field list by selecting View, Field List from the main menu. The field list shown in Figure 25.3 has a listing of all the tables and queries in your current database. This works much like the field list in a typical Access form and report.
718
Web Publishing with Access 2002 PART VIII
After you have brought up the field list, drill down to the category table and then drag the CategoryName and Description fields to the page. FIGURE 25.3 The Data Access Page field list.
Now that you have added the two fields to the page, click the Save button on the toolbar or choose File, Save from the main menu. Notice that the Save as Data Access Page dialog comes up, as shown in Figure 25.4. A Data Access Page is not stored in the MDB file like all other objects such as tables, queries, forms, reports, macros, and modules. You can quickly find out the path of the DAP by hovering your mouse pointer over a DAP in the database container and viewing the Tool Tip, as shown in Figure 25.5. FIGURE 25.4 Save as Data Access Page.
Using Data Access Pages CHAPTER 25
719
FIGURE 25.5 A ToolTip showing the path of the Data Access Page.
Note It is very important to understand that the DAPs are not stored in the MDB file. This gives you many disadvantages in distributing your application; you now have to distribute an .MDB file and all the Data Access Pages that you created. You have to keep the Data Access Pages in the database container in sync with the ones that you have on disk. You can also use this limitation to your advantage if you deploy the Data Access Pages on a Web server. As long as your users have access to the Web server via the intranet or Internet, and the DAPs are on it, just point the DAPs in the MDB or ADP file to the DAPs on the Web server.
Before I preview the Data Access Page, I’ll take a look at the Sorting and Grouping options. Right click on the group header and choose Group Level Properties. This will bring up the Grouping and Sorting dialog box as shown in Figure 25.6. Most of these options are identical to those in reports; however, there are two new ones that you need to look at. The first is Data Page Size, which will control how many records will be displayed at a time. Set this to 1 so you can view one record at a time. You can experiment with this property to view more records at a time, as I show you later on in this chapter. The next property you will want to look at is the Record Navigation Section property, which will give your section a record navigation section.
25 USING DATA ACCESS PAGES
After you have set these properties, you should apply some formatting to your page before you choose to view it. To apply some formatting to your Data Access Page, select Format, Theme to bring up the Theme dialog, as shown in Figure 25.7. Using this Theme dialog box, you can select a theme from the list and use it as is or choose from the options below the listing. To add a Background Image to the page, make the colors Vivid or include Active Graphics on the page.
720
Web Publishing with Access 2002 PART VIII
FIGURE 25.6 The Grouping and Sorting dialog for Data Access Pages.
FIGURE 25.7 The Theme dialog.
Using Data Access Pages CHAPTER 25
721
Viewing a Data Access Page You have two ways to view and use a Data Access Page: as part of your Access application, or via a capable Web browser. To view a Data Access Page from Access, either click on the Page View icon or select View, Page View from the main menu while in Design view. If you want to navigate to a DAP as part of your application, you can use the OpenDataAccessPage method of the DoCmd object as shown here: DoCmd.OpenDataAccessPage “Page1.htm”, acDataAccessPageBrowse
Figure 25.8 shows the Data Access Page in Microsoft Access. Notice that you can incorporate DAPs into your applications seamlessly as if it were a form or report. FIGURE 25.8 A Data Access Page shown in Access.
You can also view that Data Access Page in a capable Web browser. All you have to do is open the HTML file directly, either on disk or by uploading the page to a Web server. Figure 25.9 shows the Data Access Page in the Web browser.
25 USING DATA ACCESS PAGES
Notice that the Data Access Page looks exactly the same in the browser as it does in Access. Also, there is no loss of functionality when you move from Access to a Web page. Inside the Web page, try editing the data and moving to the next record. You will see that the data is bound live to the database—any changes you make in the page will be reflected in the database. If you develop your application around DAPs instead of forms and reports, you can easily transport your application to the Web.
722
Web Publishing with Access 2002 PART VIII
FIGURE 25.9 A Data Access Page shown in Internet Explorer.
Implementing Interactive DrillDowns The great power of Data Access Pages is their ability to create drill-downs based on oneto-many relationships in your database. Now, create a one-to-many drill-down from Categories to Products on your Category Data Access Page. Open the Category page and then use the Field List. Drill down to the Products table and drag the ProductName, UnitPrice, and Discontinued fields to the bottom of the detail section. Access creates a new detailed child section that is a child drill-down section, as shown in Figure 25.10. Note Even though Microsoft holds you to the Windows-compatible standards to earn the use of its logo in your applications, Microsoft does not hold itself to this standard. Data Access Pages do not contain an Undo feature at all.
Using Data Access Pages CHAPTER 25
723
FIGURE 25.10 The child drilldown section for Products in Design view.
In Design view, you will notice a plus (+) sign button. This is for the drill-down capability. You might want to move it to a better location, like the bottom of the detail section. Now, go back to the Grouping and Sorting section. Set the Category section’s Data Page Size to 1, the Products section Data Page Size to All, and the Products Record Navigation Section to No. This provides a drill-down section that shows all the related Products to each Category on your page, as shown in Figure 25.11. FIGURE 25.11 The child drilldown section for Products in Page view.
25 USING DATA ACCESS PAGES
724
Web Publishing with Access 2002 PART VIII
Incorporating the Office Web Components with DAPs The great power of Data Access Pages is their ability to contain Office Web components that are bound to live data. As you can see in Figure 25.12, you can create some greatlooking Data Access Pages using the Office Web components. FIGURE 25.12 A Data Access Page with bound Office Web components.
To get started, create a simple crosstab query in Access’s Northwind database. You will create a crosstab with all the product’s categories and their quarterly sales figures for 1997. The SQL code to do this is TRANSFORM Sum(CCur([Order Details].[UnitPrice]*[Quantity]* (1-[Discount])/100)*100) AS ProductAmount SELECT Categories.CategoryName FROM (Categories INNER JOIN Products ON Categories.CategoryID = Products.CategoryID) INNER JOIN (Orders INNER JOIN [Order Details] ON Orders.OrderID = [Order Details].OrderID) ON Products.ProductID = [Order Details].ProductID WHERE (((Orders.OrderDate) Between #1/1/97# And #12/31/97#)) GROUP BY Categories.CategoryName PIVOT “Qtr “ & DatePart(“q”,[OrderDate],1,0) In (“Qtr 1”,”Qtr 2”,”Qtr 3”,”Qtr 4”);
Save this query as QuarterlyProducts and create a new Data Access Page based off this query.
Using Data Access Pages CHAPTER 25
725
Adding an Excel Spreadsheet Web Component To add a Microsoft Office Web component to your Data Access Page, select the icon that looks like Excel’s from the toolbox, as shown in Figure 25.13, and draw your control on your Data Access Page. After you have sized your control on the page, you will want to bind spreadsheet cells to fields in your page so your spreadsheet will dynamically change when the users scroll through the records. You have to use the DHTML object model, where everything is exposed through the document object. To access a field named txtQ1, enter this in the cell: =HOST().txtQ1.value
Add a cell for each quarter. In the next cell, bind the cell to the control on your page that has the data, as shown in Table 25.1. TABLE 25.1
The Values of Your Spreadsheet Cells
Quarter
Cell Data
Q1
=HOST().txtQ1.value
Q2
=document.txtQ2.value
Q3
=document.txtQ3.value
Q4
=document.txtQ4.value
FIGURE 25.13 The Data Access Page toolbar.
25 USING DATA ACCESS PAGES
726
Web Publishing with Access 2002 PART VIII
Adding a Chart Web Component Now that you have added a Spreadsheet control, add a Chart control and bind it to the data contained in the Spreadsheet control. As you scroll through the records, not only will the spreadsheet change its values dynamically, but the Chart control will also change based on the data. Add a Chart control to the page by clicking the chart icon and drawing a Chart control on your page. Double-click the chart to bring up the Commands and Options dialog box. The first page asks you where the data comes from. Page two asks for the data ranges. The data and label locations are entered here and an example entry would be Sheet1!$B$1:$B$4. Page three is where you choose the type of chart. Now, when you look at your chart in Page view and scroll through the records, your chart will update as each new record is presented.
Scripting Data Access Pages Writing code to control a Data Access Page is not as simple as writing code to control an Access form or report. When you are working with the code behind Data Access Pages, you have to use script to control your page’s behavior. When you create a command button or any other object that exposes events, you have to write VBScript (or JavaScript). There is no VBA Editor with Data Access Pages. You have to use something called the Microsoft Script Editor (MSE), as shown in Figure 25.14. FIGURE 25.14 The Microsoft Script Editor (MSE).
Using Data Access Pages CHAPTER 25
727
Now replace the navigation bar supplied by Access with one of your own. Create four command buttons and give them the following captions: Move Previous, Next, First, and Last. Then right-click your page and then select Microsoft Script Editor from the context menu. When in the MSE, you can use the Script Outline shown in Figure 25.14 to drill down on the client objects and events. This section of the MSE enables you to view each object on your page and look at each event. You can double-click each event and then write some code. So drill down to each command button and double-click the onClick event. Inside the MSE’s HTML code, you will get HTML code like this: <SCRIPT LANGUAGE=vbscript FOR=cmdFirst EVENT=onclick> <APPLET code=”com.ms.xml.dso.XMLDSO.class” MAYSCRIPT id=xmldso WIDTH=”100%” HEIGHT=”20”> ’ +
XML Databinding From XML on Disk
ID | Name |
WEB PUBLISHING WITH ACCESS AND ASP
Now that you are done, save the file as “Customers.xml”.
26 2002
ANATR Ana Trujillo Emparedados y helados Ana Trujillo Owner ANTON Antonio Moreno Taqueria Antonio Moreno Owner
765
766
Web Publishing with Access 2002 PART VIII LISTING 26.14
continued
| |
FIGURE 26.14 Databinding XML to an HTML page.
To parse the XML file, you have to use a Java applet that is a standard part of IE 4.0. (You can also download this applet’s code from Microsoft’s Web site.) Add the APPLET code shown in Listing 26.14 to your HTML file and set its “URL” property to the location or URL of the XML document. Then you can use simple Dynamic HTML data binding with the datasrc=”#xmldso” tag in your Table declaration where #xmldso is the name of your Java applet object in your code. Lastly, just set the datafld property of each field in your table to an XML child node name. As you change the XML document, the Web page will change. It’s that easy!
Creating XML Programmatically Because XML is so easy to create and use in your applications, let’s create a generic ActiveX DLL inVB to produce XML code. You can create XML files on-the-fly from your DLL that you can use in your Web and VB applications. The DLL can be used to power Web sites or client/server applications.
Web Publishing with Access 2002 and Active Server Pages CHAPTER 26
Creating XML Programmatically Inside a VB Component
Public Function CreateXML _ (strEntity As String, strSQLStatement As String, _ strConnectionString As String) As String ‘Generic Function that will create XML from ‘An ADO Recordset and return it to an application ‘Return value can be put into an XML string or HTML doc Dim rst As ADODB.Recordset Dim fld As ADODB.Field Dim strReturn As String On Error GoTo Proc_Err Set rst = New ADODB.Recordset ‘Open the recordset based on the SQL Statement ‘That was passed in along with the connection info rst.Open strSQLStatement, strConnectionString ‘Begin the start of the XML Document ‘With the XML Version strReturn = “” If rst.EOF And rst.BOF Then ‘Produce an Empty Element if no data strReturn = strReturn & vbNewLine & “” Else ‘Open the First Entity Tag strReturn = strReturn & vbNewLine & “” ‘Loop through the Records in the recordset and ‘output them to XML Do Until rst.EOF ‘Begin the Element strReturn = strReturn & vbNewLine & “” ‘Dump each field of the recordset ‘If you want less fields, restrict
WEB PUBLISHING WITH ACCESS AND ASP
LISTING 26.15
26 2002
To begin, you are going to start VB 6 and create a new ActiveX DLL project called XML_TOOL with one class module called Database. Your class should have exactly one method, the CreateXML method. You must set a reference to ADO 2.0 under Tools, References. This method uses ADO to connect to a database, open a recordset, and return XML to the client. The method accepts three arguments, the root element you want to name, a connection string, and a SQL statement to execute. The method is shown in Listing 26.15.
767
768
Web Publishing with Access 2002 PART VIII LISTING 26.15
continued
‘them in your SQL Statement For Each fld In rst.Fields strReturn = strReturn & vbNewLine & “” strReturn = strReturn & fld.Value strReturn = strReturn & “” Next fld ‘Close the Element strReturn = strReturn & vbNewLine & “” ‘Move the next record rst.MoveNext Loop ‘Close the Entity strReturn = strReturn & vbNewLine & “” End If ‘Assign the Created text to the method CreateXML = strReturn ‘Clean up rst.Close Set rst = Nothing Proc_Exit: Exit Function Proc_Err: ‘Pass the Error Back to the Application Err.Raise Err, “XML DLL”, Err.Description Resume Proc_Exit
End Function
Now let’s put this ActiveX DLL to work. After compiling the DLL, let’s start with another Visual Basic application and set a reference to your DLL. The code to instantiate the DLL and XML is shown in Listing 26.16, where Pubs is a DSN to the Pubs database in SQL Server. LISTING 26.16
Calling the Component Created in Listing 26.15
Private Sub cmdPrintXML_Click() Dim xml As XML_Tool.Database Set xml = New XML_Tool.Database
Web Publishing with Access 2002 and Active Server Pages CHAPTER 26 LISTING 26.16
continued
The results are shown here: 0736 New Moon Books Boston <state>MA USA 0877 Binnet & Hardley Washington <state>DC USA
As you can see, your Visual Basic application prints only the XML to the debug window; however, you can create XML files by using simple File I/O or other techniques. Let’s now use your DLL from an Active Server Page. Remember the last Web example where you loaded XML into the HTML table? That XML can also be inline XML inside of the Java Applet code. Well, now let’s use this ActiveX DLL to dynamically create the in-line XML when the user requests the Web page. With a little bit of VBScript code, you can load your DLL from a Web page and dynamically create the XML used in your Web application. The following code creates the same Web page as before; however, now it is dynamic and creates the XML on-the-fly as the user requests the Web page. See Listing 26.17. LISTING 26.17 <APPLET code=”com.ms.xml.dso.XMLDSO.class” MAYSCRIPT id=xmldso WIDTH=”100%” HEIGHT=”20”>
Creating Charts with the Chart Control Chapter 25 introduced you to the Office Web Components and how to use them in Access 2002 and Visual Basic. Chapter 26 showed you how to use them in Data Access Pages with Internet Explorer 5.0. What happens when you use a browser that does not support ActiveX Controls? The chart control can also use GIFs for Web browsers that do not support ActiveX Controls. You are now going to use the simple CrossTab query from Northwind to create an unbound chart on your ASP. You are going to create a global.ASA file to keep track of some GIFs shown in Listing 26.18. LISTING 26.18
Creating a Chart GIF on the Server
<SCRIPT LANGUAGE=”VBScript” RUNAT=”Server”> Sub Session_OnStart Session(“nFiles”) = 0 Set Session(“FileSystem”) = CreateObject(“Scripting.FileSystemObject”) End Sub Sub Session_OnEnd Dim i For i = 0 To Session(“nFiles”) - 1 Session(“FileSystem”).DeleteFile “D:\ntstuff\WebStuff\” & _ Session(“szFile” & i ), True Next End Sub
Web Publishing with Access 2002 and Active Server Pages CHAPTER 26
Working with the Chart Office 2000 Web Component
Summary As you can see, you can create some very powerful Web sites with Access 2002. Using the knowledge you gained in prior chapters on ADO, you can do just about anything with Access 2002 and Active Server Pages, including e-commerce and charting. Lastly, the latest bleeding-edge technology, XML, is very operable with Access 2002 and ASP. Access 2002 users can easily import or export XML schema and data documents into and out of Access 2002. Users can automatically import the XML tables or access advanced options to customize the import. Similarly, when users export XML data, schema, and presentations, they can leave the defaults as they are, or they can select advanced options for greater control over the exported output.
INDEX
774
& (ampersand)
SYMBOLS & (ampersand), 471 * (asterisk), as wildcard, 468-470 (***) (curly brackets), 489 ∞ (infinity symbol), 67 % (percent sign), 468 . (period), 467 + (plus sign), 471 + (plus sign) button, 723 ? (question mark), as wildcard, 468-470 ? (question marks), 323 (***) (semicolon), 467 (_) underscore, 467-468 (||) vertical bars, 471 .ASP files (Active Server Pages), 733 .MDB files (Access databases), separating data from application, 373 .MDE files installing, 374 optimizing performance using, 374, 402 .Supports method, 624 /EXCL dbname command-line option, 620 /RO dbname command-line option, 620 0 based items, 333 1 based items, 333 2000 (year) issues, Oracle, 482 3000 locking error code, 626 3006 locking error code, 626 3008 locking error code, 626 3027 locking error code, 626 3046 locking error code, 626 3158 locking error code, 626 3164 locking error code, 626 3167 locking error code, 626 3186 locking error code, 626 3187 locking error code, 626 3188 locking error code, 626
3189 locking 3196 locking 3197 locking 626-628 3202 locking 3211 locking 3212 locking 3218 locking 3260 locking 627-628 3261 locking
error code, 626 error code, 626 error code, error error error error error
code, code, code, code, code,
626 626 627 627
error code, 627
A A2KU_Timer, advantages of using, 409 Access report exporting options, 263 Visual Basic. See Visual Basic, 572 Access 2000 compatibility with Access 2002, 10 conversion error logging, 11 Access 2002 accessing Oracle databases with SQL pass-through queries (SPTs), 465-468 calling stored procedures from SQL pass-through queries (SPTs), 489-490 creating connection from Access 2002 to Oracle databases, 484-487 creating stored procedures in Oracle, 487-489 unbound interfaces to Oracle, 491-502 views to Oracle databases, 483-484 creating queries advanced queries, 74-75 query design window, 62-66
query grids, 72-74 table panes, 66-70, 72 functions, Oracle vs. Access, 470-482 integrating with Microsoft Office, 531-532 auto macros, 535-536 automating Excel, 555-559 automating Graph, 567-569 automating Outlook, 562-567 automating PowerPoint, 559-562 automating Word, 540-552 class arguments, 537 Microsoft Forms, 536-537 Object Browser, 537 printing Access information in Word reports, 538-540 using the right tools, 533 writing code, Macro Recorder, 533 linking to Oracle tables, 462-465 new features, 8 ANSI-92 query mode, 13 compatibility with Access 2000 databases, 10 Data Access Pages (DAPs), 11 database conversion error logging, 11 Extensible Markup Language (XML) support, 13 integration with SQL Server 2000, 12-13 Linked Table Wizard, 13 menus, 8 PivotChart and PivotTable data views, 13 user interface, 8 Visual Basic Environment (VBE) layout changes, 9-10
ActiveX Automation
new features of, 414 opening, command-line options, 620 options affecting data contention, 628 replication, 424 setting relationships in, 51 support by Microsoft Transaction Server (MTS), 681 wildcards and position markers, 468-470 Access 97 databases, 434 Access applications creating, 433-434 optimizing, installation configurations, 374 using ActiveX components, Sound object, 579 using ActiveX Slider control, 609 using Timer ActiveX control, 594, 604-605 Access Data Projects (ADPs), 462 administering SQL Server databases, 424-425 advantages and disadvantages of, 414-415 creating, 415-417 creating projects based on new databases, 428-434 database containers, 417-418 database diagrams, 423 filling tables at startup, 440 forms, 424 macros, 424 modules, 424 pages, 424 reconnecting to SQL Server databases, 425-427 reports, 424 SQL Server queries, 421-422 stored procedures, 422-423 system requirements, 414 working with SQL Server tables and, 419-420
Access forms PivotTable control, 711-713 Spreadsheet control, 706-707 access levels, virtual directories, 684 Access Permissions option, 690 Access/Jet applications client-server security identifying blank passwords, 660 identifying current users, 658 listing user/group members, 657 managing groups/group ownership, 661 managing users, 654-655 SQL Grant/Revoke statements, 663 database security basic steps, 664 common errors, 665 default user/group settings, 646 groups, 645 permissions, 649 replication concerns, 651 split databases, 652-653 start-up options, 650 users/groups, 644 workgroup-based, 638-640 AccessError method , 367 AccessErrorTableName option, 362 accessing data, Universal Data Access Initiative, 121 data access, history of, 120-121 non-relational data, ActiveX Data Objects (ADOs), 157-162 Report Wizard, 244 workgroup templates on servers, 542 accounts, IUSER, 695-698
acDataErrContinue constant, 366 acDataErrDisplay constant, 366 acLBGetRowCount request code, 191 ACStatus property, 231 Action object, 563 action queries, executing with commands, 138-139 ActionSettings object, 560 Activate event, 268 activating, navigation buttons, 495-496 Active Server Pages (ASPs) ActiveX Data Objects (ADO) 1.0, 122 changes in, 680 full install of documentation, 680 placements and permissions, 697-698 Active Server Pages. See ASP ActiveControl, DropDown event handler, 190 ActiveX, 572 early binding, 410 ActiveX Automation assigning object variables to applications, 512 Automation objects, 517-519 closing Automation Servers, 519 creating and setting references to applications, 507-512 creating application instances, 513-516 described, 506 improving performance, 523-527 reasons to use, 506 resource requirements, 507 servers vs. clients, 506-507 steps for, 507 UserControl property, 520 WithEvents keyword, 520, 522-523
775
776
ActiveX code components
ActiveX code components See also ActiveX DLLs, 574 See also ActiveX EXEs, 574 advantages, 573 creating using Visual Basic, 572 defined, 573 deploying/distributing, 586-587 EXEs vs. DLLs, 574 installing for end users, 581 modifying source code without recompiling, 582 registering, 588-591 setup files, 581-582, 585-586 ActiveX components distributing, 581 unregistering, 589 ActiveX Control Interface Wizard adding properties/methods/events, 597-600 Create Custom Interface Members screen, 599 Set Attributes screen, 600 ActiveX controls adding to forms, 207-208 Animation, 210-212 attributes, 593 Calendar, 210, 213-214 Common Dialog, 210, 214-215 creating using VB, 593 cSlider, 607 creating interface, 608 distributing, 610 packaging for Web pages, 610-613 saving, 609 setting properties, 609 testing in Access, 609 testing in Visual Basic, 609 using in COM-compliant applications, 610 using on Web pages, 613-614
DateTimePicker, 210, 215 distributing in applications, 206, 238 FlatScrollBar, 211, 217 how to use, 204-206 ImageCombo, 211, 218 ImageList, 211, 219-221 installing, 206-207 ListView, 211, 221-222 MAPIMessages, 211, 223-224 MAPISession, 211, 222-223 MonthView, 211, 224, 226 ProgressBar, 211, 226-227 registering, 207 RichText, 211, 227-228 runtime vs. design-time, 593 setting properties, 208 Slider, 211, 228-229 StatusBar, 211, 229-230 SysInfo, 211, 231-232 TabStrip, 211, 232-233 Timer, 594 adding code, 600-601 adding properties/ methods/events, 597-600 creating interface, 595 distributing, 605 property pages for, 605-607 saving, 597 setting properties, 597 testing in Access, 604 testing in Visual Basic, 602-604 using in COM-compliant applications, 607 using multiple timers, 605 ToolBar, 211, 233-234 TreeView, 211, 234-235 UpDown, 211, 235-236 Web components, 702 Chart control, 703, 707-708, 711 PivotTable control, 703, 711-713 Spreadsheet control, 702-707
WebBrowser, 211, 236-238 writing code to execute methods or respond to events, 209 ActiveX controls, Web components, license requirements, 703 ActiveX Data Objects (ADO), parameter passing, 112-113 ActiveX Data Objects (ADOs), 125-126 accessing non-relational data, 157-162 addressing Oracle through, 499-500 Command objects, 134-137 compared with Data Access Objects (DAOs), 145 connecting to Oracle databases through, 484-487 Connection objects, 126-130 converting from Data Access Objects (DAOs) to, 142-145 data access, history of, 120-121 data definitions, ADOX, 167-176 Error objects, 141-142 executing action queries with commands, 138-139 Field objects, 139 manipulating data, 162-167 object model for, 121, 128 OLE DB Provider for Jet, 148-151, 154-157 Parameter objects, 134-137 Property objects, 139 Record objects, 137-138 Recordset objects, 130, 134-137 Stream objects, 140 Universal Data Access Initiative, 121 versions of, 122-125 ActiveX DLL, VB, creating XML, 766-770
Align property
ActiveX DLLs cError component, 591 class modules, 593 comparison with ActiveX EXEs, 574 cSound Object component compiling, 578 creating, 574 setting properties, 576-577 using in Access applications, 579 ActiveX EXEs, 574 comparison with ActiveX DLLs, 574 creating, 579 ActiveX-based Web browsers adding Office functionality to, 702-708, 711-713 See also ActiveX controls, Web components adCmdStoredProc constant, 135 adCmdTable constant, 135 Add images, 220 ADD keyword, 96 Add method, 218-219, 313-314 Add Months() function, 479-480 ADD permission, 698 Add Properties screen, VB Property Page Wizard, 606 Add Watch dialog box, 329 addIndexNullsDisallow property, 171 adding ActiveX controls to forms, 207-208 charts to Data Access Pages (DAPs), 726 constraints to fields, 114-116 detail fields, PivotTables, 199 folders, Outlook, 564-567 items to collections, 313-314 Oracle data to forms, 498-499 records to tables, 164-165 records, Oracle, 488
spreadsheets to Data Access Pages (DAPs), 725 subforms to forms, 194 tables, SQL Query designer, 421 tasks, Outlook, 564 AddNew method, 158, 164 AddressBar property, 237 AddressEntries object, 563 AddToErrorHandlerCalendar option, 362 AddToErrorHandlerOutlookC alendar method, 353 ADE files, creating, 433 adEditAdd value, 625 adEditDelete value, 625 adEditInProgress value, 625 adEditNone value, 625 adLockBatchOptimistic error constant, 624 adLockOptimistic error constant, 624 adLockPessimistic error constant, 624 adLockReadOnly error constant, 624 adLockUnspecified error constant, 624 Admin user (workgroupbased security), 646 Administer (Database) permission, 647 Administer (Other) permission, 647 administration, SQL Server databases, Access Data Projects (ADPs), 424-425 Admins group (workgroupbased security), 646 adModeReadWrite constant, 127 ADO (ActiveX Data Objects), 753-754 ADO (ActiveX Data Objects) accessing databases using, 103 parameter passing, 112-113
ADO.NET, 125 ADOR object, 624 ADOx, 123 ADOX, data definitions, 167-176 ADOX (ADO extensions) client/server security identifying blank passwords, 660 listing current users, 658 listing user/group members, 657 managing groups/group ownership, 661 for security with split databases, 652 ADOX Column object, 170 ADOX table objects, 169 ADP (Access Data Projects), 462 filling tables at startup, 440 ADP. See Access Data Projects adUpdate constant, 624 adUpdateBatch constant, 624 adUseClient property, 624 Advanced area, Microsoft Management Console (MMC), 683 AfterLabelEdit event, 235 AfterSection setting, 271-272 AfterUpdate event, 496, 498 aggregate functions. See Total queries aggregation queries applying selection criteria, Where clause, 80-83 creating with Expression function, 79-80 aggregation queries. See Total queries algorithms, Soundex, 474-475 Align property, 230, 234
777
778
aligning
aligning, pages for binding, 282-283 Alignment property, 236 All value, Row Source Type property, 190 All Views value, 185 Allow Additions property, 182 Allow Anonymous Access check box, 688 Allow Deletions property, 181 Allow Design Changes property, 185 Allow Edits property, 181 Allow Filters property, 181 Allow Nulls property, 420 Allow Viewing Code After Error, 650 Allowable Views property, 182 AllowColumnReorder property, 222 AllowCustomize property, 234 AllowSpecialKeys, 650 alphabetical groupings, data, 279 Alter keyword, 483 ALTER TABLE statement, 96, 109 ampersand (&), 471 Animation control, 210-212 AnimationSetting object, 560 ANSI-92 SQL, Jet 4.0 compatiblity with, 110 API calls timeGetTime() function, 381-383 Windows operating systems, 364 API. See application programming interfaces APP argument, 463 App Debugging tab, Internet Information Server (IIS), 691-692
App Mappings tab, Internet Information Server (IIS), 691 App Options tab, Internet Information Server (IIS), 691-692 Appearance property, 217 Apple Computer, 104 application icon, changing, security advantages, 650 Application object, 540-541, 556, 560, 563, 568 events exposed by, Microsoft Word, 521 Application object (ASP) events, 750-751 global variables, 748 properties, 747-748 sample Web page, 752 user logs, implementing, 748 application programming interfaces (APIs), 120 Application property, 352 application settings choosing, Internet Information Server (IIS), 689-692 applications Access, creating, 433-434 Access Data Projects (ADPs), 415 CGI versus ASP, 734 choosing starting points, 694 creating and setting references to, 507-512 debugging conditional compilation, 331-335 Debug menu, 326-331 Debug object, 322-323 eliminating logic errors, 318 Immediate window, 324-325 Integrated Development Environment (IDE), 318-322
sample code for practicing, 335-336 testing applications, 335 distributing ActiveX controls in, 206, 238 evaluating variables while running, 329-330 instances, 524 invoking Package and Deployment Wizard, 581 isolation of, 694-695 Microsoft Office auto macros, 535-536 automating Excel, 555-559 automating Graph, 567-569 automating Outlook, 562-567 automating PowerPoint, 559-562 automating Word, 540-552 class arguments, 537 Microsoft Forms, 536-537 Object Browser, 537 printing Access information in Word reports, 538-540 reasons for automating, 531-532 using the right tools, 533 writing code, Macro Reader, 533 multiuser, optimizing, 631-632 non-Access, error handling for, 364-365 optimizing, 370 during coding process, 400-410 during design phase, 384 establishing relationships, 386 form performance, 395-396 handling form controls, 396-399 hardware requirements, 371-373
arrays
indexes for, 386 installation configurations, 373-374 Jet engine configuration, 374-378, 380-381 normalizing data, 385-386 performance testing, 381-384 query performance, 387-395 report-printing, 399-400 table design, 385 partitioning, 631-632 testing, 335, 373 audit trails, 144 Web, security of, 692 Web-enabling Web components for, 702-708, 711-713 See also Web components, 713 applications, Web-enabling, Web components for, 703 AppointmentItem object, 563 appointments creating, Visual Basic for Applications (VBA), 566 architecture choosing, database locking, 623 client/server, OLE DB vs. ODBC, 436 Data Access Pages (DAPs), 716-717 n-tier, 488, 491 reports, 242-247 arguments AddMonths() function, 479 APP, 463 Ceil() function, 477 class, Office applications, 537 connection strings, 463 Count, 479 CreateGroupLevel() function, 278-279 Database, 463 DataErr, 366
Date, 479-481 Day, 481 Description, 346 DSN, 463 End, 475 Error event procedures, 366 FFooter, 279 FHeader, 279 Floor() function, 477 Format, 481 FromDate, 276 GetList, 191-192 HelpContext, 346 HelpFile, 346 Instr function, 472 NextDay() function, 481 Number, 346 Nvl() function, 477 Occurrence, 472-473 Options, 621 Precision, 477-478 PWD, 463 Raise method, 346 Response, 366 Round() function, 477 Set, 472, 474 Source, 346 Start, 472, 475 StrExpr, 278 String, 472, 474-475, 481 StrReport, 278 Substitute, 477 Substr function, 475 To Date() function, 481 ToDate, 276 Trim function, 474 Trunc() function, 478 UID, 463 Value, 477-478 Variant, 191 arithmetic operations, 479 Arrange property, 222 arrays enhancing performance using, 405-406 string, parsing, Split() function, 254
ASP (Active Server Pages), 732-733 Access database objects, 734-739 ActiveX Data Objects (ADOs) 1.0, 122, 753-754 Application object events, 750-751 global variables, 748 properties, 747-748 browser-independent, 733 engine, functions, 739 Global.asa file, events management, 750-751 HTML content, 733 JavaScript content, 733 listings change password form, 760-763 creating chart control for GIF collection, 770 Global.asa file for virtual directory, 751 HTML code to create login form, 755-759 HTML for Web page with VBScript variable, 745 increasing Web page font using VBScript loop, 746-747 Results of Exporting Northwind Customers Table to ASP, 735, 738 working with Chart Office 2000 Web Component, 771-772 ODBC data access, 734 Request object, Redirect method, 750 required resources, 734 Response object, 749 sample Web page, 752 server-side scripting, VBScript, 740-747
779
780
arrays
Session object, 748-751 unbound charts, 770-772 VBScript content, 733 versus CGI applications, 734 Web pages, 754-763 Web sites, 732-733 ASP.NET, 680 future version of ASP, 733 GotDotNet Web site, 733 Attachment object, 563 AttachmentCount property, 224 AttachmentIndex property, 224 AttachmentName property, 224 AttachmentType property, 224 audit trails, testing applications, 144 Aurora Development Group, 144 authentication, clear text, 689 auto macros, 535-536 Auto Syntax Check, Visual Basic Editor, 340 AutoBuddy property, 236 AutoCaption object, 540 AutoClose macro, 535 AutoCorrect, 548 AutoCorrect object, 540, 568 AutoExecute macro, 535 AutoExit macro, 535 AutoFit method, 557 AutoIncrement property, 170 AutoJoin feature, 66 automating, Microsoft Office applications auto macros, 535-536 Excel, 555-559 Graph, 567-569 class arguments, 537 Microsoft Forms, 536-537 Object Browser, 537 Outlook, 562-567
printing Access information in Word reports, 538-540 PowerPoint, 559-562 reasons for, 531-532 tools, 533 writing code, Macro Reader, 533 Word, 540-552 Automation assigning object variables to applications, 512 closing Automation Servers, 519 creating and setting references to applications, 507-512 creating application instances, 513-516 described, 506, 530 improving performance, 523-527 objects, 517-519 reasons to use, 506 resource requirements, 507 servers vs. clients, 506-507 steps for, 507 UserControl property, 520 WithEvents keyword, 520-523 Automation objects, 506 printing documents, 518-519 properties and methods, 517 releasing, 517-518 AutoNew macro, 535 AutoNumber field, 50 AutoNumber fields, Jet 4.0 capabilities, 109-110 AutoOpen macro, 535-536 AutoPlay property, 212 AutoSummarize, 549 AutoText, 548 Avg function, 75 calculating averages, 77-78 AVIFileLocation option, 362 AVIFileLocation property, 352 Axis object, 568
B Background Compile option, 340 background pictures, removing, 371 backups, SQL Server databases, 424-425 backward compatibility Data Access Objects (DAOs) error numbers, 141 VBScript engines, 673 BatteryFullTime property, 231 BatteryLifePercent property, 231 BatteryLifeTime property, 231 BatteryStatus property, 231 Before&AfterSection setting, 271-272 BeforeSection setting, 271-272 BeginTrans method, 630 BeginTransComplete method, 630 Binary Compatibility option (Windows Registry), 590-591 Binary data type (Jet 4.0), 108 binding aligning pages for, 282-283 controls to ImageLists, 221 early, 514-515, 524 late, 514-515 biweekly groupings, reports, 279 blocks, error handling, 343 bookmarks, 406 Bookmarks, Word, 545-546 Boolean data type (Jet 4.0), 108 Border Style property, 183 BorderStyle property, 234 Both value, Scrollbars property, 183
changing
bound controls, 243 bound forms, 448 breakpoints, setting, 326 brokers, transaction, 680-681 Browser Capabilities Component, 695 browsers Internet Explorer, VBScript engine, 673 retrieving properties for, 695 viewing Data Access Pages (DAPs), 721 browsers. See Web browsers btnDelete OnClick event, 500 btnNew OnClick event, 498-499 Buddy properties, 608 BuddyControl property, 236 bugs ActiveX controls, 205 fixing immediately, 333 BuildCriteria method, 446-447 building Connection objects, 126 Recordset objects, 136 reports programmatically, 274-279 built-in functions, printing values, 324 BulletIndent property, 228 Button object, 233 ButtonClick event, 233-234 ButtonHeight property, 234 ButtonMenuClick event, 234 ButtonMenus object, 233 buttons navigation creating for data pages, 727 programming to move through recordsets, 494-496 plus sign (+), 723
Buttons collection, 233 Buttons object, 233 ButtonWidth property, 234 Byte data type (Jet 4.0), 108
C cache reads, 383-384 calculated fields, creating, PivotTables, 200-201 calculating averages, 77-78 page totals, 283 calculation functions, reports, 251 calculations date, Oracle functions, 479-482 mathematical, Oracle functions, 477-478 Calendar control, 210, 213-214 Calendar folder, 566 CalendarBackColor property, 216 CalendarForeColor property, 216 CalendarTitleBackColor property, 216 CalendarTitleForeColor property, 216 CalendarTrailingForeColor property, 216 Call keyword, 346 Call Stack window, 322, 330-331 call-tree loading method, 401 called procedures, 330-331 calls, API, Windows operating systems, 364 Can Grow property, 262 Can Shrink property, 262 canceling, reports with no data, 268 candidate keys, 49
CanGrow property, 270-271 CanShrink property, 270-271 cascades, creating via triggers, 430 cascading deletes, 57 updates, 57 case sensitivity, Oracle SQL statements, 467 catalog object, 173 catalog objects, ADOX, 167-168 Catalog report, 257 Ceil() function, 477 Cell object, 560 CellRange object, 560 cError class module, 351, 358 Error object (global error handler), 364-365 cError component (ActiveX DLL), 591 class modules, 593 cError object, 289, 351-363 Certificate Server, 672, 675 CGI applications, versus ASP, 734 Challenge and Response option, 688-689 Change event, 236 Change Logon Password tab, 644 changing compiler settings, 340 data in recordsets, 162-165 DataSource control at runtime, 728-729 fields with property procedures, 293-296 grouping structures in reports, 248-250 location of log files, Internet Information Server (IIS), 686 page settings, Word documents, 551 passwords, 112
781
782
changing
recordsources in reports, 247-248 reports, 244 reports at runtime, 266-274 SQL statements, queries, 175-176 stored procedures, 422 subreports, 258 tables, 96, 109 Universal Data Links (UDL) files, 152, 155 values of variables, 324, 329 Character data type (Jet 4.0), 107 character encoding, Unicode, Jet 4.0 support for, 103-105 Characters object, 541, 556 chart, adding to Data Access Pages (DAPs), 726 chart control, Web browsers, implementing, 770-772 Chart control (Web components), 703, 707-708, 711 Chart object, 556, 568 ChartArea object, 568 ChartGroup object, 568 charts, creating Excel, 557-559 Graph, 568-569 unbound, 711 check boxes, Allow Anonymous Access, 688 CHECK CONSTRAINTS option, 114-116 Checkbox property, 216 Checkboxes property, 235 Child property, 235 children reports, 257 choosing application settings, Internet Information Server (IIS), 689-692 application starting points, 694 architecture, database locking, 623 data views, 182
default documents, Internet Information Server (IIS), 687-689 joins, 68-69 logging properties, Internet Information Server (IIS), 686-687 platforms for Web site publishing, 671-673 records for aggregation, 81 security settings, Internet Information Server (IIS), 687 class arguments, Office applications, 537 class modules cError, 351, 358 in cError ActiveX DLL, 593 inserting, 292 naming, 292 Class property, 352 classes connection, 459-460 creating, 292 described, 288 MyTimer, 306-307 properties, 295 TextFile, 304-306 User, 293 Classes pane, Object Browser, 510 classes, 541. See also objects CLASSID OLE DB Provider for Jet, 148 clauses From, 467 Where, 76, 80-83, 181, 445, 467-468 WITH, 97 Clear method, 345, 353 clear text authentication, 689 clearing, forms, 498-499 ClearSel method, 229 Click event, 213, 233, 300, 444 client/server, upsizing to, 410 client/server applications,
security identifying blank passwords, 660 identifying current users, 658 listing user/group members, 657 managing groups/group ownership, 661 managing users, 654-655 SQL Grant/Revoke statements, 663 client/server architecture, OLE DB vs. ODBC, 436 ClientHeight property, 232 ClientLeft property, 232 clients, Automation, 506-507 ClientTop property, 232 ClientWidth property, 232 Close Button property, 183 Close event, 269 Close method, 212 closing Automation Servers, 519 connections to Oracle, 501-502 recordsets/directories, enhancing performance, 407 tables when running data definition queries, 94 windows, IDE, 318, 322 code comments in, 334 declaring variables, 332 deleting, 325 mail merge automation, creating, 544 parameter queries in, 89 refactoring, 127-128 running, Automation Servers, 525-526 stepping through, Debug window, 327-328 suspending execution of, 324 using small procedures in, 334 VBA, Universal Data Links (UDLs) in, 154-155
code listings
viewing, forms, 319 writing, Macro Recorder, 533 code components See also ActiveX DLLs, 574 See also ActiveX EXEs, 574 code components (ActiveX) advantages, 573 creating using Visual Basic, 572 defined, 573 EXE vs. DLLs, 574 installing for end users, 581 deploying/distributing, 586-587 setup files, 581-582, 585-586 registering, 588-589 compatiblity options, 590-591 code listings adding records, Oracle, 488 addressing Oracle through SQL and ADOs, 499-500 aligning report pages for binding, 282-283 assigning Row Source Type property to functions, 190-191 calculating page subtotals, 283 canceling reports with no data, 268 changing positions, even and odd numbered pages, 282 CHECK CONSTRAINTS option, 115-116 cleaning up Oracle connections after closing, 501-502 clearing forms, 498-499 combining WHERE and HAVING statement, 82-83 connecting to SQL Server via OLE DB Provider, 450-451 Connection class, 459-460 Connection object, 126 connections through Uniform Resource Locators (URLs), 128
creating crosstab queries, 275-276 crosstab reports, 276-278 custom data page navigation buttons, 727 databases, ADOX, 168 empty lines at intervals, 281 fields with constraints, 95 linked tables, 170 open connections to Oracle and filling forms, 493 query procedures, 174-175 query views, 174 recordsets and filling MSHFlexGrids, Visual Basic 6.0, 160 recordsets from non-relational data, 158-159 relationships, 173 tables with AutoNumber fields and PrimaryKey indexes, 172 views in Oracle from Access 2002, 485-490, 494-502 Data Shaping, Microsoft Excel, 161-162 deleting current records, 500 drawing vertical lines, 281 Error object, 141-142 executing action queries with ActiveX Data Objects (ADO) commands, 138-139 commands with parameters, 453-454 commands without parameters, 454-455 parameter queries from code, RunParamQuery() function, 89-90 stored procedures with parameters, Access VBA, 489-490
Structured Query Language (SQL) statements against connections, 129-130 expression for saving updated fields, 498 Field object, 139 filling forms with data, global recordsets, 493-494 filling forms with values from recordsets, 449-450 handling filters and NoData report event, 447-448 Jet 4.0 enhancements security files, 111-112 Unicode compressed field in VBA, 105-106 Jet User Roster, 157-158 NextRecordset method, 451-452 opening parameter queries, ActiveX Data Objects (ADO), 113 opening recordsets, 132-133, 136 populating fields with data and checking current form context, 501 preparing forms for new records, 498 programmatic Universal Data Links (UDLs), 156-157 programming navigation button movement, 494-496 Property object, 139 providing report filters, BuildCriteria method, 446-447 querying return values, 458 reading files referred to by records across intranets, 140 reconnecting DataSource connection string at runtime, 728-729 Refresh method, using with parameters, 456-457
783
784
code listings
reports, user-defined functions, 253 resetting SQL statements, 175 running parameter queries with varying number of parameters, OpenParamQuery() function, 90-91 saving recordsets to disks, reopening, and resyncing with original databases, 165-167 simple stored procedures, 432 simple views, 432 Split() function, 254 SQL statement, 70-72 SQL statements calculating averages, 78 counting records, 77 crosstab queries, 84-85 designing crosstab queries, 86-87 parameter queries in design mode, 88-89 resultset created with custom expression, 80 rewriting and running reports, 445 selecting records for aggregation, 81 unmatched queries, 71 stored procedures, 441 transactions in VBA, 630 transactions, Jet 4.0, 114 UDLs in VBA code, 154-155 UpdateField() function, 497-498 updating connections, Access Data Projects (ADPs), 427 using Oracle parameters from Access VBA, 486-487 Web components Chart control, unbound charts, 711 Spreadsheet control, loading data at runtime, 706-707
code modules avoiding decompiling, MDEs for, 402 enhancing performance using, 401 Code window, 320 codes, locking error, 626-627 coding ActiveX controls, Timer control, 600-601 performance-optimizing techniques, 400-401 arrays, 405-406 avoiding IIF() function, 409 bookmarks, 406 bread and circuses, 410 closing recordsets/directories, 407 code modules, 401 compiling/decompiling, 401-402 constants, 406 fast loops, 408 Len() function, 404 limiting refreshes, 409 MDEs , 402 object references, 404-405 object type declarations, 403 Option Explicit, 402 testing transaction efficiency, 409 true/false evaluations, 404 true/false toggles, 403-404 upsizing to client/server, 410 using .Execute command, 409 using A2KU_Timer, 409 using collection index numbers, 407-408 using early binding, 410 using Select Case, 409 using SQL, 407 using string variables, 402-403 variable sizes, 402
Collapse event, 235 collections Buttons, 233 Connection.Properties, 631 Panels, 230 parameter, executing queries, 90-91 using index numbers for, 407-408 VBA, objects, 312-316 Color property, 215 ColorScheme object, 560 column headings, crosstab queries, 85 Column object, 560 Column property, 192 ColumnHeaders object, 222 ColumnName property, 420 columns Conversion Errors table, 11 defining, PivotTables, 198-199 COM (Component Object Model) Office 2000 Web component controls, 702-713 COM-compliant applications using ActiveX Slider control, 610 using Timer ActiveX control, 607 Combo Box control, 189-192 ComboItem object, 219 ComboItems property, 219 Command object, 134-137, 173-176, 459 Command property, 175 command-line options, opening Access, 620 CommandBar object, 541 commands EXECUTE, 441 executed, opening recordsets, 136 executing action queries with, 138-139 executing with parameters, 452-459
connection string constants
For Each, 195 SendKeys, 190 commandtext object, 174-175 commandtext property, 175 CommandType property, 135 comments, code, 334 committed transactions, 629 CommitTrans method, 630-631 CommitTransComplete method, 630 Common Dialog control, 205, 210, 214-215 Compact Reclaimed Space Amount property string, 148 compacting databases, 373, 648 compatibility Access 2000 databases with Access 2002, 10 ActiveX code components, 590 backward Data Access Objects (DAOs) error numbers, 141 VBScript engines, 673 compiler settings, modifying, 340 compiling ActiveX controls, 593 ActiveX DLLs, cSound Object component, 578 applications, conditional compilation, 331-335 code modules, 401 Component Object Model (COM), Office 2000 Web component controls, 702-713 components ActiveX, unregistering, 589 ActiveX code components See also ActiveX DLLs, 574 See also ActiveX EXEs, 574
advantages, 573 creating using Visual Basic, 572 defined, 573 EXE vs. DLLs, 574 registering, 588-591 out-of-process, 574 Compose method, 224 composite keys, 49 compression, Unicode, Jet 4.0 support for, 105-106 ComputerAvailableMemory property, 352 ComputerName property, 352 ComputerOperatingSystem property, 352 ComputerProcessor property, 352 computers discovering problems with, 359-360 hardware requirements, 371-373 ComputerTotalMemory property, 352 concatenation, strings, 471 concatenation functions, reports, 254-256 conditional compilation, 331-335 ConfigChanged event, 231 configuring ActiveX Control properties, 208 application installations, optimizing performance, 373-374 breakpoints, Debug window, 326 connections, SQL Server front-ends, 436-440 DSNs, 437-438 error trapping options, 367 field properties, 170 Internet Information Server (IIS), 685-692
Jet 4.0 engine, optimizing performance, 374-381 object methods, 517 object properties, 517 parameters, parameter queries, 135 references, applications, 507-512 relationships in Access, 51 table properties and indexes, 429 Web servers for publishing sites, 682-692 Web sites for publishing choosing platforms, 671-673 development vs. production environments, 670 installing Web servers, 675-681 managing and configuring Web servers, 682-692 security of Web applications, 692 sites vs. virtual directories, 693-699 Windows NT Option Pack, 673-682 Confirm Workgroup Information dialog box, 640 conflicts, write, 628 Connect method, 459 connection class, 459-460 Connection Control property string, 148 Connection object, 126-130 methods, 630 connection objects CurrentProject.Connection, 151 opening, 152-154 connection pooling, Microsoft Transaction Server (MTS), 681 connection string constants, ActiveX Data Objects (ADOs), 151
785
786
connection strings
connection strings Open Database Connectivity (ODBC), creating, 463 pass-through queries, 92-93 Connection.Errors(index).SQL State property, 626 Connection.Properties collection, 631 connections configuring, SQL Server front-ends, 436-440 creating from Access 2002 to Oracle databases, 484-487 databases opening, OLE DB Provider for Jet, 148 Universal Data Links (UDLs), 152-157 executing Structured Query Language (SQL) statements against, 129-130 open, 493 opening, Access 2002, 127-128 Oracle, closing, 501-502 prompts for users to enter information about, 156-157 SQL Server databases, Access Data Projects (ADPs), 425427 through Uniform Resource Locators (URLs), 128-129 to Oracle databases, costs of, 464-465 ConnectionString property, 728 Console Root, Microsoft Management Console (MMC), 685 constants acDataErrContinue, 366 acDataErrDisplay, 366 ActiveX Data Object (ADO) cursor/lock types, 164-165 adCmdStoredProc, 135 adCmdTable, 135 adModeReadWrite, 127
adUpdate, 624 adUpdateBatch, 624 connection string, ActiveX Data Objects (ADOs), 151 enhancing performance using, 406 error, LockType property, 624 Section property, 269-270 constraints, 57-58 adding to fields, 114-116 CONSTRAINTS statement, 95 constructs, With/End With, 526 ContactItem object, 563 contacts, creating, Visual Basic for Applications (VBA), 566 Contacts folder, displaying, Visual Basic for Applications (VBA), 566 containers, databases, Access Data Projects (ADPs), 417-418 Content Control dialog box, 690 contention, data, Access options affecting, 628 Continue option, Debug menu, 328 Control Box property, 183 ControlName property, 352 controls ActiveX adding to forms, 207-208 Animation, 210-212 Calendar, 210, 213-214 Common Dialog, 210, 214-215 DateTimePicker, 210, 215 distributing in applications, 206, 238 FlatScrollBar, 211, 217 how to use, 204-206 ImageCombo, 211, 218 ImageList, 211, 219-221 installing, 206-207 ListView, 211, 221-222
MAPIMessages, 211, 223-224 MAPISession, 211, 222-223 MonthView, 211, 224, 226 ProgressBar, 211, 226-227 registering, 207 RichText, 211, 227-228 setting properties, 208 Slider, 211, 228-229 StatusBar, 211, 229-230 SysInfo, 211, 231-232 TabStrip, 211, 232-233 ToolBar, 211, 233-234 TreeView, 211, 234-235 UpDown, 211, 235-236 WebBrowser, 211, 236-238 writing code to execute methods or respond to events, 209 binding to ImageLists, 221 bound controls, in reports, 243 Combo Box, 189-192 Common Dialog, 205 DataSource, changing at runtime, 728-729 forms, 189-197 forms without, 188 Image, 205 in forms, optimizing performance, 396-399 List Box, 192 ListView, 205 MonthView, 205 Multi-Select List Box, 193 Option Group, 195 tab, 195 Toolbar, 205 Treeview, 222 ControlSource property, 194 ControlValue property, 352 Conversion Errors table, columns, 11 converting DAOs to ADOs, 142-145 databases, error logging, 11 forms to Microsoft Forms, 537
creating
cookies, retrieving Web browser properties, 695 Copies property, 215 Copy method, 224 correction, transaction conflicts, 632 costs, connections to Oracle databases, 464-465 Count argument, 479 Count function, 75-77 Count object, 541 Create Any Procedure system privilege, 488 Create Custom Interface Members screen (ActiveX Control Interface Wizard), 599 CREATE INDEX statement, 96-97 Create keyword, 483 CREATE PROCEDURE statement, 112 Create Procedure system privilege, 488 Create System Database property string, 148 CREATE TABLE statement, 94, 109, 114-116 CREATE VIEW statement, 112 CreateGroupLevel() function, 278-279 CreateObject function, 515-516 CreateParameter method, 454-455 creating Access applications, 433-434 Access Data Projects (ADPs), 415, 417 ADE files, 433 aggregation queries, Expression function, 79-80 AutoOpen macro, 535-536 calculated fields, PivotTables, 200-201 cascades via triggers, 430 classes, 292
Connection object, 126 connections from Access 2002 to Oracle databases, 484-487 crosstab queries, 84-87, 724 custom data page navigation buttons, 727 Data Access Pages (DAPs), 717, 719 Database Server Names (DSNs) through Open Database Connectivity (ODBC), 463-464 databases, ADOX, 168 drop-down lists, 190-192 e-mails with attachments, Outlook, 564-565 empty lines at intervals, 280-281 enumerated data types, 297-299 events, 300-302 fields, 169 fields with constraints, 95 global variables, 492 indexes, 96-97, 171-172 inner joins, 70-72 INSERT statements, 469 instances, applications, 513-516 joins, 68-69 mail merge automation code, 544 mailing labels, 259-262 methods, 299-300 multiple instances of objects, 304 object variables, 302-303, 511-512 open connections to Oracle and filling forms, 493 Open Database Connectivity (ODBC) connection strings, 463 outer joins, 70-72
Outlook items, 565-566 parameter queries, 87-89 pass-through queries, 92-93, 465-466 PivotTable and PivotChart views, 197, 199-202 pop-up menus, 196 projects based on new databases, Access Data Projects (ADPs), 428-434 properties, 292-299 queries, 80 queries in Access advanced queries, 74-75 query design window, 62-66 query grids, 72-74 table panes, 66, 72 queries with linked tables, 464 queries, ADOX, 173-176 query procedures, 174-175 query views, 174 Recordset objects, 136 recordsets, 158-162 recordsources, 275-276 references to applications, 507-512 relationships ADOX, 172-173 relationships via diagrams, 430 report objects, 276-278 sections, 278-279 stored procedures, 432 SQL Query designer, 422 stored procedures in Oracle, 487-489 subreports, 257-258 sum results, PivotTables, 199-200 tables, 94, 169, 428-429 linked, 169-170 tables of contents, Word documents, 549-550 table panes, 67
787
788
creating
tables with AutoNumber fields and PrimaryKey indexes, 172 tables, Word documents, 550-551 templates, Word, 542 unbound interfaces to Oracle, 491-502 Universal Data Links (UDL) files, 152 UPDATE statements, 469 VBA collections, 313 views, 431-432 SQL Query designer, 422 to Oracle databases from Access 2002, 483-484 views and procedures, 112 Web pages, membership-based (ASP), 754-763 Creator object, 541 criteria, selection, defining in query grids, 73-74 criteria expressions, query performance optimization, 388 crosstab queries, 83-87 basing reports on, 274-279 creating, Northwind database, 724 cSlider Control (ActiveX runtime), 607 creating interface, 608 distributing, 610 packaging for Web pages, 610-613 saving, 609 setting properties, 609 testing in Access, 609 testing in Visual Basic, 609 using in COM-compliant applications, 610 using on Web pages, 613-614 cSound class module, code for, 573
cSound Object component (ActiveX DLL) compiling, 578 creating, 574 setting properties, 576-577 using in Access applications, 579 cSoundObject.DLL. See cSound Object component cTimerControl, 604 CubeField object, 556 curly brackets ({}), 489 Currency data type (Jet 4.0), 108 current records, deleting, 500 CurrentContext() function, 495 CurrentProject.Connection, 151 CurrentProject.Connection method, 427 CurrentProject.OpenConnecti on method, 426 CurrentRecordID property, 352 cursor types, ActiveX Data Objects (ADOs), 164-165 custom installation option, Windows NT Option Pack, 679-680 CustomFormat property, 217 Customize method, 234 customizing reports, 247 grouping process, 250 grouping structures, 248-250 recordsources, 247-248 CVErr function, 367
D DAO type library, 143 DAO. See Data Access Objects DAP. See Data Access Pages
data Access, inserting into Word documents, 543-546 canceling reports with none, 268 choosing views for, 182 grouping alphabetically in reports, 279 identifying largest and smallest numbers in, 78 in reports, bound controls for, 243 loading in forms, 492-494 manipulating, ActiveX Data Objects (ADOs), 162, 164-167 modifying in recordsets, 162, 164-165 non-relational accessing, ActiveX Data Objects (ADOs), 157-162 creating recordsets from, 158-159 normalizing, performance issues, 385-386 ODBC, reporting, 443-446, 448 Oracle adding records to forms, 498-499 deleting from forms, 500-501 saving in forms, 499-500 updating in forms, 496-498 populating fields with and checking current form context, 501 repeating, hiding in reports, 279 updating through subdatasheets, 64 validating, StDev and Var functions, 78-79 viewing in hierarchies, 159-162
database security
data access history of, 120-121 proprietary application programming interfaces (APIs), 120 Universal Data Access Initiative, 121 Data Access Objects (DAOs) compared with ActiveX Data Objects (ADOs) from of, 145 converting to ActiveX Data Objects (ADOs) from of, 142-145 error numbers, backward compatibility, 141 executing Structured Query Language (SQL) statements off databases, 129-130 initial release of, 120 performing data definition functions on non-Jet tables, 93 Data Access Pages (DAPs), 11 creating, 717-719 described, 716-717 implementing interactive drilldowns, 722-723 incorporating Office Web components with, 724-726 scripts, 726-729 storage of, 719 viewing, 721 data contention, 628 data definition queries, 93-97 data definitions, ADOX, 167-176 data dispersion, 78 Data Entry property, 182 data integrity rules, 57-58 data normalization, 54-57 data properties, 181-182 Data Shaping, 159-162 data source, for global error handler, 592 data sources, external, opening, 133-134
data types Date, 479 declaring, 332 enumerated, creating, 297-299 Jet 4.0, 107-108 SQL Server, 420 Variant, 339 data views, PivotChart and PivotTable, 13 Database argument, 463 database container objects, ADPs, 417-418 database diagrams Access Data Projects (ADPs), 423 in Access Data Projects (ADPs) database containers, 418 database engines Jet multiuser design, 619 multiuser locking, 619-622 Microsoft Jet adCmdTable constant, 135 AutoNumber capabilities, 109-110 beta release of JOLT provider, 122 CHECK CONSTRAINTS option, 114-116 data access, 120 data types, 107-108 future support for, 102-103 history, 100-101 native OLE DB provider, 103 NT compatible sorting, 106 opening external data sources, 133-134 parameter passing, 112-113 parameter support, 135 row level locking, 103 searchable memo fields, 110
security enhancements, 111-112 SQL extensions, 110 transactions, 114 Unicode Compression, 105-106 Unicode support, 103, 105 User List functionality, 110 version 4.0, 102 views and procedures, 112 DATABASE entry, 92 database locking in multiuser environments choosing proper architecture, 623 issues, 618 Jet’s multiuser design, 619 Jet’s multiuser locking, 619-622 optimizing multiuser applications, 631-632 Oracle and SQL Server locking, 632-633 responding to locking errors, 627-629 testing existence and types of locks, 623-627 transactions, 629-631 Database Locking Mode property string, 149 Database Password property string, 149 database security basic steps, 664 client/server applications identifying blank passwords, 660 identifying current users, 658 listing user/group members, 657 managing groups/group ownership, 661 managing users, 654-655 SQL Grant/Revoke statements, 663
789
790
database security
common errors, 665 replication concerns, 651 split databases detached recordsets, 652 OwnerAccess option, 653 remote access (ADOX), 652 startup options, 650 workgroup-based, 638-640 assigning users to groups, 645 creating groups, 645 default user/group settings, 646 permissions, 649 setting/changing user passwords, 644 Database Server Names (DSNs), creating, 463-464 DatabaseName option, 362 DatabasePath option, 362 databases Access 2000, compatibility with Access 2002, 10 Access 97, upsizing to SQL Server and ADPs, 434 adding records, 488 compacting, 648 connecting, UDLs, 152-157 converting, error logging, 11 creating, ADOX, 168 design of, 45-48 relational design theory, 48-58 designing, performance issues, 384-386 executing SQL statements off of, 129-130 flat file, 54 linking tables to files, 438-440 modifying, setting new object permissions, 649 new, creating projects based on, Access Data Projects (ADPs), 428-434
normalization of, 45-48 relational design theory, 48-58 Northwind Catalog report, 257 creating crosstab queries, 724 NorthWind Trading Company sample, 129 Object Linking and Embedding (OLE DB), 436 locking error codes, 626-627 Universal Data Access Initiative, 121 vs. ODBC, 436 opening, multiuser applications, 620-621 opening connections, OLE DB Provider for Jet, 148 optimizing performance coding for, 400-410 configuring Jet engine, 374-381 forms, 395-399 performance testing, 381-384 queries, 387-395 reports, 399-400 Oracle accessing in Access 2002 with SQL pass-through queries, 465-468 calling stored procedures from SQL pass-through queries (SPTs), 489-490 connection costs, 464-465 creating connections from Access 2002 to Oracle databases, 484-487 creating stored procedures, 487-489 creating unbound interfaces to Oracle, 491-502 creating views to from Access 2002, 483-484
functions, Oracle vs. Access, 470-482 wildcards and position markers, 468-470 passive shutdown, Jet 4.0 support for, 110 placements and permissions, 697-699 repairing, 648 replication, importance of, 61 resyncing recordsets with, 165-167 round-tripping, 10 secured, opening, 151 SQL Server administering, ADPs, 424-425 reconnecting to, ADPs, 425-427 testing referential integrity, 69 updating records, 621-622 databases (Access), exporting as ASP, 734-735, 738-739 databases. See also queries, 60 databinding, XML, in IE4/IE5, 765-766 DataErr argument, 366 DataLink object, 426 DataLinks object, PromptNew method, 156 DataPage object, 727 DataSheet object, 568 DataSource control, changing at runtime, 728-729 Datasource object, 727 DataTable object, 568 DataType property, 420 Date argument, 479, 481 date calculations, Oracle functions, 479-482 Date data type, 479 DateClick event, 225 DateDblClick event, 226 DateTime data type (Jet 4.0), 108
diagrams
DateTimePicker control, 210, 215 Day argument, 481 Day property, 213, 217, 225 DayOfWeek property, 217, 225 dbname command-line option, 620 Deactivate event, 268 deactivating, navigation buttons, 495-496 deadlock avoidance, transaction conflicts, 632 deadlocks, 632 Debug menu, 326-331 Debug object, 322-324 Debug toolbar, 327 Debug.Assert method, 324 Debug.Print method, 323 debugging applications conditional compilation, 331-335 Debug menu, 326-331 Debug object, 322-324 eliminating logic errors, 318 Immediate window, 324-325 Integrated Development Environment (IDE), 318-322 sample code for practicing, 335-336 testing applications, 335 Decimal data type (Jet 4.0), 109 declaratons, variables, at top of procedures, 137 declaring data types, 332 variables, 332-333 Decode function, 476 decompiling, code modules, 402 default documents, choosing, IIS, 687-689 Default Value property, 420 Default View property, 63, 182 DefaultExt property, 215
defaults ASP script language, 733 compression options, 105 workgroup-based security, 646 DefaultWebOptions object, 541, 556, 560 Delete Data permission, 647 Delete method, 224 DELETE statement, 632 deletes, cascading, 57 deleting breakpoints, 326 code, Immediate window, 325 current records, 500 logic errors, 341 objects from collections, 315 Oracle data from forms, 500-501 runtime errors, 341-364 syntax errors, 338-340 tables, 97 temporary files, 372 toolbars, 196 Description argument, 346 Description property, 63, 141, 352 descriptions, queries, 60 DeselectAll method, 233 desigining, forms creating PivotTable and PivotChart views, 197-202 form controls, 189-197 form properties, 180-188 design time, Section property, 270-271 design-time ActiveX controls, 593 Timer adding code, 600-601 adding properties/methods/events, 597-600 creating, 594 creating interface, 595 distributing, 605 property pages, 605-607
saving, 597 setting properties, 597 testing in Visual Basic, 602, 604 using in COM-compliant applications, 607 using multiple controls, 605 designing applications, performance issues, 384-386 designs, databases, 45-48 relational design theory, 48-58 destroying, object variables, 332 detached recordsets, security using, 652 detail fields, adding, PivotTables, 199 detection, transaction conflicts, 632 developing SQL Server front-ends basing reports on stored procedures with passthrough queries, 440-442 configuring connections, 436-440 connection class, 459-460 executing commands with parameters, 452-459 forms, 448-450 OLE DB Provider, advanced features of, 450-452 OLE DB vs. ODBC, 436 reporting ODBC data, 443-446, 448 development environments, 670 DeviceArrival event, 231 diagrams creating relationships via, 430 database Access Data Projects (ADPs), 423 in Access Data Projects (ADPs) database containers, 418
791
792
dialog boxes
dialog boxes Add Watch, 329 Confirm Workgroup Information, 640 Content Control, 690 displaying, 527 Export to ASP, 734-735, 738 File, New, 415 Grouping and Sorting, 719 Insert ActiveX Control (Main menu), 706-711 New Report, 259 accessing Report Wizard, 244 Page Setup, 551 Parameters, 134 Quick Watch, 330 Record Macro, 534 Save As Data Access Page, 718 Save As Web Page (File menu), 703-704, 708 Sorting and Grouping, 248 write conflict, 622 Dialog object, 541, 556 DialogTitle property, 215 Dictionary object, 541 Dim statement, 302-303, 326, 511-514, 522 Direction property, 135 directories closing, 407 permissions, 696 virtual access levels, 684 vs. Web sites, 693-699 Directory Browsing Allowed option, 690 Directory Security tab, IIS, 687-689 disabling AutoJoin, 66 Journal feature (Outlook), 372 navigation buttons, 495-496 wizards, 194
DISALLOW NULL value, 97 disk compression, 372 disk defragmentation utilities, 372 Disk full error, 347 disk reads/writes evaluating using ISAMStats() function, 383-384 disks, saving recordsets to, 165-167 dispersion, data, 78 Display Database Window hiding, security advantages, 650 displaying code, forms, 319 Data Access Pages (DAPs), 721 data in hierarchies, 159-162 dialog boxes, 527 expressions, 329 folders, Outlook, 564, 566-567 message boxes, 527 objects, Object Browser, 290 query properties, 60-62 query results, 73 tasks, Outlook, 564 values, 329 values of variables, 324, 328-329 variables, 329 DistListItem object, 563 distributing ActiveX components, 581, 586-587 ActiveX controls in applications, 206, 238 ActiveX Slider control, 610 Timer ActiveX control, 605 docking, windows, 322 DoCmd object, 279 DoCmd.OpenReport method, 445 Document Change event, 523 Document object, 541
Document Summary information, Word, 553 documentation Active Server Pages (ASPs), 680 ActiveX Data Objects (ADOs), 143, 160 DocumentItem object, 563 DocumentProperty object, 556 documents default, choosing, IIS, 687-689 Excel, formatting, 556-557 Extensible Markup Language (XML), report exporting options, 264 printing, Automation object, 518-519 saving as Word templates, 542 Word adjusting page settings, 551 AutoCorrect, 548 AutoSummarize, 549 AutoText, 548 creating tables, 550-551 document styles, 547-548 Document Summary information, 553 document views, 549 fields, 552-553 footers, 550 footnotes, 550 formatting, 546-547 headers, 550 inserting Access data into, 543-546 previewing and printing, 551-552 tables of contents, 549-550 DocumentWindow object, 560 domains foreign keys, 50-51 hosting multiple independent domains on one server, 695
engines
Don’t Copy Locale on Compact property string, 149 Double data type (Jet 4.0), 108 double-byte encoding, 104 DownClick event, 236 DownloadBegin event, 238 DownloadComplete event, 238 DownloadMail property, 223 drawing vertical lines, reports, 281 drill-downs, interactive, Data Access Pages (DAPs), 722-723 DROP INDEX statement, 97 DROP keyword, 96, 483 DROP TABLE statement, 97 drop-down lists, 190-192 DropDown event, 219 DSN argument, 463 DSN. See Database Server Names DSNs, configuring, 437-438 dynamic row-level locking, 632 Dynasets, 65
E e-commerce, security, 689 e-mail creating with attachments, Outlook, 564-565 creating, Visual Basic for Applications (VBA), 566 publishing reports, 265-266 early binding, 514-515, 524 early binding (ActiveX), 410 Edit method, 163
editing compiler settings, 340 data in recordsets, 162-165 DataSource control at runtime, 728-729 fields with property procedures, 293-296 grouping structures in reports, 248-250 location of log files, Internet Information Server (IIS), 686 page settings, Word documents, 551 passwords, 112 recordsources in reports, 247-248 reports, 244 reports at runtime, 266-274 SQL statements, queries, 175-176 stored procedures, 422 subreports, 258 tables, 96, 109 Universal Data Links (UDL) files, 152, 155 values of variables, 324 values, variables, 329 EditMode property, 625 editors, Microsoft Script (MSE), 726-727 EditRec flag, 493, 498-499 Email method, 354 Email object, 541 EmailAddress option, 363 EmailAddress property, 352 EmailAllErrors method, 354 EmailErrors option, 363 EmailSignature object, 541 empty strings, testing for, 404 Empty value, 339
enabling navigation buttons, 495-496 wizards, 194 encoding double-byte, 104 single-byte, 104 Unicode, Jet 4.0 support for, 103, 105 Encrypt Database property string, 149 End argument, 475 Engine Type property string, 149 engines ASP, 739 Jet, 619-622 Microsoft Jet adCmdTable constant, 135 AutoNumber capabilities, 109-110 beta release of JOLT provider, 122 CHECK CONSTRAINTS option, 114-116 data access, 120 data types, 107-108 future support for, 102-103 history, 100-101 native OLE DB provider, 103 NT compatible sorting, 106 opening external data sources, 133-134 parameter passing, 112-113 parameter support, 135 row level locking, 103 searchable memo fields, 110 security enhancements, 111-112 SQL extensions, 110 transactions, 114 Unicode Compression, 105-106
793
794
engines
Unicode support, 103, 105 User List functionality, 110 version 4.0, 102 views and procedures, 112 VBScript, backward compatibility of, 673 entries DATABASE, 92 locking information files, 619 SERVER, 92 TRUSTED CONNECTION, 92 UID, 92 Enum keyword, 297 enumerated data types, 297-299 environments, development vs. production, 670 erasing breakpoints, 326 code, 325 current records, 500 logic errors, 341 objects from collections, 315 Oracle data from forms, 500-501 runtime errors, 341-364 syntax errors, 338-340 tables, 97 toolbars, 196 Err object, 344-346 Err.Description property, 345 Err.HelpContext property, 345 Err.HelpFile property, 345 Err.LastDLLError property, 345 Err.Number property, 345 Err.Source property, 345 error codes, locking, 626-627 error constants, LockType property, 624 Error Description column, Conversion Errors table, 11 Error event, 268
Error event procedures, 366-367 error functions, 367 Error Goto statement, 343 error handler, global, 591-593 Error Handler object, 311-312 error handlers, 333 error handling, 338 ActiveX Automation, 527 eliminating logic errors, 341 eliminating runtime errors, 341-364 eliminating syntax errors, 338-340 Error event procedures, 366-367 error functions, 367 Error object (global error handler) with non-Access applications, 364-365 nested procedures, 365 setting error trapping options, 367 error handling blocks, 343 error logging, database conversions, 11 error numbers Data Access Objects (DAOs), backward compatibility, 141 Error object, 141-142 Error object (cError), error options, 364-365 Error Options form (global error handler), 592 ErrorDatabase property, 352 ErrorNumber property, 352 errors database security-related, 665 debugging, 318 conditional compilation, 331-335 Debug menu, 326-331 Debug object, 322-324 Immediate window, 324-325
Integrated Development Environment (IDE), 318-322 sample code for practicing, 335-336 testing applications, 335 fixing immediately, 333 locking, responding to, 627-629 responding to, 346-348 ErrorsToAccessTable option, 363 ErrorsToTextFile option, 363 ErrorTextFile property, 352 Eval function, 673 event handlers, ActiveControl.DropDown, 190 Event keyword, 300 events Activate, 268 ActiveX controls, 597-600 AfterLabelEdit, 235 AfterUpdate, 496, 498 ASP, managing (Global.asa file), 750-751 btnDelete onClick, 500 btnNew onClick, 498-499 ButtonClick, 233-234 ButtonMenuClick, 234 Change, 236 Click, 213, 233, 300, 444 Close, 269 Collapse, 235 ConfigChanged, 231 creating, 300-302 DateClick, 225 DateDblClick, 226 Deactivate, 268 DeviceArrival, 231 Document Change, 523 DownClick, 236 DownloadBegin, 238 DownloadComplete, 238 DropDown, 219 Error, 268
fields
Expand, 235 Form Close, 188 Form Maximized, 188 Form Open, 186-188 Form Resize, 188 Format, 274 Initialize, 302 Load, 300 Minimized Form Restored, 188 NoData, 268 NoData report, 447-448 NodeClick, 235 Not In List, 189-190 onClick, 727 OnCurrent, 501 OnError, 366 OnOpen, 492-494 Open, 268, 274 Page, 269 PowerStatusChanged, 231 PowerSuspend, 232 Quit, 523 responding to, ActiveX Controls, 209 running reports, 268-269 scheduling, 480 Scroll, 229 SelChange, 228 SelChangeEvent, 226 StatusBar control, 230 StatusTextChange, 238 Terminate, 302 TimeChanged, 232 Timer ActiveX control, 601 TitleChange, 238 UpClick, 236 Welcome, 300-301 Excel adding spreadsheets to Data Access Pages (DAPs), 725 class arguments, 537 creating charts, 557 Data Shaping, 161-162 exporting PivotTable to, 201 formatting documents, 556-557
object model, 555-556 Parent property, 558-559 report exporting options, 263 transferring worksheets to Web pages, 703-704, 708 when to use, 532 Except statement, 342 Exclusive Asynch Delay property string, 149 exclusive locks, 632 exclusive mode, Jet exclusive engine, 620-621 Execute access, virtual directories, 684 EXECUTE command, 441 Execute command, 409 Execute function, 673 Execute method, 129 EXECUTE permission, 697 execute permissions, 690 Execute statement, 444 executed commands, opening recordsets, 136 executing code, suspending, 324 methods, ActiveX controls, 209 stored procedures with parameters, Access VBA, 489-490 Structured Query Language (SQL) statements against connections, 129-130 unmatched queries, 71 executing commands with parameters, 452-459 Exit Function statement, 342 Exit statement, 343 Exit Sub statement, 342-343 Expand event, 235 Explorer object, 563 Export to ASP dialog box, 734-735, 738 exporting PivotTables to Excel, 201 reports, 263-264 Expression function, 76, 79-80
expressions evaluating while applications run, 329-330 grouping, 249 relationships as, 468 report groupings, 248 saving updated fields, 498 testing, Immediate window, 323 viewing, 329 Extensible Markup Language. See XML Extensible Markup Language (XML) documents, 264 extensions, server, Front Page, 676 external data sources, opening, 133-134 Extreme Programming (XP), 127
F F-prefix, 187 False value, 171 Allow Additions property, 182 Close Button property, 184 Has Module property, 185 feedback providing for users, 525 visual, 226 Fetch method, 224 FFooter argument, 279 FHeader argument, 279 Field List value, Row Source Type property, 190 Field object, 139, 624 properties, 170 field properties, 95 fields adding constraints, 114-116 adding copies of to same query, 74 AutoNumber, 50, 172 Jet 4.0 capabilities, 109-110
795
796
fields
calculated, creating, PivotTables, 200-201 changing with property procedures, 293-296 creating, 169 creating with constraints, 95 crosstab queries, 84-85 detail, adding, PivotTables, 199 Memo, Jet 4.0 capabilities, 110 Name, 293 naming, 295 placing on query grids, 73 populating with data and checking current form context, 501 private, 295 public, 293 report groupings, 248 retrieving values, 293 setting properties, 170 styles for, 187 updated, expression for saving, 498 UserType, 293 Word, 552-553 [Sum of Quantity], reports, 252 Fields.Append method, 158 File already exists error, 347 File menu, 703-704, 708 File Name identifier, 154 File, New dialog box, 415 FileName property, 228 files ADE, creating, 433 database, linking tables to, 438-440 Global.asa, 695 locking information, 619 log, changing location of, IIS, 686 MDB, storage of Data Access Pages (DAPs) with, 719 referred to by records across intranets, reading, 140
setup, for ActiveX components, 581-582, 585-586 text, report exporting options, 264 Universal Data Links (UDL) creating, 152 editing, 152, 155 workgroup information, 619 workgroups, system.mdw, 637-639 FillForm() function, 493 Filter property, 63, 181, 215, 266-267 FilterIndex property, 215 filtering Outlook items, 567 reports, 266-267 reports at runtime, 445-448 FilterOn property, 266-267 Find and Replace, Word, 546 Find method, 228 Find object, 541 finding Outlook items, 567 procedures that have been called, 330-331 firewalls, 692 First function, 76-78 First Normal Form, 54 First object, 541 FirstSibling property, 235 flags EditRec, 493, 498-499 WITH COMPRESSION, 105-106 flat file databases, 54 FlatScrollBar control, 211, 217 FlexGrid, 159 Floor() function, 477 Flush Transaction Timeout property string, 149 folders, adding and displaying, Outlook, 564-567 Folders object, 563 FontName property, 215 FontSize property, 215
footers group, 249 reports, 242 Word documents, 550 For Each command, 195 For Each loop, 315 For Next loop, 315 For statement, 727 ForceNewPage property, 272 foreign keys, 50-51 Form Close event, 188 Form Maximized event, 188 Form Open event, 186-188 Form Resize event, 188 Format argument, 481 Format event, 274 format properties, 182-183 Format property, 217 FormatCount property, 273 formatting, reports, 284 forms Access Data Projects (ADPs), 424 ActiveX controls adding to forms, 207-208 distributing in applications, 206, 238 how to use, 204-206 installing, 206-207 registering, 207 setting properties, 208 writing code to execute methods or respond to events, 209 adding Oracle data, 498-499 bound, 448 clearing, 498-499 converting to Microsoft Forms, 537 deleting Oracle data from, 500-501 designing creating PivotTable and PivotChart views, 197-202 form controls, 189-197 form properties, 180-188
fuzzy searches
filling with values from recordsets, 449-450 in Access Data Projects (ADPs) database containers, 418 initializing, 492-494 loading data in, 492-494 Microsoft Forms, 536-537 optimizing performance, 395-399 populating fields with data and checking current context of, 501 preparing for new records, 498 saving Oracle data to, 499-500 SQL Server front-ends, 448-450 subforms, 188, 194-195 unbound, 448-450 updating Oracle data with, 496-498 user interaction, managing (Request object), 750 using Chart control, simple crosstabs SQL query, 709-711 using PivotTable control, 711-713 using Spreadsheet control, 706-707 viewing code for, 319 without controls, 188 Forward method, 224 From clause, 467 FromDate argument, 276 Front Page Server Extensions, 676 front-end development SQL Server basing reports on stored procedures with passthrough queries, 440-442 configuring connections, 436-440 connection class, 459-460 executing commands with parameters, 452-459
forms, 448-450 OLE DB Provider, advanced features of, 450-452 OLE DB vs. ODBC, 436 reporting ODBC data, 443-446, 448 FrontPage, 693 FrontPage Web option, 690 FullRowSelect property property, 222 FullScreen property, 237 functions Add Months(), 479-480 aggregate. See Total queries assigning Row Source Type property to, 190-191 Avg, 75-78 built-in, printing values, 324 calculation, reports, 251 Ceil(), 477 concatenation, reports, 254-256 Count, 75-77 CreateGroupLevel(), 278-279 CreateObject, 515-516 CurrentContext(), 495 CVErr, 367 Decode, 476 error, 367 Eval, 673 Execute, 673 Expression, 76, 79-80 FillForm(), 493 First, 76, 78 Floor(), 477 GetObject, 512-515 GetRowCount, 191 GetUserName, 350 Greatest, 478 Group By, 76 IIf(), 476 Immediate IF (IIF), reports, 251-253 in reports, 250-256 Instr, 472-473, 476
IsError, 367 Last, 76, 78 Least, 478 Len, 323 LTrim, 473-474 MakeCrosstabQuery(), 275-276 Max, 75, 78 Min, 75, 78 Months Between(), 480 Next Day(), 480-481 Nvl(), 477 OpenParamQuery(), 90-91 optimizing performance, coding for, 400-410 Oracle vs. Access, 470-482 PrepareCrosstabReport(), 276-278 Round(), 477 RTrim, 473-474 running, Immediate window, 324-325 RunParamQuery(), 89-90 Sgn(), 478 Sign(), 478 Split(), 254 standard deviation. See StDev function StDev, 75, 78-79 Substr, 475-476 Sum, 75 Sysdate(), 479, 482 testing, Immediate window, 323 To Date(), 478, 481 Trim(), 262, 473-474 Trunc(), 478 TypeOf, 349 UCase(), 254 UpdateField(), 497-498 user-defined, reports, 252-254 Var, 75, 78-79 variance. See Var function fuzzy searches, Soundex algorithm, 475
797
798
GetActiveControlValue method
G GetActiveControlValue method, 354 GetLineFromChar property, 228 GetList argument, 191-192 GetNumTicks method, 229 GetObject function, 512-515 GetRowCount function, 191 GetTickCount function, 381 GetUserName function, 350 GetUserName method, 354 Global Bulk Transactions property string, 149 global error handler cError ActiveX DLL, 591 class modules, 593 data source, 592 Global Error Handler Data.mdb, 592 Global Partial Bulk Ops property string, 149 global recordsets, 493-494 global unique identifiers. See GUIDs global variables, 492 Application object (ASP), 748 Global.asa, 695 Global.asa file (ASP), events, managing, 750-751 GoBack method, 238 GoForward method, 238 GoHome method, 238 GoSearch method, 238 GotDotNet Web site, 733 Grant/Revoke statements (SQL), security settings using, 663 Graph class arguments, 537 creating graphs and charts, 568-569 object model, 567-568 when to use, 532
graphics adding to shapes on slides, 562 in forms, optimizing loading speed, 395-396 graphs, creating, 568-569 Greatest function, 478 GridLines property, 222 grids query creating queries, 72-74 placing parameters in, 87-88 Query By Example (QBE). See query design window Group By function, 76 GROUP BY statement, 82-83, 469-470 group footers, 249 group headers, 248 group intervals, in report groupings, 249 Group On property, 249 grouping data alphabetically in reports, 279 expressions, 249 Grouping and Sorting dialog box, 719 grouping structures, in reports, 248-250 groupings biweekly, reports, 279 in reports, 250 GroupInterval value, 279 GroupKeepTogether property, 267 GroupOn property, 279 groups client/server applications, listing, 657-658 in client/server applications, managing, 661
new, resetting page numbers, 281 workgroup-based security, 645-646 Grp Keep Together property, 262 GUID data type (Jet 4.0), 108 GUIDs (global unique identifiers) assigning to ActiveX components, 588-589
H handlers error, 333 event, 190 handling errors eliminating logic errors, 341 eliminating runtime errors, 341-364 eliminating syntax errors, 338-340 Error event procedures, 366-367 error functions, 367 nested procedures, 365 setting error trapping options, 367 hardware requirements for optimal performance, 371-373 Windows NT Option Pack, 674 HasContinued property, 274 HasData property, 267 HasModule property, 185 HAVING statement, 81-83, 469-470 headers group, 248 reports, 242 Word documents, 550
Insert Data permission
headings column, 85 row, 85 Height property, 270 help, Err object, 346 HelpContext argument, 346 HelpContext property, 352 HelpFile argument, 346 HelpFile property, 352 HideColumnHeaders property, 222 hiding data in reports, 279 queries, 61 hierarchies, 159-162 Home Directory tab, Internet Information Server (IIS), 689-691, 694 hosting, multiple Web sites, 695 Hour property, 217 HTML (Hypertext Markup Language), 733 ASP content, 733 code, for cSlider ActiveX control, 614 placements and permissions, 697-698 report exporting options, 264 versus XML, 764 Hyperlink Address property, 197 Hyperlink SubAddress property, 197 hyperlinks, 196-197
I IBM development of relational database management systems (RDBMSes), 46
IDE (Integrated Development Environment) debugging applications, 318-322 identifiers, File Name, 154 Identity Increment property, 420 Identity property, 420 Identity Seed property, 420 If() function, 476 If/Then conditions, using with Decode function, 476 IGNORE NULL value, 97 IIF (Immediate IF) functions, 476 avoiding, 409 reports, 251-253 IIf() statement, 85 IIS (Internet Information Server), 671-673, 680, 685-699 IIS (Internet Information Service), 129 IIS3 (Internet Information Server 3), 739 Image control (Access), 205 optimizing image-handling using, 395-396 ImageCombo control, 211, 218 ImageCount property, 220 ImageList control, 211, 219-221 ImageList control property page, 605 ImageList property, 219, 232-234 images Add, 220 adding to shapes on slides, 562 ImageList control, 220 in forms, optimizing loading speed, 395-396 Immediate window, 321-325 Implicit Commit Synch property string, 149
Inbox folder, displaying, Visual Basic for Applications (VBA), 566 Increment property, 236 Indentation property, 219, 235 Index Counts criteria expression (Jet query handling), 388 Index Intersection criteria expression (Jet query handling), 388 Index join type (Jet query handling), 389 Index property, 220 Index Server, 672 Index This Directory option, 690 Index Union criteria expression (Jet query handling), 388 Index-Merge join type (Jet query handling), 389 indexes creating, 96-97, 171-172 in queries, 387-388 Section property, 269-270 speeding queries using, 386 tables, setting, 429, 631 IndexNulls property, 171 infinity (∞) symbol, 67 Initcap string, 471-472 InitDir property, 215 Initialize event, 302 initializing, forms, 492-494 inner joins, 68-70, 72 input parameters, Jet database engine support, 135 InputBox method, 728 Insert ActiveX Control dialog box, cTimerControl option, 604 Insert ActiveX Control dialog box (Main menu), 706-711 Insert Data permission, 647
799
800
INSERT statement
INSERT statement, 469, 632 inserting Access data into Word documents, 543-546 Calendar controls, 214 class modules, 292 insertion points, moving, Immediate window, 325 Inspector object, 563 Install Locations screen, 585 installing ActiveX code components, 581 deploying/distributing, 586-587 setup files, 581-582, 585-586 ActiveX controls, 206-207 Internet Information Server (IIS), 672-673 Personal Web Server (PWS), 672 Visual InterDev RAD Support, 678 Web servers, 675-681 Windows NT 4 Server, 673 Windows NT Option Pack, 675-682 installing applications, 373-374 instances applications, 524 creating, 513-516 creating multiple instances of objects, 304 objects, 290 Instancing property (cSound object), 575-576 instantiating, ActiveX controls, 593 Instr function, 472-473, 476 Integer data type (Jet 4.0), 108 integers, Top Values property, 64 Integrated Development Environment (IDE) debugging applications, 318-322
integration Access 2002 with Microsoft Office auto macros, 535-536 automating Excel, 555-559 automating Graph, 567, 569 automating Outlook, 562-567 automating PowerPoint, 559-562 automating Word, 540-552 class arguments, 537 Microsoft Forms, 536-537 Object Browser, 537 printing Access information in Word reports, 538-540 reasons for, 531-532 using the right tools, 533 writing code, Macro Recorder, 533 integrity, referential, 69 IntelliSense, debugging code, 329 IntelliSense technology, 289, 303 interactive drill-downs, Data Access Pages (DAPs), 722-723 interactive Web sites, ASP format, 732-733 interfaces application programming (API), proprietary, 120 new features, 8 Oracle Call (OCI), 485 unbound, creating to Oracle, 491-502 UserControl object for ActiveX cSlider control, 608 Timer ActiveX control, 595 International Standards Organization (ISO), 104 Internet Connection Services for RAS, 676
Internet Explorer VBScript engine, 673 viewing Data Access Pages (DAPs), 721 Internet Information Server (IIS), 671-673, 680, 685-699 Internet Information Service (IIS), 129 Internet Package (Package and Deployment Wizard) packaging ActiveX controls for Web pages, 610-613 Internet Server Application Programming Interface. See ISAPI Internet Service Manager (ISM), 677 intranets, reading files referred to by records across, 140 Is RowGrid property, 420 ISAMStats() function (Access), performance testing using, 383-384 ISAPI (Internet Server Application Programming Interface), 739 IsError function, 367 ISM (Internet Service Manager), 677 ISO (International Standards Organization), 104 isolation, applications, 694-695 Item object, 541, 563 IUSER account, 695-698
J JavaScript, ASP content, 733 Jet (database engine) adCmdTable constant, 135 beta release of JOLT provider, 122 data access, 120
late binding
opening external data sources, 133-134 parameter support, 135 version 4.0, 102 AutoNumber capabilities, 109-110 CHECK CONSTRAINTS option, 114-116 data types, 107-108 future support for, 102-103 history, 100-101 NT compatible sorting, 106 parameter passing, 112-113 row level locking, 103 searchable memo fields, 110 security enhancements, 111-112 SQL extensions, 110 transactions, 114 Unicode Compression, 105-106 Unicode support, 103-105 User List functionality, 110 views and procedures, 112 Jet 4.0 configuring for optimal performance, 374 registry settings, 376-380 registry settings, changing, 375, 381 query-handling, performance optimization, 387-392 Jet database engine multiuser design, 619 multiuser locking, 619-622 OLE DB Provider for Jet, 148-151, 154-158 Jet Replication Objects (JRO), 123 Jet User Roster, 157-158
Jet-based security database replication concerns, 651 split databases, 652-653 startup options, 650 users/groups assigning users to groups, 645 creating groups, 645 default settings, 646 permissions, 649 setting/changing user passwords, 644 workgroup-based, 638-640 join types (Jet), in queries, 388-389 joins appearance of in table panes, 66 infinity (∞) symbol and number 1, 67 inner, 68-72 outer, 69-72 properties sheets, 67 selecting, 68-69 JOLT 4.0, 103 JOLT provider, 122 Journal feature (Outlook), 372 Journal folder, displaying, Visual Basic for Applications (VBA), 566 JournalItem object, 563 Journals, creating, Visual Basic for Applications (VBA), 566 JRO (Jet Replication Objects), 123
K KeepTogether property, 249-250, 272 key objects, 171-172
Key property, 220 Key value, 220, 313 keys candidate, 49 composite, 49 foreign, 50-51 primary, 49, 163 shortcut, field codes, 553 keystrokes, converting into Visual Basic for Applications (VBA) code, 533 keywords ADD, 96 Alter, 483 Call, 346 Create, 483 DROP, 96, 483 Enum, 297 Event, 300 Me, 333 New, 513-514, 524 or Replace, 483 ParamArray, 90 Private, 522 Public, 522 resume next, 344 Select, 467 Set, 303, 313-315, 512 WithEvents, 300-301, 520-523 Knowledge Base, 214
L Label Wizard, creating mailing labels, 259-262 LabelEdit property, 222, 235 LabelWrap property, 222 LargeChange property, 217, 229 Last function, 76-78 Last object, 541 LastDllError property, 353 LastSibling property, 235 late binding, 514-515
801
802
layouts
layouts, changes in, Visual Basic Environment (VBE), 9-10 Least function, 478 left outer joins, 68 Legend object, 568 Len function, 323, 404 Length property, 420 Letter object, 308-310 Level property, 353 libraries Microsoft OLE DB Service Component 1.0 Type, setting references to, 156 type, DAO, 143 license requirements, Web components, 703 licenses, ActiveX controls, 206 LimitToList property, 189 Line method, 269 LineNumber property, 353 lines empty, creating at intervals, 280-281 vertical, drawing, reports, 281 LineStyle property, 235 Link Child Fields property, 63, 258 Link Master Fields property, 63, 258 Linked Table Wizard, 13 linked tables, creating, 169-170 linking Oracle tables to Access 2002, 462-465 SQL Server tables to database files, 438-440 tables to database files, 438-440 links, Office, publishing reports, 265 List Box control, 192 ListImages object, 220
listings adding records, Oracle, 488 addressing Oracle through Structured Query Language (SQL) and ActiveX Data Objects (ADOs), 499-500 aligning report pages for binding, 282-283 ASP change password form, 760-763 creating chart control for GIF collection, 770 Global.asa file for virtual directory, 751 HTML code to create login form, 755-759 HTML for Web page with Simple Variable, 745 increasing Web page font size using VBScript loop, 746-747 Results of Exporting Northwind Customers Table to ASP (26.1), 735, 738 Using ADO in ASP (26.8), 753-754 Using VBScript with a Simple Variable, 744 working with Chart Office 2000 Web Component, 771-772 assigning object ownership, 660-661 assigning Row Source Type property to functions, 190-191 calculating page subtotals, 283 canceling reports with no data, 268 changing positions, even and odd numbered pages, 282 CHECK CONSTRAINTS option, 115-116
cleaning up Oracle connections after closing, 501-502 clearing forms, 498-499 combining WHERE and HAVING statement, 82-83 connecting to SQL Server via OLE DB Provider, 450-451 Connection class, 459-460 Connection object, 126 connections through Uniform Resource Locators (URLs), 128 creating crosstab queries, 275-276 crosstab reports, 276-278 custom data page navigation buttons, 727 databases, ADOX, 168 empty lines at intervals, 281 fields with constraints, 95 linked tables, 170 open connections to Oracle and filling forms, 493 query procedures, 174-175 query views, 174 recordsets and filling MSHFlexGrids, Visual Basic 6.0, 160 recordsets from nonrelational data, 158-159 relationships, 173 tables with AutoNumber fields and PrimaryKey indexes, 172 views in Oracle from Access 2002, 485-487, 489-490, 494-496, 498-500, 502 cSound class module code, 573 Data Shaping, Microsoft Excel, 161-162 deleting current records, 500 drawing vertical lines, 281 Error object, 141-142
LoadFile method
executing action queries with ActiveX Data Objects (ADO) commands, 138-139 commands with parameters, 453-454 commands without parameters, 454-455 parameter queries from code, RunParamQuery() function, 89-90 stored procedures with parameters, Access VBA, 489-490 Structured Query Language (SQL) statements against connections, 129-130 expression for saving updated fields, 498 Field object, 139 filling forms with data, global recordsets, 493-494 with values from recordsets, 449-450 handling filters and NoData report event, 447-448 Jet 4.0 enhancements security files, 111-112 Unicode compressed field in VBA, 105-106 Jet User Roster, 157-158 NextRecordset method, 451-452 opening parameter queries, ActiveX Data Objects (ADO), 113 opening recordsets, 132-133, 136 performance testing, 382-384 populating fields with data and checking current form context, 501 preparing forms for new records, 498
programmatic Universal Data Links (UDLs), 156-157 programming navigation button movement, 494-496 Property object, 139 providing report filters, BuildCriteria method, 446-447 query performance, 391-392 querying return values, 458 reading files referred to by records across intranets, 140 reconnecting DataSource connection string at runtime, 728-729 Refresh method, using with parameters, 456-457 reports, user-defined functions, 253 resetting SQL statements, 175 running parameter queries, 90-91 saving recordsets to disks, reopening, and resynching with original databases, 165-167 security binding forms to detached recordsets, 652 managing users in client/server applications, 654-655 opening databases/permissions using ADOX, 652 using ADOX to list blank passwords, 660 using ADOX to list current users, 658 using ADOX to list user/group members, 657 simple stored procedures, 432 simple views, 432 Split() function, 254, 257 SQL statements calculating averages, 78 counting records, 77
creating inner joins, 70-72 creating outer joins, 70, 72 crosstab queries, 84-85 designing crosstab queries with custom column headings, 86-87 parameter queries in design mode, 88-89 resultset created with custom expression, 80 rewriting and running reports, 445 selecting records for aggregation, 81 unmatched queries, 71 stored procedures, 441 transactions in VBA, 630 transactions, Jet 4.0, 114 Universal Data Links (UDLs) in VBA code, 154-155 UpdateField() function, 497-498 updating connections, Access Data Projects (ADPs), 427 using Oracle parameters from Access VBA, 486-487 Web components Chart control, unbound charts, 711 Spreadsheet control, loading data at runtime, 706-707 XML Creating XML Inside an ASP, 769-770 Creating XML Programmatically Inside a VB Component, 767-769 databinding in IE4 and IE5, 765-766 ListItems object, 221 lists, numbered, reports, 280 ListView control, 205, 211, 221-222 live locks, 632 Load event, 300 LoadFile method, 228
803
804
loading
loading data in forms, 492-494 forms, optimizing speed, 395 images, optimizing speed, 395-396 local paths, 690 Locals window, 321, 329 LocationName property, 237 LocationURL property, 237 Lock Delay property string, 149 lock placements/releases evaluating using ISAMStats() function, 383-384 Lock Retry property string, 149 lock starvation, 632 lock types, ActiveX Data Objects (ADOs), 164-165 locking databases in multiuser environments choosing proper architecture, 623 issues, 618 Jet’s multiuser design, 619 Jet’s multiuser locking, 619-622 optimizing multiuser applications, 631-632 Oracle and SQL Server locking, 632-633 responding to locking errors, 627-629 testing existence and types of locks, 623-627 transactions, 629-631 dynamic row-level, 632 optimistic, 621-623 page-level, 622 pessimistic, 622, 628 record-level, 622-623 row-level, 622-623 locking error codes Object Linking and Embedding databases (OLE DB), 626-627
locking errors, responding to, 627-629 locking information file, 619 LockType property, 624 Log Access option, 690 log files changing location of, Internet Information Server (IIS), 686 logging errors, database conversions, 11 logging properties, choosing, Internet Information Server (IIS), 686-687 logic errors debugging, 318 conditional compilation, 331-335 Debug menu, 326-331 Debug object, 322-324 Immediate window, 324-325 Integrated Development Environment (IDE), 318-322 sample code for practicing, 335-336 testing applications, 335 eliminating, 341 login forms, membershipbased Web page, creating (ASP), 755-759 Login method, 299-300 LogonUI property, 223 LongBinary data type (Jet 4.0), 108 LongInteger data type (Jet 4.0), 108 LongText data type (Jet 4.0), 107 Lookup join type (Jet query handling), 389 loops collections, 315 enhancing speed of, 408 For Each, 315 For Next, 315
Lower string, 471-472 LTrim function, 473-474
M Macro Recorder, writing code with, 533 macros Access Data Projects (ADPs), 424 auto, 535-536 in Access Data Projects (ADPs) database containers, 418 mail merge, Word, 544 mailing labels, creating, 259-262 Main menu, Insert ActiveX Control dialog box, 706-711 Main screen, Microsoft Management Console (MMC), 682 maintaining code modules, 401 Web sites, ASP format, 732-733 Make Table query, 94 MakeCrosstabQuery() function, 275-276 managing, Web servers, 682-692 many-to-many relationships, 52 MAPIMessages control, 211, 223-224 MAPISession control, 211, 222-223 markers, position, 468-470 Mastering Access 97 Development (italics), 180 mathematical calculations, Oracle functions, 477-478 Max Buffer Size property string, 149
methods
Max function, 75, 78 Max Locks Per File property string, 150 Max property, 227-229, 236 Max Records property, 63-65 MaxDate property, 217, 225 MaxLength property, 228 MaxSelCount property, 225 MDAC (Microsoft Data Access Components), 122, 671, 676 MDB. See Access databases MDB files, storage of Data Access Pages (DAPs) with, 719 MDW (workgroup) files naming, 640 workgroup-based security, 638-640 Me keyword, 333 Members pane, Object Browser, 510 membership-based Web pages, creating (ASP), 754-763 Memo fields, Jet 4.0 capabilities, 110 memory, requirements, 371-373 memory space, running applications in, 695 Menu Bar property, 184 Menu Options, limiting, security advantages, 650 MenuBar property, 238 menus Debug, 326-327, 329-331 File, 703-704, 708 Main, Insert ActiveX Control dialog box, 706-711 new features, 8 pop-up, creating, 196 Merge join type (Jet query handling), 389 message boxes debugging with, 334-335 displaying, 527
MessageBox method, 354 messages, e-mail creating with attachments, Outlook, 564-565 creating, Visual Basic for Applications (VBA), 566 methods .Supports, 624 AccessError, 367 ActiveX controls, Timer control, 597-600 Add, 218-219, 313-314 AddNew, 158, 164 AddToErrorHandlerOutlook, 353 Animation control, 212 AutoFit, 557 Automation object, 517 BeginTrans, 630 BeginTransComplete, 630 BuildCriteria, 446-447 Calendar control, 213 cError object, 353-354 Clear, 345, 353 ClearSel, 229 Close, 212 CommitTrans, 630-631 CommitTransComplete, 630 Common Dialog control, 215 Compose, 224 Connect, 459 Connection object, 630 Copy, 224 CreateParameter, 454-455 creating, 299-300 CurrentProject.Connection, 427 CurrentProject.OpenConnection, 426 Customize, 234 DateTimePicker control, 216-217 Debug.Assert, 324 Debug.Print, 323 Delete, 224 described, 291, 509 DeselectAll, 233
DoCmd.OpenReport, 445 Edit, 163 Email, 354 EmailAllErrors, 354 Err object, 345-346 Execute, 129 executing, ActiveX controls, 209 Fetch, 224 Fields.Append, 158 Find, 228 FlatScrollBar control, 217-218 Forward, 224 GetActiveControlValue, 354 GetNumTicks, 229 GetUserName, 354 GoBack, 238 GoForward, 238 GoHome, 238 GoSearch, 238 ImageCombo control, 219 ImageList control, 220 InputBox, 728 Line, 269 ListView control, 221-222 LoadFile, 228 Login, 299-300 MAPIMessages control, 224 MAPISession control, 223 MessageBox, 354 MonthView control, 225-226 MoveFirst, 727 MoveLast, 727 MoveNext, 727 MovePrevious, 727 MsgErrorDetails, 354 Navigate, 238 NextDay, 213 NextRecordset, 451-452 objects, setting, 517 OfficeAssistant, 354 Open, 212 Overlay, 220 Play, 212 PlaySound, 354 PreviousDay, 213 PrintOut, 518-519
805
806
methods
ProcessError, 354-357 PromptNew, 156, 426 Property Get, 186-187 Property Let, 186 publishing reports, 263-266 Quit, 519 Raise, 345-346 Record.GetChildren, 138 Refresh, 238, 456-457 Reply, 224 RestoreToolbar, 234 Restrict, 567 RichText control, 228 RollbackTrans, 630 RollbackTransComplete, 630 Run, 542 RunCommand, 279 save, 165, 224 SaveFile, 228 Screen.ActiveControl, 333 Screen.ActiveForm, 333 SelPrint, 228 Send, 224 ShowAVIForm, 354 ShowColor, 215 ShowFont, 215 ShowHelp, 215 ShowOpen, 215 ShowPrinter, 215 ShowSave, 215 SignOff, 223 SignOn, 223 Slider control, 229-236 Stop, 212, 238 TodayDay, 213 Update, 163, 496-498 UserInputBox, 354 VBA collections, 313-316 WaitState, 354 WebBrowser control, 237-238 WriteErrorToTable, 354 WriteErrorToTextFile, 354 Microsoft ActiveX Data Object (ADO) documentation, 143 ActiveX Data Objects (ADO) releases, 122-125
IntelliSense technology, 289, 303 reference to pass-through queries in documentation, 92 Universal Data Access Initiative, 121 Microsoft Access report exporting options, 263 See Access Microsoft Chart, adding charts to Data Access Pages (DAPs), 726 Microsoft Data Access Components (MDAC), 122 Microsoft Data Access Components (MDACs), 671, 676 Microsoft Excel, 532 adding spreadsheets to Data Access Pages (DAPs), 725 class arguments, 537 creating charts, 557 Data Shaping, 161-162 exporting PivotTable to, 201 formatting documents, 556-557 object model, 555-556 Parent property, 558-559 report exporting options, 263 Microsoft Forms, 536-537 Microsoft FrontPage, 693 Microsoft Graph class arguments, 537 creating graphs and charts, 568-569 object model, 567-568 when to use, 532 Microsoft Hierarchical FlexGrid, 159 Microsoft Index Server, 678 Microsoft IntelliSense, debugging code, 329 Microsoft Jet database engine, 619-622 Microsoft Jet. See Jet Microsoft Knowledge Base, 214
Microsoft Management Console (MMC), 678, 684 Microsoft Message Queue (MSMQ), 671, 676 Microsoft Office incorporating Web components with Data Access Pages (DAPs), 724-726 integrating with Access 2002 auto macros, 535-536 automating Excel, 555-559 automating Graph, 567-569 automating Outlook, 562-567 automating PowerPoint, 559-562 automating Word, 540-552 class arguments, 537 Microsoft Forms, 536-537 Object Browser, 537 printing Access information in Word reports, 538-540 reasons for, 531-532 using the right tools, 533 writing code, Macro Recorder, 533 version numbers, 516 Microsoft Office XP, 214 Microsoft OLE DB Service Component 1.0 Type Library, setting references to, 156 Microsoft Outlook adding and displaying folders, 564 adding and displaying tasks, 564 class arguments, 537 creating e-mails with attachments, 564-565 creating Outlook items, 565-566 displaying default Outlook folders, 566-567 filtering Outlook items, 567
MovePrevious method
finding Outlook items, 567 object model, 562-563 when to use, 532 Microsoft PowerPoint adding photos to shapes on slides, 562 adding slides, 561 adding transition effects, 561 class arguments, 537 formatting slides, 561-562 object model, 559-560 running PowerPoint presentations, 562 when to use, 532 Microsoft Script Debugger, 678 Microsoft Script Editor (MSE), 726-727 Microsoft Transaction Manager (MTS), 677 Microsoft Transaction Server (MTS), 680-682 Microsoft Universal Data Links (UDLs), connecting to databases, 152-157 Microsoft Visual InterDev, 676, 693 Microsoft Word adjusting page settings, 551 AutoCorrect, 548 automating, 551 AutoSummarize, 549 AutoText, 548 class arguments, 537 creating tables, 550-551 document styles, 547-548 Document Summary information, 553 document views, 549 events exposed by Application object, 521 fields, 552-553 footers, 550 footnotes, 550 formatting documents, 546-547 headers, 550
inserting Access data into documents, 543-546 object model, 540-541 previewing and printing documents, 551-552 printing Access information in reports, 538, 540 tables of contents, 549-550 templates, 542 Min function, 75, 78 Min Max Buttons property, 183 Min property, 227-229, 236 MinDate property, 217, 225 Minimized Form Restored event, 188 minimum installation option, Windows NT Option Pack, 679-680 minimum requirements for running Access, 371 Minute property, 217 MMC (Microsoft Management Console), 678, 684 MMC task pad, 695 Modal property, 184 Mode property, 127 models, Object, 509 modes exclusive, Jet exclusive engine, 620-621 query, ANSI-92, 13 shared, Jet shared engine, 620-621 Modify Design permission, 647 modifying compiler settings, 340 data in recordsets, 162, 164-165 DataSource control at runtime, 728-729 fields with property procedures, 293-296 grouping structures in reports, 248-250
location of log files, Internet Information Server (IIS), 686 page settings, Word documents, 551 passwords, 112 recordsources in reports, 247-248 reports, 244 reports at runtime, 266-274 SQL statements, queries, 175-176 stored procedures, 422 subreports, 258 tables, 96, 109 Universal Data Links (UDL) files, 152, 155 values of variables, 324 values, variables, 329 modules Access Data Projects (ADPs), 424 class cError, 351, 358 inserting, 292 naming, 292 in Access Data Projects (ADPs) database containers, 418 reports, user-defined functions, 252-254 Month property, 213, 217, 225 MonthBackColor property, 225 MonthColumns property, 225 MonthRows property, 225 Months Between() function, 480 MonthView control, 205, 211, 224-226 MoveFirst method, 727 MoveLast method, 727 MoveLayout property, 272-273 MoveNext method, 727 MovePrevious method, 727
807
808
moving
moving insertion points, Immediate window, 325 page numbers, 282 tables, 423 MSDataShape OLE DB Provider, 159 MSDN Web site VBScript documentation, 743 XML resources, 763 MSE (Microsoft Script Editor), 726-727 MsgCount property, 224 MsgDateReceived property, 224 MsgErrorDetails method, 354 MsgID property, 224 MsgNoteText property, 224 MsgRead property, 224 MsgSent property, 224 MsgSubject property, 224 MSHFlexGrids, filling, Visual Basic 6.0, 160 MSMQ (Microsoft Message Queue), 671, 676 MSYS prefix, 61 MTS (Microsoft Transaction Manager), 677 MTS (Microsoft Transaction Server), 680-682 Multi-Select List Box control, 193 Multi-Select property, 193 multi-source reports, 246-247 MultiLine property, 228 Multiple User Domains feature, 695 MultiSelect property, 222, 225, 232 multiuser environments database locking choosing proper architecture, 623 issues, 618 Jet’s multiuser design, 619 Jet’s multiuser locking, 619-622
optimizing multiuser applications, 631-632 Oracle and SQL Server locking, 632-633 responding to locking errors, 627-629 testing existence and types of locks, 623-627 transactions, 629-631 multiusers, Jet 4.0 support for, 110 MyTimer class, 306-307
N n-tier architecture, 488, 491 Name fields, 293 Name object, 556 Name property, 135, 270 named parameters, 346 names, Database Server (DSN) creating through Open Database Connectivity (ODBC), 463-464 NameSpace object, 563 naming class modules, 292 fields, 295 MDW files, 640 naming conventions, consistency in code, 334 Native Error property, 141 Navigate method, 238 navigation buttons creating for data pages, 727 programming to move through recordsets, 494-496 Navigation Buttons property, 183 Nested iteration join type (Jet query handling), 389 nested procedures, error handling, 365 nesting, transactions, 631
networks accessing workgroup templates, 542 firewalls, 692 minimizing traffic when using Word templates, 542 virtual directories, 693 New Database Password property string, 150 New keyword, 513-514, 524 New Project dialog box, 574 ActiveX Control option, 595 New Report dialog box, 259 accessing Report Wizard, 244 NewRoworCol property, 271 NewSession property, 223 Next Day() function, 480-481 Next property, 235 NextDay method, 213 NextRecord property, 272-273 NextRecordset method, 451-452 No access, 690 No Compatibility option (Windows Registry), 591 NoData event, 268 NoData report event, 447-448 NodeClick event, 235 Nodes object, 235 non-relational data accessing, ActiveX Data Objects (ADOs), 157-162 creating recordsets from, 158-159 None setting, 271-272 None value, Row Source Type property, 190 normal forms benefits of, 56-57 described, 54 First, 54 Second, 55 Third, 56
Object Type column
normalization databases, 45-48 relational design theory, 48-58 normalizing data, performance issues, 385-386 Northwind database Catalog report, 257 creating crosstab queries, 724 customer table, publishing using XML, 764-766 NorthWind Trading Company, sample database, 129 Not In List event, 189-190 NOT NULL statement, 94 NOT NULL value, 469 Note property, 353 NoteItem object, 563 notes, creating, Visual Basic for Applications (VBA), 566 Notes folder, displaying, Visual Basic for Applications (VBA), 566 Nothing value, 343-344 Nothing variable, 303, 517-518, 526 Now property, 353 NT. See Windows NT NT File System (NTFS), 696 NULL value, 469 Null values, aggregate functions, 76 Number argument, 346 Number of Update Retries option, 628 Number property, 141 numbered lists, reports, 280 numbers credit card, security of, 689 error, Data Access Objects (DAOs), 141 identifying largest and smallest in data, 78
pages moving, 282 resetting for new groups, 281 Number_parameter, 346 Nvl() function, 477 NYC Access VB Web site membership-based Web page example, 754-763
O Object Browser, 346, 509-511, 537 viewing objects in, 290 window, 321 Object Linking and Embedding (OLE) databases locking error codes, 626-627 Universal Data Access Initiative, 121 vs. ODBC, 436 Object Model, 509 object models ActiveX Data (ADOs) accessing non-relational data, 157-162 addressing Oracle through, 499-500 compared with Data Access Objects (DAOs) to, 145 connecting to Oracle databases through, 484-487 converting from Data Access Objects (DAOs) to, 142-145 data access, history of, 120-121 data definitions, ADOX, 167-176 manipulating data, 162, 164-167 object model for, 121
OLE DB Provider for Jet, 148-151, 154-157 Universal Data Access Initiative, 121 versions of, 122-125 ActiveX Data Objects (ADO), 121, 125-126 Command objects, 134-137 Connection objects, 126-130 Error objects, 141-142 executing action queries with commands, 138-139 Field objects, 139 Parameter objects, 134-137 Property objects, 139 Record objects, 137-138 Recordset objects, 130, 134-137 Stream objects, 140 Data Access (DAOs) compared with ActiveX Data Objects (ADOs) from of, 145 converting to ActiveX Data Objects (ADOs) from of, 142-145 initial release of, 120 performing data definition functions on non-Jet tables, 93 Excel, 555-556 Graph, 567-568 Jet Replication (JRO), 123 Outlook, 562-563 PowerPoint, 559-560 Remote Data (RDO), 120 Word, 540-541 Object Name column, Conversion Errors table, 11 Object Type column, Conversion Errors table, 11
809
810
object variables
object variables assigning to applications, 512 assigning to objects, 303 creating, 302-303, 511-512 destroying, 332 releasing at end of Automation sessions, 526 releasing from collections, 316 objects Action, 563 ActionSettings, 560 AddressEntries, 563 ADOR, 624 ADOX Column, 170 ADOX table, 169 AnimationSetting, 560 Application, 540-541, 556, 560, 563, 568 events exposed by, Microsoft Word, 521 AppointmentItem, 563 ASPs Application, 747-748 Request, 750 Response, 749 Session, 748-749 assigning object variables to, 303 Attachment, 563 AutoCaption, 540 AutoCorrect, 540, 568 Automation, 506 printing documents, 518-519 properties and methods, 517 releasing, 517-518 Axis, 568 benefits of using, 289-291 Button, 233 ButtonMenus, 233 Buttons, 233 catalog, 173 catalog, ADOX, 167-168 Cell, 560 CellRange, 560 cError, 289, 351-363
Characters, 541, 556 Chart, 556, 568 ChartArea, 568 ChartGroup, 568 ColorScheme, 560 Column, 560 ColumnHeaders, 222 ComboItem, 219 Command, 134-137, 173-176, 459 CommandBar, 541 commandtext, 174-175 Connection, 126-130 CurrentProject.Connection, 151 methods, 630 connection, opening, 152-154 ContactItem, 563 Count, 541 creating multiple instances of, 304 creating object variables, 302-303 Creator, 541 CubeField, 556 Data Access (DAO) executing Structured Query Language (SQL) statements off databases, 129-130 error numbers, backward compatibility, 141 database container, Access Data Projects (ADPs), 418 DataLink, 426 DataLinks, PromptNew method, 156 DataPage, 727 DataSheet, 568 Datasource, 727 DataTable, 568 Debug, 322-324 DefaultWebOptions, 541, 556, 560 described, 288, 291, 508 Dialog, 541, 556 Dictionaries, 541
Dictionary, 541 DistListItem, 563 DoCmd, 279 Document, 541 DocumentItem, 563 DocumentProperty, 556 DocumentWindow, 560 Email, 541 EmailSignature, 541 Err, 344-346 Error Handler, 311-312 Explorer, 563 Field, 139, 624 properties, 170 Find, 541 First, 541 Folders, 563 Footnotes, 550 ImageList control, 220 Inspector, 563 instances, 290 Items, 541, 563 JournalItem, 563 key, 171-172 Last, 541 Legend, 568 Letter, 308-310 ListImages, 220 ListItems, 221 ListView control, 221-222 MyTimer class, 306-307 Name, 556 NameSpace, 563 Nodes, 235 NoteItem, 563 objWord, 514 objWorkSheet, 557 Outlook, 310-311 Pages, 563 Pane, 560 Panels, 230 Paragraph, 541 Parameter, 134-137 Parent, 541 permissions, setting before object creation, 649 PivotCache, 556
On Error GoTo 0 statement
PivotTable, 556 PlotArea, 568 PostItem, 563 Presentation, 560 Property, 139 PropertyPage, 563 PublishObject, 556 query, 60-61 Querydefs, 175 Range, 541, 556 Recipient, 563 Record, 137-138 Recordset, 130, 134-137, 624 recordset, save method, 165 references, 314-315 referencing, improving speed of, 404-405 releasing, 303 removing from collections, 315 Replacement, 541 Report, 243 report, creating, 276-278 Row, 560 Selection, 541, 560 Series, 568 setting methods, 517 setting properties, 517 Shape, 556 ShapeRange, 556, 560 Singleton, 344 SlideRange, 560 SlideShowWindows, 560 Sound, 307 StatusBar control, 230 Stream, 140-141 Style, 556 SyncObject, 563 Table, 541, 560 Tabs, 232 TaskItem, 563 Template, 541 text file, 304, 306 TextRange, 560 type declarations, performance issues, 403 VBA collections, 312-316
viewing, Object Browser, 290 WebOptions, 541, 556, 560 Window, 541 Words, 541 Workbook, 556 Worksheet, 556 objects (database) exporting AS ASP, 734-735, 738 limitations, 739 See also classes, 541 objWord, 514 objWorkSheet, 557 Occurrence argument, 472-473 OCI (Oracle Call Interface), 485 OCX file extension, 593 ODAP. See Offline Data Access Pages ODBC reporting data, 443-448 vs. OLE DB, 436 ODBC (Open Database Connectivity), 120. See Open Database Connectivity ODBC (Open Database Connectivity) prefix, connection strings, 92 ODBC Command Time Out property string, 150 ODBC data sources, ASP access, 734 ODBC Refresh Interval option, 628 ODBC Timeout property, 63, 66 ODBCDirect, 120-121 Office incorporating Web components with Data Access Pages (DAPs), 724-726 integrating with Access 2002 auto macros, 535-536 automating Excel, 555-559 automating Graph, 567, 569
automating Outlook, 562-567 automating PowerPoint, 559-562 automating Word, 540-552 class arguments, 537 Microsoft Forms, 536-537 Object Browser, 537 printing Access information in Word reports, 538-540 reasons for, 531-532 using the right tools, 533 writing code, Macro Recorder, 533 version numbers, 516 Office 2000 Web components. See Web components Office Links, publishing reports, 265 Office XP, 214 OfficeAssistant method, 354 OfficeFaxNumber property, 353 OfficeID property, 353 OfficeName property, 353 OfficePhoneNumber property, 353 Offline Data Access Pages (ODAPs), 11 OLE (Object Linking and Embedding) databases, Universal Data Access Initiative, 121 OLE Automation. See ActiveX Automation OLE DB Provider advanced features of, 450-452 MSDataShape, 159 OLE DB Provider for Jet, 148-151, 154-158 OLE DB. See Object Linking and Embedding databases OLE/DDE Timeout option, 628 On Error GoTo 0 statement, 366
811
812
On Error GoTo Except statement
On Error GoTo Except statement, 342 On Error GoTo Finally statement, 343 On Error Resume Next statement, 344, 366 onClick event, 727 OnCurrent event, 501 one-to-many relationships, 52 reports, 257 one-to-one relationships, 51 OnError event, 366 online help, Err object, 346 OnOpen event, 492-494 open connections, creating to Oracle and filling forms, 493 Open Database Connectivity (ODBC), 120 accessing Oracle databases with SQL pass-through queries (SPTs), 465-468 calling stored procedures from SQL pass-through queries (SPTs), 489-490 creating connection strings, 463 creating connections from Access 2002 to Oracle databases, 484-487 creating Database Server Names (DSNs), 463-464 creating stored procedures in Oracle, 487-489 creating unbound interfaces to Oracle, 491-502 creating views to Oracle databases from Access 2002, 483-484 functions, Oracle vs. Access, 470-482 linking Access 2002 to Oracle tables, 462-465 wildcards and position markers, 468-470
Open Database Connectivity (ODBC) prefix, connection strings, 92 Open event, 268, 274 Open Exclusive permission, 647 Open method, 212 Open/Run permission, 647 OpenArgs property, 186 opening connection objects, 152, 154 connections, Access 2002, 127-128 database connections, OLE DB Provider for Jet, 148 databases, multiuser applications, 620-621 Debug toolbar, 327 external data sources, 133-134 Integrated Development Environment (IDE), 318 Object Browser, 509 parameter queries, ActiveX Data Objects (ADO), 113 recordsets, 130-136, 152, 163-167 secured databases, 151 windows, IDE, 318, 322 OpenParamQuery() function, 90-91 operations, arithmetic, Data data type, 479 optimal solution, definitions of, 370 optimistic locking, 621-623 optimizing, multiuser applications, 631-632 optimizing applications, 370 during coding phase, 400-410 during design phase, 384 establishing relationships, 386 indexes for, 386 normalizing data, 385-386 table design, 385
form performance handling controls, 396-399 increasing speed, 395-396 hardware requirements, 371-373 installation configurations, 373-374 Jet engine configuration, 374-381 performance testing, 381-384 query performance, 387-388 increasing speed, 393-395 indexes, 387-388 join types, 388-389 query result types, 390-392 Rushmore optimization, 388 scans, 387 reports, printing speed, 399-400 optimizing performance, queries, 393 Option Explicit, 339 optimizing performance using, 402 Option Explicit, forcing declaration of all variables with, 333 Option Group controls, 195 Option Pack, 673-682 Option Pack Common Program Files, 675 options /EXCL dbname, 620 /RO dbname, 620 Access Permissions, 690 AccessErrorTableName, 362 AddToErrorHandlerCalendar, 362 affecting data contention, 628 AVIFileLocation, 362 Background Compile, 340 Challenge and Response, 688-689 CHECK CONSTRAINTS, 114-116
Page Locks to Table Lock property string
command-line, opening Access, 620 DatabaseName, 362 DatabasePath, 362 dbname, 620 Directory Browsing Allowed, 690 EmailAddress, 363 EmailErrors, 363 ErrorsToAccessTable, 363 ErrorsToTextFile, 363 FrontPage Web, 690 Grouping and Sorting dialog box, 719 grouping reports, 248-249 GroupKeepTogether property, 267 Index This Directory, 690 installing Windows NT Option Pack, 679-680 Log Access, 690 Number of Update Retries, 628 ODBC Refresh Interval, 628 OLE/DDE Timeout, 628 PlaySound, 363 Refresh Interval, 628 Secure Communications, 689 ShowAVIForm, 363 ShowMsgBoxErrors, 363 ShowOfficeAssistant, 363 SoundFile, 363 tblErrorOptions table, 362-363 Totals row, 75-76 Update Retry Interval, 628 UserEnterNoteAboutError, 363 WholeGroup, 267 WithFirstDetail, 267 Options argument, 621 or Replace keyword, 483 Oracle adding records to databases, 488 closing connections to, 501-502 database locking, 632-633
Oracle Call Interface (OCI), 485 Oracle databases accessing in Access 2000 with SQL pass-through queries (SPTs), 465-468 calling stored procedures from SQL pass-through queries (SPTs), 489-490 connection costs, 464-465 creating connections from Access 2002 to Oracle databases, 484-487 creating stored procedures in Oracle, 487-489 creating unbound interfaces to Oracle, 491-502 creating views from Access 2002, 483-484 functions, Oracle vs. Access, 470-482 linking tables to Access 2002, 462-465 wilcards and position markers, 469-470 wildcards and position markers, 468 order, sort, 73 Order By property, 63, 181 Order Details Extended query, 198 Orientation property, 63, 215-217, 227-229, 236 orphaned records, 58, 69 OSBuild property, 231 OSPlatform property, 231 OSVersion property, 231 out-of-process components, ActiveX EXEs, 574 outer joins, 69-72 Outlook adding and displaying folders, 564 adding and displaying tasks, 564 class arguments, 537
creating e-mails with attachments, 564-565 creating Outlook items, 565-566 displaying default Outlook folders, 566-567 filtering Outlook items, 567 finding Outlook items, 567 Journal feature, disabling, 372 object model, 562-563 when to use, 532 Outlook object, 310-311 Output All Fields property, 63-64 Overlay method, 220 overwriting, records, databases, 621-622 Owner user (workgroupbased security), 646 OwnerAccess option, 653
P Package and Deployment Wizard deploying/distributing ActiveX components, 586-587 installing ActiveX components, 581-582, 585-586 Internet Package option, 610 packaging ActiveX controls for Web pages, 610-613 registering GUIDs, 589 packaging ActiveX components deployment/distribution, 586-587 setup files, 581-582, 585-586 ActiveX slider control for Web pages, 610-613 Page event, 269 Page Locks to Table Lock property string, 150
813
814
page numbers
page numbers moving, 282 resetting, new groups, 281 page settings, changing, 551 Page Setup dialog box, 551 Page Timeout property string, 150 page-level locking, 622 pages Access Data Projects (ADPs), 424 Active Server (ASP), ActiveX Data Objects (ADO) 1.0, 122 Active Server (ASPs) changes in, 680 full install of documentation, 680 placements and permissions, 697-698 aligning for binding, 282-283 calculating totals, 283 Data Access (DAPs), 11 creating, 717, 719 described, 716-717 implementing interactive drill-downs, 722-723 incorporating Office Web components with, 724-726 scripts, 726-729 viewing, 721 in Access Data Projects (ADPs) database containers, 418 Offline Data Access (ODAPs), 11 Web, transferring worksheets to, 703-704, 708 Pages object, 563 Pane object, 560 Panel object, 230 PanelClick property, 230 PanelDblClick property, 230 Panels collection, 230 Panels object, 230
panes Classes, Object Browser, 510 Members, Object Browser, 510 table creating queries, 66-72 described, 62 Paragraph object, 541 ParamArray keyword, 90 parameter collections executing queries, 90-91 Parameter object, 134-137 parameter queries, 87-91 setting parameters, 135 parameters executing commands with, 452-459 executing stored procedures with, Access VBA, 489-490 input, Jet database engine support, 135 named, 346 Number_parameter, 346 Oracle, using from Access VBA, 486-487 passing, Jet 4.0 support for, 112-113 placing in query grids, 87-88 providing to stored procedures at runtime, 444-445 Recordset objects, 130-132 setting, parameter queries, 135 Value, 187 Parameters dialog box, 134 PARAMETERS statement, 89 Parent object, 541 Parent property, 235, 558-559 parent reports, 257 parsing, string arrays, Split() function, 254 partitioning, applications, 631-632 pass-through queries, 91-93 accessing Oracle databases in Access 2002, 465-468 basing reports on stored procedures with, 440-442
calling stored procedures from, 489-490 creating, 465-466 Password property, 223 passwords changing, 112 in client/server applications, identifying blanks, 660 membership-based Web page, changing (ASP), 760-763 paths, local, 690 percent sign (%), 468 percentages, Top Values property, 64 performance improving, ActiveX Automation, 523-527 optimizing, 370 during application design, 384 during coding process, 400-410 establishing relationships, 386 form performance, 395-396 handling form controls, 396-399 hardware requirements, 371-373 indexes for, 386 installation configurations, 373-374 Jet engine configuration, 374-381 normalizing data, 385-386 query performance, 387-395 report-printing, 399-400 table design, 385 testing, 381 query execution times, 381-383 system usage information, 383-384
procedures
performance, optimizing, query techniques, 393 period (.), 467 permissions Active Server Pages (ASPs), 697-698 databases, 697-699 directories, 696 executes, 690 Hypertext Markup Language (HTML), 697-698 objects, setting before object creation, 649 queries, 648 reports and, 642 scripts, 690 Users group, removing administrative rights, 642 virtual directories, 684 Web sites, 690 workgroup-based security, 649 persisted recordsets, 165-167 Personal Identifiers (PIDs), 644 Personal Web Manager, 678, 682-684 Personal Web Services (PWS), 671-672 pessimistic locking, 622, 628 photographs, adding to shapes on slide, 562 PictureBox controls, 595 PIDs (Personal Identifiers), 644 PIVOT statement, 85-86 PivotCache object, 556 PivotChart views, 13, 197-202 PivotTable control (Web components), 703, 711-713 PivotTable data views, 13 PivotTable object, 556 PivotTable views, creating, 197, 199-202
Placement property, 232 placing fields on query grids, 73 parameters in query grids, 87-88 platforms, selecting for Web site publishing, 671-673 Play method, 212 PlaySound method, 354 PlaySound option, 363 PlotArea object, 568 plus sign (+), 468, 471, 723 pooling, connection, Microsoft Transaction Server (MTS), 681 POP (Post Office Protocol), 677 pop-up menus, creating, 196 Pop-up property, 184 populating, fields with data and checking current form context, 501 portable objects, 291 position markers, 468-470 Post Office Protocol (POP), 677 PostItem object, 563 postponement, variable declarations, 137 PowerPoint adding photos to shapes on slides, 562 adding slides, 561 adding transition effects, 561 class arguments, 537 formatting slides, 561-562 object model, 559-560 running PowerPoint presentations, 562 when to use, 532 PowerStatusChanged event, 231 PowerSuspend event, 232 Precision argument, 477-478
Precision property, 420 prefixes F-prefix, 187 MSYS, 61 USYS, 61 PrepareCrosstabReport() function, 276-278 Presentation object, 560 previewing, Word documents before printing, 551-552 Previous property, 235 PreviousDay method, 213 primary keys, 49, 163 creating, 386 PRIMARY value, 97 PrimaryKey index, 172 PrimaryKey value, 448 PrintCount property, 274 printers, identifying, 282 printing Access information in Word reports, 538-540 documents, Automation object, 518-519 information to Immediate window, 323 reports, optimizing speed, 399-400 values, built-in functions, 324 variables, 324 Word documents, 551-552 PrintOut method, 518-519 PrintSection property, 272-273 PrintStats function, 384 private fields, 295 Private keyword, 522 privileges Create Any Procedure, 488 Create Procedure, 488 Procedure property, 353 procedures called, finding, 330-331 declaring variables at top of, 137
815
816
procedures
described, 112 Error event, error handling, 366-367 jumping to, 331 nested, error handling, 365 property, 293-294, 296-297 Property Get, 293, 295-296 Property Let, 295 Property Set, 293, 296-297 queries, creating, 174-175 small, using in code, 334 stored Access Data Projects (ADPs), 422-423 basing reports on with pass-through queries, 440-442 calling from SQL passthrough queries (SPTs), 489-490 creating, 432 creating in Oracle, 487-489 providing parameters to at runtime, 444-445 ProcessError method, 354-357 production environments, vs. development environments, 670 programming report building, 274-279 XML documents, 766-770 programs. See applications ProgressBar control, 211, 226-227 Project Compatiblity option (Windows Registry), 590 Project Explorer window, 319-320 Project Properties Component tab, 577 projects, creating based on new databases, Access Data Projects (ADPs), 428-434
PromptNew method, 156, 426 prompts, connection information, 156-157 properties ACStatus, 231 ActiveX, setting in forms, 208 ActiveX controls, Timer control, 597-600 ActiveX DLLs, cSound Object component, 576-577 ActiveX runtime controls, cSlider, 609 addIndexNullsDisallow, 171 AddressBar, 237 adUseClient, 624 Align, 230, 234 Alignment, 236 Allow Additions, 182 Allow Deletions, 181 Allow Design Changes, 185 Allow Edits, 181 Allow Filters, 181 Allow Nulls, 420 Allowable Views, 182 AllowColumnReorder, 222 AllowCustomize, 234 Animation control, 212 Appearance, 217 Application, 352 Application object (ASP), 747-748 Arrange, 222 AttachmentCount, 224 AttachmentIndex, 224 AttachmentName, 224 AttachmentType, 224 AutoBuddy, 236 AutoIncrement, 170 Automation object, 517 AutoPlay, 212 AVIFileLocation, 352 BatteryFullTime, 231 BatteryLifePercent, 231 BatteryLifeTime, 231 BatteryStatus, 231
Border Style, 183 BorderStyle, 234 BuddyControl, 236 BuddyProperty, 236 BulletIndent, 228 ButtonHeight, 234 ButtonWidth, 234 Calendar control, 213 CalendarBackColor, 216 CalendarForeColor, 216 CalendarTitleBackColor, 216 CalendarTitleForeColor, 216 CalendarTrailingForeColor, 216 Can Grow, 262 Can Shrink, 262 CanGrow, 270-271 CanShrink, 270-271 cError object, 352-353 Checkbox, 216 Checkboxes, 235 Child, 235 Class, 352 ClientHeight, 232 ClientLeft, 232 ClientTop, 232 ClientWidth, 232 Close Button, 183 Color, 215 Column, 192 ColumnName, 420 ComboItems, 219 Command, 175 Commandtext, 175 CommandType, 135 Common Dialog control, 215 ComputerAvailableMemory, 352 ComputerName, 352 ComputerOperatingSystem, 352 ComputerProcessor, 352 ComputerTotalMemory, 352 Connection.Errors(index).SQL State property, 626 ConnectionString, 728
properties
Control Box, 183 ControlName, 352 ControlSource, 194 ControlValue, 352 Copies, 215 creating, 292-299 CurrentRecordID, 352 CustomFormat, 217 data, 181-182 Data Entry, 182 DataType, 420 DateTimePicker control, 216-217 Day, 213, 217, 225 DayOfWeek, 217, 225 Default Value, 420 Default View, 63, 182 DefaultExt, 215 described, 291, 509 Description, 63, 141, 352 DialogTitle, 215 Direction, 135 DownloadMail, 223 EditMode, 625 EmailAddress, 352 Err object, 345-346 Err.Description, 345 Err.HelpContext, 345 Err.HelpFile, 345 Err.LastDLLError, 345 Err.Number, 345 Err.Source, 345 Error object, 141 ErrorDatabase, 352 ErrorNumber, 352 ErrorTextFile, 352 field, defining, data definition queries, 95 Field object, 170 fields, setting, 170 FileName, 228 Filter, 63, 181, 215, 266-267 FilterIndex, 215 FilterOn, 266-267 FirstSibling, 235
FlatScrollBar control, 217-218 FontName, 215 FontSize, 215 ForceNewPage, 272 format, 182-183, 217 FormatCount, 273 forms, 180-188 FullRowSelect property, 222 FullScreen, 237 GetLineFromChar, 228 GridLines, 222 Group On, 249 GroupKeepTogether, 267 GroupOn, 279 Grp Keep Together, 262 Has Module, 185 HasContinued, 274 HasData, 267 Height, 270 HelpContext, 352 HelpFile, 352 HideColumnHeaders, 222 Hour, 217 Hyperlink Address, 197 Hyperlink SubAddress, 197 Identity, 420 Identity Increment, 420 Identity Seed, 420 ImageCombo control, 219 ImageCount, 220 ImageList, 219, 232, 234 ImageList control, 220 in classes, creating using property procedures, 295 Increment, 236 Indentation, 219, 235 Index, 220 IndexNulls, 171 InitDir, 215 Is RowGrid, 420 KeepTogether, 249-250, 272 Key, 220 LabelEdit, 222, 235 LabelWrap, 222 LargeChange, 217, 229 LastDllError, 353
LastSibling, 235 Length, 420 Level, 353 LimitToList, 189 LineNumber, 353 LineStyle, 235 Link Child Fields, 63, 258 Link Master Fields, 63, 258 ListView control, 221-222 LocationName, 237 LocationURL, 237 LockType, 624 logging, choosing, Internet Information Server (IIS), 686-687 LogonUI, 223 MAPIMessages control, 224 MAPISession control, 223 Max, 227-229, 236 Max Records, 63-65 MaxDate, 217, 225 MaxLength, 228 MaxSelCount, 225 Menu Bar, 184 MenuBar, 238 Min, 227-229, 236 Min Max Buttons, 183 MinDate, 217, 225 Minute, 217 Modal, 184 Mode, 127 Month, 213, 217, 225 MonthBackColor, 225 MonthColumns, 225 MonthRows, 225 MonthView control, 225-226 MoveLayout, 272-273 MsgCount, 224 MsgDateReceived, 224 MsgID, 224 MsgNoteText, 224 MsgRead, 224 MsgSent, 224 MsgSubject, 224 MultiLine, 228 MultiSelect, 193, 222, 225, 232
817
818
properties
Name, 135, 270 Native Error, 141 Navigation Buttons, 183 NewRoworCol, 271 NewSession, 223 Next, 235 NextRecord, 272-273 Note, 353 Now, 353 Number, 141 objects, setting, 517 ODBC Timeout, 63, 66 OfficeFaxNumber, 353 OfficeID, 353 OfficeName, 353 OfficePhoneNumber, 353 OpenArgs, 186 Order By, 63, 181 Orientation, 63, 215-217, 227-229, 236 OSBuild, 231 OSPlatform, 231 OSVersion, 231 Output All Fields, 63-64 PanelClick, 230 PanelDblClick, 230 Parent, 235, 558-559 Password, 223 Placement, 232 Pop-up, 184 Precision, 420 Previous, 235 PrintCount, 274 PrintSection, 272-273 Procedure, 353 ProgressBar control, 227 Provider, 148 provider-specific connection, 148-150 queries list of, 63-64 viewing, 60-62 RecipAddress, 224 Record Locks, 63-65 Record Selectors, 183 Recordset Type, 63-65 RecordSource, 181, 247-248
RepeatSection, 271 requirements for pass-through queries, 466 Response object (ASP), 749 RichText control, 228 RightMargin, 228 Root, 235 Row Source Type, 190-191 Run Permissions, 63-64 Running Sum, 280 Scale, 420 Scrollbars, 183, 228 Scrolling, 227 Second, 217 Section, 269-274 SelAlignment, 228 SelBold, 228 SelColor, 228 SelectedItem, 219, 222, 233-235 SelectRange, 229 SelEnd, 225 SelFontName, 228 SelFontSize, 228 SelImage, 219 SelItalic, 228 SelStart, 225 SelStrikeThru, 228 SelUnderline, 228 Session object (ASP), 748-749 SessionID, 223 Shortcut Menu, 184 ShowDateSelectors, 213 ShowToday, 225 SimpleText, 230 Slider control, 229, 231-236 SmallChange, 218, 229 Sorted, 222, 235 SortKey, 222 SortOrder, 222 SoundFile, 353 Source, 141, 353 Source Connect Str, 63-65 Source Database, 63-65 SourceObject, 258 SQL State, 141 StartOfWeek, 225
StatusBar control, 230 Style, 230, 233-235 styles for, 187 Subdatasheet Expanded, 64-65 Subdatasheet Height, 64 Subdatasheet Name, 63-65 SubItems, 222 SyncBuddy, 236 table designer, 420 tables, setting, 429 Tag, 185 TickStyle, 229 Toolbar, 184 Top Values, 63-64 Total, 85 TransactionDDL, 631 Type, 135, 238 Unique Records, 63 Unique Values, 63-64 user-defined, 186-187 UserControl, 520 UserEnterNoteFlag, 353 UserName, 223, 350, 353 Value, 135, 213, 217, 225-229, 236 VBA collections, 313-316 View, 222 Visible, 270 WaitStateFlag, 353 WebBrowser control, 237-238 Week, 225 WillContinue, 274 Year, 213, 217, 225 Properties sheet, 60-61, 67 Properties window, 320-321 Property Get method, 186-187 Property Get procedure, 293-296 Property Let method, 186 Property Let procedure, 295 Property object, 139 property page for Timer ActiveX control, 605-606 creating, 607 using, 606
queries
property procedures, 293-297 private modules, variable creation, 295 Property Set procedure, 293, 296-297 PropertyPage object, 563 proprietary application programming interfaces (APIs), 120 protecting, resources, 343 protocols Post Office (POP), 677 Secure Socket Layer (SSL), 689 Simple Mail Transfer (SMTP), 677 Provider property, 148 Provider string, 127 provider-specific connection properties, 148-150 public fields, 293 Public folders, displaying, Outlook, 566-567 Public keyword, 522 public variables, 297 publishing, reports, 262-265 publishing Web sites configuring for choosing platforms, 671-673 development vs. production environments, 670 installing Web servers, 675-681 managing and configuring Web servers, 682-692 security of Web applications, 692 sites vs. virtual directories, 693-699 Windows NT Option Pack, 673-682 PublishObject object, 556 PWD argument, 463 PWS (Personal Web Server), 671-673, 676, 680, 694 PWS (Personal Web Services), 671-672
Q QBE grid. See query design window queries action, executing with commands, 138-139 adding copies of same fields to, 74 aggregation applying selection criteria, Where clause, 80-83 creating with Expression function, 79-80 aggregation. See Total queries ANSI-92 query mode, 13 calculating averages, Avg function, 77-78 counting records, Count function, 76-77 creating, 80 creating in Access advanced queries, 74-75 query design window, 62-66 query grids, 72-74 table panes, 66-72 creating with linked tables, 464 creating, ADOX, 173-176 crosstab, 83-87 basing reports on, 274-279 creating, Northwind database, 724 crosstabulation. See crosstab queries data definition, 93-97 described, 60 descriptions of, 60 First function, 78 hiding, 61 in Access Data Projects (ADPs) database containers, 418 Last function, 78 Make Table, 94 Max function, 78
Min function, 78 optimizing, 393 optimizing performance, 387-388 increasing speed, 393-395 indexes, 387-388 join types, 388-389 result types, 390-392 Rushmore optimization, 388 scans, 387 Order Details Extended, 198 parameter, 87-91 setting parameters, 135 parameter queries, opening, ActiveX Data Objects (ADO), 113 parameterized. See parameter queries pass-through, 91-93 accessing Oracle databases in Access 2002, 465-468 basing reports on stored procedures with, 440-442 calling stored procedures from, 489-490 creating, 465-466 placing in table panes, 66 query object, 60-61 return values, 458 rollup. See Total queries speeding execution, indexes for, 386 SQL in Access 2000 forms, simple crosstabs, 709-711 SQL pass-through (SPT). See pass-through queries SQL Server, Access Data Projects (ADPs), 421-422 SQL-specific. See passthrough queries timing query execution, 381-383 Totals, 75-76 unmatched, 70
819
820
queries
validating data, StDev and Var functions, 78-79 viewing, 60-61 queries. See also databases Query Builder, changing recordsources, 247-248 Query By Example (QBE) grid. See query design window query design window creating parameter queries, 87-89 creating queries, 62-66 query grids creating queries, 72-74 placing parameters in, 87-88 query object, 60-61 query permissions, 648 query properties list of, 63-64 viewing, 60-62 Querydefs, 175 QueryTimer function, 384 question mark (?) as wildcard, 468-470 testing expressions and functions, Immediate window, 323 Quick Watch dialog box, 330 Quit event, 523 Quit method, 519
R Raise method, 345-346 raising events, Timer ActiveX control example, 601 RAM disks, 372 RAM, see memory, 371 Range object, 541, 556 RDO (Remote Data Objects), 120 RDS (Remote Data Services), 122
Read access, 690, 697-698 Read access, virtual directories, 684 Read and Write access, 690 Read Data permission, 647 Read Design permission, 647 read-ahead cache reads, evaluating using ISAMStats() function, 383-384 Read/Write access, 698 reading, files referred to by records across intranets, 140 ReadProperties event, Timer ActiveX control example, 601 RecipAddress property, 224 Recipient object, 563 Record Locks property, 63-65 Record Macro dialog box, 534 Record object, 137-138 Record Selectors property, 183 record-level locking, 622-623 Record.GetChildren method, 138 records adding to tables, 164-165 adding, Oracle, 488 counting, Count function, 76-77 current, deleting, 500 new, preparing forms for, 498 orphaned, 58, 69 reading files referred to across intranets, 140 selecting for aggregation, 81 updating, 621-622 Recordset object, 130, 134-137, 624 recordset object, save method, 165 Recordset Type property, 63-65 Recordset variable, 143
recordsets closing, 407 creating, 158-162 global, filling forms with data, 493-494 modifying data in, 162-165 opening, 130-136, 152, 163-167 persisted, 165-167 programming navigation buttons to move through, 494-496 saving to disks, 165-167 recordsets, detached filling forms with values from, 449-450 security using, 652 RecordSource property, 181, 247-248 recordsources creating, 275-276 in reports, changing, 247-248 Recycle Long-Valued Pages property string, 150 recycling bin, emptying, 372 Redirect method Request object (ASP), 750 Response object (ASP), 749 Refactoring, Improving the Design of Existing Code (italics), 127 refactoring code, 127-128 references creating and setting, applications, 507-512 objects, 314-315 setting to Microsoft OLE DB Service Component 1.0 Type Library, 156 referencing, ActiveX components, in Access applications, 579 referential integrity, testing, databases, 69 Refresh Interval option, 628
reports
Refresh method, 238, 456-457 refreshing applications, limiting refreshes, 409 registering ActiveX components, 588-589 compatibility options, 590-591 ActiveX controls, 207 ActiveX DLLs cSound Object component, 578 Registry Path property string, 150 registry settings Jet 4.0 performance optimization, 376-380 relational design theory benefits of, 48 data integrity rules, 57-58 data normalization, 54-57 foreign keys and domains, 50-51 relationships, 51-52 tables and uniqueness, 49-50 relationship + (plus sign), 468 relationships appearance of in table panes, 66 creating via diagrams, 430 creating, ADOX, 172-173 described, 51 establishing, performance issues, 386 expressions as, 468 many-to-many, 52 one-to-many, 52 reports, 257 one-to-one, 51 Oracle databases, 468 setting in Access, 51 table panes, 67 Relationships window, 66
releasing Automation object, 517-518 objects, 303 Remote Data Objects (RDO), 120 Remote Data Services (RDS), 122 removing breakpoints, 326 code, Immediate window, 325 current records, 500 logic errors, 341 objects from collections, 315 Oracle data from forms, 500-501 runtime errors, 341-364 syntax errors, 338-340 tables, 97 toolbars, 196 repeating data, hiding in reports, 279 RepeatSection property, 271 Replacement object, 541 replication Access 2002, 424 databases, importance of, 61 of databases, security concerns, 651 SQL Server databases, 425 Reply method, 224 Report object, 243 report objects, creating, 276-278 Report Wizard multi-source reports, 246-247 single-table reports, 244-245 reports Access Data Projects (ADPs), 424 aligning pages for binding, 282-283 architecture of, 242-243 basing on stored procedures with pass-through queries, 440-442
biweekly groupings, 279 building programatically, 274-279 calculating page totals, 283 Catalog, 257 changing, 244 creating empty lines at intervals, 280-281 creating mailing labels, 259-262 customizing, 247 grouping process, 250 grouping structures, 248-250 recordsources, 247-248 drawing vertical lines, 281 exporting, 263-264 filtering, 266-267 filtering at runtime, 445-446, 448 formatting, 284 functions in, 250-256 grouping data alphabetically, 279 hiding repeating data, 279 identifying users printing, 282 in Access Data Projects (ADPs) database containers, 418 limitations of, 242 modifying at runtime, 266-274 multi-source, creating with Report Wizard, 246-247 numbered lists, 280 ODBC data, 443-446, 448 optimizing performance, print speeds, 399-400 page numbers moving, 282 resetting for new groups, 281 permissions and, 642 presenting data on, 243 printing Access information in, 538-540 publishing, 262-265
821
822
reports
resetting page numbers, new groups, 281 single-table, creating with Report Wizard, 244-245 sorting, 266-267 subreports, 256-258 request codes, acLBGetRowCount, 191 Request object (ASP), Web pages, forms management, 750 reserved words, UNIQUE, 97 Reset ISAM Stats property string, 150 Resizing, tables, 423 Resource role, 488 resources, protecting, 343 responding, to errors, 346-348 Response argument, 366 Response object (ASP) properties, 749 Redirect method, 749 Write method, 749 responses, locking errors, 627-629 RestoreToolbar method, 234 Restrict method, 567 Results, in queries, performance issues, 390-392 results from queries, displaying, 73 resultsets, example created with custom expression, 80 resume next keyword, 344 Resume Next statement, 348 Resume statement, 348 return values, handling, 457-458 Rich Text Format (RTF), report exporting options, 264 RichText control, 211, 227-228 right outer joins, 68 RightMargin property, 228
roles, Resource, 488 RollbackTrans method, 630 RollbackTransComplete method, 630 rolled back transactions, 629 rollup queries. See Total queries Root property, 235 Round() function, 477 round-tripping, 10 row headings, crosstab queries, 85 row level locking, Jet 4.0 support for, 103 Row object, 560 Row Source Type property, 190-191 row-level locking, 622-623 rows, Totals, options available in, 75-76 RTF (Rich Text Format), report exporting options, 264 RTrim function, 473-474 Run method, 542 Run Permissions property, 63-64 Run Permissions property (Tools menu), 649 Run to Cursor option, Debug menu, 327 RunCommand method, 279 running applications, evaluating variables, 329-330 code, Automation Servers, 525-526 data definition queries, closing tables while, 94 functions, Immediate window, 324-325 PowerPoint presentations, 562 statements, Immediate window, 325 subprocedures, Immediate window, 324-325
Running Sum property, 280 RunParamQuery() function, 89-90 runtime changing DataSource control at, 728-729 choosing data views for, 182 events run in reports at, 268-269 filtering reports at, 445-448 modifying reports at, 266-274 providing stored procedures to parameters at, 444-445 Section property, 272-274 runtime ActiveX controls, 593 cSlider, 607 creating interface, 608 distributing, 610 packaging for Web pages, 610-613 saving, 609 setting properties, 609 testing in Access, 609 testing in Visual Basic, 609 using in COM-compliant applications, 610 using on Web pages, 613-614 runtime errors, eliminating, 341-364 runtime files, (VB), 583 Rushmore optimization in queries, 388 listing, 391
S safety, ActiveX controls, 205 Save As Data Access Page dialog box, 718 Save As Web Page dialog box (File menu), 703-704, 708 save method, 165, 224 SaveFile method, 228
SelImage property
saving ActiveX controls, 593 Timer control, 597 ActiveX runtime controls, cSlider, 609 Oracle data in forms, 499-500 recordsets to disks, 165-167 templates, Word, 542 updated fields, 498 Scale property, 420 scans, in queries, 387 scheduling, events, 480 screen savers, removing, 371 Screen.ActiveControl method, 333 Screen.ActiveForm method, 333 screens, Main, Microsoft Management Console (MMC), 682 ScreenUpdating, 524-525 script permissions, 690 scripts, Data Access Pages (DAPs), 726-729 Scripts access, virtual directories, 684 Scroll event, 229 Scrollbars property, 183, 228 Scrolling property, 227 searches, fuzzy, Soundex algorithm, 475 searching Outlook items, 567 procedures that have been called, 330-331 Second Normal Form, 55 Second property, 217 Section property, 269-274 sections creating, 278-279 reports, 242-243 Secure Communications option, 689 Secure Socket Layer (SSL), 689 secured databases, opening, 151
security Active Server Pages (ASPs), 697-698 choosing settings for, Internet Information Server (IIS), 687 client/server applications identifying blank passwords, 660 identifying current users, 658 listing user/group members, 657 managing groups/group ownership, 661 managing users, 654-655 SQL Grant/Revoke statements, 663 database applications basic steps, 664 common errors, 665 databases, 697-699 e-commerce, 689 firewalls, 692 Hypertext Markup Language (HTML), 697-698 Jet 4.0 enhancements, 111-112 objects, 291 split databases detached recordsets, 652 OwnerAccess option, 653 remote access (ADOX), 652 SQL Server databases, 425 start-up options, 650 virtual directories, 684, 694 Visual InterDev RAD Support, 678 Web applications, 692 Web site permissions, 690 with database replication, 651 workgroup-based, 638-640 assigning users to groups, 645 creating groups, 645 default user/group settings, 646
permissions, 649 setting/changing passwords, 644 SelAlignment property, 228 SelBold property, 228 SelChange event, 226, 228 SelColor property, 228 SELECT @IDENTITY statement, 110 Select Case statements, arranging, 409 Select keyword, 467 Select statement, 467 SelectedItem property, 219, 222, 233-235 selecting application settings, Internet Information Server (IIS), 689-692 application starting points, 694 architecture, database locking, 623 data views, 182 default documents, Internet Information Server (IIS), 687-689 joins, 68-69 logging properties, Internet Information Server (IIS), 686-687 platforms, for Web site publishing, 671-673 records for aggregation, 81 security settings, Internet Information Server (IIS), 687 selection criteria applying to aggregation queries, Where clause, 80-83 defining in query grids, 73-74 Selection object, 541, 560 SelectRange property, 229 SelEnd property, 225 SelFontName property, 228 SelFontSize property, 228 SelImage property, 219
823
824
SelItalic property
SelItalic property, 228 SelPrint method, 228 SelStart property, 225 SelStrikeThru property, 228 SelUnderline property, 228 semicolon (;), 467 Send method, 224 SendKeys command, 190 Series object, 568 SERVER entry, 92 server extensions, FrontPage, 676 server-side scripting for ASP, VBScript, 740-747 servers accessing workgroup templates, 542 Automation, 506-507 closing, 519 running code with, 525-526 browser interaction, managing (Response object), 749 firewalls, 692 virtual directories, 693 Web installing, 675-681 managing and configuring, 682-692 Service Pack 3, Windows NT, 674 Service Pack 6a, Windows NT, 674 service packs, Microsoft Data Access Components (MDACs), 671 Session object (ASP) events, 750-751 properties, 748-749 sample Web page, 752 SessionID property, 223 Set argument, 472-474 Set Attributes screen (ActiveX Control Interface Wizard), 600 Set keyword, 303, 313-315, 512
Set Next Statement option, Debug menu, 328 Set statement, 513-514 setting ActiveX Control properties, 208 breakpoints, Debug window, 326 connections, SQL Server front-ends, 436-440 DSNs, 437-438 error trapping options, 367 field properties, 170 object methods, 517 object properties, 517 parameters, parameter queries, 135 Recordset Type property, 65 references to Microsoft OLE DB Service Component 1.0 Type Library, 156 references, applications, 507-512 relationships in Access, 51 table properties and indexes, 429 settings application, choosing, Internet Information Server (IIS), 689-692 compiler, modifying, 340 ForceNewPage property, 272 group intervals, 249 KeepTogether property, 250 MoveLayout, NextRecord, and PrintSection properties, 273 NewRoworCol property, 271 pages, changing, Word documents, 551 security, choosing, Internet Information Server (IIS), 687 setup files, for ActiveX components, 581-582, 585-586 Sgn() function, 478 Shape object, 556 ShapeRange object, 556, 560
shapes, adding photos to, 562 Shared Asynch Delay property string, 150 shared locks, 632 shared mode, Jet shared engine, 620-621 sheets, Properties, 60-61, 67 shortcut keys field codes, 553 opening and closing windows, 322 Shortcut Menu Bar property, 184 Shortcut Menu property, 184 Show checkbox, 64 ShowAVIForm method, 354 ShowAVIForm option, 363 ShowColor method, 215 ShowDateSelectors property, 213 ShowFont method, 215 ShowHelp method, 215 ShowMsgBoxErrors option, 363 ShowOfficeAssistant option, 363 ShowOpen method, 215 ShowPrinter method, 215 ShowSave method, 215 ShowToday property, 225 shutdown, passive, Jet 4.0 support for, 110 Sign() function, 478 SignOff method, 223 SignOn method, 223 silent error handlers, 344 simple joins. See inner joins Simple Mail Transfer Protocol (SMTP), 677 SimpleText property, 230 Single data type (Jet 4.0), 108 single-byte encoding, 104 single-stepping through code, Debug window, 327
SQL pass-through queries
single-table reports, creating with Report Wizard, 244-245 Singleton object, 344 sites Active Server Pages (ASPs), changes in, 680 ActiveX Data Object (ADO) documentation, 143 ActiveX Data Objects (ADO), current version of, 124 ActiveX Data Objects (ADOs) documentation, 160 ASP.NET, 680 configuring for publishing choosing platforms, 671-673 development vs. production environments, 670 installing Web servers, 675-681 managing and configuring Web servers, 682-692 security of Web applications, 692 sites vs. virtual directories, 693-699 Windows NT Option Pack, 673-682 hosting multiple independent user domains on one server, 695 Internet Explorer download, 673 Internet Information Server (IIS), 672, 694 Knowledge Base, 214 Microsoft Data Access Components (MDACs), 671 OLE DB/ADOx, 123 recent releases of Microsoft’s Web server software, 674 vs. virtual directories, 693-699 sites. See Web sites sizing, tables, 423 Slider control, 211, 228-229
Slider control (ActiveX). See cSlider Control (ActiveX) SlideRange object, 560 Sliders, ActiveX runtime controls for, 607-614 slides adding photos to shapes, 562 adding, PowerPoint, 561 PowerPoint, formatting, 561-562 SlideShowWindows object, 560 SmallChange property, 218, 229 SMTP (Simple Mail Transfer Protocol), 677 SMTP Server, 672 Snap Shot Format (SNP) report exporting options, 264 sending using e-mail, 265 Snapshots, 65 SNP (Snap Shot Format) report exporting options, 264 sending using e-mail, 265 software. See applications sort order establishing for query grids, 73 report groupings, 248 Sorted property, 222, 235 sorting NT-compatible, Jet 4.0 support for, 106 reports, 266-267 Sorting and Grouping dialog box, 248 SortKey property, 222 SortOrder property, 222 Sound object, 307 Sound object (cSound), code for, 573 Soundex, 474-475 SoundFile option, 363 SoundFile property, 353 sounds, playing, ActiveX DLL for, 574-579 Source argument, 346
Source Connect Str property, 63, 65 Source Database property, 63-65 Source property, 141, 353 SourceObject property, 258 space, memory, running applications in, 695 Specify Cab options screen, 583 speed form-performance handling controls, 396-399 optimizing, 395-396 object references, optimizing, 404-405 query-execution, optimizing, 393-395 report-printing, optimizing, 399-400 splash screens, optimizing performance, 395 split databases, security detached recordsets, 652 OwnerAccess option, 653 remote access (ADOX), 652 Split() function, 254 Spreadsheet control (Web components), 702-704, 706-707 spreadsheets, adding to Data Access Pages (DAPs), 725 SPT. See pass-through queries SQL (structure query language), client/server security, SQL Grant/Revoke statements, 663 SQL (Structured Query Language) Jet 4.0 SQL extension additions, 110 WITH COMPRESSION flag, 105-106 SQL pass-through queries. See pass-through queries
825
826
SQL queries
SQL queries described, 60 enhancing database performance using, 407 in Access 2000 forms, simple crosstabs, 709, 711 optimizing performance, 387-388 increasing speed, 393-395 indexes, 387-388 join types, 388-389 result types, 390-392 Rushmore optimization, 388 scans, 387 SQL Query designer adding tables, 421 creating stored procedures, 422 creating views, 422 SQL Server Access Data Projects (ADPs) administering SQL Server databases, 424-425 advantages and disadvantages of, 414-415 creating, 415-417 creating projects based on new databases, 428-434 database containers, 417-418 database diagrams, 423 forms, 424 macros, 424 modules, 424 pages, 424 reconnecting to SQL Server databases, 425-427 reports, 424 SQL Server queries, 421-422 stored procedures, 422-423 system requirements, 414 working with SQL Server tables and, 419-420
data types, 420 database locking, 632-633 developing front-ends basing reports on stored procedures with passthrough queries, 440-442 configuring connections, 436-440 connection class, 459-460 executing commands with parameters, 452-459 forms, 448-450 OLE DB Provider, advanced features of, 450-452 OLE DB vs. ODBC, 436 reporting ODBC data, 443-448 linking tables to database files, 438-440 queries, Access Data Projects (ADPs), 421-422 SQL Server 2000, integration with Access 2002, 12-13 SQL State property, 141 SQL statements, changing, queries, 175-176 SQL-specific query. See passthrough queries SSL (Secure Socket Layer), 689 standard deviation function. See StDev function standard deviations, described, 79 Start argument, 472, 475 Start Menu Items screen, 585 start-up screens, optimizing performance, 395 starting, Web services, Microsoft Management Console (MMC), 683 starting points, choosing for applications, 694 StartOfWeek property, 225
startup filling tables at, 440 security options during, 650 starvation, lock, 632 statements ALTER TABLE, 96, 109 case sensitivity of, 467 CONSTRAINTS, 95 CREATE INDEX, 96-97 CREATE PROCEDURE, 112 CREATE TABLE, 94, 109, 114-116 CREATE VIEW, 112 DELETE, 632 Dim, 302-303, 326, 511, 513-514, 522 DROP INDEX, 97 DROP TABLE, 97 Error Goto, 343 Except, 342 Execute, 444 Exit, 343 Exit Function, 342 Exit Sub, 342-343 For, 727 GROUP BY, 82-83, 469-470 HAVING, 81-83, 469-470 IIf(), 85 INSERT, 469, 632 NOT NULL, 94 On Error GoTo 0, 366 On Error GoTo Except, 342 On Error Goto Finally, 343 On Error Resume Next, 344, 366 PARAMETERS, 89 PIVOT, 85-86 Resume, 348 Resume Next, 348 running, Immediate window, 325 Select, 467 SELECT @IDENTITY, 110 Set, 513-514 SQL, changing, queries, 175-176
system.mdw file
Stop, 324 Structured Query Language (SQL), executing against connections, 129-130 TRANSFORM, 85 UPDATE, 469, 632 states, default, wizards, 194 status screens, using, 410 StatusBar control, 211, 229-230 StatusTextChange event, 238 StDev function, 75, 78-79 Step Into option, Debug menu, 327 Step Out option, Debug menu, 327 Step Over option, Debug menu, 327 stepping through code, Debug window, 327-328 Stop method, 212, 238 Stop statement, 324 storage Data Access Pages (DAPs), 719 requirements for optimal performance, 371-373 storage conventions, Visual Basic vs. Access, 578 stored procedures Access Data Projects (ADPs), 422-423 basing reports on with passthrough queries, 440-442 calling from SQL passthrough queries (SPTs), 489-490 creating, 432 creating in Oracle, 487-489 providing parameters to at runtime, 444-445 reporting ODBC data, 443-448 Stream object, 140 StrExpr argument, 278 String argument, 472-475, 481
string arrays, parsing, Split() function, 254 string variables, 402-404 strings concatenation of, 471 connection Open Database Connectivity (ODBC), creating, 463 pass-through queries, 92-93 Initcap, 471-472 Lower, 471-472 Provider, 127 provider-specific connection properties, 148-150 Upper, 471-472 StrReport argument, 278 Structured Query Language (SQL) addressing Oracle through, 499-500 Jet 4.0 SQL extension additions, 110 queries. See queries WITH COMPRESSION flag, 105-106 Structured Query Language (SQL) statements executing against connections, 129-130 Style object, 556 Style property, 230, 233-235 styles fields and property names, 187 Word documents, 547-548 Subdatasheet Expanded property, 64-65 Subdatasheet Height property, 64 Subdatasheet Name property, 63-65 subdatasheets, updating data through, 64 subforms, 188, 194-195 SubItems property, 222
Subprocedures, running, Immediate window, 324-325 subreports, 243, 256-258 subroutines, optimizing performance, coding for, 400-410 Substitute argument, 477 Substr function, 475-476 Sum function, 75 sum results, creating, PivotTables, 199-200 support Extensible Markup Language (XML), Access 2002, 13 Microsoft Transaction Server (MTS), 681 suspending, execution of code, 324 swap file, 372-373 SyncBuddy property, 236 SyncObject object, 563 syntax requirements for pass-through queries, 466-467 SQL Add User, 662 Create User/Create Group, 662 Grant/Revoke keywords, 663 workgroups, setting membership, 640 syntax errors, eliminating, 338-340 Sysdate() function, 479, 482 SysInfo control, 211, 231-232 System database property string, 150 system privileges, 488 system requirements Access Data Projects (ADPs), 414 Windows NT Option Pack, 674 system.mdw file, security information, 637-639
827
828
tab controls
T tab controls, 195 table designer, properties, 420 Table object, 541, 560 table panes creating queries, 66-70, 72 described, 62 tables adding, SQL Query designer, 421 adding records to, 164-165 changing, 96, 109 closing when running data definition queries, 94 Conversion Errors, columns, 11 creating, 94, 109, 169, 428-429 Word documents, 550-551 creating with AutoNumber fields and PrimaryKey indexes, 172 data integrity rules, 57-58 data normalization, 54-57 deleting, 97 designing, performance issues, 385-386 filling at startup, 440 foreign keys and domains, 50-51 in Access Data Projects (ADPs) database containers, 418 indexing, 631 linked, creating, 169-170 linking, 53 moving and resizing, 423 Oracle databases, linking to Access 2002, 462-465 placing in table panes, 66 relationships, 51-52 SQL Server, working with Access Data Projects (ADPs) and, 419-420 tblErrorOptions, 362-363 uniqueness of, 49-50
tables of contents, creating, Word documents, 549-550 tabs App Debugging, Internet Information Server (IIS), 691-692 App Mappings, Internet Information Server (IIS), 691 App Options, Internet Information Server (IIS), 691-692 Directory Security, Internet Information Server (IIS), 687-689 Home Directory, Internet Information Server (IIS), 689-691, 694 Tabs object, 232 TabStrip control, 211, 232-233 Tag property, 185 TaskItem object, 563 tasks, adding and displaying, Outlook, 564 Tasks folder, displaying, Visual Basic for Applications (VBA), 566 tblErrorOptions table, 362-363 Template object, 541 templates, Word, 542 temporary files, deleting, 372 Terminate event, 302 testing ActiveX runtime controls, cSlider, 609 application performance, 381 query execution times, 381-383 system usage information, 383-384 applications, 335, 373 audit trails, 144 creation of views, 484 existence and types of locks, 623-627
expressions and functions, Immediate window, 323 referential integrity, databases, 69 Stop statements in, 324 Timer ActiveX control in Access, 604 in Visual Basic, 602-604 text, character encoding, Unicode for, 103-105 text file objects, 304-306 text files, report exporting options, 264 TextFile class, 304-306 TextRange object, 560 Third Normal Form, 56 TickStyle property, 229 time ranges, ODBC Timeout property, 66 TimeChanged event, 232 timeGetTime() function (API), performance testing using, 381-383 Timer Control (ActiveX design-time) adding code, 600 raising events, 601 ReadProperties event, 601 adding properties/methods/events, 597-600 creating, 594 creating interface, 595 distributing, 605 property pages for, 605-607 saving, 597 setting properties, 597 testing in Access, 604 in Visual Basic, 602-604 using in COM-compliant applications, 607 using multiple controls in Access, 605
Update Retry Interval option
Timer() function (VBA), performance testing using, 381 timers ActiveX design-time controls, 594-601, 604-607 testing in Visual basic, 602, 604 TitleChange event, 238 titles, adding to Data Access Pages (DAPs), 717 To Date() function, 478, 481 ToDate argument, 276 TodayDay method, 213 Too many files error, 347 Tool Tips, Data Access Pages (DAPs), 718 Toolbar control, 205, 211, 233-234 Toolbar property, 184 toolbars Debug, 327 deleting, 196 Top Values property, 63-64 Total property, 85 Totals queries, 75-76 Totals row, options available in, 75-76 Tour, Microsoft Management Console (MMC), 683 Transact SQL stored procedures, 422 transaction brokers, 680-681 Transaction Commit Mode property string, 150 Transaction DDL property, 631 transactions committed, 629 database locking, 629-631 Jet 4.0 support for, 114 rolled back, 629 testing efficiency of, 409 transferring, worksheets to Web pages, 703-704, 708 TRANSFORM statement, 85
transition effects, adding, PowerPoint, 561 trapping, errors, 367 TreeView control, 211, 222, 234-235 triggers, creating cascades via, 430 Trim function, 262, 473-474 troubleshooting, computer problems, 359-360 True value, 170-171, 493, 621 Allow Deletions property, 181 Allow Edits property, 181 Close Button property, 183 Data Entry property, 182 Events property, 187 Has Module property, 185 LimitToList property, 189 Min Max Buttons property, 183 Modal property, 184 Navigation Buttons property, 183 Record Selectors property, 183 Shortcut Menu property, 184 true/false toggles enhancing performance using, 403-404 Trunc() function, 478 TRUSTED CONNECTION entry, 92 turning off navigation buttons, 495-496 wizards, 194 turning on navigation buttons, 495-496 wizards, 194 type libraries, DAO, 143 Type property, 135, 238 TypeOf function, 349 typical installation option, Windows NT Option Pack, 679-680
U U.S. Census Bureau, 474 UCase() function, 254 UDL (Universal Data Links), connecting to databases, 152-157 UID argument, 463 UID entry, 92 unbound charts, creating, 711 unbound forms, 448-450 unbound interfaces, creating to Oracle, 491-502 underscore (_), 467-468 undocking, windows, 322 Unicode history, 104 Jet 4.0 support for, 103-105 NT compatible sorting, 106 Unicode Compression, 105-106 Unicode Consortium, 104 Uniform Resource Locators (URLs), connections through, 128-129 Unique Records property, 63 UNIQUE reserved word, 97 Unique Values property, 63-64 Universal Data Access Initiative, 121 Universal Data Links (UDLs), connecting to databases, 152-157 unmatched queries, 70 unregistering components, 589 UpClick event, 236 Update Data permission, 647 Update method, 163, 496-498 Update Retry Interval option, 628
829
830
UPDATE statement
UPDATE statement, 469, 632 UpdateField() function, 497-498 updates, cascading, 57 updating data through subdatasheets, 64 records, 621-622 UpDown control, 211, 235-236 UpDown control (ActiveX). See cSlider Control (ActiveX), 607 Upper string, 471-472 upsizing, Access 97 databases to SQL Server and Access Data Projects (ADPs), 434 URL (Uniform Resource Locators), connections through, 128-129 User class, 293 User Commit Sync property string, 150 user interface, new features, 8 user interfaces, UserControl object for ActiveX cSlider control, 608 Timer ActiveX control, 595 User List, Jet 4.0 capabilities, 110 User Roster, 157-158 user-based security, 637 default user/group settings, 646 groups assigning users to, 645 creating, 645 permissions, 649 users, setting/changing passwords, 644 user-defined functions, reports, 252-254 user-defined properties, 186-187
UserControl object, creating ActiveX control interfaces cSlider control, 608 Timer control, 595 UserControl property, 520 UserEnterNoteAboutError option, 363 UserEnterNoteFlag property, 353 UserInputBox method, 354 UserName property, 223, 350, 353 users ActiveX code components for, 581 creating setup files, 581-582, 585-586 deploying/distributing files, 586-587 assigning permissions, 637 client/server applications listing, 657-658 managing security, 654-655 database locking in multiuser environments choosing proper architecture, 623 issues, 618 Jet’s multiuser design, 619 Jet’s multiuser locking, 619-622 optimizing multiuser applications, 631-632 Oracle and SQL Server locking, 632-633 responding to locking errors, 627-629 testing existence and types of locks, 623-627 transactions, 629-631 logging on ASPs, 748 personalized Web site sessions, Session object (ASP), 748-749
providing feedback to, 525 workgroup-based security Admins, 646 creating, 644 Owner, 646 Users group (workgroupbased security), 646 UserType fields, 293 USYS prefix, 61
V validating, data, StDev and Var functions, 78-79 Value argument, 477-478 Value parameter, 187 Value property, 135, 213, 217, 225-229, 236 values adEditAdd, 625 adEditDelete, 625 adEditInProgress, 625 adEditNone, 625 All, Row Source Type property, 190 All Views, Allow Design Changes property, 185 Both, Scrollbars property, 183 built-in functions, printing, 324 crosstab queries, 85 DISALLOW NULL, 97 EditMode property, 625 Empty, 339 False, 171 Allow Additions property, 182 Close Button property, 184 Has Module property, 185 Field List, Row Source Type property, 190 GroupInterval, 279 IGNORE NULL, 97 IndexNulls property, 171 Key, 220, 313
VBA
None, Row Source Type property, 190 NOT NULL, 469 Nothing, 343-344 NULL, 469 Null, aggregate functions, 76 PRIMARY, 97 PrimaryKey, 448 retrieving from fields, 293 return, handling, 457-458 spreadsheet calls, 725 True, 170-171, 493, 621 Allow Deletions property, 181 Allow Edits property, 181 Close Button property, 183 Data Entry property, 182 Events property, 187 Has Module property, 185 LimitToList property, 189 Min Max Buttons property, 183 Modal property, 184 Navigation Buttons property, 183 Record Selectors property, 183 Shortcut Menu property, 184 variables changing, 324, 329 viewing, 324, 328-329 viewing, 329 Var function, 75, 78-79 variables changing values of, 324, 329 declaring on separate lines of code, 332 with narrowest scope, 332 with Option Explicit, 333 declaring at top of procedures, 137 global, 492 Nothing, 303, 517-518, 526
object assigning to applications, 512 assigning to objects, 303 creating, 302-303, 511-512 releasing at end of Automation sessions, 526 releasing from collections, 316 object variables, 332 printing, 324 public, 297 Recordset, 143 size of, performance issues, 402 string variables empty, testing for, 404 performance issues, 402-403 viewing, 329 viewing values of, 324, 328-329 variance function. See Var function variances, 79 Variant argument, 191 Variant data type, 339 VB, ActiveX DLL, creating XML, 766-770 VB Property Page Wizard Add Properties screen, 606 creating property page for Timer ActiveX control, 607 creating, 605-606 Introduction screen, 606 VB. See Visual Basic VBA (Visual Basic for Applications), 740-741 adding and displaying Outlook folders, 564 adding and displaying tasks, 564 adding code to Word templates, 542
adding photos to shapes on slides, 562 adding slides, 561 adding transition effects, 561 adjusting page settings, Word documents, 551 AutoSummarize, 549 AutoText, 548 Bookmarks, 546 calling code in Word from Access, 542 compared with Visual Basic 6, 572 converting keystrokes and mouse clicks into, Macro Reader, 533 creating charts, 557 creating e-mails with attachments, 564-565 creating Outlook items, 565-566 creating tables, Word documents, 550-551 described, 531 displaying default Outlook folders, 566-567 document styles, Word, 547-548 document views, 549 filtering Outlook items, 567 Find and Replace, 546 finding Outlook items, 567 footers, Word documents, 550 footnotes, Word documents, 550 formatting Excel documents, 556-557 formatting PowerPoint slides, 561-562 formatting Word documents, 546-547 headers, Word documents, 550 mail merge, 544 Parent property, 558-559 previewing and printing Word documents, 551-552
831
832
VBA
running PowerPoint presentations, 562 tables of contents, Word documents, 549-550 Timer() function, testing performance using, 381 versus VBScript, 740-741 Word fields, 552-553 VBA code, Universal Data Links (UDLs) in, 154-155 VBA collections, objects, 312-316 VBE. See Visual Basic Environment VBScript (Visual Basic Script), 733 ASP content, 733 category features, 741-743 documentation resources, MSDN Web site, 743 learning to write, 743 server-side scripting for ASP, 740-743 uses, 744-747 versus VBA (Visual Basic for Applications), 740-741 VBScript (Visual Basic Scripting Edition) for ASPs, 733 VBScript engines, backward compatibility of, 673 verifying, creation of views, 484 version numbers, Microsoft Office, 516 versions, ActiveX Data Objects (ADO), 122-125 vertical bars (||), 471 vertical lines, drawing, reports, 281 View property, 222 viewing code, forms, 319 Data Access Pages (DAPs), 721 data in hierarchies, 159-162 dialog boxes, 527
expressions, 329 folders, Outlook, 564-567 message boxes, 527 objects, Object Browser, viewing objects in, 290 queries, 60-61 query properties, 60-62 query results, 73 tasks, Outlook, 564 values, 329 values of variables, 324, 328-329 variables, 329 views creating, 431-432 SQL Query designer, 422 creating to Oracle databases from Access 2002, 483-484 data choosing, 182 PivotChart and PivotTable, 13 described, 112 PivotTable and PivotChart, creating, 197-202 queries, creating, 174 Word documents, 549 virtual directories access levels, 684 saving ASP to, 735, 738 vs. Web sites, 693-699 virtual memory. See swap file, 372 Visible property, 270 Visual Basic Component tab, compatibility options, 590 VB runtime file, 583 Visual Basic (VB), 572 ActiveX, 572 creating ActiveX controls using, 593 version 6, compared with VBA, 572 Visual Basic 6.0, creating recordsets and filling MSHFlexGrids, 160
Visual Basic Editor, Auto Syntax Check, 340 Visual Basic Environment (VBE), layout changes, 9-10 Visual Basic for Applications (VBA) adding and displaying Outlook folders, 564 adding and displaying tasks, 564 adding code to Word templates, 542 adding photos to shapes on slides, 562 adding slides, 561 adding transition effects, 561 adjusting page settings, Word documents, 551 AutoSummarize, 549 AutoText, 548 Bookmarks, 546 calling code in Word from Access, 542 converting keystrokes and mouse clicks into, Macro Reader, 533 creating charts, 557 creating e-mails with attachments, 564-565 creating Outlook items, 565-566 creating tables, Word documents, 550-551 described, 531 displaying default Outlook folders, 566-567 document styles, Word, 547-548 document views, 549 filtering Outlook items, 567 Find and Replace, 546 finding Outlook items, 567 footers, Word documents, 550 footnotes, Word documents, 550 formatting Excel documents, 556-557
WebOptions object
formatting PowerPoint slides, 561-562 formatting Word documents, 546-547 headers, Word documents, 550 mail merge, 544 Parent property, 558-559 previewing and printing Word documents, 551-552 running PowerPoint presentations, 562 tables of contents, Word documents, 549-550 word fields, 552-553 Visual C++, 214 visual feedback, 226 Visual InterDev, 676, 693 Visual InterDev RAD Support, 678 Visual Studio. NET, 125
W W3C (World Wide Web Consortium), 763 WaitState method, 354 WaitStateFlag property, 353 wallpaper, removing, 371 Watch window, 321, 329-330 Web applications ASP, performance enhancement, 733 security of, 692 Web browsers chart control, implementing, 770-772 Internet Explorer, VBScript engine, 673 retrieving properties for, 695 server interaction, managing (Response object), 749 user interaction, managing (Request object), 750
Web browsers (ActiveXbased), adding Office functionality, 702-713 Web Browsers (ActiveXbased), adding Office functionality, license requirements, 703 Web components, 702 Chart control, 703, 707-708, 711 PivotTable control, 703, 711-713 Spreadsheet control, 702-707 Web components, license requirements, 703 Web pages ASP Application object, 752 Session object, 752 interactive (ASP), 732 uses, 732-733 membership-based example, creating (ASP), 754-763 Northwind database, customer table, publishing (XML), 764-766 transferring worksheets to, 703-704, 708 using ActiveX runtime controls, cSlider, 610-614 Web Publishing Wizard, 683 Web servers installing, 675-681 managing and configuring, 682-692 Web sites Active Server Pages (ASPs), changes in, 680 ActiveX Data Object (ADO) documentation, 143 ActiveX Data Objects (ADO), current version of, 124 ActiveX Data Objects (ADOs) documentation, 160 ASP.NET, 680 charts, implementing chart control, 770-772
configuring for publishing choosing platforms, 671-673 development vs. production environments, 670 installing Web servers, 675-681 managing and configuring Web servers, 682-692 security of Web applications, 692 sites vs. virtual directories, 693-699 Windows NT Option Pack, 673-682 GotDotNet, ASP.NET resources, 733 hosting multiple independent user domains on one server, 695 Internet Explorer download, 673 Internet Information Server (IIS), 672, 694 Knowledge Base, 214 Microsoft Data Access Components (MDACs), 671 MSDN VBScript documentation, 743 XML resources, 763 OLE DB/ADOx, 123 PivotTable and PivotChart examples, 713 recent releases of Microsoft’s Web server software, 674 states maintenance, Session object (ASP), 748-749 vs. virtual directories, 693-699 Web-enabled applications, Web components, 702-713 Web-enabled applications, Web components, 703 WebBrowser control, 211, 236-238 WebOptions object, 541, 556, 560
833
834
Week property
Week property, 225 weights, of controls, relative, 396-397 Welcome event, 300-301 Where clause, 76, 80-83, 181, 445, 467-468 WholeGroup option, 267 wildcards, 468-470 WillContinue property, 274 Window object, 541 windows Call Stack, 322, 330-331 closing, Integrated Development Environment (IDE), 318 Code, 320 docking and undocking, 322 Immediate, 321-325 Locals, 321, 329 Object Browser, 321 opening, Integrated Development Environment (IDE), 318 Project Explorer, 319-320 Properties, 320-321 query design creating parameter queries, 87-89 creating queries, 62-66 Relationships, 66 Watch, 321, 329-330 Windows 2000 Server, 671-672 Windows NT choosing application starting points, 694 NT File System (NTFS), 696 Service Pack 3, 674 Service Pack 6a, 674 sorting, Jet 4.0 support for, 106 Windows NT 4 Server, 671-673 Windows NT Option Pack, 673-682 Windows operating systems, API calls, 364
Windows Registry registering ActiveX components, 588-589 compatibility options, 590-591 Windows Scripting Host, 678 WITH clause, 97 WITH COMPRESSION flag, 105-106 With/End With construct, 526 WithEvents keyword, 300-301, 520-523 WithFirstDetail option, 267 wizards ActiveX Control Interface Wizard, adding properties/methods/events, 597-600 creating charts and graphs with, 568 default state, 194 Label, creating mailing labels, 259-262 Linked Table, 13 Package and Deployment Wizard deploying/distributing ActiveX components, 586-587 installing ActiveX components, 581-582, 585-586 Package and Deployment Wizard), packaging ActiveX controls for Web pages, 610-613 Report, 244-247 VB Property Page Wizard, creating property page for Timer ActiveX control, 605-607 Web Publishing, 683 Word adjusting page settings, 551 AutoCorrect, 548 automating, 551 AutoSummarize, 549 AutoText, 548
class arguments, 537 creating tables, 550-551 document styles, 547-548 Document Summary information, 553 document views, 549 events exposed by Application object, 521 fields, 552-553 footers, 550 footnotes, 550 formatting documents, 546-547 headers, 550 inserting Access data into documents, 543-546 object model, 540-541 previewing and printing documents, 551-552 printing Access information in reports, 538, 540 tables of contents, 549-550 templates, 542 words reserved, UNIQUE, 97 whole, readability of, 137 Words object, 541 Workbook object, 556 workgroup file, 373 workgroup files (.MDW) avoiding conflicts, 640 components, 638 setting workgroup membership, 640 workgroup information file, 619 Workgroup Security ID (SID), 639 workgroup-based security, 638-640 users/groups assigning users to groups, 645 creating groups, 645 default settings, 646 permissions, 649 setting/changing user passwords, 644
|| (vertical bars)
workgroup files (.MDW) avoiding conflicts, 640 components, 638 setting workgroup membership, 640 workgroups, accessing templates on servers, 542 Worksheet object, 556 worksheets, transferring to Web pages, 703-704, 708 World Wide Web Server, 677 Write access, 690 write conflict dialog box, 622 write conflicts, 628 Write method, Response object (ASP), 749 WriteErrorToTable method, 354 WriteErrorToTextFile method, 354 writing, code, Macro Recorder, 533
X-Y-Z Xerox, 104 XML (Extensible Markup Language), 763 creating programmatically, 766-770 databinding in IE4 and IE5, 765-766 listings Creating XML Programmatically Inside a VB Component, 767-769 Creating XML Programmatically Inside an ASP, 769-770 Northwind database example, 764-766 versus HTML, 764 W3C development, 763
XML (Extensible Markup Language) documents report exporting options, 264 XML. See Extensible Markup Language, 13 XP (Extreme Programming), 127 Year 2000 issues, Oracle, 482 Year property, 213, 217, 225 [Sum of Quantity] field, reports, 252 _ (underscore), 467-468 || (vertical bars), 471
835