.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)
Решение 2
Вызов с поздним связыванием 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-вопрос, в котором изначально было указано "! было изменено, чтобы показать, как это возможно в .NET 2.0.
Другие советы
Обновление . На основе обновлений вопросов я удалил части своего ответа, которые больше не относятся к вопросу. Однако, если другие читатели ищут быстрый и грязный способ создания HTML в приложении winforms и не требуют встроенного IE, я оставлю следующее:
Возможный сценарий 1. Конечная цель - просто показать HTML конечному пользователю и использовать Windows Forms
System.Windows.Forms.WebBrowser
- кропотливо простая оболочка .NET для интерфейса, который вы пытаетесь реализовать вручную. Чтобы получить его, перетащите экземпляр этого объекта с панели инструментов (указан как & Quot; веб-браузер & Quot; в разделе & Quot; все Windows Forms & Quot; раздел) на вашу форму , Затем на некотором подходящем обработчике событий:
webBrowser1.Navigate("about:blank");
webBrowser1.Document.Write("<html><body>Hello World</body></html>");
В моем тестовом приложении это правильно отображало навязчивое сообщение, которое мы все научились бояться и ненавидеть.
Ответы в сообщении, на которое вы ссылаетесь, на самом деле неверны. Как правило, очень легко иметь дело с объектами на основе 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 автоматизация .
Смотрите эту статью: http://www.codeproject.com/KB/cs/IELateBindingAutowod.aspx р>
Internet Explorer Поздняя привязка автоматизации По Инцекара
Пример кода автоматизации 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);