DOBON.NETプログラミング道掲示板

■35636 / 親記事)  GetObjectでExcelファイルを加工するとExcelファイルが壊れる
  
□投稿者/ suekun 一般人(1回)-(2024/12/11(Wed) 11:53:59)
  • アイコン環境/言語:[windows11/vb.net] 
    分類:[.NET] 

    お世話になっております。
    vb.netでgetobjectでExcelファイルを読込み保存するとExcelファイルが破損します。
    下記ソースになります。何か問題がありますでしょうか?

    Private Sub LIST_Clear()
    Dim wb As Excel.Workbook
    Dim sh As Excel.Worksheet
    Dim LastRow As Integer
    Try
    wb = GetObject(ListFile)
    Catch ex As Exception

    MessageBox.Show(ex.Message)

    End Try
    sh = wb.Sheets(“Sheet1”)
    LastRow = sh.Cells(sh.Rows.Count, 1).end(Excel.XlDirection.xlUp).row
    Dim tl = sh.Cells(2, 1)
    Dim br = sh.Cells(LastRow + 1, 7)
    Dim wrange = sh.Range(tl, br)
    wrange.ClearContents()
    wb.Save() ←ここでExcelブックが壊れる(シートが参照できなくなります)
    wb.Close()
    End Sub
マルチポストを報告
違反を報告
引用返信 削除キー/
■35637 / ResNo.1)  Re[1]: GetObjectでExcelファイルを加工するとExcelファイルが壊れる
□投稿者/ suekun 一般人(3回)-(2024/12/11(Wed) 15:14:25)
  • アイコンImports System.Text
    Imports Microsoft.Office.Interop.Excel
    Imports Spire.Xls
    Imports Microsoft.Office.Interop
    Imports Microsoft.Office.Core
    をインポート定義しております。
違反を報告
引用返信 削除キー/
■35638 / ResNo.2)  Re[1]: GetObjectでExcelファイルを加工するとExcelファイルが壊れる
□投稿者/ 魔界の仮面弁士 大御所(1578回)-(2024/12/11(Wed) 16:44:32)
  • アイコン2024/12/11(Wed) 16:50:44 編集(投稿者)

    No35636に返信(suekunさんの記事)
    > vb.netでgetobjectでExcelファイルを読込み保存するとExcelファイルが破損します。
    一口に「破損」としか書かれていませんが、
    具体的にはどういう状態になってしまうのでしょうか?

    保存処理自体が失敗するのか、
    保存処理時にエラーメッセージが出るのか、
    保存は行われるが、保存結果が「シート数 0 の不正なブック」になるのか
    .xlsx 形式で保存したはずが .xls 形式になってしまうのか、
    処理が無応答状態となってフリーズするのか、など。


    また、全面的に Marshal.ReleaseComObject メソッドの呼び出しが漏れているように見えます。

    「非表示の EXCEL.EXE」がタスクマネージャー上で残留するなどしていた場合に
    誤動作を引き起こしてしまう危険性があります。念のため、
    コードを修正する前に Windows を再起動しておきましょう。



    で…既に起動済みの Excel インスタンスに対して
    = GetObject(,"Excel.Application")
    などでアクセスするのならば良いですが、
    = GetObject("Z:\Example.xlsx")
    などでのアクセスはあまりお奨めしません。

    Excel が起動していない状態で『GetObject(ワークブックファイルパス)』を呼び出した場合、
    その時点で「非表示状態の Excel.exe」が Embedded モードで起動することがあり、
    このインスタンスが誤動作の要因となることがしばしばあります。
    (Application インスタンスだけでなく、Window インスタンスも不可視状態で起動します)

    GetObject で起動した場合に解放させにくいという問題は、
    VB6 などから呼び出した場合にも言えることなのですが、
    VB.NET からの呼び出しにおいては、COM オブジェクトの
    解放手続きの手間がさらに増えるので、個人的にはあまりお奨めしません。



    > wb.Close()

    Workbook オブジェクト自身を Close した時点で、内部の COM オブジェクトは
    『COM クライアント側から切断された状態』になります。

    それ自体は想定された動作ですが、.NET 側としては、切断状態にあるオブジェクトを
    そのまま保持しているだけなので、もはやこの変数を通じて Excel を操作することはできなくなります。
    (不要になったExcel本体を明示的に終了させる時などに困る)

    操作後も Excel インスタンスを使い続ける必要があるならば、GetObject は使わず、
    新規に Excel.Application インスタンスを New して、それを起点として操作する設計の方が無難です。

    GetObject を起点した場合も、Application プロパティを通じて、Excel.Application インスタンスに
    アクセスすることはできますが、それによって取得したインスタンスが、
    新しく起動した Exce なのか、自身以外の他のアプリなどからも参照されている状態なのかは区別できません。
    他のアプリなども同じインスタンスを操作していた場合、勝手に Application を
    勝手に(Quit メソッドなどで)終了させてしまうと、まだ使用している他の処理が
    『COM サーバー側から切断された状態』に陥ってしまい、やはり予期せぬ動作の要因となりえます。
違反を報告
引用返信 削除キー/
■35639 / ResNo.3)  Re[2]: GetObjectでExcelファイルを加工するとExcelファイルが壊れる
□投稿者/ suekun 一般人(4回)-(2024/12/11(Wed) 17:03:45)
  • アイコン魔界の仮面弁士さん ありがとうございます。
    > 一口に「破損」としか書かれていませんが、
    > 具体的にはどういう状態になってしまうのでしょうか?
    >
    > 保存処理自体が失敗するのか、
    →成功します。
    > 保存処理時にエラーメッセージが出るのか、
    →エラーメッセージも出ません。
    > 保存は行われるが、保存結果が「シート数 0 の不正なブック」になるのか
    →その通りです。(開きますが、シートが無く、ブック名も表示されません。Excelだけを開いた状態になります。)
    > .xlsx 形式で保存したはずが .xls 形式になってしまうのか、
    →なりません。
    > 処理が無応答状態となってフリーズするのか、など。
    →フリーズはしません。
    >
    >
    > また、全面的に Marshal.ReleaseComObject メソッドの呼び出しが漏れているように見えます。
    →別ルーチンで行っていますが、今回は割愛させていただきました。
    >
    > 「非表示の EXCEL.EXE」がタスクマネージャー上で残留するなどしていた場合に
    > 誤動作を引き起こしてしまう危険性があります。念のため、
    > コードを修正する前に Windows を再起動しておきましょう。
    →承知いたしました。
    >
    >
    > で…既に起動済みの Excel インスタンスに対して
    > = GetObject(,"Excel.Application")
    > などでアクセスするのならば良いですが、
    > = GetObject("Z:\Example.xlsx")
    > などでのアクセスはあまりお奨めしません。

    Dim Appxl As New Excel.Application
    wb = Appxl.Workbooks.Open(ListFile)
    で開くようにしたところ、破損せずに保存することが出来るようになりました。
    この方法で大丈夫でしょうか?
    よろしくお願い申し上げます。
違反を報告
引用返信 削除キー/
■35640 / ResNo.4)  Re[3]: GetObjectでExcelファイルを加工するとExcelファイルが壊れる
□投稿者/ 魔界の仮面弁士 大御所(1579回)-(2024/12/11(Wed) 18:15:40)
  • アイコン2024/12/11(Wed) 23:22:28 編集(投稿者)

    No35639に返信(suekunさんの記事)
    >>保存は行われるが、保存結果が「シート数 0 の不正なブック」になるのか
    > →その通りです。(開きますが、シートが無く、ブック名も表示されません。Excelだけを開いた状態になります。)

    そのブックは本当に壊れていますか?
    不可視状態になっているだけではないですか?

    シート数 0 のブックなら、Excel で開いた時に
    「{filename} の一部の内容に問題が見つかりました。」
    などのエラーあるいは修復案などが提示されるはずなんですが。

    該当のブックを Excel で開いた状態で、
    [表示]リボンの[ウィンドウ]>[再表示] などに不可視ウィンドウが無いか確認してみてください。


    ブックがまだ開かれていない状態で、GetObject を通じて起動された場合、
    各種ウィンドウが非表示モードになる可能性が高いです。

    プログラムから処理する場合は、Windows プロパティを列挙して
    該当の Window オブジェクトの Visible プロパティを切り替えます。



    > Dim Appxl As New Excel.Application
    > wb = Appxl.Workbooks.Open(ListFile)
    これは NG。

    books = Appxl.Workbooks
    wb = books.Open(ListFile)

    のようにしないと、Workbooks プロパティが COM オブジェクトを
    返却してきたときに解放漏れに繋がります。


    同様の理由で
    > Dim tl = sh.Cells(2, 1)
    これも
     Dim oCells = sh.Cells
     Dim tl = DirectCast(oCells(2, 1), Excel.Range)
    になるべきですね。どちらも As Excel.Range 型の変数です。

    この時、変数を tl が As Excel.Range であることを確認してください。
    As Object として受けた場合、COM 相互運用アセンブリの実装によっては、
     wrange = sh.Range(tl, br)
    などの処理を通過したときに、内部の型変換時に、COM の参照カウントが
    予期せず増加してしまうことがあります(増加しないこともあります)。
    増加していた場合は、Marshal.ReleaseComObject の戻り値が 0 になりません。


    それと、「COM オブジェクトを返すプロパティ」を複数回呼び出した場合にも注意が必要です。
     Dim oRange1 = sh.Cells
     Dim oRange2 = sh.Cells
    などでは、oRange1 と oRange2 は別インスタンスになるので、
    それぞれを ReleaseComObject する必要があります。

    それに対して
     Dim oRange1 = sh.Cells
     Dim oRange2 = oRange1
    のようなケースでは、同じ COM オブジェクトを複数の変数から見ているだけなので
    いずれかの変数のみを ReleaseComObject するようにします。両方やると過解放になってしまいます。
違反を報告
引用返信 削除キー/
■35641 / ResNo.5)  Re[4]: GetObjectでExcelファイルを加工するとExcelファイルが壊れる
□投稿者/ suekun 一般人(5回)-(2024/12/12(Thu) 07:28:30)
  • アイコン2024/12/12(Thu) 09:13:59 編集(投稿者)

    関連なのですが
    Excelファイルを開いていたら、getobjectするようにしている個所があるのですが
    下記の通りです。
    そうしないと、読み取り専用で開いてしまいます。
    これはいかがでしょうか?正常に動作しています。

    If IO.File.Exists(ListFile) = True Then
    Try
    System.IO.File.Move(ListFile, ListFile)
    Catch ex As Exception
    '移動できなかったら起動していると判定
    GoTo ExcelOpen
    End Try
    End If
    ''ファイルオープン
    wb = Appxl.Workbooks.Open(ListFile)
    GoTo ExcelOpenExit
    ExcelOpen:
    Try
    wb = GetObject(ListFile)
    Catch ex As Exception

    MessageBox.Show(ex.Message)
    End Try
    ExcelOpenExit:

違反を報告
引用返信 削除キー/
■35642 / ResNo.6)  Re[5]: GetObjectでExcelファイルを加工するとExcelファイルが壊れる
□投稿者/ 魔界の仮面弁士 大御所(1580回)-(2024/12/12(Thu) 16:11:45)
  • アイコンNo35641に返信(suekunさんの記事)
    > 関連なのですが
    ファイルが破損してしまっている状態だったのか
    それともブックが非表示になっていただけなのか
    確認はとれましたか?


    > そうしないと、読み取り専用で開いてしまいます。
    この判定を行う目的を教えてください。

    編集・保存することを目的としたものでしょうか。
    それとも、他で同時に開かれていないことを保証したいのでしょうか。
    目的を満たすために、GetObject 以外の手法を使うことは許容されますか?

    他で開かれていても保存までできるパターンはありますし
    他で開かれていなくても保存できないパターンもありますし、
    他で開かれていて Workbooks.Open で排他エラーになるパターンもあるので
    念のために確認しています。

    最終的に、ファイルに「読み取り専用属性が付いているか」とか
    アクセス権で「読み取りは許可されているが編集は許可されていない」などの
    パターンまでチェックするのか、何のためにどこまで調査したいのか…。

    GetObject に拘りが無ければ、ROT から Excel.Application オブジェクトを辿って、
    Excel.Application インスタンス (表示名「!{00024500-0000-0000-C000-000000000046}」)から
    それぞれの Workbooks コレクションを列挙判定するという手段もあります。
    コードとしては些か回りくどくなりますが…。
    https://alax.info/blog/1444
    http://bbs.wankuma.com/index.cgi?mode=one&namber=103471


    > これはいかがでしょうか?正常に動作しています。
    うぅむ?
    ListFile と NyukoCSVListFile の関連性が謎ですね??

    変数宣言や Visible の指定有無など、色々と省略され過ぎていて、
    是非の判断がしづらいところです。

    > wb = Appxl.Workbooks.Open(ListFile)
    この書き方は避けるべきですが、ここも掲示板投稿時に
    簡略化しているだけだと思うので、ひとまず目を瞑るとして…。


    > If IO.File.Exists(ListFile) = True Then
    >  Try
    >   System.IO.File.Move(ListFile, ListFile)
    1 行目では「System」を省略して、
    3 行目では明記するという非対称性がとても気になる…。

    Boolean 値の判定時に「= True」を書くべきか否かとか、
    GoTo の是非とか、素の Exception を Catch するべきかどうかとか、
    Message を表示するだけでどの Exception かを記録しないのか、
    そういった宗教論についてはとりあえず保留するとして。


    閑話休題

    >   '移動できなかったら起動していると判定
    この手順ですが、確実性のある手法では無いです。
    そのファイルが Excel で開かれているからといって、同名 Move が失敗する保証は無いからです。

    たとえば、扱っていたファイルが MultiUserEditing = True なものだった場合です。
    共有モードで開かれたファイルであれば、複数ユーザーが同時に開いて編集して保存できますし、
    開かれている最中でも、コマンドプロンプトからの同名 MOVE や VB からの同名 File.Move が
    エラーを発することはありません。

    もちろん、そうした前提条件が明確になっていて、それで目的を果たせる場合は
    現在の手法でも構わないと思います。
違反を報告
引用返信 削除キー/
■35643 / ResNo.7)  Re[6]: GetObjectでExcelファイルを加工するとExcelファイルが壊れる
□投稿者/ 魔界の仮面弁士 大御所(1581回)-(2024/12/12(Thu) 16:23:59)
  • アイコンNo35642に追記(魔界の仮面弁士の記事)
    > ListFile と NyukoCSVListFile の関連性が謎ですね??

    この一文は無視してください。
    元質問が編集される前に書いた回答文が紛れていました。
    > > 2024/12/12(Thu) 09:13:59 編集(投稿者)

    (投稿時にパスワードミスったかな…再編集できなかった)
違反を報告
引用返信 削除キー/
■35644 / ResNo.8)  Re[6]: GetObjectでExcelファイルを加工するとExcelファイルが壊れる
□投稿者/ suekun 一般人(6回)-(2024/12/13(Fri) 08:56:58)
  • アイコンNo35642に返信(魔界の仮面弁士さんの記事)
    > ■No35641に返信(suekunさんの記事)
    >>関連なのですが
    > ファイルが破損してしまっている状態だったのか
    > それともブックが非表示になっていただけなのか
    > 確認はとれましたか?

    →ブックが非表示になっていたことがわかりました。
     ファイル自体は、破損していなかったようです。
     ただ、毎回同じ状態になるので、getobjectは、ファイルが開かれている場合のみにしようと考えております。
     ありがとうございます。
違反を報告
引用返信 削除キー/
■35645 / ResNo.9)  Re[6]: GetObjectでExcelファイルを加工するとExcelファイルが壊れる
□投稿者/ suekun 一般人(7回)-(2024/12/18(Wed) 15:31:06)
  • アイコンNo35642に返信(魔界の仮面弁士さんの記事)
    魔界の仮面弁士さん
    ありがとうございました。
    大変勉強になりました。
解決み!
違反を報告
引用返信 削除キー/



スレッド内ページ移動 / << 0 >>

このスレッドに書きこむ

Mode/  Pass/


- Child Tree -