C# と VB.NET の質問掲示板

わんくま同盟

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

ツリー一括表示

OpenXML 図形 /su (25/07/01(Tue) 17:33) #103746
Re[1]: OpenXML 図形 /am (25/07/07(Mon) 22:44) #103755
  └ Re[2]: OpenXML 図形 /su (25/07/09(Wed) 16:39) #103763
    └ Re[3]: OpenXML 図形 /魔界の仮面弁士 (25/07/09(Wed) 17:24) #103764
      └ Re[4]: OpenXML 図形 /魔界の仮面弁士 (25/07/09(Wed) 17:43) #103765
        └ Re[5]: OpenXML 図形 /su (25/07/10(Thu) 13:04) #103766
          └ Re[6]: OpenXML 図形 /とくま (25/07/11(Fri) 09:30) #103767
            └ Re[7]: OpenXML 図形 /魔界の仮面弁士 (25/07/11(Fri) 11:42) #103768
              └ Re[8]: OpenXML 図形 /su (25/07/14(Mon) 09:14) #103775 解決済み


親記事 / ▼[ 103755 ]
■103746 / 親階層)  OpenXML 図形
□投稿者/ su (1回)-(2025/07/01(Tue) 17:33:27)

分類:[VB.NET/VB2005 以降] 

開発環境:VisualStudio2022
言語:VB

OpenXMLを使用して既存のEXCELに値を書き込み、書類を作成するアプリを作成しています。
普通のシートに書き込むことは出来たのですが、
図形があるシートに下記の処理をした後、EXCELを開くと
「'XXXX.xlsx'の一部の内容に問題が見つかりました。可能な限り内容を回復しますか?ブックの発行元が信頼できる場合は、[はい]をクリックしてください。」
というメッセージが出てきて、はいをクリックすると
「削除されたレコード: /xl/worksheets/sheet6.xml パーツ内のセル情報」
という修復ログのようなものが表示されます。
値は書き込まれていません。

原因、対処法を教えて頂けると幸いです。

    Using destDoc As SpreadsheetDocument = SpreadsheetDocument.Open(FileName, True)

        ' シート名からWorksheetオブジェクトを取得する
        Dim workbookPart As WorkbookPart = destDoc.WorkbookPart
        Dim sheet As Sheet = workbookPart.Workbook.GetFirstChild(Of Sheets)().Elements(Of Sheet)().FirstOrDefault(Function(s) s.Name = "書類A")
        Dim sheetID As String = sheet.Id.Value
        Dim worksheetPart As WorksheetPart = workbookPart.GetPartById(sheetID)
        Dim worksheet As Worksheet = worksheetPart.Worksheet

        '' セルA1をワークシートに追加する
        Dim cellA1 As Cell = InsertCellInWorksheet("A", 1, worksheetPart)
        '' セルA1に値をセットする
        cellA1.CellValue = New CellValue("ABC")
        cellA1.DataType = New EnumValue(Of CellValues)(CellValues.String)
    
        ' 保存
        worksheet.Save()
        workbookPart.Workbook.Save()
        destDoc.Save()


    End Using

  Private Function InsertCellInWorksheet(columnName As String, rowIndex As UInt32, worksheetPart As WorksheetPart) As Cell

      Dim worksheet As Worksheet = worksheetPart.Worksheet
      Dim sheetData As SheetData = worksheet.GetFirstChild(Of SheetData)()
      Dim cellReference As String = columnName + rowIndex.ToString()

      ' 指定された位置のRowオブジェクト
      Dim row As Row = sheetData.Elements(Of Row)().FirstOrDefault(Function(r) r.RowIndex.Value = rowIndex)
      If (row Is Nothing) Then
          ' Rowオブジェクトがまだ存在しないときは作る
          row = New Row() With {.RowIndex = rowIndex}
          sheetData.Append(row)
      End If

      ' 指定された位置のCellオブジェクト
      Dim refCell As Cell = row.Elements(Of Cell)().FirstOrDefault(Function(c) c.CellReference.Value = cellReference)
      If (refCell IsNot Nothing) Then
          Return refCell ' すでに存在するので、それを返す
      End If

      ' Cellオブジェクトがまだ存在しないときは作って挿入する
      Dim nextCell As Cell = row.Elements(Of Cell)().FirstOrDefault(Function(c) String.Compare(c.CellReference.Value, cellReference, True) > 0)
      Dim newCell As Cell = New Cell() With {.CellReference = cellReference}
      row.InsertBefore(newCell, nextCell)

      worksheet.Save()
      Return newCell
  End Function

[ □ Tree ] 返信 編集キー/

▲[ 103746 ] / ▼[ 103763 ]
■103755 / 1階層)  Re[1]: OpenXML 図形
□投稿者/ am (1回)-(2025/07/07(Mon) 22:44:26)
No103746 (su さん) に返信
> 開発環境:VisualStudio2022
> 言語:VB
>
> OpenXMLを使用して既存のEXCELに値を書き込み、書類を作成するアプリを作成しています。
> 普通のシートに書き込むことは出来たのですが、
> 図形があるシートに下記の処理をした後、EXCELを開くと
> 「'XXXX.xlsx'の一部の内容に問題が見つかりました。可能な限り内容を回復しますか?ブックの発行元が信頼できる場合は、[はい]をクリックしてください。」
> というメッセージが出てきて、はいをクリックすると
> 「削除されたレコード: /xl/worksheets/sheet6.xml パーツ内のセル情報」
> という修復ログのようなものが表示されます。
> 値は書き込まれていません。
>
> 原因、対処法を教えて頂けると幸いです。
>
> Using destDoc As SpreadsheetDocument = SpreadsheetDocument.Open(FileName, True)
>
> ' シート名からWorksheetオブジェクトを取得する
> Dim workbookPart As WorkbookPart = destDoc.WorkbookPart
> Dim sheet As Sheet = workbookPart.Workbook.GetFirstChild(Of Sheets)().Elements(Of Sheet)().FirstOrDefault(Function(s) s.Name = "書類A")
> Dim sheetID As String = sheet.Id.Value
> Dim worksheetPart As WorksheetPart = workbookPart.GetPartById(sheetID)
> Dim worksheet As Worksheet = worksheetPart.Worksheet
>
> '' セルA1をワークシートに追加する
> Dim cellA1 As Cell = InsertCellInWorksheet("A", 1, worksheetPart)
> '' セルA1に値をセットする
> cellA1.CellValue = New CellValue("ABC")
> cellA1.DataType = New EnumValue(Of CellValues)(CellValues.String)
>
> ' 保存
> worksheet.Save()
> workbookPart.Workbook.Save()
> destDoc.Save()
>
>
> End Using
>
> Private Function InsertCellInWorksheet(columnName As String, rowIndex As UInt32, worksheetPart As WorksheetPart) As Cell
>
> Dim worksheet As Worksheet = worksheetPart.Worksheet
> Dim sheetData As SheetData = worksheet.GetFirstChild(Of SheetData)()
> Dim cellReference As String = columnName + rowIndex.ToString()
>
> ' 指定された位置のRowオブジェクト
> Dim row As Row = sheetData.Elements(Of Row)().FirstOrDefault(Function(r) r.RowIndex.Value = rowIndex)
> If (row Is Nothing) Then
> ' Rowオブジェクトがまだ存在しないときは作る
> row = New Row() With {.RowIndex = rowIndex}
> sheetData.Append(row)
> End If
>
> ' 指定された位置のCellオブジェクト
> Dim refCell As Cell = row.Elements(Of Cell)().FirstOrDefault(Function(c) c.CellReference.Value = cellReference)
> If (refCell IsNot Nothing) Then
> Return refCell ' すでに存在するので、それを返す
> End If
>
> ' Cellオブジェクトがまだ存在しないときは作って挿入する
> Dim nextCell As Cell = row.Elements(Of Cell)().FirstOrDefault(Function(c) String.Compare(c.CellReference.Value, cellReference, True) > 0)
> Dim newCell As Cell = New Cell() With {.CellReference = cellReference}
> row.InsertBefore(newCell, nextCell)
>
> worksheet.Save()
> Return newCell
> End Function

以下のコードを修正しましたので参考になればどうそ

' 既存のファイルを読み込み、新しいファイルに保存する
Dim newFileName As String = "XXXX_modified.xlsx" ' 新しいファイル名

Using originalDoc As SpreadsheetDocument = SpreadsheetDocument.Open(FileName, False) ' 読み取り専用で開く
originalDoc.SaveAs(newFileName) ' 新しいファイルとして保存
End Using

' 新しいファイルを開いて編集する
Using destDoc As SpreadsheetDocument = SpreadsheetDocument.Open(newFileName, True)

' シート名からWorksheetオブジェクトを取得する
Dim workbookPart As WorkbookPart = destDoc.WorkbookPart
Dim sheet As Sheet = workbookPart.Workbook.GetFirstChild(Of Sheets)().Elements(Of Sheet)().FirstOrDefault(Function(s) s.Name = "書類A")
Dim sheetID As String = sheet.Id.Value
Dim worksheetPart As WorksheetPart = workbookPart.GetPartById(sheetID)
Dim worksheet As Worksheet = worksheetPart.Worksheet

'' セルA1をワークシートに追加する
Dim cellA1 As Cell = InsertCellInWorksheet("A", 1, worksheetPart)
'' セルA1に値をセットする
cellA1.CellValue = New CellValue("ABC")
cellA1.DataType = New EnumValue(Of CellValues)(CellValues.String)

' 保存
worksheet.Save()
workbookPart.Workbook.Save()
' destDoc.Save() は Using Block の最後で自動的に行われるため、明示的に記述する必要はないことが多いですが、記述しても問題ありません。

End Using


[ 親 103746 / □ Tree ] 返信 編集キー/

▲[ 103755 ] / ▼[ 103764 ]
■103763 / 2階層)  Re[2]: OpenXML 図形
□投稿者/ su (2回)-(2025/07/09(Wed) 16:39:41)
No103755 (am さん) に返信
> ■No103746 (su さん) に返信
>
> 以下のコードを修正しましたので参考になればどうそ
>
> ' 既存のファイルを読み込み、新しいファイルに保存する
> Dim newFileName As String = "XXXX_modified.xlsx" ' 新しいファイル名
>
> Using originalDoc As SpreadsheetDocument = SpreadsheetDocument.Open(FileName, False) ' 読み取り専用で開く
> originalDoc.SaveAs(newFileName) ' 新しいファイルとして保存
> End Using
>
> ' 新しいファイルを開いて編集する
> Using destDoc As SpreadsheetDocument = SpreadsheetDocument.Open(newFileName, True)

お返信ありがとうございます。

試してみたのですが、
新しいファイルを開いて編集する箇所で、「別のプロセスで使用されている為、プロセスファイル’XXX.xlsx’にアクセスできません。」
とエラーが出てしまいました。
CloseやDisposeも試してみたのですが、ダメでした。
End Usingで解放されていると思ったのですが、新しいファイルは開いたままになってしまうみたいです。


[ 親 103746 / □ Tree ] 返信 編集キー/

▲[ 103763 ] / ▼[ 103765 ]
■103764 / 3階層)  Re[3]: OpenXML 図形
□投稿者/ 魔界の仮面弁士 (3858回)-(2025/07/09(Wed) 17:24:48)
No103763 (su さん) に返信
> CloseやDisposeも試してみたのですが、ダメでした。

ファイルパスではなく、Stream を渡してみるのはどうでしょうか?

New MemoryStream( File.ReadAllBytes( filePath ) ) とか
New FileStream(filePath, Open, Read, ReadWrite) とか。

https://stackoverflow.com/questions/49885226/open-locked-file-with-openxml-in-read-only-mode
https://github.com/ClosedXML/ClosedXML/issues/1589
[ 親 103746 / □ Tree ] 返信 編集キー/

▲[ 103764 ] / ▼[ 103766 ]
■103765 / 4階層)  Re[4]: OpenXML 図形
□投稿者/ 魔界の仮面弁士 (3859回)-(2025/07/09(Wed) 17:43:47)
No103764 (魔界の仮面弁士) への追記
> ファイルパスではなく、Stream を渡してみるのはどうでしょうか?

あるいは保存時の方かな?

SaveAs(string) メソッドは使わず、同じ機能を持つ Clone を使うように案内されている模様。
https://learn.microsoft.com/ja-jp/dotnet/api/documentformat.openxml.packaging.openxmlpackage.saveas?WT.mc_id=DT-MVP-8907&view=openxml-3.0.1
[ 親 103746 / □ Tree ] 返信 編集キー/

▲[ 103765 ] / ▼[ 103767 ]
■103766 / 5階層)  Re[5]: OpenXML 図形
□投稿者/ su (3回)-(2025/07/10(Thu) 13:04:50)
No103765 (魔界の仮面弁士 さん) に返信
> ■No103764 (魔界の仮面弁士) への追記
>>ファイルパスではなく、Stream を渡してみるのはどうでしょうか?
>
> あるいは保存時の方かな?
>
> SaveAs(string) メソッドは使わず、同じ機能を持つ Clone を使うように案内されている模様。
> https://learn.microsoft.com/ja-jp/dotnet/api/documentformat.openxml.packaging.openxmlpackage.saveas?WT.mc_id=DT-MVP-8907&view=openxml-3.0.1

お返信ありがとうございます。

Excelライブラリは使用せずに処理をしたいので、
Streamはむずかしいです。

Cloneでもダメでした。
[ 親 103746 / □ Tree ] 返信 編集キー/

▲[ 103766 ] / ▼[ 103768 ]
■103767 / 6階層)  Re[6]: OpenXML 図形
□投稿者/ とくま (29回)-(2025/07/11(Fri) 09:30:46)
No103766 (su さん) に返信
https://www.infoq.com/jp/news/2011/07/ClosedXML/
https://qiita.com/shironana/items/07822e51b53289bf0aa3
ClosedXML は OpenXML をベースにしているから参考になる場合はある
そして一般に言われる Excelライブラリ(Office Automation ライブラリ、COM API、Microsoft Excel N.N タイプ ライブラリ)
は使ってない。

ファイル ストリーム (ローカル ファイル システム)
https://learn.microsoft.com/ja-jp/windows/win32/fileio/file-streams
はファイル入出力の基本だし、専用の関数が用意されており、関数を変えるだけなので
普通にファイルを開く操作と変わらない。大抵、引数のオーバーロードでストリームにも
対応している。難しいものではない。
そしてこれも Excelライブラリは関係ない。

[ 親 103746 / □ Tree ] 返信 編集キー/

▲[ 103767 ] / ▼[ 103775 ]
■103768 / 7階層)  Re[7]: OpenXML 図形
□投稿者/ 魔界の仮面弁士 (3860回)-(2025/07/11(Fri) 11:42:14)
No103767 (とくま さん) に返信
> ClosedXML は OpenXML をベースにしているから参考になる場合はある
OpenXML では SpreadsheetDocument を起点にしますが
ClosedXML では XLWorkbook を起点とするのが最大の違いですね。
(ClosedXML の XLWorkbook も、内部的で SpreadsheetDocument を呼び出しています)


> そして一般に言われる Excelライブラリ(Office Automation ライブラリ、COM API、Microsoft Excel N.N タイプ ライブラリ)
> は使ってない。

そうですね。COM 版の Excel ライブラリは使っていません。

No103755 で am さんが SaveAs(String) メソッド案を出してくださいましたが、
このメソッドは OpenXml 3.3.0 の DocumentFormat.OpenXml.Packaging.SpreadsheetDocument クラスには
存在していません。この方法で No103746 の課題を解決できるのかは、当方未検証です。

SaveAs メソッドは OpenXML 2.x に存在していた "インスタンス メソッド" ですが、
3.0.0 以降では存在していません。

2.19.0 までは使用可能でしたが
2.20.0 にて非推奨(Obsolete)に変更されたという経緯があります。
No103765 で初回した URL の末尾の「適用対象」の「バージョン (廃止)」情報を参照

su さんがお使いのバージョンはどれなのでしょうか?



一方、
 Clone() As SpreadsheetDocument
 Clone(Stream) As SpreadsheetDocument
 Clone(String) As SpreadsheetDocument
などのメソッドは 3.x 系でも使用できるようです。これらは "拡張メソッド" です。


> 大抵、引数のオーバーロードでストリームにも対応している。
SpreadsheetDocument.Open(String, Boolean) のオーバーロードの代わりに
SpreadsheetDocument.Open(Stream, Boolean) のオーバーロードを呼び出してみてください。

Open メソッドだけでなく、Create メソッドや Clone メソッドも
ファイルパス(String) を引数に受け取るオーバーロードと
ストリーム(Stream) を引数に受け取るオーバーロードを持っています。

もし、FileStream を使ってもファイルがロックされてしまうようであれば、
MemoryStream を使うようにすれば、ファイルがロックされることを防げるのではないでしょうか。
編集後に、MemoryStream の中身のバイナリをファイルに書き出す処理は必要ですが。
[ 親 103746 / □ Tree ] 返信 編集キー/

▲[ 103768 ] / 返信無し
■103775 / 8階層)  Re[8]: OpenXML 図形
□投稿者/ su (5回)-(2025/07/14(Mon) 09:14:28)
No103768 (魔界の仮面弁士 さん) に返信
> ■No103767 (とくま さん) に返信
>>ClosedXML は OpenXML をベースにしているから参考になる場合はある
> OpenXML では SpreadsheetDocument を起点にしますが
> ClosedXML では XLWorkbook を起点とするのが最大の違いですね。
> (ClosedXML の XLWorkbook も、内部的で SpreadsheetDocument を呼び出しています)
>
>
>>そして一般に言われる Excelライブラリ(Office Automation ライブラリ、COM API、Microsoft Excel N.N タイプ ライブラリ)
>>は使ってない。
>
> そうですね。COM 版の Excel ライブラリは使っていません。
>
> No103755 で am さんが SaveAs(String) メソッド案を出してくださいましたが、
> このメソッドは OpenXml 3.3.0 の DocumentFormat.OpenXml.Packaging.SpreadsheetDocument クラスには
> 存在していません。この方法で No103746 の課題を解決できるのかは、当方未検証です。
>
> SaveAs メソッドは OpenXML 2.x に存在していた "インスタンス メソッド" ですが、
> 3.0.0 以降では存在していません。
>
> 2.19.0 までは使用可能でしたが
> 2.20.0 にて非推奨(Obsolete)に変更されたという経緯があります。
> ※ No103765 で初回した URL の末尾の「適用対象」の「バージョン (廃止)」情報を参照
>
> su さんがお使いのバージョンはどれなのでしょうか?
>

当初は3.3.0で使っていたのですが、SaveAsが使用できないとのことで
2.10.0を使用しています。

>>大抵、引数のオーバーロードでストリームにも対応している。
> SpreadsheetDocument.Open(String, Boolean) のオーバーロードの代わりに
> SpreadsheetDocument.Open(Stream, Boolean) のオーバーロードを呼び出してみてください。
>
> Open メソッドだけでなく、Create メソッドや Clone メソッドも
> ファイルパス(String) を引数に受け取るオーバーロードと
> ストリーム(Stream) を引数に受け取るオーバーロードを持っています。
>
> もし、FileStream を使ってもファイルがロックされてしまうようであれば、
> MemoryStream を使うようにすれば、ファイルがロックされることを防げるのではないでしょうか。
> 編集後に、MemoryStream の中身のバイナリをファイルに書き出す処理は必要ですが。

Streamを使用してみたのですが、やはりうまくいきませんでした。


当初はClosedXMLで既存のEXCELを操作すると、元々シートに張り付いていた図形等の文字サイズがおかしくなるため
OpenXMLを使いプログラムを組んでいました。
しかし、既存のEXCELの図形がグループ化されており、それを解除したらClosedXMLでも
問題なく動くことが出来ました。

ですので、一応やりたいことは解決いたしました。

ご相談に乗っていただき、ありがとうございました。
解決済み
[ 親 103746 / □ Tree ] 返信 編集キー/


管理者用

- Child Tree -