P.S. Тема PIA (Primary Interop Assemblies) не раскрыта.
Re: Взаимодействие Microsoft Excel с приложениями .NET - поз
От:
Аноним
Дата:
12.07.05 08:48
Оценка:
Здравствуйте, Гасанов Ровшан Закариевич, Вы писали:
А я всегда взаимодействовал с ексцелем и кучей других приложений за одно генерацией html страничек. А совсем давно — comma-separated + шаблон. А зачем всё остальное нужно вообще — это только лишние проблемы с СОМ interop и версиями оффиса и других приложений у клиента?
Неужели всё это нужно только
ради использования двух-трех методов
?
Re[2]: Взаимодействие Microsoft Excel с приложениями .NET -
Да, это обновленный вариант, который был специально подготовлен для журнала RSDN.
По поводу PIA — это те же DLL, которые можно получить с помощью утилиты tlbimp.exe, просто подготовленные Microsoft отдельно. Тема статьи — Позднее связывание, поэтому рассмотрение PIA выходит за рамки материала. При позднем связывании не нужны никакие заранее скомпилированные классы-посредники, все создается на этапе выполнения.
Re[2]: Взаимодействие Microsoft Excel с приложениями .NET -
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, Гасанов Ровшан Закариевич, Вы писали:
А>А я всегда взаимодействовал с ексцелем и кучей других приложений за одно генерацией html страничек. А совсем давно — comma-separated + шаблон. А зачем всё остальное нужно вообще — это только лишние проблемы с СОМ interop и версиями оффиса и других приложений у клиента?
А>Неужели всё это нужно только
А>ради использования двух-трех методов
?
И трех-четырех версий excel. 97, 2000, 2003 имеют несовместимые интеропы (или что-то в этом духе). На этом форуме кто-то уже бился с различными excel, поищи.
Re: Взаимодействие Microsoft Excel с приложениями .NET - поз
Полезно написать библиотеку с классами-обвязками объектов Excel'a, например:
public class Excel : IDisposable
{
object _comObject=null;
public Excel()
{
Type excelType=Type.GetTypeFromProgID("Excel.Application");
_comObject=Activator.CreateInstance(excelType);
}
...
}
public class Workbook
{
object _comObject;
internal Workbook(object ComObject)
{
_comObject=ComObject;
}
public string Name //пример обращения к свойству
{
get
{
return (string)_comObject.GetType().InvokeMember("Name", BindingFlags.GetProperty, null, _comObject, new object[0]);
}
set
{
_comObject.GetType().InvokeMember("Name", BindingFlags.SetProperty, null, _comObject, new object[]{value});
}
}
public void SaveAs(string fileName) //пример обращения к методу
{
_comObject.GetType().InvokeMember("SaveAs", BindingFlags.InvokeMethod, null, _comObject, new object[]{fileName});
}
...
}
public class Range
{
object _comObject=null;
internal Range(object ComObject)
{
_comObject=ComObject;
}
...
}
и так далее.
Для "портирования" объектов и методов Excel'a в дотнет достаточно либо посмотреть помощь по VBA в самом Excel'e, либо открыть Reflector'ом созданную студией interop-сборку для объектов того же Excel'a.
Для обращения к спискам объектов используются самописные коллекции:
public class WorkbooksCollection : ICollection
{
object _comObject;
internal WorkbooksCollection(object ExcelObject)
{
_comObject=ExcelObject.GetType().InvokeMember("Workbooks", BindingFlags.GetProperty, null, ExcelObject, new object[0]);
}
public Workbook this[int index] //обращение к элементу коллеции по индексу
{
get
{
try
{
return new Workbook(_comObject.GetType().InvokeMember("Item", BindingFlags.GetProperty, null, _comObject, new object[]{index}));
}
catch(Exception)
{
return null;
}
}
}
public Workbook this[string name] //обращение к элементу коллеции по имени
{
get
{
try
{
return new Workbook(_comObject.GetType().InvokeMember("Item", BindingFlags.GetProperty, null, _comObject, new object[]{name}));
}
catch
{
return null;
}
}
}
...
}
Как видно из приведённых примеров, все классы (в том числе и коллекции) имеют "указатель" на соответствующий COM-объект и все обращения к методам proxy-класса приводят к обращениям вида:
return new Workbook(_comObject.GetType().InvokeMember("Add", BindingFlags.InvokeMethod, null, _comObject, new object[]{Missing.Value}));
Таким образом, получается библиотека из порядка восьми классов-proxy и десятка коллекций.
Пользователь, работающий с данной библиотекой, не видит никаких низкоуровневых операций типа InvokeMember.
Например, так:
using(Excel excel=new Excel())
{
excel.Visible=true;
int wbCount=excel.Workbooks.Count;
object workbook1=excel.Workbooks.Add();
foreach(Worksheet wsh in excel.Workbooks[1].Worksheets) //даже так
Console.WriteLine(wsh.Name);
Worksheet ws=excel.Workbooks[1].Worksheets["Лист1"];
for(int i=1; i<10; i++)
for(int j=1;j<3;j++)
ws.Cells[i,j].Value=i*10+j;
Range range=excel.Workbooks[1].Worksheets[1].GetRange("A1", "B9");
range.Select();
excel.Quit();
}
На последок замечу, что библиотека, описывающая практически все объекты Excel'a, была написана начинающим программистом за 2 дня. И библиотека с удовольствием используется всей фирмой.
В человечишке все должно быть прекрасненьким: и одёжка, и душенка, и мордочка, и мыслишки.
Re[2]: Взаимодействие Microsoft Excel с приложениями .NET -
Маленькое добавление:
разрабатывается и тестируется библиотека под Excel97 для обеспечения обратной совместимости, а далее при желании можно проверить работоспособность под Excel 2003.
В человечишке все должно быть прекрасненьким: и одёжка, и душенка, и мордочка, и мыслишки.
Re[2]: Взаимодействие Microsoft Excel с приложениями .NET -
Здравствуйте, bo, Вы писали:
bo>От себя немного добавлю к статье... bo>Полезно написать библиотеку с классами-обвязками объектов Excel'a, например: bo>На последок замечу, что библиотека, описывающая практически все объекты Excel'a, была написана начинающим программистом за 2 дня. И библиотека с удовольствием используется всей фирмой.
Здравствуйте!
Один вопрос: а как вы решили проблему с передачей в Range массива объектов? Присваивание по ячейкам работает довольно медленно, а попытка передать в качестве последнего параметра InvokeMember object[][] заканчивается exception. Если можно, то просто кусочек кода. Заранее спасибо.
Re[3]: Взаимодействие Microsoft Excel с приложениями .NET -
Здравствуйте, SISerge, Вы писали:
SIS>Здравствуйте! SIS>Один вопрос: а как вы решили проблему с передачей в Range массива объектов? Присваивание по ячейкам работает довольно медленно, а попытка передать в качестве последнего параметра InvokeMember object[][] заканчивается exception.
Присваивание по ячейкам работает медленно и из самих макросов Excel (сейчас не знаю, но на моём первом P1-133 и Excel'97 процесс пробегания по ячейкам можно было наблюдать невооруженным взглядом ).
В любом случае следует стараться использовать range сразу для нескольких ячеек.
SIS>Если можно, то просто кусочек кода. Заранее спасибо.
Можно:
public class Worksheet
{
object _comObject=null;
internal Worksheet(object ComObject)
{
_comObject=ComObject;
}
//первый способ получения объекта Rangepublic Range GetRange(string cell1)
{
return new Range(_comObject.GetType().InvokeMember("Range", BindingFlags.GetProperty, null, _comObject, new object[]{cell1}));
}
public Range GetRange(string cell1, string cell2)
{
return new Range(_comObject.GetType().InvokeMember("Range", BindingFlags.GetProperty, null, _comObject, new object[]{cell1, cell2}));
}
public Range GetRange(Range cell1, Range cell2)
{
return new Range(_comObject.GetType().InvokeMember("Range", BindingFlags.GetProperty, null, _comObject, new object[]{cell1.GetObject(), cell2.GetObject()}));
}
//второй способ полученияpublic Range Cells
{
get
{
return new Range(_comObject.GetType().InvokeMember("Cells", BindingFlags.GetProperty, null, _comObject, new object[0]));
}
}
public Range Columns
{
get
{
return new Range(_comObject.GetType().InvokeMember("Columns", BindingFlags.GetProperty, null, _comObject, new object[0]));
}
}
public Range Rows
{
get
{
return new Range(_comObject.GetType().InvokeMember("Rows", BindingFlags.GetProperty, null, _comObject, new object[0]));
}
}
}
//и собственно сам Rangepublic class Range
{
object _comObject=null;
public Range(object ComObject)
{
_comObject=ComObject;
}
public Range Cells
{
get
{
return new Range(_comObject.GetType().InvokeMember("Cells", BindingFlags.GetProperty, null, _comObject, new object[0]));
}
}
public int Column
{
//возвращает номер первого столбцаget
{
return (int)_comObject.GetType().InvokeMember("Column", BindingFlags.GetProperty, null, _comObject, new object[0]);
}
set
{
_comObject.GetType().InvokeMember("Column", BindingFlags.SetProperty, null, _comObject, new object[]{value});
}
}
public Range Columns
{
get
{
return new Range(_comObject.GetType().InvokeMember("Columns", BindingFlags.GetProperty, null, _comObject, new object[0]));
}
}
//пример публичного свойстваpublic string Text
{
get
{
return (string)_comObject.GetType().InvokeMember("Text", BindingFlags.GetProperty, null, _comObject, new object[0]);
}
}
}
Остальные методы классов покоцаны...
В человечишке все должно быть прекрасненьким: и одёжка, и душенка, и мордочка, и мыслишки.
Re[4]: Взаимодействие Microsoft Excel с приложениями .NET -
Спасибо, но методы получения Range я знаю как реализовать. Я хотел узнать, как вы свойству Value самого Range присваиваете двумерный массив? Т.е. был у меня wrapper для Excel на VC++ 6, сейчас его нужно перетащить на C#. На С++ я Invoke передавал в качестве параметра для DISPATCH_PROPERTYPUT свойства Value простой двумерный массив COleSafeArray и Range быстренько заполнялась им. Сейчас я передаю заполненый object[][] в таком вызове:
Здравствуйте, SISerge, Вы писали:
SIS>Спасибо, но методы получения Range я знаю как реализовать. Я хотел узнать, как вы свойству Value самого Range присваиваете двумерный массив? Т.е. был у меня wrapper для Excel на VC++ 6, сейчас его нужно перетащить на C#. На С++ я Invoke передавал в качестве параметра для DISPATCH_PROPERTYPUT свойства Value простой двумерный массив COleSafeArray и Range быстренько заполнялась им. Сейчас я передаю заполненый object[][] в таком вызове:
Здравствуйте, bo, Вы писали: bo>На последок замечу, что библиотека, описывающая практически все объекты Excel'a, была написана начинающим программистом за 2 дня. И библиотека с удовольствием используется всей фирмой.
А нет ли у Вас желания выложить библиотеку в форуме Исходники ?
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re[3]: Взаимодействие Microsoft Excel с приложениями .NET -
Здравствуйте, slv, Вы писали:
slv>Здравствуйте, bo, Вы писали: bo>>На последок замечу, что библиотека, описывающая практически все объекты Excel'a, была написана начинающим программистом за 2 дня. И библиотека с удовольствием используется всей фирмой. slv>А нет ли у Вас желания выложить библиотеку в форуме Исходники ?
поддерживаю предыдущего оартора
Re[6]: Взаимодействие Microsoft Excel с приложениями .NET -
Здравствуйте, ravex, Вы писали:
R>А можно глянуть, как реализовано свойство Value класса Range?
public object Value
{
get
{
return _comObject.GetType().InvokeMember("Value", BindingFlags.GetProperty, null, _comObject, new object[0]);
}
set
{
_comObject.GetType().InvokeMember("Value", BindingFlags.SetProperty, null, _comObject, new object[]{value});
}
}
_comObject представляет Excel'евское зеркало данного объекта range.
В человечишке все должно быть прекрасненьким: и одёжка, и душенка, и мордочка, и мыслишки.
Re: Взаимодействие Microsoft Excel с приложениями .NET - поз
Здравствуйте, Tamagochi, Вы писали:
T>А не легче ли будет сделать Excel'евский XML файл, чем работать напрямую с Excel чукуз позднее связывание...?
T>данное сообщение получено с www.gotdotnet.ru T>ссылка на оригинальное сообщение
Ну если учесть, что пользователи юзают широкий спектр офисов, начиная с 97-го и заканчивая 2003-м, то никак не легче
Re[8]: Взаимодействие Microsoft Excel с приложениями .NET -
Здравствуйте, bo, Вы писали:
bo>Здравствуйте, ravex, Вы писали:
R>>А можно глянуть, как реализовано свойство Value класса Range?
bo>
bo>public object Value
bo>{
bo> get
bo> {
bo> return _comObject.GetType().InvokeMember("Value", BindingFlags.GetProperty, null, _comObject, new object[0]);
bo> }
bo> set
bo> {
bo> _comObject.GetType().InvokeMember("Value", BindingFlags.SetProperty, null, _comObject, new object[]{value});
bo> }
bo>}
bo>
bo>_comObject представляет Excel'евское зеркало данного объекта range.
Ага, спасибо! Я в общем-то так и сделал. Просто меня насторожил момент, когда в Value передается не одно значение, а массив, т.е. когда Range представляет собой массив ячеек. Но проверил — все работает нормально.