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

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

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

OpenXML 図形

[トピック内 9 記事 (1 - 9 表示)]  << 0 >>

■103746 / inTopicNo.1)  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

引用返信 編集キー/
■103755 / inTopicNo.2)  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


引用返信 編集キー/
■103763 / inTopicNo.3)  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で解放されていると思ったのですが、新しいファイルは開いたままになってしまうみたいです。


引用返信 編集キー/
■103764 / inTopicNo.4)  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
引用返信 編集キー/
■103765 / inTopicNo.5)  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
引用返信 編集キー/
■103766 / inTopicNo.6)  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でもダメでした。
引用返信 編集キー/
■103767 / inTopicNo.7)  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ライブラリは関係ない。

引用返信 編集キー/
■103768 / inTopicNo.8)  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 の中身のバイナリをファイルに書き出す処理は必要ですが。
引用返信 編集キー/
■103775 / inTopicNo.9)  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でも
問題なく動くことが出来ました。

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

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

このトピックをツリーで一括表示


トピック内ページ移動 / << 0 >>

このトピックに書きこむ