AccessViolationException в ThisWorkbook.Application.Workbooks.Open

Я работаю над проектом, в котором мне нужно автоматизировать некоторые рабочие процессы в Excel, и я столкнулся с довольно неприятным препятствием. В проекте я использую Visual Studio Tools For Office для создания надстройки уровня документа. Пользователь использует элемент управления ленты, который является частью этого проекта, для автоматизации копирования рабочих листов из рабочих книг, внешних по отношению к проекту. Внешние книги загружаются из больших двоичных объектов SQL и записываются на диск. Код надстройки открывает каждую книгу, копирует лист в книгу надстройки, а затем закрывает эту внешнюю книгу. Как правило, первая книга работает нормально, но открытие следующей книги вызовет исключение 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.


person jaysqrd    schedule 19.05.2012    source источник


Ответы (2)


После долгой отладки, обращения в службу поддержки команды Microsoft VSTO и нескольких долгих ночей я наконец нашел ответ. Проблема была не в моем коде, а в самой книге. Первоначально мы написали код как отдельный проект, а затем интегрировали листы из модели электронных таблиц наших пользователей. Основная проблема заключается в том, что когда вы начинаете копировать листы из других книг, вы приносите с собой именованные ссылки на книги. Наша группа пользователей предоставила нам файл с сотнями плохих ссылок, которых мы раньше не видели.

Даже если вы подавите предупреждения с помощью объекта Application, эти события все равно будут запускаться в фоновом режиме. Большое количество событий, инициированных, когда C# манипулировал состоянием книги, привело к возникновению исключения AccessViolationException.

Урок, который я усвоил: обязательно очистите книгу и посмотрите, как она ведет себя без вашего кода. Из-за проблем со временем мы были вынуждены переписать решение на VBA, пока Microsoft отлаживала наш код. До того, как мы очистили ресурсы, код VBA работал стабильнее, что может быть связано с тем, что он интерпретируется и, по моим наблюдениям, работает в одном потоке.

Кроме того, если вы работаете с VSTO в контексте надстройки документа, вы должны быть осторожны с освобождением ссылок. Во многих случаях вам, вероятно, не нужно этого делать, потому что Excel, вероятно, убирает это за вас. Освобождение COM-объектов считается опасным< /а>.

person jaysqrd    schedule 25.06.2012
comment
Как вы определили эти плохие ссылки - person user2081126; 15.02.2020

Помню, что-то подобное было. Пока подробностей не помню, вот мой шаблон сохранения и закрытия книги. Я надеюсь, что это поможет в некотором роде.

    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();
    }
}
person Lukas    schedule 22.05.2012
comment
Спасибо за ваш отзыв, но я пытался освободить память, когда мог, и у меня все еще есть проблемы. Одно важное отличие, которое я также вижу, заключается в том, что мой код предназначен для настройки на уровне документа. Рабочая книга, которую я копирую, — это то, с чем взаимодействует пользователь... есть также определенные ресурсы, которые я не могу освободить, потому что они являются частью элементов Global Host, экземпляры которых создаются во время выполнения. - person jaysqrd; 29.05.2012
comment
Я также удалил свои вызовы FinalReleaseComObject и вместо этого использовал ReleaseComObject. - person jaysqrd; 29.05.2012