Crystal Report Designer Component in VC++

Download as pdf or txt
Download as pdf or txt
You are on page 1of 16

Crystal Reports

Getting Started with the Crystal Report Designer Component in


Microsoft Visual C++

Overview
This document discusses integrating the Crystal Report Designer Component
(RDC) in Microsoft Visual C++.

General C++ language considerations are also discussed. Finally, a number of


examples of common report handling tasks are given and explained. This
document is for use with Crystal Reports 7 and higher.

Contents
INTRODUCTION ............................................................................................2
ADDING THE RDC RUNTIME LIBRARY IN VISUAL C++ ....................................2
WORKING WITH THE REPORT OBJECT ...........................................................3
VARIANT AND BSTR.................................................................................4
BSTR............................................................................................................ 4
VARIANT..................................................................................................... 5
Table of Variant Types ................................................................................ 5
WORKING WITH THE CRYSTAL REPORT VIEWER CONTROL ............................6
CLEANING UP..............................................................................................6
LOGGING ON TO A DATABASE ......................................................................7
LogonServer ................................................................................................ 7
SetLogonInfo ............................................................................................... 8
PASSING AN ACTIVE DATA RECORDSET TO A REPORT .................................9
PASSING PARAMETER VALUES ..................................................................10
Passing a String parameter....................................................................... 10
Passing a Number parameter.................................................................... 11
Passing a DateTime parameter................................................................. 11
OPENING A SUBREPORT............................................................................11
APPENDIX A - THE INTERFACES OF THE RDC.............................................14
CONTACTING CRYSTAL DECISIONS FOR TECHNICAL SUPPORT ....................16

6/24/2002 3:52 PM Copyright  2001 Crystal Decisions, Inc. All Rights Reserved. Page 1
Crystal Decisions Getting Started with the Crystal Report Designer Component in Microsoft Visual C++

Introduction
The Crystal Report Designer Component Automation Server (Craxdrt.dll, often
referred to as the RDC or Report Designer Component) is designed to take
advantage of several features of the Microsoft Visual Basic IDE. However, the
RDC can be integrated into other developer tools such as Microsoft Visual C++.

Visual C++ 5 and 6 provide native COM support which the RDC requires.
Accessing and implementing this support in Visual C++ is somewhat more
involved than is the case with Visual Basic. The method used to expose the
RDC’s object model in Visual C++ also differs as to how it is exposed in Visual
Basic.

The RDC is broken into two main components. The Automation server
component (Craxdrt.dll), and the Crystal Report Viewer control (Crviewer.dll)

The “Previewing a Report” section of this paper shows how to use these two
components of the RDC to preview a report. The “Setting report properties”
section extends on this and shows how to pass values (such as database logon
information or parameter field values) to a report at runtime.

NOTE You may find the RDC Browser utility helpful to understand and visualize the RDC’s
object hierarchy. The RDC Browser utility allows you to navigate through the object
hierarchy using an "Explorer tree" type interface.
RDC Browser can be downloaded from the Crystal Decisions Support site at:
support.crystaldecisions.net/docs
If you are using Crystal Reports 8.0 or higher, download the file:
RDC8_Browser.exe
If you are using Crystal Reports 7.0, download the file:
RDC_Browser.exe

Adding the RDC runtime library in Visual C++


1. Start a new project in Visual C++. Click the File menu and then click New.
Select MFC AppWiz and name the project. Click OK. Choose "Dialog
Based" in the first screen of the wizard and select "Finish"

2. In the application’s header file add the runtime library using the #import
keyword, for example:

#import “craxdrt.dll” no_namespace;

3. In the application’s header file, use the IApplicationPtr class to declare an


Application object, for example:

IApplicationPtr pApplication;

4. In the class where you want use the Application object, create the
Application object, for example:

6/24/2002 3:52 PM Copyright  2001 Crystal Decisions, Inc. All Rights Reserved. Page 2
Crystal Decisions Getting Started with the Crystal Report Designer Component in Microsoft Visual C++

extern CMyApplicationApp theApp;


theApp.pApplication.CreateInstance("CrystalRuntime.Applicat
ion");

Whereas the steps for Visual Basic are fairly intuitive, the same perhaps cannot
be said for the Visual C++ example given above. A few points bear explanation.

• The #import keyword exposes the various interfaces of Craxdrt.dll. You


should add the c:\program files\seagate software\report designer
component\ directory to the list of directories in Visual C++. To add the
directory, go the Tools menu, select Options and click the Directories tab.
• The no_namespace keyword indicates that you are not concerned with
creating a namespace for the set of interfaces you are importing.
• Craxdrt.dll exposes an interface called IApplication, however, the #import
keyword adds "Ptr" to the end of each interface to indicate that it has
wrapped the interfaces into smart pointers. Smart pointers do a bit of the
work for you, such as managing the life-cycle of the object created from the
interface and wrapping arguments into safe-arrays.
• Microsoft Foundation Classes (MFC) applications automatically create a
global application variable called theApp. Since you’ve declared RDC
interfaces in the application's header, they too are global and can be
accessed from the theApp variable. To do so, you need to declare theApp
in any class where we are using it using the extern keyword.
Although you have an Application object declared, you need to create the object
using the runtime. To do so, use the CreateInstance method of the object. The
method takes the programmatic identifier (ProgID) of the interface (for example,
CrystalRuntime.Application).

Working with the Report object


The Application object is the root object in the RDC object model. By creating
this object, we can access any of the other objects in the RDC.

Two objects are mandatory when working with the RDC, the Application object
and the Report object. Other objects may or may not be necessary when
working with a particular .rpt file (for example, if you need to connect a report
to its ODBC datasource, you would also need to use the Database object,
DatabaseTables collection and DatabaseTable object).

However, as a bare minimum, you need to instantiate at least an Application


object and Report object, for example:

theApp.pApplication.CreateInstance("CrystalRuntime.Applicat
ion");
_bstr_t FileName("c:\\Reports\\myReport.rpt");
theApp.pReport = theApp.pApplication->OpenReport(FileName);

6/24/2002 3:52 PM Copyright  2001 Crystal Decisions, Inc. All Rights Reserved. Page 3
Crystal Decisions Getting Started with the Crystal Report Designer Component in Microsoft Visual C++

• Since the RDC runtime uses the IDispatch interface, method arguments
need to be passed using Basic Strings (type BSTR) for strings, and as
variants for every other data type. There will be more details on this in the
next section. “_bstr_t” is a class that creates a BSTR by passing a string in
quotations to the classes constructor.
• The back-slash is an escape character in Visual C++ so we need to double
them up. The first slash indicates that the next is to be interpreted literally.
The OpenReport method creates the Report object pReport. The OpenReport
method of the Application object takes the path to the .rpt file as a BSTR and
returns an IReport object.

VARIANT and BSTR


BSTR
When calling methods of the various objects and collections of the RDC, it is
necessary to give the method arguments special treatment. String arguments can
most easily be passed using BSTR. The “_bstr_t” class is a handy shortcut.

When building a user-interface, CStrings are typically used to collect user input.
Fortunately, the CString class has a method called AllocSysString() which turns
the CStrings contents into a convenient BSTR.

For example, to instantiate an Application and Report object using a file dialog
prompting for a .rpt file, use the following lines of code:

theApp.pApplication.CreateInstance("CrystalRuntime.Applicat
ion");

//Create a FileDialog object using the MFC //CFileDialog


‘class and display the FileDialog using //the DoModal()
‘method.
CFileDialog Dlg(TRUE);
Dlg.DoModal();
_bstr_t FileName(Dlg.GetPathName().AllocSysString());

theApp.pReport = theApp.pApplication->OpenReport(FileName);

• Create a BSTR using the “_bstr_t” class using the AllocSysString()


method of the CString returned by the GetPathName() method of the
FileDialog. If the nested construct is a little confusing, keep in mind that
this is the same as using:
Dlg.DoModal();
CString FilePath(Dlg.GetPathName());
_bstr_t FileName(FilePath.AllocSysString());

6/24/2002 3:52 PM Copyright  2001 Crystal Decisions, Inc. All Rights Reserved. Page 4
Crystal Decisions Getting Started with the Crystal Report Designer Component in Microsoft Visual C++

With the BSTR containing the path to the .rpt file ready, the BSTR can now be
passed to the OpenReport() method of the Application object. This creates a
Report object that has all of the attributes of the .rpt file

VARIANT
For other argument data types, such as number, use a VARIANT. Whereas
VARIANT is a data type in VB, in Visual C++ VARIANT is a union. There are
always two members of the union that you need to set values to.

When using the VARIANT union, the first member indicates the VariantType
(.vt). The second member contains a value dependent upon the first member.

For example, if you want a VARIANT to contain an Integer value of ‘3’, use the
following code:

//Declare a Variant, ‘var’


VARIANT var;
//Initialize ‘var’ using VariantInit, passing in the
//address of the Variant.
VariantInit(&var);
//The first member, .vt, is set to VT_12, which is //the
‘Variant Type for ‘Short’.
var.vt = VT_I2;
//The second member is set to ‘iVal’ (integer value) //to
correspond to the first member.
var.iVal = 3;

Table of Variant Types

C++ Type VARIANT TYPE VARIANT MEMBER


LONG VT_I4 lVal
BYTE VT_UI1 bVal
SHORT VT_I2 iVal
FLOAT VT_R4 fltVal
DOUBLE VT_R8 dblVal
BOOL VT_BOOL boolVal
CY (currency) VT_CY cyVal
DATE VT_DATE date
IUnknown VT_UNKNOWN *punkVal
IDispatch VT_DISPATCH *pdispVal
SAFEARRAY VT_ARRAY *parray

6/24/2002 3:52 PM Copyright  2001 Crystal Decisions, Inc. All Rights Reserved. Page 5
Crystal Decisions Getting Started with the Crystal Report Designer Component in Microsoft Visual C++

Working with the Crystal Report Viewer Control


The Crystal Reports Viewer Control (CRViewer) is an ActiveX control placed
on a form to preview a report. If your application only prints or export reports,
then the CRViewer is not needed in your C++ project.

To add the Crystal Viewer to a project, open the Dialog resource for the class
that is too be responsible for displaying the report. With the Dialog resource
displayed, right-click on the dialog and choose "Insert ActiveX control…"
from the contextual-menu. A list of all of the registered controls will appear
from which the Crystal Viewer Control should be chosen. This will place the
Crystal Viewer control on the Dialog on which it should be resized as
appropriate.

The Crystal Viewer control has properties and methods that can be accessed
using the following steps:

1. Go to the Class Wizard and, ensuring that your Dialog's class is


selected in the Class Drop-down list, click on the Member Variables
Tab. There will be a CRViewer object listed in the tabbed page.
Double-click on this item and a prompt appears asking whether to add
the CRViewer's class to the project, click OK then give the Crystal
Viewer a meaningful name like m_Viewer.

2. Go to the InitDialog() method of the Dialog's class. In order to


preview a report, you must let the Crystal Viewer know which report to
preview using the SetReportSource() method.

m_Viewer.SetReportSource(theApp.pReport);

3. Once the Crystal Viewer knows which report to preview, use the
ViewReport() method to display the report in the Crystal Viewer
control.

m_Viewer.ViewReport();

Cleaning Up
After the report has been previewed, there are some clean-up tasks that should
be attended to.

Assuming that clicking the OK button in your application dismisses the preview
dialog and terminates the application, some housekeeping code should be added
to the handler for the OK button.

In this example, only an Application and Report object have been created.
These objects should be released and destroyed in reverse order from which they
were created.

theApp.pReport.Release();
theApp.pReport = NULL;

6/24/2002 3:52 PM Copyright  2001 Crystal Decisions, Inc. All Rights Reserved. Page 6
Crystal Decisions Getting Started with the Crystal Report Designer Component in Microsoft Visual C++

theApp.pApplication.Release();
theApp.pApplication = NULL;

Logging on to a Database
There are two methods that can be used to connect a report to a database:

• LogonServer
• SetLogonInfo

LogonServer
When connecting a report to a database at runtime, if the logon information is
the same, as used when designing the report, the LogOnServer method of the
Application object should be used.

To use the LogonServer method:

1. The LogOnServer method takes five BSTR arguments. Use the “_bstr_t”
class to create BSTRs for the arguments.

//The Server member should be set to the server name //if


connecting directly (natively), if connecting //using ODBC,
use the datasource (DSN) name instead.
_bstr_t Server("TSVANFPS01");
_bstr_t Database("pubs");
_bstr_t UserID("vantech");
_bstr_t Password("vantech");
_bstr_t DLLName("p2ssql.dll");

NOTE • To find the DLLname for a report, click Convert Database Driver from the
Database menu in the Crystal Reports Designer. The DLLname is the value
displayed beside the From field.
• The Server, Database, UserID, parameters for the LogonServer method can be
found by clicking Set Location from the Database menu in the Crystal Report
Designer. The dialog screen that appears will contain the members just
mentioned.

2. Check if the LogonServer connection to the database has failed or


succeeded by obtaining the return value with an HRESULT. Then, check
the HRESULT value for SUCCEEDED (or, conversely, FAILED) and
display a message box with a status message. For example:
HRESULT hr =
theApp.pApplication->LogOnServer(DLLName, Server, Database,
UserID, Password);
if (SUCCEEDED(hr)) AfxMessageBox("Connected Successfully");

6/24/2002 3:52 PM Copyright  2001 Crystal Decisions, Inc. All Rights Reserved. Page 7
Crystal Decisions Getting Started with the Crystal Report Designer Component in Microsoft Visual C++

3. Once the connection succeeds, dispose of the BSTRs using the


SysFreeString function.

SysFreeString(Server);
SysFreeString(Database);
SysFreeString(UserID);
SysFreeString(Password);
SysFreeString(DLLName);

SetLogonInfo
The other method that can be used to connect a report to a database is the
SetLogOnInfo method of the DatabaseTable object. This method does not
establish a connection immediately, but rather provides the report with the
information it needs to create a connection.

SetLogoninfo functions on a table-by-table basis. If a report connects to


multiple servers or databases, individual tables can be provided with the
appropriate log on criteria.

Additionally, SetLogonInfo propagates the log on criteria to other tables in the


report with the same originating criteria (for example, if all tables in the report
are pointing at the same datasource (DSN), database and userid, then using the
SetLogOnInfo method of the first DatabaseTable object in the report is
sufficient to connect to all of the tables in the report).

To use the SetLogonInfo method:

1. The LogOnServer method takes four BSTR arguments. Use the “_bstr_t”
class to create BSTRs for the arguments.

NOTE Unlike LogOnServer, there is no need to pass the DLL name argument (the DLL is read
from the report.

//The Server member should be set to the server name //if


connecting natively, if connecting via ODBC, use //the DSN
name instead.
_bstr_t Server("TSVANFPS01");
_bstr_t Database("pubs");
_bstr_t UserID("vantech");
_bstr_t Password("vantech");

NOTE The Server, Database, UserID, parameters for the LogonServer method can be found by
clicking Set Location from the Database menu in the Crystal Report Designer. The
dialog screen that appears will contain the members just mentioned.

theApp.pReport->Database->Tables->GetItem(1)-
>SetLogOnInfo(Server, Database, UserID, Password);

6/24/2002 3:52 PM Copyright  2001 Crystal Decisions, Inc. All Rights Reserved. Page 8
Crystal Decisions Getting Started with the Crystal Report Designer Component in Microsoft Visual C++

NOTE The last line of code above (used to access the first DatabaseTable object) is equivalent
to the following lines of code:
IDatabasePtr pDatabase;
pDatabase = theApp.pReport->GetDatabase();
IDatabaseTablesPtr pTables;
pTables = pDatabase->GetTables();
IDatabaseTablePtr pTable;
pTable = pTables->GetItem(1);

Passing An Active Data RecordSet to a Report


If a report has been designed to report off of an Active Data Recordset (such as
ADO, RDO, DAO or CDO), the SetDataSource method of the Database object
is used.

To pass an ADO Recordset to a report:

1. Import the ADO library and rename EOF as EndOfFile to avoid naming
conflicts

// reference the ADO object model and declare a //RecordSet


and Connection object
#import "msado15.dll" no_namespace rename("EOF",
"EndOfFile")
_RecordsetPtr pRecordSet = NULL;
_ConnectionPtr pConnection = NULL;

2. Use the “_bstr_t” function to create a connection string

// set up a connection string


_bstr_t conn("Provider=MSDASQL.1;Persist Security
Info=False;Data Source=Xtreme Sample Database");

3. When uncertain of the ProgID of an object, use the “__uuidof()” function


to access it automatically.

pConnection.CreateInstance(__uuidof(Connection));

4. Open the connection using the connection string.

pConnection->Open(conn, "", "", NULL);


::SysFreeString(conn);

6/24/2002 3:52 PM Copyright  2001 Crystal Decisions, Inc. All Rights Reserved. Page 9
Crystal Decisions Getting Started with the Crystal Report Designer Component in Microsoft Visual C++

5. In the Open method of the Recordset, cast the Connection object as a


VARIANT DISPATCH (for example, using _variant_t(IDispatch *) ) since
the method involves passing an object as an argument.

The recommended cursor type is adOpenKeyset just as


adLockBatchOptimistic is the preferred Lock setting for the cursor when
binding a recordset to a report.

pRecordSet.CreateInstance(__uuidof(Recordset));
pRecordSet->Open("Customer", _variant_t((IDispatch
*)pConnection,true), adOpenKeyset, adLockBatchOptimistic,
8);

6. When passing the Recordset argument in the SetDataSource method, be


sure to cast the object as a VARIANT DISPATCH.

theApp->pReport->Database->Tables->GetItem(1)->SetDataSource
(_variant_t((IDispatch *)pRecordSet,true));

Passing Parameter Values


When reports have parameters, it is often desirable to handle parameter value
assignment either in a custom dialog or programmatically. To pass a parameter
value to a report, use the AddCurrentValue method of the
ParameterFieldDefinition object.

The following examples demonstrate how to pass a string parameter, a number


parameter and a datetime parameter.

Passing a String parameter


Since the string value is being hard-coded, use an OLECHAR to hold the string
value, convert the OLECHAR to a BSTR using the SysAllocString function.
Create a VARIANT BSTR and assign the BSTR to the VARIANT.
OLECHAR str[] = "a string parameter";
BSTR bstr;
bstr = ::SysAllocString(str);
VARIANT StringParam;
VariantInit(&StringParam);
StringParam.vt = VT_BSTR;
StringParam.bstrVal = bstr;
app->pReport->ParameterFields->Item[1]->AddCurrentValue(StringParam);

6/24/2002 3:52 PM Copyright  2001 Crystal Decisions, Inc. All Rights Reserved. Page 10
Crystal Decisions Getting Started with the Crystal Report Designer Component in Microsoft Visual C++

Passing a Number parameter


Since the number value is also being hard-coded, create a VARIANT of type
VT_I2 (short) and assign the number to the VARIANT.

VARIANT NumberParam;
VariantInit(&NumberParam);
NumberParam.vt = VT_I2;
NumberParam.iVal = 10;
app->pReport->ParameterFields->Item[2]-
>AddCurrentValue(NumberParam);

Passing a DateTime parameter


In the case of the date-time, m_Date is a CTime object obtained from a
DateTime picker control.

VARIANT DateParam;
VariantInit(&DateParam);
DateParam.vt = VT_DATE;

// Declare a SYSTEMTIME variable.


SYSTEMTIME sysTime;

// Use the GetAsSystemTime method of the Ctime


// object.
m_Date.GetAsSystemTime(sysTime);

//Declare a DATE variable


DATE varDT;

// Convert the SYSTEMTIME into a DATE using the


// SystemTimeToVariantTime function.
SystemTimeToVariantTime(&sysTime, &varDT);
DateParam.date = varDT;
app->pReport->ParameterFields->Item[3]->AddCurrentValue(DateParam);

Opening A Subreport
Sometimes it is necessary to open a subreport into its own Report object. For
example, you may want to log on for each table in the subreport using the
SetLogonInfo method.

6/24/2002 3:52 PM Copyright  2001 Crystal Decisions, Inc. All Rights Reserved. Page 11
Crystal Decisions Getting Started with the Crystal Report Designer Component in Microsoft Visual C++

In order to open the subreport, it is necessary to go through the Sections


collection and then though the ReportObjects collection within each Section
Object.

To open a subreport:

1. Get the number of sections in the report in order iterate through each section
searching for subreport objects.

// Some objects we'll need later on...


ISectionsPtr pSections = NULL;
ISectionPtr pSection = NULL;
IReportObjectsPtr pRepObjects = NULL;
IReportObjectPtr pRepObject = NULL;
ISubreportObjectPtr pSubObject = NULL;
IReportPtr pSubreport = NULL;
// Get the number of sections in the report
pSections = theApp.pReport->GetSections();

2. Search through two separate collections (the collection of Section Objects


and the collection of ReportObject objects). Set up a pair of nested loops
using the Count property of the Sections collection and ReportObjects
collection.

// Loop through each section in the report


for (long i = 1;i <= pSections->GetCount();i++)
{
// Create a variant to hold the value of i
VARIANT var2;
VariantInit(&var2);
var2.vt = VT_I4;
var2.lVal = i;

// Pass the value of i to get the i-th section


pSection = pSections->GetItem(var2);

// Get the report objects collection from the section


pRepObjects = pSection->GetReportObjects();

// as long as there are report object....


if (!pRepObjects->GetCount() == 0)
{

6/24/2002 3:52 PM Copyright  2001 Crystal Decisions, Inc. All Rights Reserved. Page 12
Crystal Decisions Getting Started with the Crystal Report Designer Component in Microsoft Visual C++

// ... loop through each


for (long j = 1;j <= pRepObjects->GetCount(); j++)
{
// Create a variant to hold the value of j
VARIANT var;
VariantInit(&var);
var.vt = VT_I4;
var.lVal = j;
// Pass the value of j to the the j-th report
// object
pRepObject = pRepObjects->GetItem(var);

3. Test each ReportObject's Kind property to see if it is a SubreportObject


using the crSubreportObject enumerated type.

// if the report object is a subreport


if (pRepObject->GetKind() = crSubreportObject)
{
4. Once a SubreportObject is identified, the ReportObject is assigned to the
SubreportObject object. Then, the OpenSubreport() method of the
SubreportObject is used to populate a Report object.

// Re-assign the report object as a subreport object


pSubObject = pRepObject;

5. The OpenSubreport() method of the SubreportObject object is used to


populate a Report object with the attributes of the subreport.
// Open the subreport
pSubreport = pSubObject->OpenSubreport();

// To ensure that the subreport is really open


// create a message box
AfxMessageBox(pRepObject->GetName());

// You can now use the methods from the subreport's


// report object
}
}
}

6/24/2002 3:52 PM Copyright  2001 Crystal Decisions, Inc. All Rights Reserved. Page 13
Crystal Decisions Getting Started with the Crystal Report Designer Component in Microsoft Visual C++

Appendix A - The Interfaces of the RDC


The following is a complete listing of the Interfaces exposed in version
8.0.100.1 of Craxdrt.dll. For information on the methods and properties of these
interfaces, please refer to the Developer's Help file (Developr.hlp) installed with
Crystal Reports.

These objects and collections are listed in the Developer’s Help file index
(minus the leading "I").

IApplication

IArea

IAreas

IBlobFieldObject

IBoxObject

ICROleObject

ICrossTableGroup

ICrossTablGroups

ICrossTabObject

IDatabase

IDatabaseFieldDefinition

IDatabaseFieldDefinitions

IDatabaseTable

IDatabaseTables

IExportOptions

IFieldDefinition

IFieldDefinitions

IFieldMappingData

IFieldObject

IFormattingInfo

6/24/2002 3:52 PM Copyright  2001 Crystal Decisions, Inc. All Rights Reserved. Page 14
Crystal Decisions Getting Started with the Crystal Report Designer Component in Microsoft Visual C++

IFormulaFieldDefinition

IFormulaFieldDefinitions

IGraphObject

IGroupNameFieldDefinition

IGroupNameFieldDefinitions

ILineObject

IMapObject

IObjectSummaryFieldDefinitions

IOlapGridObject

IPage

IPageEngine

IPageGenerator

IPages

IParameterFieldDefinition

IParameterFieldDefinitions

IPrintingStatus

IReport

IReportEvent

IReportObject

IReportObjects

IRunningTotalFieldDefinitions

IRunningTotalFieldDefintion

ISection

ISectionEvent

ISections

6/24/2002 3:52 PM Copyright  2001 Crystal Decisions, Inc. All Rights Reserved. Page 15
Crystal Decisions Getting Started with the Crystal Report Designer Component in Microsoft Visual C++

ISortFields

ISpecialVarFieldDefinition

ISQLExpressionFieldDefinition

ISQLExpressionFieldDefinitions

ISubreportLink

ISubreportLinks

ISubreportObject

ISummaryFieldDefinition

ITableLink

ITableLinks

ITextObject

Contacting Crystal Decisions for Technical Support


We recommend that you refer to the product documentation and that you visit
our Technical Support web site for more resources.

Self-serve Support:
http://support.crystaldecisions.com/

Email Support:
http://support.crystaldecisions.com/support/answers.asp

Telephone Support:
http://www.crystaldecisions.com/contact/support.asp

6/24/2002 3:52 PM Copyright  2001 Crystal Decisions, Inc. All Rights Reserved. Page 16

You might also like