處理 Crystal 報表引擎的例外狀況
當您透過應用程式執行 Crystal Reports 時,有時候 Crystal 報表引擎可能會造成例外狀況。當例外狀況發生時,可能會出現錯誤對話方塊,或造成報表停止處理。以下是例外狀況的部份原因:
- 報表引擎無法連接到資料庫。原因可能是資料庫位置或傳遞給報表引擎的登入參數不正確,也可能是因為其他的資料庫錯誤,例如別的使用者將資料表鎖定,資料庫引擎安裝不正確,或資料表已毀損。
- 傳遞給參數的資料不正確。如果參數從程式碼或使用者輸入所接收的資料不正確,可能會造成報表停止執行。例如,將字串值傳遞給數值參數便會使報表停止執行。
- 在 Crystal Reports 公式中發現錯誤。如果報表中有一個公式無法正常進行驗算,便會發生例外狀況。原因可能是公式中的語法不正確或是其他的程式設計錯誤,例如除以零的錯誤。
- 報表引擎無法開啟報表。如果指定的檔案名稱或路徑不正確、選取的不是 Crystal Report,或報表已毀損,便會發生這個錯誤。
您可以選擇由 Crystal 報表引擎自行處理例外狀況並顯示自己的錯誤訊息,也可以由您自己來處理例外狀況。用程式設計的方法來處理例外狀況有以下這些優點:
- 您可以自訂顯示給使用者看的錯誤訊息。例如,當報表無法連接到資料來源時,可以不要顯示「登入失敗」,而改為顯示更完整的訊息,例如「提供的登入參數不正確,無法連接到資料庫」。這麼做可為使用者提供較為詳細的資訊。
- 您可以將事件寫入應用程式的記錄檔。您可以選擇將錯誤訊息、錯誤 ID、發生時間及其他相關資訊寫入記錄檔中。如此一來,便可稍後再追蹤及檢視錯誤。
- 您可以使用自己的程式碼來處理例外狀況,讓報表繼續進行處理。您可以檢查錯誤的內容,再撰寫能處理該錯誤的程式碼,然後使用正確的資訊重新執行一次報表。
Crystal 報表引擎提供了自己的例外狀況類別,可讓您判斷發生的錯誤類型。例外狀況類別是從 System.ApplicationException 類別繼承而來,唯一的差別是例外狀況類別多提供了一個 ErrorID 屬性。ErrorID 是列舉型別,可讓您判斷發生的錯誤類型。以下表格會顯示不同的例外狀況類別和列舉值。
例外狀況類別
例外狀況類別 | 說明 |
---|---|
DataSourceException | 從 EngineException 繼承而來。當資料來源有問題時,便會發生這個例外狀況。 |
EngineException | 從 System.ApplicationException 繼承而來。這是 Crystal 報表引擎的基底例外狀況類別。它會提供 ErrorID 屬性,以便判斷出錯誤類型。 |
ExportException | 從 EngineException 繼承而來。當匯出有問題時,便會發生這個例外狀況。 |
FormattingException | 從 EngineException 繼承而來。當設定報表格式有問題時,便會發生這個例外狀況。 |
FormulaException | 從 EngineException 繼承而來。當公式欄位發生錯誤時,便會發生這個例外狀況。 |
InternalException | 從 EngineException 繼承而來。當 Crystal 報表引擎內部發生錯誤時,便會發生這個例外狀況。 |
InvalidArgumentException | 從 EngineException 繼承而來。當使用的引數無效時,便會發生這個例外狀況。 |
LoadSaveReportException | 從 EngineException 繼承而來。如果開啟報表時發生錯誤,便會發生這個例外狀況。 |
LogOnException | 從 DataSourceException 繼承而來。如果登入資料來源時發生錯誤,便會發生這個例外狀況。 |
OutOfLicenseException | 從 EngineException 繼承而來。當同時使用權的數目超過時,便會發生這個例外狀況。 |
ParameterFieldCurrentValueException | 從 ParameterFieldException 繼承而來。如果尚未設定參數欄位目前的值,便會發生這個例外狀況。 |
ParameterFieldException | 從 EngineException 繼承而來。當參數欄位發生錯誤時,便會發生這個例外狀況。 |
PrintException | 從 EngineException 繼承而來。如果列印報表時發生錯誤,便會發生這個例外狀況。 |
SubreportException | 從 EngineException 繼承而來。如果開啟子報表時有問題,便會發生這個例外狀況。 |
EngineExceptionErrorID 列舉值
ErrorID | 說明 |
---|---|
DataSourceError | 存取資料來源時發生錯誤。 |
ExportingFailed | 匯出報表時發生錯誤。 |
IndexOutOfBound | 嘗試存取的值超出陣列界限。 |
InternalError | Crystal 報表引擎內部發生錯誤。 |
InvalidArgument | 使用無效的引數來設定值。 |
InvalidExportOptions | 傳遞給報表引擎的匯出選項值不正確或遺漏資料。 |
InvalidFormula | 處理公式欄位時發生錯誤。 |
InvalidParameterField | 處理參數欄位時發生錯誤。 |
InvalidParameterValue | 傳遞給參數欄位目前值的數值無效,因此發生錯誤。 |
InvalidPrintOptions | 傳遞給報表引擎的列印選項值不正確或遺漏資料。 |
LoadingReportFailed | 開啟報表時發生錯誤。 |
LogOnFailed | 登入資料來源時發生錯誤。 |
MissingParameterFieldCurrentValue | 參數欄位目前不包含任何值。 |
OpenSubreportFailed | 開啟子報表時發生錯誤。 |
OutOfLicense | 使用權的數目超過,因此發生錯誤。 |
PageFormattingFailed | 設定報表格式時發生錯誤。 |
PrintingFailed | 列印報表時發生錯誤。 |
SavingReport | 儲存報表時發生錯誤。 |
使用程式碼處理例外狀況
根據您使用的是 ReportDocument 物件、Windows Form Viewer 或 Web Form Viewer,處理例外狀況的方式會略有不同。這兩種檢視器控制項有預先定義的 HandleException 事件,您可以在其中加入程式碼。ReportDocument 物件則必須透過 Try-Catch 陳述式來處理例外狀況。如果將報表傳遞給檢視器,然後發生錯誤,檢視器將可攔截到例外狀況。
使用檢視器控制項處理例外狀況
使用檢視器控制項處理例外狀況時,會使用檢視器的 HandleException 事件。在這個事件中,您可以將事件的 ExceptionEventArgs.Exception 參數型別轉換成 EngineException 類別。有了 EngineException 類別之後,您就可以檢查 ErrorID 屬性來判斷發生的錯誤類型。
以下範例將說明,如果登入資料庫或存取資料來源有問題時,要如何覆寫顯示的錯誤訊息。所有其他的錯誤訊息則交由 Crystal 報表引擎顯示。
[Visual Basic]
Private Sub CrystalReportViewer1_HandleException(ByVal source As Object, _ ByVal e As CrystalDecisions.Windows.Forms.ExceptionEventArgs) Handles CrystalReportViewer1.HandleException
If TypeOf (e.Exception) Is EngineException Then
Dim engEx As EngineException
engEx = e.Exception
If engEx.ErrorID = EngineExceptionErrorID.DataSourceError Then
e.Handled = True
MessageBox.Show _
("An error has occurred while connecting to the database.")
ElseIf engEx.ErrorID = EngineExceptionErrorID.LogOnFailed Then
e.Handled = True
MessageBox.Show _
("Incorrect Logon Parameters. Check your user name and password.")
End If
End If
End Sub
[C#]
private void crystalReportViewer1_HandleException(object source, CrystalDecisions.Windows.Forms.ExceptionEventArgs e)
{
if (e.Exception is EngineException)
{
EngineException engEx = (EngineException)e.Exception;
if (engEx.ErrorID == EngineExceptionErrorID.DataSourceError)
{
e.Handled = true;
MessageBox.Show _
("An error has occurred while connecting to the database.");
}
else if (engEx.ErrorID == EngineExceptionErrorID.LogOnFailed)
{
e.Handled = true;
MessageBox.Show _
("Incorrect Logon Parameters. Check your user name and password.");
}
}
}
[C++]
void crystalReportViewer1_HandleException (Object* sender, CrystalDecisions::Windows::Forms::ExceptionEventArgs * e)
{
try
{
EngineException* engEx;
engEx = __try_cast<EngineException*>(e->Exception);
if (engEx->ErrorID == EngineExceptionErrorID::DataSourceError)
{
e->Handled = true;
MessageBox::Show ("An error has occurred while connecting to the database.");
}
else if (engEx->ErrorID == EngineExceptionErrorID::LogOnFailed)
{
e->Handled = true;
MessageBox::Show ("Incorrect Logon Parameters. Check your user name and password.");
}
}
catch(System::InvalidCastException*)
{
// 加入錯誤碼。
}
};
[VJ#]
private void crystalReportViewer1_HandleException(System.Object source, CrystalDecisions.Windows.Forms.ExceptionEventArgs e)
{
if (e.get_Exception() instanceof EngineException)
{
EngineException engEx = (EngineException)e.get_Exception();
if (engEx.get_ErrorID() == EngineExceptionErrorID.DataSourceError)
{
e.set_Handled(true);
MessageBox.Show
("An error has occurred while connecting to the database.");
}
else if (engEx.get_ErrorID() == EngineExceptionErrorID.LogOnFailed)
{
e.set_Handled(true);
MessageBox.Show
("Incorrect Logon Parameters. Check your user name and password.");
}
}
}
使用 ReportDocument 物件處理例外狀況
使用 ReportDocument 物件處理例外狀況時,必須使用 Try-Catch 陳述式。在 Catch 中,您可以參考其中一個 Crystal 例外狀況類別。EngineException 是基底類別,可以攔截到 Crystal 報表引擎發生的所有例外狀況。其他的例外狀況類別則可用來判斷發生的錯誤類型。只要在 EngineException 類別和 ApplicationException 之前攔截到這些例外狀況類別,就可以知道發生的例外狀況類型。
以下範例將說明,如果登入資料庫或存取資料來源有問題時,要如何覆寫顯示的錯誤訊息。其他所有的例外狀況則會顯示原始的錯誤訊息。
[Visual Basic]
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As _ System.EventArgs) Handles Button1.Click
Try
Dim report As New ReportDocument()
report.Load("c:\sample.rpt")
report.PrintToPrinter(1, True, 1, 2)
Catch engEx As LogOnException
MessageBox.Show _
("Incorrect Logon Parameters. Check your user name and password.")
Catch engEx As DataSourceException
MessageBox.Show _
("An error has occurred while connecting to the database.")
Catch engEx As EngineException
MessageBox.Show(engEx.Message)
End Try
End Sub
[C#]
private void button1_Click(object sender, System.EventArgs e)
{
try
{
ReportDocument report = new ReportDocument();
report.Load ("c:\\sample.rpt");
report.PrintToPrinter (1,true,1,2);
}
catch (LogOnException engEx)
{
MessageBox.Show _
("Incorrect Logon Parameters. Check your user name and password.");
}
catch (DataSourceException engEx)
{
MessageBox.Show _
("An error has occurred while connecting to the database.");
}
catch (EngineException engEx)
{
MessageBox.Show (engEx.Message);
}
}
[C++]
void ButtonClick(Object* sender, System::EventArgs * e)
{
try
{
ReportDocument* report = new ReportDocument();
report->Load ("c:\\sample.rpt");
report->PrintToPrinter (1,true,1,2);
}
catch (LogOnException* engEx)
{
MessageBox::Show("Incorrect Logon Parameters. Check your user name and password.");
}
catch (DataSourceException* engEx)
{
MessageBox::Show("An error has occurred while connecting to the database.");
}
catch (EngineException* engEx)
{
MessageBox::Show (engEx->Message);
}
};
[VJ#]
private void button1_Click(System.Object sender, System.EventArgs e)
{
try
{
ReportDocument report = new ReportDocument();
report.Load ("c:\\sample.rpt");
report.PrintToPrinter (1,true,1,2);
}
catch (LogOnException engEx)
{
MessageBox.Show
("Incorrect Logon Parameters. Check your user name and password.");
}
catch (DataSourceException engEx)
{
MessageBox.Show
("An error has occurred while connecting to the database.");
}
catch (EngineException engEx)
{
MessageBox.Show (engEx.get_Message());
}
}