Поддерживает ли C# .NET позднюю привязку IDispatch?


Вопрос

Мой вопрос: поддерживает ли C# нативную позднюю привязку IDispatch?


Представьте, что я пытаюсь автоматизировать Office, и в то же время совместим с любой версией, которую установил клиент.

В мире.NET, если вы разрабатывали с установленным Office 2000, каждый разработчик и каждый клиент, от настоящего момента до конца времени, должен иметь Office 2000.

В мире до.NET мы использовали COM для общения с приложениями Office.

Например:

1) Используйте независимый от версии ProgID

"Excel.Application"

который разрешает:

clsid = {00024500-0000-0000-C000-000000000046}

и затем, используя COM, мы просим один из этих классов создать экземпляр объекта:

IUnknown unk;
CoCreateInstance(
    clsid, 
    null,
    CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER,
    IUnknown, 
    out unk);

А теперь мы в гонках - можем использовать Excel из моего приложения. Конечно, если вы действительно хотите использовать объект, вы должны вызвать метод вызова методов.

Мы могли бы получить различные объявления интерфейса, переведенные на наш язык. Эта техника хороша, потому что мы получаем

  • раннее связывание
  • код-прозрение
  • проверка синтаксиса типа компиляции

и пример кода может быть:

Application xl = (IExcelApplication)unk;
ExcelWorkbook workbook = xl.Workbooks.Add(template, lcid);
Worksheet worksheet = workbook.ActiveSheet;

Но есть и обратная сторона использования интерфейсов: мы должны получить доступ к различным объявлениям интерфейса, переведенным на наш язык. И мы застряли, используя вызовы, основанные на методах, и нам нужно указать все параметры, например:

ExcelWorkbook workbook = xl.Workbooks.Add(template, lcid);
xl.Worksheets.Add(before, after, count, type, lcid);

В реальном мире это доказало, что у нас есть такие недостатки, что мы охотно отказались бы:

  • раннее связывание
  • код-прозрение
  • проверка синтаксиса времени компиляции

и вместо этого используйте позднюю привязку IDispatch:

Variant xl = (IDispatch)unk;
Variant newWorksheet = xl.Worksheets.Add();

Поскольку автоматизация Excel была разработана для VB Script, многие параметры могут быть пропущены, даже если без них нет перегрузки.

Примечание: не путайте мой пример Excel с причиной, по которой я хочу использовать IDispatch. Не каждый COM-объект является Excel. Некоторые COM-объекты не имеют никакой поддержки, кроме как через IDispatch.

6 ответов

Решение

Относительно, вы можете использовать позднюю привязку IDispatch привязки в C#.

http://support.microsoft.com/kb/302902

Вот пример использования Excel. Таким образом, вам не нужно добавлять ненужную зависимость от распухшей Microsoft PIA:

//Create XL
Object xl = Activator.CreateInstance(Type.GetTypeFromProgID("Excel.Application"));

//Get the workbooks collection.
//   books = xl.Workbooks;
Object books = xl.GetType().InvokeMember( "Workbooks", 
      BindingFlags.GetProperty, null, xl, null);

//Add a new workbook.
//   book = books.Add();
Objet book = books.GetType().InvokeMember( "Add", 
      BindingFlags.InvokeMethod, null, books, null );

//Get the worksheets collection.
//   sheets = book.Worksheets;
Object sheets = book.GetType().InvokeMember( "Worksheets",
      BindingFlags.GetProperty, null, book, null );

Object[] parameters;

//Get the first worksheet.
//   sheet = sheets.Item[1]
parameters = new Object[1];
parameters[0] = 1;
Object sheet = sheets.GetType().InvokeMember( "Item", 
      BindingFlags.GetProperty, null, sheets, parameters );

//Get a range object that contains cell A1.
//   range = sheet.Range["A1];
parameters = new Object[2];
parameters[0] = "A1";
parameters[1] = Missing.Value;
Object range = sheet.GetType().InvokeMember( "Range",
      BindingFlags.GetProperty, null, sheet, parameters );

//Write "Hello, World!" in cell A1.
//   range.Value = "Hello, World!";
parameters = new Object[1];
parameters[0] = "Hello, World!";
objRange_Late.GetType().InvokeMember( "Value", BindingFlags.SetProperty, 
      null, range, parameters );

//Return control of Excel to the user.
//   xl.Visible = true;
//   xl.UserControl = true;
parameters = new Object[1];
parameters[0] = true;
xl.GetType().InvokeMember( "Visible", BindingFlags.SetProperty,
      null, xl, Parameters );
xl.GetType().InvokeMember( "UserControl", BindingFlags.SetProperty,
      null, xl, Parameters );

Вам нужно дождаться выхода C# 4.0, чтобы получить позднюю привязку, которую вы ищете. Каждый раз, когда мне нужны возможности взаимодействия, я переключаюсь обратно в режим VB.Net, чтобы я мог воспользоваться возможностями COM, которых, как кажется, нет в C#.

Простой метод, который я использую, - это создание класса в VB.Net, который работает с IDispatch, а затем предоставление методов, которые я хочу использовать в качестве методов моей оболочки, и затем я могу вызывать их по своему желанию из своего кода C#. Не самое элегантное из решений, но оно вывело меня из-под варенья за последние несколько месяцев.

C# 4 dynamic Ключевое слово поддерживает IDispatch и позднюю привязку. Вы можете прочитать динамический ряд Сэма Нга для получения дополнительной информации

О, и C# 4 доступен только как ОСАГО сегодня. Вам придется либо подождать Visual Studio vNext, либо использовать бета-версию (которая работает на виртуальном ПК с Windows Server 2008), чтобы использовать это.

Как уже говорили другие - использование "динамического" ключевого слова в C#4. Вот простой пример - он гораздо более лаконичен, чем "InvokeMethod"

dynamic xl = Activator.CreateInstance(Type.GetTypeFromProgID("Excel.Application"));
dynamic books = xl.Workbooks;
dynamic book = books.Add();

Console.WriteLine(books.Count);     //Writes 1

foreach (dynamic b in books)        
{
Console.WriteLine(b.Name);      //Writes "Book1"
}

Возможно, вам удастся получить гораздо более приятный код в C# 2.0/3.0, если вы потратите время на написание интерфейса, содержащего нужные вам методы и свойства объекта, и добавите некоторые атрибуты (я пишу его из памяти, поэтому детали могут не правильно, но я клянусь, это сработало для меня...)

    using System.Runtime.Interopservices;

    [Guid("00024500-0000-0000-C000-000000000046")]
    [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    interface IExcel
    {
      // sample property
      string Name{get;}
      // more properties
    }

    // and somewhere else
    void main()
    {
      Object xl = Activator.CreateInstance(Type.GetTypeFromProgID("Excel.Application"));
      IExcel excel = (IExcel)xl;
      string name = xl.name
    }

Как уже упоминалось, код не будет работать из коробки, это скорее подсказка, что искать в msdn.

Эй, чувак, у меня есть 2 проекта codeplex в настоящее время, чтобы решить эту проблему.

Первый из них - LateBindingApi.Excel http://excel.codeplex.com/ отображающий поздний вызов invoke для хорошо известной объектной модели. это был тестовый проект для следующего проекта.

второй - CodeGenerator http://latebindingapi.codeplex.com/ инструмент создает проекты C# из библиотек COM-типов. Сгенерированные проекты включают объекты Mapper с поздней привязкой, обращающиеся к COM-серверу. основным моментом является то, что инструмент преобразует библиотеки типов COM в разных версиях в один проект (например, Excel 9,10,11) и помечает все объекты с помощью самоопределяемого атрибута SupportByLibrary. Я проанализировал все офисные приложения в версии 9,10,11,12,14 с помощью этого инструмента и сгенерировал решение aC#, которое доступно в виде протестированной бета-версии с примером кода на главной странице.

Другие вопросы по тегам