none
C# Excel 操作 EXEが残り続ける RRS feed

  • 質問

  • Excelを操作しているのですが

    oXL.Quit();でExcel.Applicationを破棄しても

    タスクマネ-ジャにEXCEL.EXEが残り続けます。

    繰り返し動作させるとEXCEL.EXEが増加し続けるのですが、

    タスクバ-にExcelは表示されていません。

    EXCEL.EXEを破棄するにはどうすればよいでしょうか。

     

    private Excel.Application oXL = null;

    private Excel.Workbook oWB = null;

    public ExcelFile(){

    oXL = new Excel.Application();

                oXL.Visible = true;

                oXL.DisplayAlerts = false;

    private void Clear(){

                if (oWB != null) { 

                    oWB.Close(false, Missing.Value, Missing.Value);

                    oWB = null;

                }

                if (oXL != null) {

                    oXL.Quit();

                    oXL = null;

                }

    }

     

    2010年4月2日 9:26

回答

  • 解放する順番を制御する必要がなく、最後に GC.Collect されるのでしたら、ReleaseComObject は必須ではありません。(もちろん行ってもいいです。)
    その場合、必要なオブジェクトのみ変数定義すればいいので、コードをシンプルにできます。
    (ひらぽんさんの2番目のコードでは、Cells についてこれに依存していることになります。)

    以下のコードをリリースモードでビルドし、「デバッグなしで開始」で実行して、一度ご確認ください。
    (デバッグビルドやデバッグ実行では、デバッガーなどによって参照の寿命が延ばされますので、プロセスは残ります。)

    // using Excel = Microsoft.Office.Interop.Excel;
    // using System.Reflection;
    
    private void button1_Click(object sender, EventArgs e)
    {
        for (var i = 0; i < 20; i++)
            ExcelTest();
    }
    
    private void ExcelTest()
    {
        try
        {
            var xlApplication = new Excel.Application();
            var xlBook = xlApplication.Workbooks.Add(Missing.Value);
            var xlSheet = (Excel.Worksheet)xlBook.Sheets[1];
            var xlRange = (Excel.Range)xlSheet.Cells[1, 1];
            // xlRange.Value2 = DateTime.Now;
            xlRange.set_Value(Missing.Value, DateTime.Now);
            xlRange.EntireColumn.AutoFit();
    
            xlApplication.Visible = true;
            xlApplication.DisplayAlerts = false;
            xlApplication.Quit();
        }
        finally
        {
            GC.Collect();
        }
    }



    ひらぽんさん
    > xlRange.Value2 = xlBook.Name;

    念のため補足させてください。
    Value プロパティと Value2 プロパティは異なるもので、通常は Value プロパティを利用します。
    ただ、Excel の Value プロパティには省略可能な引数がありますが、C# の言語仕様においてそれは表現できないため、ランタイム呼び出し可能ラッパーではメソッド(set_Value および get_Value)に変換されます。
    上記コードのように、セルに DateTime 値を設定した場合、違いを確認できます。

    • 回答としてマーク 菊地俊介 2010年4月23日 4:16
    2010年4月5日 5:39
  • ずいぶん続いてますね。問題の切り分けをしましょう。

    まず呼び出しもとのアプリケーションを終了したときにExcelのインスタンスが残っているかどうか。残らないなら主に参照やインターフェイスポインタの解放の問題で、残るならApplication.Quit/SaveAs/CloseなどExcelのオペレーションの問題となります。

    これまでの流れを見ると後者のように思います。後者の場合にExcelを非表示のまま操作しているなら表示した状態に変更してください。いろいろ見えてくるはずです。Excel側のAPIは文字通りOLEオートメーションなのでマクロのように人間のオペレーションをほぼそのままコードで指定するもので、バージョンどころか微妙な設定の違いで動作に違いが出ます。例えばある操作をしようとすると警告のダイアログを表示する設定とそうしない設定と違いがあった場合、前者ではダイアログが表示されて止まっていることがあるわけです。シートを表示状態にすることで予想外の状態になっていることを確認することができます。

    • 回答としてマーク 菊地俊介 2010年4月23日 4:17
    2010年4月5日 10:28
  • C# から Excel を操作するときは COMオブジェクトの破棄を常に注意しなければいけません。オブジェクトの破棄漏れがあると、Excelのプロセスが残ったままになってしまいます。

     

    COMオブジェクトの破棄には、 System.Runtime.InteropServices.Marshal.ReleaseComObject を使います。

     

    Excel 操作に役立つと思われるWebページのURLを紹介しておきます。ぜひ熟読をオススメします。

    http://jeanne.wankuma.com/tips/csharp/excel/

    http://jeanne.wankuma.com/tips/csharp/programming/releasecom.html

     


    なかむら(http://d.hatena.ne.jp/griefworker)
    • 回答としてマーク 菊地俊介 2010年4月23日 4:16
    2010年4月2日 10:39
  • 確か

    ((Excel.Worksheet)oWB.Workshees[i + 1].Select(true);
    

    みたいにプロパティを介してオブジェクトを操作したときも、COMオブジェクトの解放漏れになります。

    Excel.Worksheet worksheet = oWB.Worksheets[i + 1];
    worksheet.Select(true);
    System.Runtime.InteropServices.Marshal.ReleaseComObject(worksheet);

     という風に、一度変数に格納してからメソッドを呼び出し解放する必要があります。面倒ですけどね。

    プロパティを介してオブジェクトを操作しているところが他にもあると思いますので、チェックしてみて下さい。

     


    なかむら(http://d.hatena.ne.jp/griefworker)
    • 回答としてマーク 菊地俊介 2010年4月23日 4:16
    2010年4月5日 1:39
  • GCコレクトによって本当に全て解決するのか疑問になり、色々調べてみました。この辺はかなり複雑な話になっているのですね・・・

    COM参照を確実に解放するコードの可読性をあげたい

    COM オブジェクトの参照カウントを解放する

    Marshal.ReleaseComObjectは危険な場合がある

    ちなみに COM だからといって何でもかんでも ReleaseComObject すればいいというものではなく、ベンダー製で VB6時代 に作られた COM を ReleaseComObject で解放しようとすると、COMException 例外が発生する場合があったりするようです。

    #いまのプロジェクトで使っている ActiveX はまさにそう。(-ω-;


    ひらぽん http://blogs.yahoo.co.jp/hilapon/
    • 編集済み ひらぽんModerator 2010年4月5日 6:09 よく見たら件名と関係のない記事のリンクが貼ってあったので削除
    • 回答としてマーク 菊地俊介 2010年4月23日 4:16
    2010年4月5日 4:59
    モデレータ

すべての返信

  • アンマネージリソースを解放するには Dispose メソッドを実行します。提示されたコードですと、以下のようになります。

    private void Clear(){

      if (oWB != null) {
        oWB.Close(false, Missing.Value, Missing.Value);
        oWB.Dispose(); // 解放処理
        oWB = null;
      }
      if (oXL != null) {
        oXL.Quit();
        oXL.Dispose(); // 解放処理
        oXL = null;
      }
    }

    詳しくは、Dispose パターンで検索してみてください。

     


    ひらぽん http://blogs.yahoo.co.jp/hilapon/
    2010年4月2日 9:50
    モデレータ
  • C# から Excel を操作するときは COMオブジェクトの破棄を常に注意しなければいけません。オブジェクトの破棄漏れがあると、Excelのプロセスが残ったままになってしまいます。

     

    COMオブジェクトの破棄には、 System.Runtime.InteropServices.Marshal.ReleaseComObject を使います。

     

    Excel 操作に役立つと思われるWebページのURLを紹介しておきます。ぜひ熟読をオススメします。

    http://jeanne.wankuma.com/tips/csharp/excel/

    http://jeanne.wankuma.com/tips/csharp/programming/releasecom.html

     


    なかむら(http://d.hatena.ne.jp/griefworker)
    • 回答としてマーク 菊地俊介 2010年4月23日 4:16
    2010年4月2日 10:39
  • ありがとうございます。

    oWB.Dispose();

    oXL.Dispose();

    で以下のエラーが発生します。

    Microsoft.Office.Interop.Excel.WorkbookにDisposeの

    定義が含まれておらず、型Microsoft.Office.Interop.Excel.Workbookの

    最初の引数を受け付ける拡張メソッドが見つかりませんでした。

    usingディレクティブまたはアセンブリ参照が不足しています。

     

    Disposeメソッドを解決できていないようなのですが、

    現在参照しているライブラリは以下の通りです。

    #define PIA_OFFICE_XP

    using System;

    using System.Data;

    using System.Runtime.InteropServices;

    using System.Reflection;

    #if PIA_OFFICE_XP

    using Excel = Microsoft.Office.Interop.Excel;

    #endif

     

    2010年4月5日 0:41
  • ありがとうございます。

    URLよりCOMオブジェクトに関しては大体理解しました。

    ガーベジコレクションが破棄できなかった可能性を考慮し

    メンバ・ローカル双方でReleaseComObjectを実行したのですが、

    結果は変わりませんでした。

    public String Out(DataRow[] Row1, DataRow[] Row2){

    Excel.Sheets sheets = null;

    Excel.Worksheet sheet = null;

    try{

    int k = 0;

    for(int i = 0; i <= Row1[].Length - 1; i++){

    ((Excel.Worksheet)oWB.Workshees[i + 1].Select(true);

    oSheet = (Excel.Worksheet)oWB.ActiveSheet;

    shees = oWB.Sheets;

    sheet = (Excel.Worksheet)sheets.Add(System.Reflection.Missing.Value,

         sheets[sheets.Count], 1, 

         Excel.XlSheetType.xlWorksheet);

    }

    }Finally{

    System.Runtime.InteropServices.Marshal.ReleaseComObject(sheets);

    System.Runtime.InteropServices.Marshal.ReleaseComObject(sheet);

    Clear();//メンバCOMオブジェクト破棄処理

    }

     

    2010年4月5日 1:18
  • 確か

    ((Excel.Worksheet)oWB.Workshees[i + 1].Select(true);
    

    みたいにプロパティを介してオブジェクトを操作したときも、COMオブジェクトの解放漏れになります。

    Excel.Worksheet worksheet = oWB.Worksheets[i + 1];
    worksheet.Select(true);
    System.Runtime.InteropServices.Marshal.ReleaseComObject(worksheet);

     という風に、一度変数に格納してからメソッドを呼び出し解放する必要があります。面倒ですけどね。

    プロパティを介してオブジェクトを操作しているところが他にもあると思いますので、チェックしてみて下さい。

     


    なかむら(http://d.hatena.ne.jp/griefworker)
    • 回答としてマーク 菊地俊介 2010年4月23日 4:16
    2010年4月5日 1:39
  • oWB.Dispose();
    oXL.Dispose();

    で以下のエラーが発生します。

    Microsoft.Office.Interop.Excel.WorkbookにDisposeの
    定義が含まれておらず、型Microsoft.Office.Interop.Excel.Workbookの
    最初の引数を受け付ける拡張メソッドが見つかりませんでした。


    ありゃ、外してしまいましたか!><
    Interop サービス(?名前うろ覚え)でラップされたCOM って Dispose が自動的に実装されるという話を聞きましたが、
    (実際いまのプロジェクトで使っている ActiveX は Dispose が自動的に実装される)
    どうやら Excel には当てはまらなかったようですね。失礼しました。


    ひらぽん http://blogs.yahoo.co.jp/hilapon/
    2010年4月5日 2:09
    モデレータ
  • ありがとうございます。

    >プロパティを介してオブジェクトを操作しているところ

    現在一行ずつリークのチェックをしているのですが、

    この辺りの法則性がはっきりしません。

     

    ((Excel.Worksheet)oWB.Workshees[i + 1].Select(true);の場合
    (Excel.Worksheet)oWB.Workshees[i + 1]というシートオブジェクトを暗黙的に生成し、
    それに対してSelect(true)をかけているためリークするというのはわかります。

     

    しかし、例えば

     

    oSheet = (Excel.Worksheet)oSheets.get_Item(2);

    oSheet.Delete();

    //System.Runtime.InteropServices.Marshal.ReleaseComObject(oSheet);

    oSheet = (Excel.Worksheet)oSheets.get_Item(2);

    oSheet.Delete();

    Clear();

    上記を実行するとリークします。

    コメントを外すとリークしませんでした。

    これはなぜでしょうか?

     

     

    2010年4月5日 3:00
  • > プロパティを介してオブジェクトを操作したときも、COMオブジェクトの解放漏れになります。
    > ・・・一度変数に格納してからメソッドを呼び出し解放する必要があります。面倒ですけどね。

    Excel は、私も今後扱う可能性があるので、試してみました。

    public void sample(){
    
        Microsoft.Office.Interop.Excel.Application xlApplication = null;
        Workbooks xlBooks = null;
        Workbook xlBook = null;
        Worksheet xlSheet = null;
        Range xlRange = null;
    
        try {
            xlApplication = new Microsoft.Office.Interop.Excel.Application();
            xlBooks = xlApplication.Workbooks;
            xlBook = xlBooks.Add(string.Empty);
            xlSheet = (Worksheet)xlBook.Sheets[1];
            xlRange = (Range)xlSheet.Cells[1, 1];
            xlRange.Value2 = xlBook.Name;
    
            // Excel を表示する
            xlApplication.Visible = true;
            // 1000 ミリ秒 (1秒) 待機する
            System.Threading.Thread.Sleep(1000);
            // Excel を終了する
            xlBook.Saved = true;
            xlApplication.Quit();
        }
        finally {
            // COM オブジェクトの参照カウントを解放する
            Marshal.ReleaseComObject(xlRange);
            Marshal.ReleaseComObject(xlSheet);
            Marshal.ReleaseComObject(xlBook);
            Marshal.ReleaseComObject(xlBooks);
            Marshal.ReleaseComObject(xlApplication);
        }
    }
    

    上記のメソッドを定義し、20回ループして実行したら、

    for (int i = 0; i < 20; i++ )
        this.sample();

    呼び出し側のアプリケーションが実行中ならプロセスは確かに残ったままですね。(-ω-;

    正確には、しばらく経つとガベージコレクションより解放されますが、すぐには解放されません。私の環境では4~5分待たされました。しかし呼び出し側のアプリを終了すると、ただちに Excel のプロセスも終了します。

    そこで finally 節で明示的にガベージをコレクトしたら、ただちに EXCEL のプロセスが終了されるようになりました。

        finally {
            // COM オブジェクトの参照カウントを解放する
            Marshal.ReleaseComObject(xlRange);
            Marshal.ReleaseComObject(xlSheet);
            Marshal.ReleaseComObject(xlBook);
            Marshal.ReleaseComObject(xlBooks);
            Marshal.ReleaseComObject(xlApplication);
    
            GC.Collect(); // これを追加
        }
    

    以上、参考までにどうぞ。<(_ _)>


    ひらぽん http://blogs.yahoo.co.jp/hilapon/
    2010年4月5日 3:07
    モデレータ
  • 先ほどのレス。環境を書いてませんでした。

    Windows XP SP3 / Visual Studio 2008 SP1 / .NET Framework 3.5 SP1

    です。あと

    > プロパティを介してオブジェクトを操作したときも、COMオブジェクトの解放漏れになります。
    > ・・・一度変数に格納してからメソッドを呼び出し解放する必要があります。面倒ですけどね。

    もう一点。プロパティを介して操作してみました。

    public void sample(){
    
        Microsoft.Office.Interop.Excel.Application xlApplication = null;
        Workbooks xlBooks = null;
        Workbook xlBook = null;
    
        try {
            xlApplication = new Microsoft.Office.Interop.Excel.Application();
            xlBooks = xlApplication.Workbooks;
            xlBook = xlBooks.Add(string.Empty);
            ((Range)((Worksheet)xlBook.Sheets[1]).Cells[1, 1]).Value2 = ((Worksheet)xlBook.Sheets[1]).Name;
    
            // Excel を表示する
            xlApplication.Visible = true;
            // 1000 ミリ秒 (1秒) 待機する
            System.Threading.Thread.Sleep(1000);
            // Excel を終了する
            xlBook.Saved = true;
            xlApplication.Quit();
        }
        finally {
            // COM オブジェクトの参照カウントを解放する
            Marshal.ReleaseComObject(xlBook);
            Marshal.ReleaseComObject(xlBooks);
            Marshal.ReleaseComObject(xlApplication);
            GC.Collect();
        }
    }
    

    100回ループして実行してみましたが、プロセスが残っていたりページファイルのリークは認められませんでした。
    明示的にガベージコレクトしてやれば、プロパティを介してオブジェクトを操作しても COM オブジェクトの解放漏れはないようです。


    ひらぽん http://blogs.yahoo.co.jp/hilapon/
    2010年4月5日 3:22
    モデレータ
  • GCコレクトによって本当に全て解決するのか疑問になり、色々調べてみました。この辺はかなり複雑な話になっているのですね・・・

    COM参照を確実に解放するコードの可読性をあげたい

    COM オブジェクトの参照カウントを解放する

    Marshal.ReleaseComObjectは危険な場合がある

    ちなみに COM だからといって何でもかんでも ReleaseComObject すればいいというものではなく、ベンダー製で VB6時代 に作られた COM を ReleaseComObject で解放しようとすると、COMException 例外が発生する場合があったりするようです。

    #いまのプロジェクトで使っている ActiveX はまさにそう。(-ω-;


    ひらぽん http://blogs.yahoo.co.jp/hilapon/
    • 編集済み ひらぽんModerator 2010年4月5日 6:09 よく見たら件名と関係のない記事のリンクが貼ってあったので削除
    • 回答としてマーク 菊地俊介 2010年4月23日 4:16
    2010年4月5日 4:59
    モデレータ
  • 解放する順番を制御する必要がなく、最後に GC.Collect されるのでしたら、ReleaseComObject は必須ではありません。(もちろん行ってもいいです。)
    その場合、必要なオブジェクトのみ変数定義すればいいので、コードをシンプルにできます。
    (ひらぽんさんの2番目のコードでは、Cells についてこれに依存していることになります。)

    以下のコードをリリースモードでビルドし、「デバッグなしで開始」で実行して、一度ご確認ください。
    (デバッグビルドやデバッグ実行では、デバッガーなどによって参照の寿命が延ばされますので、プロセスは残ります。)

    // using Excel = Microsoft.Office.Interop.Excel;
    // using System.Reflection;
    
    private void button1_Click(object sender, EventArgs e)
    {
        for (var i = 0; i < 20; i++)
            ExcelTest();
    }
    
    private void ExcelTest()
    {
        try
        {
            var xlApplication = new Excel.Application();
            var xlBook = xlApplication.Workbooks.Add(Missing.Value);
            var xlSheet = (Excel.Worksheet)xlBook.Sheets[1];
            var xlRange = (Excel.Range)xlSheet.Cells[1, 1];
            // xlRange.Value2 = DateTime.Now;
            xlRange.set_Value(Missing.Value, DateTime.Now);
            xlRange.EntireColumn.AutoFit();
    
            xlApplication.Visible = true;
            xlApplication.DisplayAlerts = false;
            xlApplication.Quit();
        }
        finally
        {
            GC.Collect();
        }
    }



    ひらぽんさん
    > xlRange.Value2 = xlBook.Name;

    念のため補足させてください。
    Value プロパティと Value2 プロパティは異なるもので、通常は Value プロパティを利用します。
    ただ、Excel の Value プロパティには省略可能な引数がありますが、C# の言語仕様においてそれは表現できないため、ランタイム呼び出し可能ラッパーではメソッド(set_Value および get_Value)に変換されます。
    上記コードのように、セルに DateTime 値を設定した場合、違いを確認できます。

    • 回答としてマーク 菊地俊介 2010年4月23日 4:16
    2010年4月5日 5:39
  • ありがとうございます。

    GC.Collect();を使用し、アプリ終了時に破棄されていたものを

    明示的に破棄することが可能になりました。

    しかし、アプリ終了後にも破棄されていないものは以前残ったままでした。

    最終的に破棄されない処理はシートの追加とブックの保存です。

    (ブックの追加は大丈夫でした)

    追加

                        Count = oSheets.Count;

                        after = oSheets[Count];

                        type = Excel.XlSheetType.xlWorksheet;

                        MissValue = System.Reflection.Missing.Value;

                        oSheets.Add(MissValue, after, 1, type);

    保存

                    Mode = Excel.XlSaveAsAccessMode.xlNoChange;

                    Format = Excel.XlFileFormat.xlXMLSpreadsheet;

                    oWB.SaveAs(FileName, Format, MissValue, MissValue,

                               false, false, Mode, MissValue, MissValue,

                               MissValue, MissValue, MissValue);

     

    以下のURLはVB.NETですが、ほぼ同様の事例を発見しました。

    http://ap.atmarkit.co.jp/bbs/core/fdotnet/10767

    Application.DoEvents()なども試してみましたが駄目でした。

     

    >この辺はかなり複雑な話になっているのですね・・・

    URL参照しました。

    .NETからExcelをいじるのがこんなに大変だとは・・・

    最終的には確実に解放する為の処理も実装しなければなりませんし。

     

    2010年4月5日 5:47
  • 解放する順番を制御する必要がなく、最後に GC.Collect されるのでしたら、ReleaseComObject は必須ではありません。(もちろん行ってもいいです。)
    その場合、必要なオブジェクトのみ変数定義すればいいので、コードをシンプルにできます。
    (ひらぽんさんの2番目のコードでは、Cells についてこれに依存していることになります。)

    以下のコードをリリースモードでビルドし、「デバッグなしで開始」で実行して、一度ご確認ください。
    (デバッグビルドやデバッグ実行では、デバッガーなどによって参照の寿命が延ばされますので、プロセスは残ります。)


    補足ありがとうございます。

    > (デバッグビルドやデバッグ実行では、デバッガーなどによって参照の寿命が延ばされますので、プロセスは残ります。)

    これははじめて知りました。まだまだ修行が必要ですね。φ(`д´)メモメモ...

    ひらぽんさん
    > xlRange.Value2 = xlBook.Name;

    念のため補足させてください。
    Value プロパティと Value2 プロパティは異なるもので、通常は Value プロパティを利用します。
    ただ、Excel の Value プロパティには省略可能な引数がありますが、C# の言語仕様においてそれは表現できないため、ランタイム呼び出し可能ラッパーではメソッド(set_Value および get_Value)に変換されます。
    上記コードのように、セルに DateTime 値を設定した場合、違いを確認できます。



    おお!そういうことだったのですね。どおりで Value プロパティがないと思いました。ありがとうございます。<(_ _)>
    ひらぽん http://blogs.yahoo.co.jp/hilapon/
    2010年4月5日 5:47
    モデレータ
  • 失礼しました。

    シートの追加も問題ないようです。

    ブックの保存(SaveAs)でアプリ終了後もEXCEL.EXEが残ります。

    ReleaseObject

    GC.Collect();

    どちらでも破棄されませんでした。

     

    2010年4月5日 6:29
  • どうしてでしょう?こちらの環境ではきちんと解放されてます。解放のタイミングにもよるのかもしれませんね。

    以下のコードを sikake_chui  さんの環境で実行するとどうなりますか?

    public void sample(){
    
        Microsoft.Office.Interop.Excel.Application xlApplication = null;
        Workbooks xlBooks = null;
        Workbook xlBook = null;
        Object MissValue = System.Reflection.Missing.Value;
    
        try {
            xlApplication = new Microsoft.Office.Interop.Excel.Application();
            xlApplication.DisplayAlerts = false;
    
            xlBooks = xlApplication.Workbooks;
            xlBook = xlBooks.Add(string.Empty);
            ((Range)((Worksheet)xlBook.Sheets[1]).Cells[1, 1]).Value2 = ((Worksheet)xlBook.Sheets[1]).Name;
    
            // Excel を表示する
            xlApplication.Visible = true;
            // 1000 ミリ秒 (1秒) 待機する
            System.Threading.Thread.Sleep(1000);
            // Excel を終了する
            xlBook.SaveAs("test.xls", XlFileFormat.xlWorkbookNormal, 
                    MissValue, MissValue, MissValue, MissValue, 
                    XlSaveAsAccessMode.xlNoChange, MissValue, MissValue, MissValue, MissValue, MissValue);
            xlApplication.Quit();
        }
        finally {
            // COM オブジェクトの参照カウントを解放する
            Marshal.ReleaseComObject(xlBook);
            Marshal.ReleaseComObject(xlBooks);
            Marshal.ReleaseComObject(xlApplication);
            GC.Collect();
        }
    }

    ひらぽん http://blogs.yahoo.co.jp/hilapon/
    2010年4月5日 6:43
    モデレータ
  • ありがとうございます。

    駄目ですね・・・残ってます。

    環境はひらぽんさんとまったく同じでした。

    参照の追加はMicrosoft Excel 12.0 Object Libraryのみ

    プロジェクトはWindowsFormアプリケーション

    あとは

    Workbooks xlBooks =null;
    などとするとエラーになるので
    Excel.Workbooks xlBooks =null;
    とした位です、これはおそらく外で設定しているんですよね。

    一応PC再起動もしましたが駄目でした。

    2010年4月5日 7:10
  • ちょっと他のPC二台で試してみたのですが、ちゃんと破棄されていました。

    再現性の低いエラーのようですね。

    http://ap.atmarkit.co.jp/bbs/core/fdotnet/13832

    最悪本番環境が動いてさえくれれば御の字なんですけど。

    でもなんででしょうかホント・・・

    2010年4月5日 7:52
  • 他の PC なら大丈夫ということは
    C#プログラム以外のことも確認したほうがいいかもしれません

    Excelのプロセスが消えません
    http://questionbox.jp.msn.com/qa2925157.html
    2010年4月5日 8:49
  • ありがとうございます。

    XLSTARTフォルダ + Excel.xlb を削除まで試しましたが駄目でした。

    再インストールは諸事情で難しいところです。

    ですがC#以外の要因は考慮していませんでしたので、色々探してみようと思います。

    2010年4月5日 9:35
  • 正常に破棄されるPC とそうでない PC では何が違うのか、ひとつずつ問題を切り分けて調べてみた方がいいと思います。

    ・OS とサービスパックの有無
    ・インストールされている .NET Framework のバージョンとサービスパックの有無
    ・Excel のバージョンとサービスパックの有無、またはExcel のオプション設定の違い
    ・IE のバージョン
    ・ウイルスソフトの種類
    ・保存先のフォルダの属性etc・・・

    これらをひとつずつ潰していく中に、なにか見えてきそうな気がします。


    ひらぽん http://blogs.yahoo.co.jp/hilapon/
    2010年4月5日 9:51
    モデレータ
  • ずいぶん続いてますね。問題の切り分けをしましょう。

    まず呼び出しもとのアプリケーションを終了したときにExcelのインスタンスが残っているかどうか。残らないなら主に参照やインターフェイスポインタの解放の問題で、残るならApplication.Quit/SaveAs/CloseなどExcelのオペレーションの問題となります。

    これまでの流れを見ると後者のように思います。後者の場合にExcelを非表示のまま操作しているなら表示した状態に変更してください。いろいろ見えてくるはずです。Excel側のAPIは文字通りOLEオートメーションなのでマクロのように人間のオペレーションをほぼそのままコードで指定するもので、バージョンどころか微妙な設定の違いで動作に違いが出ます。例えばある操作をしようとすると警告のダイアログを表示する設定とそうしない設定と違いがあった場合、前者ではダイアログが表示されて止まっていることがあるわけです。シートを表示状態にすることで予想外の状態になっていることを確認することができます。

    • 回答としてマーク 菊地俊介 2010年4月23日 4:17
    2010年4月5日 10:28
  • 普通は問題ないことなので、はずしてそうなことも書いてみます。

    コーディング上の問題の続きですが、SaveAs で何らかの例外が発生したために Quit が実行されていないということはないでしょうか?
    ひらぽんさんのコードを試される時、既存のプロジェクト上で試され、そのプロジェクトの独自フレームワークでは例外は握りつぶしてしまっているためにそれに気が付けていないなどはないですよね?
    Quit の直前で MessageBox を行った場合、ちゃんと表示されますでしょうか?
    (もともと sikake_chui さんは Excel を表示されていますので(試されたひらぽんさんのコードも)、ぱらぐらいさんの補足になると思い、書かせていただきました。)

    それと sikake_chui さんは
    #if PIA_OFFICE_XP
    と書かれていますので Excel XP(Excel 2002)かと思ったのですが、参照は 12.0(Excel 2007)なんですね?
    PIA_OFFICE_XP はたんなる識別子なので問題というわけではありませんが、少し気になりました。
    プロセスが残る場合、Excel は表示されたままだと思いますが、その際のバージョンは Excel 2007 ですね?

    2010年4月5日 10:56
  • ありがとうございます。

    環境を以下に羅列します。

    WindowsXP Pro SP3

    .NET3.5 SP1

    Excel2007 SP2(オプションは特に変更していないと思います)

    Windowsアプリなのでブラウザは関係ないと思います。

    ウイルスソフトはMSEですが、止めてみても結果は変わりませんでした。

    保存先はC直下で、特に権限をつけたりはしていません。

     

    上記までは他の二台とまったく同じ環境でした。

    (ウイルスソフトは止めていただきました)

    2010年4月6日 0:12
  • ありがとうございます。

    現状はおっしゃる通り後者です。

    Excelは表示状態(アラートあり)で操作していますが、特に変わった点はありません。

    しいて挙げるなら作成されたxlsを開くと

    拡張子の形式が異なりますがよろしいですか~

    といった確認メッセージがでるくらいですね。
    2010年4月6日 0:39
  • ありがとうございます。

    デバッグで確認しましたが、Quitまで問題なく通っています。

     

                if (oXL != null) {

                    oXL.Quit();

                    System.Runtime.InteropServices.Marshal.ReleaseComObject(oXL);

                    oXL = null;

                }

    GC.Cllect();は2ブック出力したあとやるので、自作したExcel出力クラスの外で呼んでいます。

    >Excel XP(Excel 2002)

    OfficeXp→Excel2002なんですね・・・はじめて知りました。

    流用元のソースが2002の記述だったみたいです。

    Microsoft Excel 12.0 Object Libraryしか参照していませんので、

    特に問題はないと思いますが、恥ずかしいので直しておきます。

    >プロセスが残る場合、Excel は表示されたまま

    いえ、ウインドウ(インターフェイス)は閉じています。

    プロセスにのみ残り続けている状態です。

    2007以外の環境では動かないはずです(2002では動作しませんでした)

     

    2010年4月6日 0:55
  • ご存知のように、Excel は 1プロセスで複数ファイルを扱えるのに
    複数プロセス起動もできるという面白いアプリです
    なので Excel プロセスが既にあればそれを使うというアプローチにしたのが下のコードです
    ただしこれだけだと、既起動だった Excel プロセスも終了してしまうのが問題ですw
    検証不十分ですが、ご参考まで
    (前略)
                try {
                    xlApplication = (Microsoft.Office.Interop.Excel.Application)System.Runtime.InteropServices.Marshal.GetActiveObject("Excel.Application");
                }
                catch { //Excel 未起動時は Get できないが下で new するので処理続行。
                }

                try {
                    //xlApplication = new Microsoft.Office.Interop.Excel.Application();
                    if (xlApplication == null) xlApplication = new Microsoft.Office.Interop.Excel.Application(); //Excel が Get できてなかったら new する。
    (以下略)
    2010年4月6日 1:32
  • sikake_chui さん
    > いえ、ウインドウ(インターフェイス)は閉じています。
    > プロセスにのみ残り続けている状態です。

    あれ? すみませんが、再度確認させてください。

    新規作成したプロジェクトに、ひらぽんさんのコード(先の返信での私のコードで試していただける場合は、DisplayAlerts と Quit の間に SaveAs を追加してください)で試された場合でも、アプリケーションを終了しても Excel のプロセスが残り、その Excel は非表示になっているということでしょうか?
    (動作確認の際は、リリースビルドをデバッグなしで実行してください。)

    それとも、「(アプリケーションが終了しても非表示の Excel が)プロセスにのみ残り続けている状態」とは、sikake_chui さんの実際のアプリケーションでの話でしょうか?
    念のためカッコ内に状況を補いましたが、「ウィンドウは閉じている」とはアプリケーションの終了ではなく、フォームを Close した状態でしょうか?

    sikake_chui さん
    > 2002では動作しませんでした

    xlXMLSpreadsheet 形式は 2003 からの機能ですからね。
    それもあり、念のために実際に起動した Excel のバージョンを確認させていただきました。

    sikake_chui さん
    > しいて挙げるなら作成されたxlsを開くと
    > 拡張子の形式が異なりますがよろしいですか~
    > といった確認メッセージがでるくらいですね。

    xlXMLSpreadsheet 形式の既定の拡張子は .xml ですからね。

    2010年4月6日 1:54
  • ありがとうございます。

    どのExcelプロセスかを指定できれば使えるんですが。

    別なExcelを見ながら使用する可能性が考えられる為難しいですね。

     

     

    2010年4月6日 2:19
  • ありがとうございます。

    >新規作成したプロジェクトに、ひらぽんさんのコード(先の返信での私のコードで試していただける場合は、DisplayAlerts と Quit の間に SaveAs を追加してください)で試された場合でも、アプリケーションを終了しても Excel のプロセスが残り、その Excel は非表示になっているということでしょうか?
    (動作確認の際は、リリースビルドをデバッグなしで実行してください。)

    その通りです、私のソースとひろぽんさんのソースで同じ結果になります。

    アプリの終了とウインドウcloseの違いが良くわかりませんが、

    画面から(プロセス以外の要素から)Excelが起動している事を認識することはできません。

    すいません、何か紛らわしい書き方をしてしまっているかも。

     

    >xlXMLSpreadsheet 形式の既定の拡張子は .xml ですからね。

    これですか・・・別件でずっと探してたんですよね、どこが問題なのか。

    Excel.XlFileFormat.xlWorkbookNormal;

    に修正したら直りました、ありがとうございます。


    2010年4月6日 2:31
  • sikake_chui さん
    > その通りです、私のソースとひろぽんさんのソースで同じ結果になります。
    > アプリの終了とウインドウcloseの違いが良くわかりませんが、
    > 画面から(プロセス以外の要素から)Excelが起動している事を認識することはできません。

    不可解な現象なため、前に書かれたことについても、細かく、くどくどと確認させていただきました。
    今回は「ウインドウ(インターフェイス)は閉じています」という表現で書かれましたので、たとえばアプリには2画面あり、2画面目でエクセルを作成してから画面を閉じた(アプリは終了していない)場合の話の可能性も考えられましたので。
    アプリが終了していない場合には、GC.Collect の対象になっていない参照がどこかに残っている可能性がありますが、終了されているのでしたらその可能性もないですね。

    > これですか・・・別件でずっと探してたんですよね、どこが問題なのか。

    補足的に書いたことがお役に立てたようで良かったです。

    現状としては、

    ・コードの書き方の問題ではなさそう。
    (私のコードに SaveAs を追加された場合の結果はずっと気になってますが)
    ・Excelのアドインが影響しているわけではなさそう。
    (anningo さんのリンク先手順を試されたようですので)
    ・OSやExcelのバージョンの問題でもない。
    (ひらぽんさんと同じ環境のようですので)
    ・アンチウィルスソフトの影響でもない。
    (停止しても変わらないとのことなので)

    ですね。
    常駐ソフトが影響している可能性のために、セーフモードで起動したOS上でも同じですかね。
    原因は気になりますので、もし今後、何か解ったら教えてください。

     

    追記:
    そうそう、すると、
    VBスクリプトや VB.NET でも同じようにプロセスが残るんですよね?
    以下の内容を .vbs という拡張子のファイルで保存して実行してみていただけますでしょうか?

    Set excelApp = CreateObject("Excel.Application")
    excelApp.Visible = True
    excelApp.DisplayAlerts = False
    Set book = excelApp.Workbooks.Add
    Set sheet = book.Sheets(1)
    Set range = sheet.Cells(1, 1)
    range.Value = Now
    range.EntireColumn.AutoFit
    Const xlWorkbookNormal = -4143
    book.SaveAs "c:\test.xls", xlWorkbookNormal
    excelApp.Quit

    • 編集済み TH01 2010年4月7日 0:28 追記
    2010年4月6日 11:01