Поддерживает ли C # .NET позднюю привязку IDispatch? - PullRequest
26 голосов
/ 31 декабря 2008

Вопрос

Мой вопрос: Поддерживает ли 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 ]

25 голосов
/ 28 января 2009

Вы можете использовать относительную привязку 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 );
9 голосов
/ 31 декабря 2008

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

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

5 голосов
/ 31 декабря 2008
Ключевое слово

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

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

2 голосов
/ 09 ноября 2011

Как уже говорили другие - использование "динамического" ключевого слова в 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"
}
2 голосов
/ 31 октября 2009

, вероятно, вы можете получить гораздо более приятный код в 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.

1 голос
/ 28 апреля 2011

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

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

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

...