I have a VS2008 project in which I generate a Crystal report and use it to create an output file. The report is never displayed onscreen, so I don't use any report viewer controls. This is my current VB Code:

Code:
Imports CrystalDecisions.CrystalReports.Engine
Imports CrystalDecisions.Shared

    Public Shared Function GenerateReport(ByVal databaseConnectionDetails As ConnectionDetails, ByVal reportTemplate As String, ByVal reportArchive As String, ByVal parameters As IDictionary(Of String, Object)) As Boolean
        Try
            Dim crTableLogonInfo As TableLogOnInfo = New TableLogOnInfo
            Dim crConnectionInfo As ConnectionInfo = New ConnectionInfo
            Dim crReportDocument As ReportDocument = New ReportDocument

            Dim crParameterDiscreteValue As ParameterDiscreteValue
            Dim crParameterFieldDefinitions As ParameterFieldDefinitions
            Dim crParameterFieldLocation As ParameterFieldDefinition
            Dim crParameterValues As ParameterValues

            '# Use the report template.
            crReportDocument.Load(reportTemplate)

            '# Create a Crystal connection object and give it the database details and credentials
            With crConnectionInfo
                .ServerName = databaseConnectionDetails.Server
                .DatabaseName = databaseConnectionDetails.Database
                .UserID = databaseConnectionDetails.UserID
                .Password = databaseConnectionDetails.Password
            End With

            '# For each table in the report, apply the same database settings.
            For Each CrTable As CrystalDecisions.CrystalReports.Engine.Table In crReportDocument.Database.Tables
                crTableLogonInfo = CrTable.LogOnInfo
                crTableLogonInfo.ConnectionInfo = crConnectionInfo
                CrTable.ApplyLogOnInfo(crTableLogonInfo)
            Next

            '# If the report has any parameters defined, loop through them.
            crParameterFieldDefinitions = crReportDocument.DataDefinition.ParameterFields
            For Counter As Integer = 0 To crParameterFieldDefinitions.Count - 1
                Dim FoundIt As Boolean = False
                Dim CrystalParameterName As String = crParameterFieldDefinitions.Item(Counter).Name
                crParameterFieldLocation = crParameterFieldDefinitions.Item(CrystalParameterName)
                crParameterValues = crParameterFieldLocation.CurrentValues
                crParameterDiscreteValue = New CrystalDecisions.Shared.ParameterDiscreteValue
                '# For each parameter in the report, check the parameters we passed in to see if there's a matching one.
                If Not parameters Is Nothing Then
                    For Each ParameterName As String In parameters.Keys
                        '# If there is a matching one, apply it and note that we've found it.
                        If ParameterName = CrystalParameterName Then
                            FoundIt = True
                            If crParameterFieldLocation.ValueType = FieldValueType.StringField AndAlso IsDate(parameters(ParameterName)) Then
                                crParameterDiscreteValue.Value = CType(parameters(ParameterName), Date).ToString("dd-MMMM-yyyy HH:mm:ss")
                            Else
                                crParameterDiscreteValue.Value = parameters(ParameterName)
                            End If
                        End If
                    Next ParameterName
                End If
                '# If we haven't found it, it must be optional. Cystal will still require a value, so give a dummy one.
                If Not FoundIt Then
                    Select Case crParameterFieldLocation.ValueType
                        '# Only tested on string fields (even though it's supposed to be a date), so can't guarantee
                        '# that the DBNull will work for other data types.
                        Case FieldValueType.StringField
                            crParameterDiscreteValue.Value = ""
                        Case Else
                            crParameterDiscreteValue.Value = DBNull.Value
                    End Select
                End If
                '# Add the value we chose into the parameter.
                crParameterValues.Add(crParameterDiscreteValue)
                crParameterFieldLocation.ApplyCurrentValues(crParameterValues)
            Next Counter

            '# Run the report and export it to the specified location in the required format.
            Dim Extension As String
            If Not reportArchive.LastIndexOf(".") = -1 Then
                Extension = reportArchive.Substring(reportArchive.LastIndexOf("."), reportArchive.Length - reportArchive.LastIndexOf("."))
            Else
                Extension = ".PDF"
            End If
            Select Case Extension.ToUpper
                Case ".XLS", ".XLSX"
                    crReportDocument.ExportToDisk(ExportFormatType.Excel, reportArchive)
                Case ".RTF"
                    crReportDocument.ExportToDisk(ExportFormatType.RichText, reportArchive)
                Case ".DOC", ".DOCX"
                    crReportDocument.ExportToDisk(ExportFormatType.WordForWindows, reportArchive)
                Case ".RPT"
                    crReportDocument.ExportToDisk(ExportFormatType.WordForWindows, reportArchive)
                Case ".PDF"
                    crReportDocument.ExportToDisk(ExportFormatType.PortableDocFormat, reportArchive)
                Case Else
                    crReportDocument.ExportToDisk(ExportFormatType.PortableDocFormat, reportArchive)
            End Select

            crReportDocument = Nothing

            Return True
        Catch ex As Exception
            WriteLog(ex)
            Return False
        End Try

    End Function
Now, this all works swimmingly apart from one thing: I've recently discovered that the value required by crConnectionInfo.ServerName is actually a DSN name, not the server name. However, I use my ConnectionDetails elsewhere in the code, and its Server property does need to be the server name for everything except these reports.

Obviously I could rewrite it to add an extra property for the DSN, but I'd rather not have to create a DSN for each data connection if it's not required. So, I figured I might go completely overboard and rewrite the entire thing in VS2010 (which I'll need to do in the near future anyway) and use the reporting tools from that. I know that the connection string for that doesn't require a DSN, but does anyone know if I can do a similar thing to what I've done here: create the report without a viewer or Dataset object, and just export it, having passed in the connection info and entered it manually.

Any advice would be gratefully received.