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