実現したいこと
別途説明するエクセルファイルをClosedXMLを使用して読み込めるようにしたい
エクセルがインストールされていない端末でもそれが可能な事
発生している問題・分からないこと
以下のスレッドの続きです。(見なくても、ここに必要要項は書きますが。)
https://teratail.com/questions/lmfwqvnb5lx442#reply-85ukqmng7vq6ft
ClosedXMLを使用してExcelファイルを読み込む時に、式が入ったセルでエラーになるので、上記のスレッドを立てました。
しかし、ClosedXMLやVBのソースではなく、Excelファイルが壊れていました。
なぜ、Excelファイルが壊れていると言えるのか
ClosedXMLからは読めないが、本物のエクセルでは読み込む事ができるので、本物のエクセルで再保存したところ正しく処理が行えた。
ClosedXMLの読込能力の低さを残念がる事もできますが、エクセルで再保存したものは正しく処理できたので、ファイル作成者が悪いという見方もできます。
そのファイル作成者は外ならぬClosedXMLライブラリでした。
ですので、今回は書き込み側中心の話になります。
エラーメッセージ
error
1型 'System.StackOverflowException' のハンドルされていない例外が mscorlib.dll で発生しました 2 3埋め込みドキュメント 'System.Globalization\CompareInfo.cs' を 'C:\Users\sys_developer\AppData\Local\Temp\.vsdbgsrc\82f5c4d99137273f37e4fbe578de1a03843e6011bebc9ed0966c7a983ed26386\CompareInfo.cs' に抽出しました 4プログラム '[18536] WindowsApp1.exe' はコード 4294967295 (0xffffffff) で終了しました。
該当のソースコード
Form1.vb
1Imports System.IO 2Imports System.Reflection 3Imports ClosedXML.Excel 4Imports DocumentFormat.OpenXml.Spreadsheet 5 6Public Class Form1 7 Const Temp1 As String = "d:\work\TEMP1.xlsx" 8 Const Temp2 As String = "d:\work\TEMP2.xlsx" 9 Dim DataTableDGView1 As New DataTable 10 Dim ExcelResourceName As String = "WindowsApp1.TEST1.xlsx" 11 12 Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load 13 TabControl1.Dock = DockStyle.Fill 14 DataGridView1.Dock = DockStyle.Fill 15 DataGridView2.Dock = DockStyle.Fill 16 Call DataTable1Set() 17 End Sub 18 19 Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click 20 Dim Eod As Integer 21 Using ResourceStream As Stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(Me.ExcelResourceName) 22 If ResourceStream Is Nothing Then 23 MessageBox.Show("リソースが見つかりませんでした。") 24 Exit Sub 25 End If 26 Using fileStream As FileStream = File.Create(Temp1) 27 ResourceStream.CopyTo(fileStream) 28 End Using 29 End Using 30 31 Using XLWorkbook1 = New XLWorkbook(Temp1) 32 Dim WorksheetX = XLWorkbook1.Worksheet(1) 33 Dim CntRow As Integer = 1 34 For Each DataRow1 As DataRow In Me.DataTableDGView1.Rows 35 CntRow += 1 36 Dim CntCol As Integer = 1 37 WorksheetX.Cell(CntRow, CntCol).Value = DataRow1("A-Item").ToString 38 WorksheetX.Cell(CntRow, CntCol + 1).Value = DataRow1("B-Key").ToString 39 WorksheetX.Cell(CntRow, CntCol + 2).Value = ExcelCellDate(DataRow1("C-Date").ToString) 40 For For1 As Integer = 1 To 28 41 WorksheetX.Cell(CntRow, CntCol + 2 + For1).Value = "Dummy" & For1.ToString(0) 42 Next 43 WorksheetX.Cell(CntRow, CntCol + 31).Value = DataRow1("AG-String").ToString 44 WorksheetX.Cell(CntRow, CntCol + 32).Value = ExcelCellDate(DataRow1("AH-Date").ToString) 45 WorksheetX.Cell(CntRow, CntCol + 33).Value = ExcelCellDate(DataRow1("AI-Date").ToString) 46 WorksheetX.Cell(CntRow, CntCol + 35).Value = ExcelCellDate(DataRow1("AK-Date").ToString) 47 WorksheetX.Cell(CntRow, CntCol + 36).Value = DataRow1("AL-String").ToString 48 WorksheetX.Cell(CntRow, CntCol + 37).Value = DataRow1("AM-String").ToString 49 WorksheetX.Cell(CntRow, CntCol + 38).Value = ExcelCellNumeric(DataRow1("AN-フラグ").ToString) 50 WorksheetX.Cell(CntRow, CntCol + 41).Value = ExcelCellNumeric(DataRow1("AQ-フラグ").ToString) 51 WorksheetX.Cell(CntRow, CntCol + 43).Value = ExcelCellNumeric(DataRow1("AS-フラグ").ToString) 52 WorksheetX.Cell(CntRow, CntCol + 44).Value = ExcelCellDate(DataRow1("AT-Date").ToString) 53 Next 54 Eod = CntRow 55 XLWorkbook1.Save() 56 End Using 57 58 Call ExcelRemoveRows(Temp1, Eod + 1, 1000) 59 Call ExcelSaveAs(Temp1, Temp2) 60 MsgBox("Excelファイルへの書き込みが正常に完了しました。") 61 End Sub 62 63 Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click 64 Call Put_DataGrid(Temp1) 65 End Sub 66 67 Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click 68 Call Put_DataGrid(Temp2) 69 End Sub 70 71 Private Sub DataTable1Set() 72 Me.DataTableDGView1 = New DataTable() 73 With Me.DataTableDGView1.Columns 74 .Add("A-Item", GetType(String)) 75 .Add("B-Key", GetType(String)) 76 .Add("C-Date", GetType(String)) 77 .Add("AG-String", GetType(String)) 78 .Add("AH-Date", GetType(String)) 79 .Add("AI-Date", GetType(String)) 80 .Add("AK-Date", GetType(String)) 81 .Add("AL-String", GetType(String)) 82 .Add("AM-String", GetType(String)) 83 .Add("AN-フラグ", GetType(String)) 84 .Add("AQ-フラグ", GetType(String)) 85 .Add("AS-フラグ", GetType(String)) 86 .Add("AT-Date", GetType(String)) 87 End With 88 89 Dim Random1 As New Random() 90 For Count1 As Integer = 1 To 255 91 Dim RandomNumb As Integer = Random1.Next(0, 2) 92 Dim NewRow1 As DataRow = Me.DataTableDGView1.NewRow() 93 NewRow1("A-Item") = "あああ" 94 NewRow1("B-Key") = "KEY" & Count1.ToString("0000000000") 95 NewRow1("C-Date") = DateTime.Now.AddDays(Random1.Next(-50, 61)).ToString("yyyy/MM/dd") 96 NewRow1("AG-String") = If(RandomNumb = 0, String.Empty, "AG" & RandomNumb.ToString) 97 NewRow1("AH-Date") = DateTime.Now.AddDays(Random1.Next(-10, 10)).ToString("yyyy/MM/dd") 98 NewRow1("AI-Date") = DateTime.Now.AddDays(Random1.Next(-10, 10)).ToString("yyyy/MM/dd") 99 NewRow1("AK-Date") = DateTime.Now.AddDays(Random1.Next(-10, 10)).ToString("yyyy/MM/dd") 100 NewRow1("AL-String") = "AL" & (Random1.Next(-10, 10)).ToString 101 NewRow1("AM-String") = "AM" & (Random1.Next(-10, 10)).ToString 102 NewRow1("AN-フラグ") = Random1.Next(0, 2).ToString 103 NewRow1("AQ-フラグ") = Random1.Next(0, 2).ToString 104 NewRow1("AS-フラグ") = Random1.Next(0, 2).ToString 105 NewRow1("AT-Date") = DateTime.Now.AddDays(Random1.Next(-10, 10)).ToString("yyyy/MM/dd") 106 Me.DataTableDGView1.Rows.Add(NewRow1) 107 Next 108 DataGridView1.DataSource = Me.DataTableDGView1 109 End Sub 110 111 Private Function ExcelCellDate(inputDateTime As String) As String 112 Try 113 Return DateTime.Parse(inputDateTime).ToString("yyyy-MM-dd") 114 Catch ex As Exception 115 Debug.WriteLine($"エラー: {ex.Message}") 116 Return String.Empty 117 End Try 118 End Function 119 120 Private Function ExcelCellNumeric(numericValue As String) As Double 121 Try 122 If String.IsNullOrWhiteSpace(numericValue) Then Return 0.0 123 Dim result As Double 124 If Double.TryParse(numericValue, result) Then Return result 125 Debug.WriteLine($"数値変換失敗: 入力値 '{numericValue}' は数値ではありません。") 126 Return 0.0 127 Catch ex As Exception 128 Debug.WriteLine($"エラー発生: {ex.Message}") 129 Return 0.0 130 End Try 131 End Function 132 133 Private Sub ExcelRemoveRows(filePath As String, startRow As Integer, endRow As Integer) 134 Debug.WriteLine($"ファイルパス: {filePath}, 削除範囲: {startRow} から {endRow}") 135 Using XLWorkbook1 As New XLWorkbook(filePath) 136 Dim IXLWorksheet1 As IXLWorksheet = XLWorkbook1.Worksheet("Sheet1") 137 Dim delcnt = 0 138 For rowIndex As Integer = endRow To startRow Step -1 139 delcnt += 1 140 IXLWorksheet1.Row(rowIndex).Delete() 141 If delcnt >= 50 Then 142 XLWorkbook1.Save() 143 Application.DoEvents() 144 Debug.WriteLine($"50件削除") 145 delcnt = 0 146 End If 147 Next 148 XLWorkbook1.Save() 149 Debug.WriteLine($"削除完了") 150 End Using 151 End Sub 152 153 Private Sub ExcelSaveAs(filePath1 As String, filePath2 As String) 154 Using XLWorkbook1 As New XLWorkbook(filePath1) 155 XLWorkbook1.SaveAs(filePath2) 156 Debug.WriteLine($"保存しなおし完了") 157 End Using 158 End Sub 159 160 Private Sub Put_DataGrid(TempFile As String) 161 Dim DataTable1 = New DataTable 162 163 'Headerのセット 164 Using XLWorkbook1 As New XLWorkbook(TempFile) 165 Dim Worksheet1 = XLWorkbook1.Worksheet(1) 166 Dim lastColumn As Integer = Worksheet1.LastColumnUsed().ColumnNumber() 167 For col As Integer = 1 To lastColumn 168 Dim IXLCell1 As IXLCell = Worksheet1.Cell(1, col) 169 Dim headerValue As String = If(IXLCell1.IsEmpty(), String.Empty, IXLCell1.GetValue(Of String)()) 170 DataTable1.Columns.Add(headerValue) 171 Next 172 End Using 173 DataGridView2.DataSource = DataTable1 174 175 'Bodyのデータは割愛 176 MsgBox("Excelファイルへの読み込みが正常に完了しました。") 177 End Sub 178End Class
TEST1.xlsx
1ある程度負荷をかけないと落ちてくれないので、列はあまり減らしていない。 2任意の場所で作成し、直下にアドしてビルドアクションを埋め込みリソースに変更する。 3 4<ヘッダ> 5A-Item 6【式】="B-Key" & CHAR(10) & COUNTA(B2:B999) & "件" 7C-Date 8dummy1 9dummy2 10dummy3 11dummy4 12dummy5 13dummy6 14dummy7 15dummy8 16dummy9 17dummy10 18dummy11 19dummy12 20dummy13 21dummy14 22dummy15 23dummy16 24dummy17 25dummy18 26dummy19 27dummy20 28dummy21 29dummy22 30dummy23 31dummy24 32dummy25 33dummy26 34dummy27 35dummy28 36【式】="アラート3⇒" & CHAR(10) & COUNTIF(AF2:AF1000,"!") & "件" 37AG-String 38AH-Date 39AI-Date 40【式】="アラート1⇒" & CHAR(10) & COUNTIF(AJ2:AJ1000,"!") & "件" 41AK-Date 42AL-String 43AM-String 44AN-フラグ 45AO-式 46AP-式 47AQ-フラグ 48【式】="アラート2⇒" & CHAR(10) & COUNTIF(AR2:AR1000,"!") & "件" 49AS-フラグ 50AT-Date 51 52 53<ボディ> 54ボディに式があるのは下記の5項目、行2~1000まで埋まっているものとする。 55 56アラート3⇒ 57=IF(C2+30<TODAY(), IF(LEN(TRIM(AG2)) < 1, TRIM("!"), TRIM("")), TRIM("")) 58 59アラート1⇒ 60=IF(AK2+0<TODAY(), TRIM("!"), TRIM("")) 61 62AO-式 63=IF(TEXT(AS2, "0") <> TRIM("1"), IF(C2 + 7 * 2 < TODAY(), TRIM(1), TRIM(0)), TRIM(0)) 64 65AP-式 66=IF(TRIM(AT2) = TRIM(""), IF(TRIM(AH2) = TRIM(""), 1, IF(AH2 > TODAY(), 1, 0)), IF(AT2 - 7 * 2 >= TODAY(), 1, 0)) 67 68アラート2⇒ 69=IF((TRIM(AP2) & TRIM(AQ2)) = TRIM("11"), TRIM(""), IF(TRIM(AO2) = "1", TRIM("!"), TRIM(""))) 70
試したこと・調べたこと
- teratailやGoogle等で検索した
- ソースコードを自分なりに変更した
- 知人に聞いた
- その他
上記の詳細・結果
起動⇒ページ2にテストデータ
ボタン1⇒TEMP1、TEMP2が作成される
ボタン2⇒TEMP1から読み込めない
ボタン3⇒SaveAsで保存したTEMP2から読み込めない
その後
TEMP1を開きExcelで再保存するとボタン2でページ3にデータが読み込めるのを確認できる
TEMP2を開きExcelで再保存するとボタン2でページ3にデータが読み込めるのを確認できる
補足
■バージョン
vb 2022
.NET Framework 4.8
ClosedXML(0.104.2)
