[C#]呼叫Excel VBA Macro
紀錄一下過程。
問題描述
之前開發的報表是用Excel VBA將資料庫查詢結果呈現;預計使用C#去呼叫相關VBA函式,產生跑批資料。
建置環境
Visual Studio 2010。
Office 2007。
步驟
1. 建立相關C#函式
參考相關網址
內容如下
using System.Collections.Generic;
using System.Text;
using Excel = Microsoft.Office.Interop.Excel;
using System.IO;
namespace utility.excelmacro
{
/// <summary>
/// 執行Excel VBA
/// </summary>
public class ExcelMacroHelper
{
/// <summary>
/// 執行Excel中的module
/// </summary>
/// <param name="excelFilePath">Excel文件路徑</param>
/// <param name="macroName">module名稱</param>
/// <param name="parameters">module參數組</param>
/// <param name="rtnValue">module返回值</param>
/// <param name="isShowExcel">執行時是否顯示Excel</param>
public void RunExcelMacro(
string excelFilePath,
string macroName,
object[] parameters,
out object rtnValue,
bool isShowExcel
)
{
try
{
#region 檢查入參
// 檢查文件是否存在
if (!File.Exists(excelFilePath))
{
throw new System.Exception(excelFilePath + " 文件不存在");
}
// 檢查是否輸入module名稱
if (string.IsNullOrEmpty(macroName))
{
throw new System.Exception("請輸入module的名稱");
}
#endregion
#region 調用module處理
// 準備打開Excel文件時的缺省參數對象
object oMissing = System.Reflection.Missing.Value;
// 根據參數組是否為空,準備參數組對象
object[] paraObjects;
if (parameters == null)
{
paraObjects = new object[] { macroName };
}
else
{
// module參數組長度
int paraLength = parameters.Length;
paraObjects = new object[paraLength + 1];
paraObjects[0] = macroName;
for (int i = 0; i < paraLength; i++)
{
paraObjects[i + 1] = parameters[i];
}
}
// 創建Excel對象示例
Excel.ApplicationClass oExcel = new Excel.ApplicationClass();
// 判斷是否要求執行時Excel可見
if (isShowExcel)
{
// 使創建的對象可見
oExcel.Visible = true;
}
// 創建Workbooks對象
Excel.Workbooks oBooks = oExcel.Workbooks;
// 創建Workbook對象
Excel._Workbook oBook = null;
// 打開指定的Excel文件
oBook = oBooks.Open(
excelFilePath,
oMissing,
oMissing,
oMissing,
oMissing,
oMissing,
oMissing,
oMissing,
oMissing,
oMissing,
oMissing,
oMissing,
oMissing,
oMissing,
oMissing
);
// 執行Excel中的module
rtnValue = this.RunMacro(oExcel, paraObjects);
// 保存更改
oBook.Save();
// 退出Workbook
oBook.Close(false, oMissing, oMissing);
#endregion
#region 釋放對象
// 釋放Workbook對象
System.Runtime.InteropServices.Marshal.ReleaseComObject(oBook);
oBook = null;
// 釋放Workbooks對象
System.Runtime.InteropServices.Marshal.ReleaseComObject(oBooks);
oBooks = null;
// 關閉Excel
oExcel.Quit();
// 釋放Excel對象
System.Runtime.InteropServices.Marshal.ReleaseComObject(oExcel);
oExcel = null;
// 調用垃圾回收
GC.Collect();
#endregion
}
catch (Exception ex)
{
throw ex;
}
}
/// <summary>
/// 執行module
/// </summary>
/// <param name="oApp">Excel對象</param>
/// <param name="oRunArgs">參數(第一個參數為指定module名稱,後面為指定module的參數值)</param>
/// <returns>module返回值</returns>
private object RunMacro(object oApp, object[] oRunArgs)
{
try
{
// 聲明一個返回對象
object objRtn;
// 反射方式執行module
objRtn = oApp.GetType().InvokeMember(
"Run",
System.Reflection.BindingFlags.Default |
System.Reflection.BindingFlags.InvokeMethod,
null,
oApp,
oRunArgs
);
// 返回值
return objRtn;
}
catch (Exception ex)
{
// 如果有底層異常,拋出底層異常
if (ex.InnerException.Message.ToString().Length > 0)
{
throw ex.InnerException;
}
else
{
throw ex;
}
}
}
}
}
引用點
using System; using System.Collections.Generic; using System.Linq; using System.Text; using utility.excelmacro; using System.IO; using log4net; using System.Diagnostics; namespace batch.basic { /// <summary> /// 判斷是否產生報表及相關函式 /// </summary> public class GenReport { protected ILog log = LogManager.GetLogger(typeof(GenReport)); /// <summary> /// 報表週期 /// </summary> [Flags] public enum ReportPeriodEnum { None = 0, Monthly = 2, Quarterly = 4, Annual = 8, }; #region 參數區 /// <summary> /// 資料日期 /// </summary> private string dataDate = string.Empty; /// <summary> /// 年報的excel檔案(vba) /// </summary> private string aFile = string.Empty; /// <summary> /// 季報的excel檔案(vba) /// </summary> private string qFile = string.Empty; /// <summary> /// 月報的excel檔案(vba) /// </summary> private string mFile = string.Empty; /// <summary> /// 執行excel vba工具 /// </summary> ExcelMacroHelper excelHelper = null; /// <summary> /// 報表檔存放的路徑 /// </summary> private string reportFolderPath = string.Empty; /// <summary> /// 月報預設檔案名稱 /// </summary> private string _monthReportFileName = "M.xls"; /// <summary> /// 季報預設檔案名稱 /// </summary> private string _quarterReportFileName = "Q.xls"; /// <summary> /// 年報預設檔案名稱 /// </summary> private string _annualReportFileName = "A.xls"; #endregion public GenReport() { if (excelHelper == null) { excelHelper = new ExcelMacroHelper(); } } /// <summary> /// 設定報表檔存放的路徑 /// </summary> /// <param name="reportPath"></param> public void SetReportPath(string reportPath) { reportFolderPath = string.Copy(reportPath); } /// <summary> /// 設定年報VBA檔案位置 /// </summary> /// <param name="filename"></param> public void SetAnnelBaseFile(string filename) { aFile = string.Copy(filename); } /// <summary> /// 設定年報VBA檔案位置 /// </summary> /// <param name="filename"></param> public void SetQuqrterBaseFile(string filename) { qFile = string.Copy(filename); } /// <summary> /// 設定年報VBA檔案位置 /// </summary> /// <param name="filename"></param> public void SetMonthBaseFile(string filename) { mFile = string.Copy(filename); } /// <summary> /// 是否產生月報(預設為是) /// </summary> /// <returns></returns> public bool GenMonthReport() { return true; } /// <summary> /// 是否產生年報 /// </summary> public bool GenAnnelReport() { return (dataDate.Substring(4, 2) == "06" || dataDate.Substring(4, 2) == "12") ? true : false; } /// <summary> /// 是否產生季報 /// </summary> public bool GenQuarterReport() { return (dataDate.Substring(4, 2) == "03" || dataDate.Substring(4, 2) == "06" || dataDate.Substring(4, 2) == "09" || dataDate.Substring(4, 2) == "12") ? true : false; } /// <summary> /// Gets the name of the month report file. /// </summary> /// <param name="date">The date(YYYYMMDD).</param> /// <returns></returns> public string GetMonthReportFileName(string date) { return date.Substring(0, 6) + "_" + _monthReportFileName; } /// <summary> /// Gets the name of the quarter report file. /// </summary> /// <param name="date">The date(YYYYMMDD).</param> /// <returns></returns> public string GetQuarterReportFileName(string date) { if (date.Substring(4, 2) == "01" || date.Substring(4, 2) == "02" || date.Substring(4, 2) == "03") { return date.Substring(0, 4) + "Q1" + "_" + _quarterReportFileName; } else if (date.Substring(4, 2) == "04" || date.Substring(4, 2) == "05" || date.Substring(4, 2) == "06") { return date.Substring(0, 4) + "Q2" + "_" + _quarterReportFileName; } else if (date.Substring(4, 2) == "07" || date.Substring(4, 2) == "08" || date.Substring(4, 2) == "09") { return date.Substring(0, 4) + "Q3" + "_" + _quarterReportFileName; } else { return date.Substring(0, 4) + "Q4" + "_" + _quarterReportFileName; } } /// <summary> /// Gets the name of the annual report file. /// </summary> /// <param name="date">The date.</param> /// <returns></returns> public string GetAnnualReportFileName(string date) { return date.Substring(0, 4) + "_" + _annualReportFileName; } /// <summary> /// Gets the name of the report file. /// </summary> /// <returns></returns> public string GetReportFileName(ReportPeriodEnum selectedReportPeriod) { string fileName = string.Empty; string pathReport = Path.Combine(reportFolderPath, selectedReportPeriod.ToString()); if (Directory.Exists(pathReport) == false) { Directory.CreateDirectory(pathReport); } switch (selectedReportPeriod) { case ReportPeriodEnum.Monthly: fileName = Path.Combine(pathReport, GetMonthReportFileName(dataDate)); break; case ReportPeriodEnum.Quarterly: fileName = Path.Combine(pathReport, GetQuarterReportFileName(dataDate)); break; case ReportPeriodEnum.Annual: fileName = Path.Combine(pathReport, GetAnnualReportFileName(dataDate)); break; default: break; } return fileName; } /// <summary> /// 產生報表 /// </summary> /// <param name="inDataDate"></param> public void Generate(string inDataDate) { dataDate = string.Copy(inDataDate); object objRtn = new object(); log.Info("Start Generate"); string resultFileName = string.Empty; try { //月報 if (GenMonthReport() == true) { log.Info("Start GenMonthReport"); resultFileName = GetReportFileName(ReportPeriodEnum.Monthly); log.Info("filename : " + resultFileName); excelHelper.RunExcelMacro(mFile, "GenReport", new object[] { dataDate }, out objRtn, false); log.Info("GenReport Result : " + objRtn.ToString()); excelHelper.RunExcelMacro(mFile, "SaveReportToNewFile", new object[] { resultFileName }, out objRtn, false); log.Info("SaveReportToNewFile Result : " + objRtn.ToString()); log.Info("END GenMonthReport"); } //季報 if (GenQuarterReport() == true) { log.Info("Start GenQuarterReport"); resultFileName = GetReportFileName(ReportPeriodEnum.Quarterly); log.Info("filename : " + resultFileName); excelHelper.RunExcelMacro(qFile, "GenReport", new object[] { dataDate }, out objRtn, false); log.Info("GenReport Result : " + objRtn.ToString()); excelHelper.RunExcelMacro(qFile, "SaveReportToNewFile", new object[] { resultFileName }, out objRtn, false); log.Info("SaveReportToNewFile Result : " + objRtn.ToString()); log.Info("END GenQuarterReport"); } //年報,半年報 if (GenAnnelReport() == true) { log.Info("Start GenAnnelReport"); resultFileName = GetReportFileName(ReportPeriodEnum.Annual); log.Info("filename : " + resultFileName); excelHelper.RunExcelMacro(aFile, "GenReport", new object[] { dataDate }, out objRtn, false); log.Info("GenReport Result : " + objRtn.ToString()); excelHelper.RunExcelMacro(aFile, "SaveReportToNewFile", new object[] { resultFileName }, out objRtn, false); log.Info("SaveReportToNewFile Result : " + objRtn.ToString()); log.Info("END GenAnnelReport"); } log.Info("END Generate"); } catch (Exception ex) { //把啟動的Excel關閉 foreach (System.Diagnostics.Process proc in System.Diagnostics.Process.GetProcesses()) { if (proc.ProcessName == "EXCEL") { proc.Kill(); } } log.Error(ex.Message); throw ex; } } } }
2. 準備Excel VBA Macro
內容如下,VBA Macro要放在模組底下,寫在ThisWorkbook沒辦法被識別:
'產生報表
Public Function GenReport(ByVal dataDate As String) As String
'設定資料日期
On Error GoTo ERRORHANDLE1
Call MenuGenMrpt.SetDataDate(dataDate)
'重新設定畫面
On Error GoTo ERRORHANDLE2
Call MenuGenMrpt.CmdReset_Click
'讀取報表資料
On Error GoTo ERRORHANDLE3
Call MenuGenMrpt.CmdGenMP_Click
'針對金額欄位進行格式調整
On Error GoTo ERRORHANDLE4
Call MenuGenMrpt.CmdRound_Click
'關閉資料庫連線及畫面
On Error GoTo ERRORHANDLE5
Call MenuGenMrpt.CmdExit_Click
GoTo ALLOK
ERRORHANDLE1:
GenReport = "設定資料日期發生錯誤!"
ERRORHANDLE2:
GenReport = "重新設定畫面發生錯誤!"
ERRORHANDLE3:
GenReport = "讀取報表資料發生錯誤!"
ERRORHANDLE4:
GenReport = "針對金額欄位進行格式調整發生錯誤!"
ERRORHANDLE5:
GenReport = "關閉資料庫連線及畫面發生錯誤!"
ALLOK:
GenReport = "Finish!"
End Function
'將產生結果另存新檔
Public Function SaveReportToNewFile(ByVal filename As String)
'另存新檔
Application.DisplayAlerts = False
On Error GoTo ERRORHANDLE6
Call FileSave.SaveAllSheetToNewFile(filename)
Application.DisplayAlerts = True
GoTo ALLOK
ERRORHANDLE6:
SaveReportToNewFile = "另存新檔發生錯誤!"
ALLOK:
SaveReportToNewFile = "Finish!"
End Function
3. 關閉安全性警告
開啟excel。
Excel選項。
信任中心→信任中心設定。
新增Excel報表底稿存放的位置。
開啟時就不會發出警告安全性。
結果畫面
將相關跑批結果用log紀錄,有正常產生。
注意事項
參考Excel物件版本為12.0(office 2007),視實際使用的Office版本調整。
參考資料