2012-05-19 3 views
2

Я работаю над проектом, где мне нужно автоматизировать некоторые рабочие процессы в Excel, и я ударил довольно неприятный блокпост. В проекте я использую Visual Studio Tools для Office для создания надстройки уровня документа. Пользователь использует ленточный элемент управления, который является частью этого проекта, для автоматизации копирования рабочих листов из книг, внешних по отношению к проекту. Внешние книги загружаются из SQL-блоков и записываются на диск. Код надстройки открывает каждую рабочую книгу, копирует рабочий лист в книгу надстроек и закрывает эту внешнюю книгу. Как правило, первая книга работает очень хорошо, но открытие следующей книги вызовет исключение AccessViolationException.AccessViolationException в ThisWorkbook.Application.Workbooks.Open

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. Когда вышеописанное исключение, я вижу следующий стек вызовов.

[email protected]() + 0x15 bytes 
[email protected]() + 0x15 bytes 
[email protected]() + 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.

ответ

2

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

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

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

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

+0

Пожалуйста, отметьте свой ответ в качестве ответа. – Artemix

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(); 
    } 
} 
+0

Благодарим за отзыв, но я попытался освободить память, когда смогу, и у меня все еще возникают проблемы. Одно существенное отличие, которое я также вижу, это то, что мой код предназначен для настройки уровня документа. Рабочая книга, которую я копирую в и из которой заключается в том, что пользователь взаимодействует с ... Есть также определенные ресурсы, которые я не могу освободить, потому что они являются частью элементов глобального хоста, которые создаются во время выполнения. – jaysqrd

+0

Я также удалил свои вызовы в FinalReleaseComObject и вместо этого использовал ReleaseComObject. – jaysqrd

Смежные вопросы