В предыдущем посте я писал о создании csv-файла и экспорта в него данных из ASP.NET и при этом, перечисляя разные варианты, не упомянул один совсем уже бредовый для web-приложения, но частенько очень подходящий для приложений десктопных. Это работа с установленным в системе COM-объектом c помощью технологии под названием COM Interop.
COM Interop используется в .Net для предоставления возможности взаимодействия управляемого .Net кода с COM-объектами. Тля того чтобы использовать какой-либо COM-объект из управляемого кода, необходимо создать сборку, содержащую информацию о типах содержащихся в COM-библиотеке, в совместимом с CRL формате.
В процессе работы приложения CLR создает для каждого COM-объекта внутренний объект, называемый Runtime Callable Wrapper (Вызываемая оболочка времени выполнения) или RCW, которая используется для создания COM-объекта и маршалинга данных между управляемой и неуправляемой средой. Также, RCW используется для мониторинга количества активных ссылок на COM-объект и его уничтожение, когда количество активных ссылок станет равным нулю. Выглядит это примерно так:
Раз
И добавить соответствующую строку using в ваш код
using Excel = Microsoft.Office.Interop.Excel;
В качестве источника данных для примера будем использовать список массивов строк ( List<string[]>). В коде я написал комментарии, которые, думаю, будут вполне понятно объяснять что происходит.
//Missing представляет собой отcутствующий объект, для передачи в качестве не используемого параметра. //В принципе, начиная с C# 4.0 можно использовать стандартный синтаксис для
//необязательных параметров. А можно по старинке, вместо отсутствующего
// параметра передать Missing.Value System.Reflection.Missing missingValue = System.Reflection.Missing.Value; //создаем и инициализируем объекты Excel Excel.Application App; Excel.Workbook xlsWB; Excel.Worksheet xlsSheet; App = new Microsoft.Office.Interop.Excel.Application(); //добавляем в файл Excel книгу. Параметр в данной функции - используемый для создания книги шаблон. //если нас устраивает вид по умолчанию, то можно спокойно передавать пустой параметр. xlsWB = App.Workbooks.Add(missingValue); //и использует из нее xlsSheet = (Excel.Worksheet)xlsWB.Worksheets.get_Item(1); List<string[]> rows = new List<string[]>(); rows.Add(new string[] {"1","2","3","4"}); rows.Add(new string[] {"5","6","7","8"}); rows.Add(new string[] {"9","10","11","12"}); for (int i = 0; i < rows.Count; i++) { for (int j = 0; j < rows[i].Length; j++) { xlsSheet.Cells[i + 1, j + 1] = rows[i][j]; } } //у SaveAs масса параметров, некоторые из которых могут оказаться для вас полезными. //Сейчас мы используем только тип формата Excel12 (Excel 97) и права доступа. //Полное описание что и как смотрите на MSDN: // http://msdn.microsoft.com/ru-ru/library/microsoft.office.tools.excel.workbook.saveas.aspx xlsWB.SaveAs(@"C:\excel_text.xls", Excel.XlFileFormat.xlExcel12, missingValue, missingValue, missingValue, missingValue, Excel.XlSaveAsAccessMode.xlNoChange, missingValue, missingValue, missingValue, missingValue, missingValue); //закрываем книгу xlsWB.Close(true, missingValue, missingValue); //закрываем приложение App.Quit(); //уменьшаем счетчики ссылок на COM объекты, что, по идее должно их освободить. //почему это не произойдет - читайте ниже ;) System.Runtime.InteropServices.Marshal.ReleaseComObject(xlsSheet); System.Runtime.InteropServices.Marshal.ReleaseComObject(xlsWB); System.Runtime.InteropServices.Marshal.ReleaseComObject(App);
Ну и пара нюансов с которыми вы можете столкнуться. В случае, если в COM-компоненте происходит исключение, которое вы не обработаете, то, как и положено, весть домен приложения будет аварийно закрыт. Вместе с RCW. В результате чего, COM-объект так и останется висеть в памяти. То есть, вполне реально получить вот такую картинку:
И нюанс второй. Код сверху не совсем правильный. Дело в том, что работая с COM Interop нужно быть предельно внимательным и осторожным, потому, что случайно можно создать COM-объект, совершенно об этом не подозревая. В данном случае, создается даже не один "случайный" объект, а несколько.
Во-первых, в строке xlsWB = App.Workbooks.Add(missingValue); создается объект App.Workbooks, который не присваевается переменной и, соответственно, не освобожается дл тех пор, пока не выгрузится домен приложения.
Во-вторых, такая же проблема в строке в строке: xlsSheet = (Excel.Worksheet)xlsWB.Worksheets.get_Item(1);
Для того, чтобы эту проблему решить необходимо добавить две новые переменные и немного изменить код инициализации:
//создаем и инициализируем объекты Excel Excel.Application App; //переменная для Workbooks Excel.Workbooks xlsWBs; Excel.Workbook xlsWB; //переменная для Sheets Excel.Sheets xlsSheets; Excel.Worksheet xlsSheet; App = new Microsoft.Office.Interop.Excel.ApplicationClass(); //добавляем в файл Excel книгу. Параметр в данной функции - используемый для создания книги шаблон. //если нас устраивает вид по умолчанию, то можно спокойно передавать пустой параметр. xlsWBs = App.Workbooks; xlsWB = xlsWBs.Add(missingValue); xlsSheets = xlsWB.Worksheets; xlsSheet = (Excel.Worksheet)xlsSheets.get_Item(1);
Ну и добавить освобождение этих переменных в конце с помощью System.Runtime.InteropServices.Marshal.ReleaseComObject.
Вообще же, работая с COM объектами старайтесь придерживаться простого правила:
Никогда не используйте больше одной точки с COM.
Комментариев нет:
Отправить комментарий