質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Windows 10

Windows 10は、マイクロソフト社がリリースしたOSです。Modern UIを標準画面にした8.1から、10では再びデスクトップ主体に戻され、UIも変更されています。PCやスマホ、タブレットなど様々なデバイスに幅広く対応していることが特徴です。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Q&A

解決済

2回答

9280閲覧

C# Excel Close時の解放もれ

taptaptaptap

総合スコア5

Windows 10

Windows 10は、マイクロソフト社がリリースしたOSです。Modern UIを標準画面にした8.1から、10では再びデスクトップ主体に戻され、UIも変更されています。PCやスマホ、タブレットなど様々なデバイスに幅広く対応していることが特徴です。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

0グッド

0クリップ

投稿2020/04/15 23:16

前提・実現したいこと

開発環境:C# VS2017 Excel2013(32bit) windows10

テンプレートのExcelファイルを用意し、テンプレートファイルにはtempシートとstampシートがあります。
stampシートには、印影を用意しており、オートシェイプで作成しています。
実行時に、オートシェイプのテキストに姓と日付を設定し、オートシェイプのあるセルごとtempシートにコピーしています。
その後、stampシートを削除します。
SaveAsにて別名に保存します。
上記手順にて処理を行うとまれに、Book.Close時にエラーが発生するため困っています。
Book.Close時にエラーが発生するときは、Excelのプロセスが解放されず残っています。
どこかに開放漏れがあるのではと思っています。

お力を貸して頂けないでしょうか。

発生している問題・エラーメッセージ

System.Runtime.InteropServices.COMException (0x80010105): サーバーによって例外が返されました。 (HRESULT からの例外:0x80010105 (RPC_E_SERVERFAULT))
場所 Microsoft.Office.Interop.Excel._Workbook.Close(Object SaveChanges, Object Filename, Object RouteWorkbook)

該当のソースコード

C#(VS2017) ■■■■■メイン処理■■■■■ ---------------------------------------------------------------------- Application xlsApp = null; Workbook xlsWorkBook = null; Workbooks xlsWorkBooks = null; Sheets xlsWorkSheets = null; Worksheet xlsTempSheet = null; Worksheet xlsStampSheet = null; try { xlsApp = new Application(); xlsApp.Visible = false; xlsApp.DisplayAlerts = false; xlsApp.ScreenUpdating = false; xlsWorkBooks = xlsApp.Workbooks; xlsWorkBook = xlsWorkBooks.Open(System.IO.Path.Combine(System.Windows.Forms.Application.StartupPath, TEMPLATE_FILE)); xlsWorkSheets = xlsWorkBook.Worksheets; xlsTempSheet = xlsWorkSheets["temp"]; //--------------------------------------------------- //検印欄の処理 xlsStampSheet = xlsWorkSheets["stamp"]; int data_index = 7; //7行目から開始 foreach (var row in query) { //印1 string name = "test"; string date = "date"; SetShapeItemText(ref xlsStampSheet, "name1", name); SetShapeItemText(ref xlsStampSheet, "date1", date); StampCellCopy(ref xlsStampSheet, "J1", ref xlsTempSheet, string.Format("J{0}", data_index)); //印2 string name2 = row["name2"].ToString(); string date2 = "date"; if (!string.IsNullOrEmpty(name2)) { SetShapeItemText(ref xlsStampSheet, "name2", name2); SetShapeItemText(ref xlsStampSheet, "date2", date2); StampCellCopy(ref xlsStampSheet, "B1", ref xlsTempSheet, string.Format("B{0}", data_index)); } data_index++; } System.Runtime.InteropServices.Marshal.FinalReleaseComObject(xlsStampSheet); xlsStampSheet = null; ここ ----------------------------------------------------- //stampシートの削除 Worksheet xlsDelWorkSheet = xlsWorkSheets["stamp"]; xlsDelWorkSheet.Delete(); System.Runtime.InteropServices.Marshal.FinalReleaseComObject(xlsDelWorkSheet); xlsDelWorkSheet = null; ----------------------------------------------------- //--------------------------------------------------- //保存 string outPath = System.IO.Path.Combine(_outputPath, "test.xlsx"); System.IO.File.Delete(outPath); xlsWorkBook.SaveAs(outPath, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, XlSaveAsAccessMode.xlNoChange, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing); xlsWorkBook.Close(Type.Missing, Type.Missing, Type.Missing); return true; } catch (Exception ex) { return false; } finally { if (xlsStampSheet != null && System.Runtime.InteropServices.Marshal.IsComObject(xlsStampSheet)) { System.Runtime.InteropServices.Marshal.FinalReleaseComObject(xlsStampSheet); xlsStampSheet = null; } if (xlsTempSheet != null && System.Runtime.InteropServices.Marshal.IsComObject(xlsTempSheet)) { System.Runtime.InteropServices.Marshal.FinalReleaseComObject(xlsTempSheet); xlsTempSheet = null; } if (xlsWorkSheets != null && System.Runtime.InteropServices.Marshal.IsComObject(xlsWorkSheets)) { System.Runtime.InteropServices.Marshal.FinalReleaseComObject(xlsWorkSheets); xlsWorkSheets = null; } if (xlsWorkBook != null && System.Runtime.InteropServices.Marshal.IsComObject(xlsWorkBook)) { try { //xlsWorkBook.Saved = true; //xlsWorkBook.Close(false, Type.Missing, Type.Missing); } catch (Exception) { } finally { System.Runtime.InteropServices.Marshal.FinalReleaseComObject(xlsWorkBook); xlsWorkBook = null; } } if (xlsWorkBooks != null && System.Runtime.InteropServices.Marshal.IsComObject(xlsWorkBooks)) { System.Runtime.InteropServices.Marshal.FinalReleaseComObject(xlsWorkBooks); xlsWorkBooks = null; } GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); if (xlsApp != null && System.Runtime.InteropServices.Marshal.IsComObject(xlsApp)) { xlsApp.ScreenUpdating = true; xlsApp.DisplayAlerts = true; xlsApp.Quit(); System.Runtime.InteropServices.Marshal.FinalReleaseComObject(xlsApp); xlsApp = null; } GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); } ---------------------------------------------------------------------- メイン処理ここまで ここからメソッド /// <summary> /// 印影に値を設定する /// </summary> /// <param name="xlsStampSheet"></param> /// <param name="stamp_Item_name"></param> /// <param name="value"></param> protected void SetShapeItemText(ref Worksheet xlsStampSheet, string stamp_Item_name, string value) { Shapes xlsShapes = null; Shape xlsShape = null; TextFrame xlsTextFrame = null; Characters xlsCharacters = null; try { xlsShapes = xlsStampSheet.Shapes; xlsShape = xlsShapes.Item(stamp_Item_name); xlsTextFrame = xlsShape.TextFrame; xlsCharacters = xlsTextFrame.Characters(); xlsCharacters.Text = value; } finally { if (xlsCharacters != null && System.Runtime.InteropServices.Marshal.IsComObject(xlsCharacters)) { System.Runtime.InteropServices.Marshal.FinalReleaseComObject(xlsCharacters); xlsCharacters = null; } if (xlsTextFrame != null && System.Runtime.InteropServices.Marshal.IsComObject(xlsTextFrame)) { System.Runtime.InteropServices.Marshal.FinalReleaseComObject(xlsTextFrame); xlsTextFrame = null; } if (xlsShape != null && System.Runtime.InteropServices.Marshal.IsComObject(xlsShape)) { System.Runtime.InteropServices.Marshal.FinalReleaseComObject(xlsShape); xlsShape = null; } if (xlsShapes != null && System.Runtime.InteropServices.Marshal.IsComObject(xlsShapes)) { System.Runtime.InteropServices.Marshal.FinalReleaseComObject(xlsShapes); xlsShapes = null; } } } protected void StampCellCopy(ref Worksheet xlsStampSheet, string stamp_cell, ref Worksheet xlsDestinationSheet, string destination_cell) { Range xlsStampRange = null; Range xlsDestinatioRange = null; try { xlsStampRange = xlsStampSheet.Range[stamp_cell]; xlsDestinatioRange = xlsDestinationSheet.Range[destination_cell]; xlsStampRange.Copy(xlsDestinatioRange); } finally { if (xlsDestinatioRange != null && System.Runtime.InteropServices.Marshal.IsComObject(xlsDestinatioRange)) { System.Runtime.InteropServices.Marshal.FinalReleaseComObject(xlsDestinatioRange); xlsDestinatioRange = null; } if (xlsStampRange != null && System.Runtime.InteropServices.Marshal.IsComObject(xlsStampRange)) { System.Runtime.InteropServices.Marshal.FinalReleaseComObject(xlsStampRange); xlsStampRange = null; } } }

試したこと

stampシートの削除の部分をコメントにすると、エラーにはなりませんでした。
Close処理の位置を変えてみたりもしましたが、結果は変わらずエラーが発生します。
検印欄の処理を行わないと、stampシートを削除してもエラーは発生しませんでした。
おそらく、stampシートの操作で、解放漏れがあるからだとは思いますが。。。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Zuishin

2020/04/16 00:29

> xlsWorkBook.Close(Type.Missing, Type.Missing, Type.Missing); ここを次のように変えるとどうなりますか? xlsWorkBook.Close(false, Type.Missing, false);
taptaptaptap

2020/04/16 00:58

返信ありがとうございます。 試してみましたが、同じように「System.Runtime.InteropServices.COMException (0x80010105): サーバーによって例外が返されました」となってしまいました。
Zuishin

2020/04/16 01:12

では、削除部分から FinalReleaseComObject を取り除くとどうなりますか? このメソッドは COM オブジェクトの参照カウントを無理やり 0 にして強制解放するものですが、Excel 内部でそのオブジェクトが使われていた場合、解放済みオブジェクトへのアクセスで不具合が起こると思います。
Zuishin

2020/04/16 01:24 編集

というか、よく見ると結構使われていますね。全部コメントアウトしてみてください。COM はガベージコレクタによって自動的に解放されるので、基本的に使わなくていいメソッドです。 以上は、うろ覚えによる間違いでした。 説明をよく読むと、COM の参照カウントではなくマネージ参照カウントだったので、Excel 内部の COM の参照カウントには影響を及ぼさないようです。 しかし、念のため試してみてください。
taptaptaptap

2020/04/16 01:25

回答くださり、ありがとうございます。 System.Runtime.InteropServices.Marshal.FinalReleaseComObject(xlsStampSheet); xlsStampSheet = null; と System.Runtime.InteropServices.Marshal.FinalReleaseComObject(xlsDelWorkSheet); xlsDelWorkSheet = null; を削除して確認してみましたが、2回成功後3回目でエラーとなりました。 stampシートのオートシェイプの処理を行っている間、タスクマネージャーを確認するとCPUが 100%になっていたりします。そうとう負荷がかかってますね・・
Zuishin

2020/04/16 01:38

ビジーで失敗しているのかもしれませんね。 キャッチして時間を置きながら数回リトライしていたらどうでしょうか。 Exception exception = null; int tryCount = 3; do { try { 例外の生じる処理() } catch(Exception e) { exception = e; tryCount--; Thread.Sleep(1000); } } while (exception != null && tryCount > 0);
taptaptaptap

2020/04/16 01:55

ありがとうございます。 試してみました。 ーーーーー Exception exception = null; int tryCount = 3; do { exception = null; try { xlsWorkBook.Close(false, Type.Missing, false); } catch (Exception e) { exception = e; tryCount-=1; System.Threading.Thread.Sleep(1000); } Console.WriteLine("tryCount:" + tryCount); } while (exception != null && tryCount > 0) ; ーーーーー としてみると、1度Closeで失敗しても、2回目では成功しているようです。 しかし、1度Closeで失敗したときは、タスクマネージャーにExcelのプロセスが残ってしまいます。 出力したExcelを開くと、データ自体は出力されていますが、左側にドキュメントの回復が表示されてしまいます。
Zuishin

2020/04/16 02:09 編集

処理の前に 10 秒くらいの十分な遅延を入れて成功率百パーセントになればビジーによる失敗と確定できるので、確定したとしたら、あとはその遅延をどのくらいにするか調整次第ということになるかもしれません。
taptaptaptap

2020/04/16 02:21

Close処理の前に10秒Sleepを入れてみました。 10秒Sleepの間にタスクマネジャーのCPU使用率は、0%になりますが、3回に1回くらい、Closeで失敗し、上記で入れたリトライ処理を行っています。 今回は、タスクマネジャーにプロセスが残っていませんでしたが、出力したExcelファイルを開くとやはり 左側にドキュメントの回復が表示されてしまいます。
Zuishin

2020/04/16 02:37

ビジーが原因というわけでもなさそうですね。 サーバーエラーなので、結局 Excel が閉じる際に何らかの理由でクラッシュしているということですから、もしかしたらクローズ処理にバグがあり、複雑なシートを処理できなかったのかもしれません。保存時にはクラッシュしていないので、ワークシートを閉じるのではなく Excel Application そのものを解放する形にしてみたらどうでしょうか。
taptaptaptap

2020/04/16 02:53

何度も申し訳ありません。ありがとうございます。 Close処理をコメントアウトして試してみました。 結果は変わらず3回に1回失敗します。(なぜCloseしなくても成功することがあるのか、そちらのほうが謎ですが) オートシェイプが原因だとは思いますが、さっぱり分かりません・・・
Zuishin

2020/04/16 03:05

クローズしなかった時はどこで例外が起きてるんでしょうか? もし保存時に起きているんだとしたら、ウィルス駆除ソフトやドロップボックスなど、Windows の場合はインデックスサービスやファイル履歴も切って試してみてください。
taptaptaptap

2020/04/16 03:12

すみません。書き方が悪かったですね。 プログラム上では例外は起きていません。 保存したExcelファイルを開いたときに、左側にドキュメントの回復が表示されていたので、 失敗したのかと判断しました。
Zuishin

2020/04/16 03:32

ドキュメントの作成までを自動で行い、別名での保存と終了を手動で行うと、何かエラーが出ませんか? もしそれでもクラッシュするようであれば、根本的に方法を変えなければいけないかもしれません。
taptaptaptap

2020/04/16 06:00

ありがとうございます。 試してみました。別名で保存前までを、xlsApp.Visible = true;で作成し、別名で保存を手動で行いました。 その後、ファイルを閉じるという操作を5回ほど繰り返したところ、ファイルを閉じたにも関わらず、Excelのプロセスが残ってしまいました。 エラーは特に表示されませんでした。
Zuishin

2020/04/16 06:06

処置なしですね。私なら Excel をあきらめて HTML で出力します。
taptaptaptap

2020/04/16 06:13

色々考えてくださりありがとうございました。 ファイルに出力後、Excelに埋め込んだ式にてさらに処理を行うので、Excelをあきらめることができません・・・ もう少し頑張ってみます。 ありがとうございました。
guest

回答2

0

あくまで予想になりますが、Excel側で時間の掛かる処理を実行していたら、すぐ閉じたりする事が出来なさそうな気がします。重い処理の合間に、SleepやTask.Delayで遅延を入れてみたりしたら大丈夫かもしれません。
Excel InteropをやめてEPPlus、ClosedXML等のExcel操作ライブラリを使うのが一番安全かとは思いますが、私はShapeを扱った事はないので、同じ事を実現出来るかどうかは分かりません。実験してみるしかないですね。

投稿2020/04/16 00:04

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

taptaptaptap

2020/04/16 01:01

そうでうね。ClosedXMLで実現できるといいのですが、ClosedXMLの情報をみていると、開けないExcelも存在するという情報をみたります。 過去に、大量(5万行以上)のデータを扱った際に、落ちてしまった経験があります。 今回は、そこまで大量ではないので、大丈夫かもしれませんが、オートシェイプの情報が見当たらないので躊躇してます。 Sleep試してみます!!
taptaptaptap

2020/04/16 01:29

Sleep(200)で試してみましたが、複数回行うとCloseでエラーとなってしまいました。 ほかの方の回答でも記載しましたが、CPUの使用率が異常に上がるので、オートシェイプの操作は 負荷がかかりそうです。
guest

0

自己解決

色々試してみたところ、シートを削除するのではなく、非表示とすることで動作が安定しました。
根本解決ではありませんが、これにて解決とさせて頂きます。
回答をくださった、Zuishin様、radian様ありがとうございました!!

xlsStampSheet.Visible = XlSheetVisibility.xlSheetVeryHidden;

投稿2020/04/21 02:32

taptaptaptap

総合スコア5

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問