Providing an External Advantage Connection Handle to the Advantage Crystal Reports Driver

Crystal Reports

  Previous topic Next topic  

Note Passing an external connection to the Advantage Crystal Reports Driver is only supported with Crystal Reports version 9 and newer.

The following information describes how an Advantage connection can be provided to the Advantage Crystal Reports Driver for use in running reports. The default behavior of the Advantage Crystal Reports Driver is to create a new connection for each report. Using a single connection shared between an application and the Advantage Crystal Reports Driver has the following benefits:

Benefits

Performance – Normally, the Advantage Crystal Reports Driver creates and destroys a new connection to the Advantage server each time a report is run. Depending on the speed of the report and the frequency in which an application runs the report, this can be a significant performance issue. By providing an established connection to the Advantage Crystal Reports Driver, the time required to create the new connection and destroy it can be eliminated.
Resource Conservation – For each connection made to the Advantage server, memory resources are consumed both on the client and server. Although the maximum number of connections is configurable on the server, exceeding the maximum amount is not allowed. If the Advantage server reaches maximum connection capacity, subsequent connection attempts are denied. By providing an established connection to the Advantage Crystal Reports Driver, memory resources can be conserved and connection denied errors minimized.
Availability of Connection Specific Resources – Some resources, such as temporary tables and the application ID, are available on a per connection basis. This means that only the connection that created them can access them. By providing an established connection to the Advantage Crystal Reports Driver, connection specific resources can be used by reports. For example, temporary tables created on the connection can be referenced by reports when the connection that created the tables is passed to the report.

Creating an External Connection to the Advantage Crystal Reports Driver

Advantage connection handles are simply 32-bit integer values. Any application module can use these handles, as long as the handle is used within the same process space as the application that created it. This is precisely how the Advantage Crystal Reports Driver can use an external Advantage connection.

First, the application must connect to the Advantage server. A simple way to make this connection is with the AdsConnect60 ACE API. Once the connection is established, its integer value is converted into a string form, then provided to the Advantage Crystal Reports Driver via an ODBC style connection option.

Note Do not disconnect the connection provided to the Advantage Crystal Reports Driver while the report is running. This can produce unexpected results and undesirable behavior.

Designing Crystal Reports to use External Connections

When reports are designed from within the Crystal Reports IDE, the Advantage Crystal Reports Driver creates its own connection based on the selected alias found in the ads.ini file. This means that at design time, an alias must exist for the report to use. It also means that per-connection items such as temp tables are not available at design time. This makes designing reports based on temporary items difficult. Two solutions exist to get around this issue:

1.Create a static version of the item for use during the design of the report. If the report is meant to extract data from a temporary table, create a normal table with the same table structure for use when creating the report. When the report is finished, delete the normal table. Then when the report is run, change the name of the table used by the report at runtime. Finally, pass in the connection handle which contains the temporary table to the report at runtime.
2.Create the temporary item at design time from within the Crystal Reports IDE. Begin by designing the report to select data from an existing table or system table (i.e., system.iota). Once the report is created, open the Database Expert utility and locate the command which the report is using. Right-click on the command and select Edit Command. Then from within the Modify Command dialog, enter an SQL statement to create the temporary item (for example CREATE TABLE #temp …). Click the OK button and an error will be returned. This is expected. The SQL statement will be run by the Advantage Crystal Reports Driver and as a result, the temporary item will now exist on the Advantage Crystal Reports driver connection. Finally, change the SQL statement to the desired SELECT statement using the temporary item.

Changing the Report to use a Different Table at Runtime

There are two methods for changing the table(s) which a report is run against. First, you can change just the table name(s) directly using the TCrpeTablesItem.Name and TCrpeTablesItem.SubName properties. Second, you can change the SQL query the report is based on by replacing the existing table name(s) in the query text.

1.Changing the Name and SubName properties. Changing just the table name(s) the report was designed for is probably the most simple and natural way to run a report against a table the report was not designed with (a temporary table for instance). Simply change the TCrpeTablesItem.Name and TCrpeTablesItem.SubName properties of the tables in the report to that of the new table name(s). If the new tables are temporary tables, remember that you must also pass to the Advantage Crystal Reports driver the connection handle in which the temporary tables were created on. See the Delphi TCrpe example titled "Delphi, using the TCrpe components to change the table name at runtime" below for a code sample.
2.Changing the SQL query text. Using the Crystal Reports API, you can change the SQL query stored in the report to reference different tables than what the report was created with. According to the Crystal Reports help documentation, changes to the query are allowed in the FROM, WHERE, and ORDER BY clauses but are not allowed in the SELECT clause. So by changing the table names in the FROM clause, the query can be executed on different tables (temporary tables for instance). See the Delphi TCRpe example titled "Delphi, using the TCRpe components" below for a code sample.

Examples

Delphi, using COM:

procedure COMTest( hConnect : ADSHANDLE );

var

oCRReport : OleVariant;

oCRApplication : OleVariant;

oCRDatabase : OleVariant;

oCRTable : OleVariant;

i : Integer;

begin

 

oCRApplication := CreateOleObject( 'crystalruntime.application.11' );

oCRReport := oCRApplication.OpenReport( 'C:\test_report.rpt' );

oCRDatabase := oCRReport.Database;

 

// If the report has multiple tables, we need to set the connection

// handle for each table.

if oCRDatabase.Tables.Count > 1 then

for i := 1 to oCRDatabase.Tables.Count do

begin

oCRTable := oCRDatabase.Tables.Item[ i ];

oCRTable.SetLogOnInfo( 'ADS_ADT', // Alias name used by the report

'ConnectionHandle = ' + IntToStr( hConnect ) + ';',

'', '' )

end

else

oCRDatabase.LogOnServer( 'crdb_ads.dll',

'ADS_ADT', // Alias name used by the report

'ConnectionHandle = ' + IntToStr( hConnect ) + ';',

'', '' );

 

oCRReport.DiscardSavedData;

oCRReport.ExportOptions.DestinationType := 1; // file

oCRReport.ExportOptions.DiskFileName := 'C:\test_report.txt';

oCRReport.ExportOptions.FormatType := 10; // paginated text

 

// Run (export) the report

oCRReport.Export( FALSE );

 

// Clean up the OLE variables

oCRTable := Unassigned;

oCRDatabase := Unassigned;

oCRReport := Unassigned;

oCRApplication := Unassigned;

end;

 

Delphi, using the TCRpe components:

procedure CrystalTest( AdsConn : TAdsConnection );

var

Crpe1 : TCRpe;

begin

try

Crpe1 := TCRpe.Create( NIL );

Crpe1.DiscardSavedData;

Crpe1.ReportName := 'C:\test_report.rpt';

Crpe1.Connect.DatabaseName := 'ConnectionHandle=' + IntToStr( AdsConn.Handle ) + ';';

Crpe1.SQL.Query.Text := 'SELECT "CustTable"."id", "CustTable"."Name" FROM "#TempCustTable" "CustTable"';

Crpe1.Show;

 

finally

Crpe1.CloseWindow;

Crpe1.CloseJob;

Crpe1.Destroy;

end;

end;

 

Delphi, using the TCrpe components to change the table name at runtime:

procedure TableNameExample;

var

Crpe: TCrpe;

hConn : ADSHANDLE;

hStmt : ADSHANDLE;

ulRetVal : UNSIGNED32;

begin

ulRetVal := AdsConnect60( 'X:\data', ADS_REMOTE_SERVER, NIL, NIL, 0, @hConn );

ulRetVal := AdsCreateSQLStatement( hConn, @hStmt );

ulRetVal := AdsExecuteSQLDirect( hStmt, pChar( 'CREATE TABLE #mytab ( id integer, name char(50));' +

'INSERT INTO #mytab VALUES ( 57, ''temp data'' );' ), NIL );

crpe := TCrpe.Create( NIL );

crpe.ReportName := 'C:\Report1.rpt';

crpe.connect.DatabaseName :='ConnectionHandle='+IntToStr(hConn)+';';

crpe.DiscardSavedData;

crpe.Tables[0].Name := '#mytab';

crpe.Tables[0].SubName := '#mytab';

crpe.Show;

crpe.Clear;

crpe.Destroy();

AdsDisconnect( hConn );

end;

 

Visual Basic .NET:

Private Sub ReportTest(ByVal hConnect As UInt32)

Dim tbCurrent As CrystalDecisions.CrystalReports.Engine.Table

Dim tliCurrent As CrystalDecisions.Shared.TableLogOnInfo

Dim rptCustomersOrders As New CrystalDecisions.CrystalReports.Engine.ReportDocument

 

Try

' Load the report file

rptCustomersOrders.Load("C:\test_report.rpt")

 

' Refresh the report's data

rptCustomersOrders.Refresh()

 

' Set the DatabaseName for each of the report's tables

tliCurrent = New CrystalDecisions.Shared.TableLogOnInfo

For Each tbCurrent In rptCustomersOrders.Database.Tables

tliCurrent.ConnectionInfo.DatabaseName = "ConnectionHandle = " + Convert.ToString(hConnect) + ";"

tbCurrent.ApplyLogOnInfo(tliCurrent)

Next tbCurrent

 

' Pass the report to the report viewer control

crvReportViewer.ReportSource = rptCustomersOrders

 

Catch Exp As CrystalDecisions.CrystalReports.Engine.LoadSaveReportException

MsgBox("Incorrect path for loading report.", MsgBoxStyle.Critical, "Load Report Error")

Catch Exp As Exception

MsgBox(Exp.Message, MsgBoxStyle.Critical, "General Error")

End Try

End Sub