.NET2.0 C# Взаимодействие: Как вызвать COM-код из C#?

В моей последней среде разработки я мог легко взаимодействовать с COM, вызывая методы для объектов COM. Вот оригинальный код, переведенный в код стиля C# (для маскировки исходного языка):

public static void SpawnIEWithSource(String szSourceHTML)
{
    OleVariant ie; //IWebBrowser2
    OleVariant ie = new InternetExplorer();
    ie.Navigate2("about:blank");

    OleVariant webDocument = ie.Document;
    webDocument.Write(szSourceHTML);
    webDocument.close;

    ie.Visible = True;
}

Теперь начинается утомительный, болезненный процесс попытки взаимодействия с COM из управляемого кода.

PInvoke.net уже содержит перевод IWebBrower2, соответствующий раздел которого:

[ComImport, 
   DefaultMember("Name"), 
   Guid("D30C1661-CDAF-11D0-8A3E-00C04FC9E26E"), 
   InterfaceType(ComInterfaceType.InterfaceIsIDispatch), 
   SuppressUnmanagedCodeSecurity]
public interface IWebBrowser2
{
    [DispId(500)]
    void Navigate2([In] ref object URL, [In] ref object Flags, [In] ref object TargetFrameName, [In] ref object PostData, [In] ref object Headers);

    object Document { [return: MarshalAs(UnmanagedType.IDispatch)] [DispId(0xcb)] get; }
}

Я создал класс COM:

[ComImport]
[Guid("0002DF01-0000-0000-C000-000000000046")]
public class InternetExplorer
{
}

Итак, теперь пришло время для моей фактической транзакции C#:

public static void SpawnIEWithSource(String szHtml)
{
    PInvoke.ShellDocView.IWebBrowser2 ie;
    ie = (PInvoke.ShellDocView.IWebBrowser2)new PInvoke.ShellDocView.InternetExplorer();

    //Navigate to about:blank to initialize the browser
    object o = System.Reflection.Missing.Value;
    String url = @"about:blank";
    ie.Navigate2(ref url, ref o, ref o, ref o, ref o);

    //stuff contents into the document
    object webDocument = ie.Document;
    //webDocument.Write(szHtml);
    //webDocument.Close();

    ie.Visible = true;
}

Внимательные читатели отмечают, что IWebBrowser2.Document является поздним IDispatch. Мы используем Visual Studio 2005 с.NET 2.0 на наших и наших клиентских компьютерах.

Так что же такое метод.NET 2.0 для вызова методов объекта, который на каком-то уровне поддерживает только IDispatch с поздней привязкой?

Быстрый поиск по переполнению стека для использования IDispatch из C# поднимает этот пост, говоря, что то, что я хочу, невозможно в.NET.

Так можно ли использовать COM из C# .NET 2.0?


Вопрос в том, что есть принятый шаблон проектирования, который я хочу использовать в C#/.NET. Он включает в себя запуск Internet Explorer вне процесса и предоставление ему содержимого HTML, при этом временные файлы не используются.

Отклоненная идея дизайна - хостинг Internet Explorer на WinForm.

Приемлемой альтернативой является запуск зарегистрированного в системе веб-браузера с отображением HTML-кода без использования временного файла.

Камнем преткновения продолжает использование COM-объектов в мире.NET. Конкретная проблема заключается в выполнении вызовов поздней привязки к IDispatch без использования C# 4.0. (т.е. при использовании.NET 2.0)

4 ответа

Решение

Вызывать IDispatch с поздним связыванием в.NET относительно легко, хотя и безнадежно:

public static void SpawnIEWithSource(String szHtml)
{
    // Get the class type and instantiate Internet Explorer.
    Type ieType = Type.GetTypeFromProgID("InternetExplorer.Application");
    object ie = Activator.CreateInstance(ieType);

    //Navigate to the blank page in order to make sure the Document exists
    //ie.Navigate2("about:blank");
    Object[] parameters = new Object[1];
    parameters[0] = @"about:blank";
    ie.GetType().InvokeMember("Navigate2", BindingFlags.InvokeMethod | BindingFlags.IgnoreCase, null, ie, parameters);

    //Get the Document object now that it exists
    //Object document = ie.Document;
    object document = ie.GetType().InvokeMember("Document", BindingFlags.GetProperty | BindingFlags.IgnoreCase, null, ie, null);

    //document.Write(szSourceHTML);
    parameters = new Object[1];
    parameters[0] = szHtml;
    document.GetType().InvokeMember("Write", BindingFlags.InvokeMethod | BindingFlags.IgnoreCase, null, document, parameters);

    //document.Close()
    document.GetType().InvokeMember("Close", BindingFlags.InvokeMethod | BindingFlags.IgnoreCase, null, document, null);

    //ie.Visible = true;
    parameters = new Object[1];
    parameters[0] = true;
    ie.GetType().InvokeMember("Visible", BindingFlags.SetProperty | BindingFlags.IgnoreCase, null, ie, parameters);
}

Ссылочный вопрос SO, который первоначально говорил "невозможно до C# 4.0", был изменен, чтобы показать, как это возможно в.NET 2.0.

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

Обновление: на основе обновлений вопросов я удалил части своего ответа, которые больше не относятся к вопросу. Однако, если другие читатели ищут быстрый и грязный способ создания HTML в приложении winforms и не требуют встроенного IE, я оставлю следующее:

Возможный сценарий 1: конечная цель - просто отобразить HTML для конечного пользователя и использовать Windows Forms

System.Windows.Forms.WebBrowser это кропотливо простая оболочка.NET для интерфейса, который вы пытаетесь реализовать вручную. Чтобы получить его, перетащите экземпляр этого объекта с панели инструментов (в списке "Веб-браузер" в разделе "Все формы Windows") на форму. Затем на некотором подходящем обработчике событий:

webBrowser1.Navigate("about:blank");
webBrowser1.Document.Write("<html><body>Hello World</body></html>");

В моем тестовом приложении это правильно отображало навязчивое сообщение, которое мы все научились бояться и ненавидеть.

Смотрите эту статью: http://www.codeproject.com/KB/cs/IELateBindingAutowod.aspx

Internet Explorer Поздняя привязка автоматизации Yincekara

Пример кода автоматизации Internet Explorer с использованием позднего связывания, без зависимости Microsoft.mshtml и shdocvw.

для htmlDoc.write(htmlString); модифицировать

   [Guid("332C4425-26CB-11D0-B483-00C04FD90119")]
    [ComImport]
    [TypeLibType((short)4160)]
    [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)]
    internal interface IHTMLDocument2
    {
        [DispId(1054)]
        void write([MarshalAs(UnmanagedType.BStr)] string psArray);
        //void write([MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_VARIANT)] object[] psarray);

Ответы в посте, на который вы ссылаетесь, на самом деле неверны. Как правило, очень легко иметь дело с объектами на основе IDispatch в.Net. В основном вы проходите три этапа:

Большинство объектов автоматизации (вероятно, более чем на 90%), которые представлены как интерфейсы IDispatch, имеют другие интерфейсы, которые могут использоваться COM-клиентами не-сценариев типа (либо интерфейс IDispatch на самом деле является полным COM-интерфейсом, производным от IDispatch, либо объект поддерживает один или больше других IUnknown производных интерфейсов). В этом случае вы просто импортируете соответствующее определение интерфейса COM и затем приведете объект к соответствующему интерфейсу. Приведение вызывает QueryInterface под обложками и возвращает упакованную ссылку на нужный интерфейс.

Это техника, которую вы бы использовали в сценарии, который вы представили выше. Объект Document, возвращаемый из объекта автоматизации IE, поддерживает интерфейсы IHTMLDocument, IHTMLDocument2, IHTMLDocument3, IHTMLDocument4 и IHTMLDocument5 (в зависимости от используемой версии IE). Вы должны привести к соответствующему интерфейсу и затем вызвать соответствующий метод. Например:

IHTMLDocument2 htmlDoc = (IHTMLDocument2)webDocument;
htmlDoc.Write(htmlString);
htmlDoc.Close();

В редком случае, когда объект автоматизации не поддерживает альтернативный интерфейс. Затем вы должны использовать VB.Net, чтобы обернуть этот интерфейс. Если для параметра Option Strict задано значение off (только для класса-оболочки), вы можете использовать встроенную поддержку VB для вызовов с поздним связыванием, чтобы просто вызывать соответствующие методы IDispatch под прикрытием. В редких случаях с необычными типами аргументов вам может понадобиться немного поиграть с вызовом, но, как правило, в VB вы можете просто сделать это! Даже с динамическими добавлениями в C# v4 VB, вероятно, все еще будет значительно лучше поддерживать COM-вызовы с поздней привязкой.

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

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