之前寫的類存在Excel進程不能結束的Bug,重寫ExcelReader類,類實例清理時Excel進程自動結束。
1 class ExcelReader 2 { 3 // Excel Object 4 public Application app; 5 public Workbooks wbs; 6 public Workbook wb; 7 public Worksheet ws; 8 public Range rng; 9 10 private bool disposed = false; 11 12 public ExcelReader() 13 { 14 // New Excel Application 15 app = new Application 16 { 17 Visible = false, 18 DisplayAlerts = false 19 }; 20 } 21 22 /// <summary> 23 /// 關閉對象 24 /// </summary> 25 public void Close() 26 { 27 Dispose(); 28 } 29 30 /// <summary> 31 /// 清理對象 32 /// </summary> 33 public void Dispose() 34 { 35 Dispose(true); 36 GC.SuppressFinalize(this); 37 } 38 39 /// <summary> 40 /// 清理所有正在使用的资源。 41 /// </summary> 42 /// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param> 43 protected virtual void Dispose(bool disposing) 44 { 45 if (disposed) 46 { 47 return; 48 } 49 if (disposing) 50 { 51 // Excel Application Quit 52 app.Quit(); 53 54 // Finally, Release app. 55 System.Runtime.InteropServices.Marshal.FinalReleaseComObject(app); 56 app = null; 57 58 disposed = true; 59 } 60 } 61 62 #region Worksheet Operation 63 /// <summary> 64 /// 從工作表讀取數據到 DataTable 65 /// </summary> 66 /// <param name="fileName">文件</param> 67 /// <param name="sheet">工作表名</param> 68 /// <returns>DataTable</returns> 69 public DataTable GetDataTableFromSheet(string fileName, string sheet, int rows, int cols) 70 { 71 // Create Table 72 DataTable dt = new DataTable(); 73 74 // Get Excel‘s WorkBooks. Attention: Don‘t use .Net cascade, ex.: app.Workbooks.Add() 75 // Every variable Must set to reference, then Release it one by one. 76 // If not, Can‘t quit Excel Process. 77 wbs = app.Workbooks; 78 79 // Get WorkBook 80 wb = wbs.Open(fileName); 81 82 // Get WorkSheet 83 ws = wb.Sheets[sheet]; 84 85 // Columns & Rows Count 86 //int colCount = ws.UsedRange.CurrentRegion.Columns.Count; 87 //int rowCount = ws.UsedRange.CurrentRegion.Rows.Count; 88 89 // Get worksheet‘s used range 90 //rng = ws.UsedRange; 91 rng = ws.Range[ws.Range["A1"], ws.Range[GetColumnName(cols)+rows]]; 92 dt = GetDataTableFromRange(rng, rows, cols); 93 94 // Relase Range, Set to null. (variable reference that COM Object‘s Count is 0) 95 System.Runtime.InteropServices.Marshal.FinalReleaseComObject(rng); 96 rng = null; 97 98 // Release WorkSheet 99 System.Runtime.InteropServices.Marshal.FinalReleaseComObject(ws); 100 ws = null; 101 102 // Release WorkBook 103 System.Runtime.InteropServices.Marshal.FinalReleaseComObject(wb); 104 wb = null; 105 106 // Release WorkBooks 107 System.Runtime.InteropServices.Marshal.FinalReleaseComObject(wbs); 108 wbs = null; 109 110 return dt; 111 } 112 #endregion 113 114 #region Range Operation 115 /// <summary> 116 /// 從單元格範圍讀取數據到 DataTable 117 /// </summary> 118 /// <param name="range">單元格範圍</param> 119 /// <returns>DataTable</returns> 120 private DataTable GetDataTableFromRange(Range range, int rows, int cols) 121 { 122 DataTable dataTable = new DataTable(); 123 124 // First Row Range 125 Range titleRange = range.Rows[1]; 126 127 // Columns Count 128 //int colCount = titleRange.CurrentRegion.Columns.Count; 129 130 // Title Row has Empty Cell Or Replication, Use Excel Column Header. 131 if (titleRange.Cells.Cast<Range>().Any(s => s.Value2 == null) 132 || titleRange.Cells.Cast<Range>().GroupBy(s => s.Value2).Count() != titleRange.Cells.Count 133 || titleRange.CurrentRegion.Columns.Count != cols) 134 { 135 for (int i = 1; i <= cols; i++) 136 { 137 dataTable.Columns.Add(GetColumnName(i), typeof(string)); 138 } 139 } 140 else 141 dataTable = GetTableStructureFromTitleRange(range); 142 143 // Release Range Object 144 System.Runtime.InteropServices.Marshal.FinalReleaseComObject(titleRange); 145 titleRange = null; 146 147 // Insert Data To DataTable Wtih Range Value 148 object[,] arr = range.Value; 149 for (int i = 0; i < arr.GetLength(0); i++) 150 { 151 DataRow dr = dataTable.NewRow(); 152 for (int j = 0; j < arr.GetLength(1); j++) 153 { 154 if (arr[i + 1, j + 1] != null) 155 dr[j] = arr[i + 1, j + 1]; 156 else 157 dr[j] = ""; 158 } 159 160 dataTable.Rows.Add(dr); 161 } 162 163 return dataTable; 164 } 165 166 /// <summary> 167 /// 生成表頭。如果首行表頭規範,則採用首行表頭,否則用Excel表頭代替 168 /// </summary> 169 /// <param name="titleRow">首行範圍</param> 170 /// <param name="isAllString">全部採用字符串格式</param> 171 /// <returns>DataTable</returns> 172 private DataTable GetTableStructureFromTitleRange(Range titleRow, bool isAllString = true) 173 { 174 DataTable dataTable = new System.Data.DataTable(); 175 foreach (Range cell in titleRow.Cells) 176 { 177 if (isAllString) 178 dataTable.Columns.Add(cell.Value2, typeof(string)); 179 else 180 dataTable.Columns.Add(cell.Value2, typeof(object)); 181 } 182 return dataTable; 183 } 184 #endregion 185 186 #region Column Operation 187 /// <summary> 188 /// 使用 Excel 標頭的方式生成字母列頭 189 /// </summary> 190 /// <param name="index">索引號</param> 191 /// <returns>字母列頭</returns> 192 public string GetColumnName(int index) 193 { 194 var dividend = index; 195 var columnName = string.Empty; 196 197 while (dividend > 0) 198 { 199 var modulo = (dividend - 1) % 26; 200 columnName = Convert.ToChar(65 + modulo) + columnName; 201 dividend = (dividend - modulo) / 26; 202 } 203 204 return columnName; 205 } 206 #endregion 207 }