[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版本調整。
參考資料