2

私はExcelでいくつかのワークフローを自動化する必要があるプロジェクトに取り組んでおり、かなり厄介な障害にぶつかりました。このプロジェクトでは、Visual Studio ToolsForOfficeを使用してドキュメントレベルのアドインを作成しています。ユーザーは、このプロジェクトの一部であるリボンコントロールを使用して、プロジェクトの外部にあるワークブックからのワークシートのコピーを自動化します。外部ワークブックはSQLBLOBから読み込まれ、ディスクに書き込まれます。アドインコードは、各ブックを開き、ワークシートをアドインブックにコピーしてから、その外部ブックを閉じます。通常、最初のブックは問題なく機能しますが、後続のブックを開くとAccessViolationExceptionがスローされます。

    public void AddSheetFromTempFile(string tempfilePath)
    {
        Sheets sheets = null;
        Excel.Workbook workbook = null;
        Excel.Workbooks books = null;
        try
        {
            books = this.Application.Workbooks;

            //Throws AccessViolationException
            workbook = books.Open(tempfilePath, 0, true, 5,
                String.Empty, String.Empty, true, XlPlatform.xlWindows,
                String.Empty, true, false, 0, true, true, false);

            sheets = workbook.Worksheets;

            sheets.Copy(After: this.GetLastWorksheet());

            workbook.Close(SaveChanges: false);
        }
        finally
        {
            if (sheets != null)
            {
                Marshal.FinalReleaseComObject(sheets);
            }

            if (workbook != null)
            {
                Marshal.FinalReleaseComObject(workbook);
            }

            if (books != null)
            {
                Marshal.FinalReleaseComObject(books);
            }

            GC.Collect();
            GC.WaitForPendingFinalizers();
        }
    }

   //extension method for getting last worksheet
   public static Microsoft.Office.Interop.Excel.Worksheet 
   GetLastWorksheet(this Microsoft.Office.Tools.Excel.WorkbookBase workbook)
   {
        int veryHiddenSheets = 0;

        foreach(Worksheet sheet in workbook.Worksheets)
        {
            if(sheet.Visible == XlSheetVisibility.xlSheetVeryHidden)
            {
                veryHiddenSheets++;
            }
        }
        int lastIndex = workbook.Worksheets.Count - veryHiddenSheets;
        return workbook.Worksheets[lastIndex];
    }

そこで、問題を繰り返し可能な一連の手順に絞り込みました。この問題は、ワークブックにN枚のシートを追加してから削除し、シートを再度追加した場合に発生しているようです。ここで提案されているネイティブデバッグを有効にしましたhttp://social.msdn.microsoft.com/forums/en-US/vsto/thread/48cd3e88-d3a6-4943-b272-6d7ea81e11e3。上記の例外の場合、次のコールスタックが表示されます。

ntdll.dll!_ZwWaitForSingleObject@12()  + 0x15 bytes 
ntdll.dll!_ZwWaitForSingleObject@12()  + 0x15 bytes 
kernel32.dll!_WaitForSingleObjectExImplementation@12()  + 0x43 bytes    
[External Code]


First-chance exception at 0x2ff2489e in Excel.exe: 0xC0000005: Access violation reading   location 0x00000000.
A first chance exception of type 'System.AccessViolationException' occurred in PublicCompModel.DLL
An exception of type 'System.AccessViolationException' occurred in PublicCompModel.DLL but was not handled in user code

COMオブジェクトを誤用しているかどうかはわかりませんが、すべてのシートを削除してこれを複製できること、およびこれがExcelに対してローカルであることは間違いなく奇妙です。

4

2 に答える 2

3

たくさんのデバッグ、MicrosoftのVSTOチームとのサポートチケット、そして長い夜を過ごした後、私はついに答えにたどり着きました。この問題は私のコードに起因するのではなく、ブック自体に起因していました。最初はスタンドアロンプ​​ロジェクトとしてコードを記述し、次にユーザーのスプレッドシートモデルからシートを統合しました。重要な問題は、他のワークブックからシートをコピーし始めるときに、ワークブックへの名前付き参照を一緒に持ってくることです。私たちのユーザーグループは、これまで見たことのない何百もの悪い参照を含むファイルを提供してくれました。

Applicationオブジェクトで警告を抑制しても、これらのイベントはバックグラウンドで発生し続けます。C#がブックの状態を操作しているときに発生する多数のイベントにより、AccessViolationExceptionが発生しました。

私が学んだ教訓:ワークブックをクリーンアップし、コードなしでワークブックがどのように動作するかを観察してください。タイミングの問題により、Microsoftがコードをデバッグしている間、VBAでソリューションを書き直す必要がありました。リソースをクリーンアップする前は、VBAコードはより安定して実行されていました。これは、VBAコードが解釈され、私の観察によれば、単一のスレッドで実行されていることが原因である可能性があります。

余談ですが、ドキュメントアドインのコンテキストでVSTOを使用している場合は、参照の解放に注意する必要があります。多くの場合、Excelがこれをクリーンアップしているため、おそらくこれを行う必要はありません。COMオブジェクトを解放することは危険であると見なされます。

于 2012-06-25T14:42:55.180 に答える
0

似たようなものを持っていたのを覚えています。詳細は覚えていませんが、ブックを保存して閉じるためのテンプレートを次に示します。これが何らかの形で役立つことを願っています。

    xlWorkBook.SaveAs(fileName, Microsoft.Office.Interop.Excel.XlFileFormat.xlWorkbookNormal, misValue, misValue, misValue, misValue, Microsoft.Office.Interop.Excel.XlSaveAsAccessMode.xlExclusive, misValue, misValue, misValue, misValue, misValue);
    xlWorkBook.Close(true, misValue, misValue);
    xlApp.Quit();

    //Release objects
    releaseObject(xlWorkSheet);
    releaseObject(xlWorkBook);
    releaseObject(xlApp);

..。

private void releaseObject(object obj)
{
    try
    {
        System.Runtime.InteropServices.Marshal.ReleaseComObject(obj);
        obj = null;
    }
    catch (Exception ex)
    {
        obj = null;
        Response.Write("Exception Occured while releasing object " + ex.ToString());
    }
    finally
    {
        GC.Collect();
    }
}
于 2012-05-22T22:52:44.673 に答える