Pregunta

En mi último entorno de desarrollo, pude interactuar fácilmente con COM, invocando métodos en objetos COM. Aquí está el código original, traducido al código de estilo C # (para enmascarar el idioma original):

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;
}

Ahora comienza el tedioso y doloroso proceso de intentar interoperar con COM desde el código administrado.

PInvoke.net ya contiene la traducción IWebBrower2 , la porción correspondiente de que es:

[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; }
}

He creado la clase COM:

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

Así que ahora es el momento de mi transacción real de 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;
}

Los lectores cuidadosos notan que IWebBrowser2.Document es un IDispatch con retraso. Estamos utilizando Visual Studio 2005, con .NET 2.0 en nuestras máquinas y las de nuestros clientes.

Entonces, ¿cuál es el método .NET 2.0 para invocar métodos en un objeto que, en algún nivel, solo admite IDispatch vinculado de forma tardía?

Aparece una búsqueda rápida de Stack Overflow para usar IDispatch desde C # esta publicación decir que lo que quiero no es posible en .NET.

Entonces, ¿es posible usar COM desde C # .NET 2.0?


La pregunta es que hay un patrón de diseño aceptado que quiero usar en C # /. NET. Implica iniciar Internet Explorer fuera de proceso y darle contenido HTML, todo el tiempo sin usar archivos temporales.

Una idea de diseño rechazada es alojar Internet Explorer en un WinForm.

Una alternativa aceptable es iniciar el navegador web registrado del sistema, dándole HTML para mostrar, sin usar un archivo temporal.

El escollo sigue utilizando objetos COM en el mundo .NET. El problema específico implica realizar llamadas de enlace tardío a IDispatch sin necesidad de C # 4.0. (es decir, mientras usa .NET 2.0)

¿Fue útil?

Solución 2

El IDispatch enlazado tarde llamado es relativamente fácil en .NET, aunque pobre: ??

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);
}

La pregunta SO referenciada que originalmente decía "no es posible hasta C # 4.0" se modificó para mostrar cómo es posible en .NET 2.0.

¿C # .NET admite el enlace tardío IDispatch?

Otros consejos

  

Actualización: según las actualizaciones de las preguntas, he eliminado las partes de mi respuesta que ya no son relevantes para la pregunta. Sin embargo, en caso de que otros lectores estén buscando una forma rápida y sucia de generar HTML en una aplicación winforms y no requieran un IE en proceso, dejaré lo siguiente:

Escenario posible 1: el objetivo final es simplemente mostrar HTML a su usuario final y utilizar Windows Forms

System.Windows.Forms.WebBrowser es el envoltorio .NET minuciosamente fácil para la interfaz que está intentando implementar manualmente. Para obtenerlo, arrastre y suelte una instancia de ese objeto desde su barra de herramientas (listada como " Navegador web " bajo la sección " Todos los formularios de Windows ") en su formulario. Luego, en algún controlador de eventos adecuado:

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

En mi aplicación de prueba, esto muestra correctamente el mensaje inquietante que todos hemos aprendido a temer y detestar.

Las respuestas en la publicación a las que enlaza son realmente incorrectas. En general, es muy fácil tratar con objetos basados ??en IDispatch en .Net. Básicamente, sigue tres pasos:

La mayoría de los objetos de automatización (probablemente más del 90%) que están expuestos como interfaces IDispatch tienen otras interfaces que pueden ser utilizadas por clientes COM de tipo sin scripting (la interfaz IDispatch es en realidad una interfaz COM completa derivada de IDispatch o el objeto admite una o más interfaces derivadas de IUnknown). En este caso, simplemente importaría la definición de interfaz COM adecuada y luego convertiría el objeto en la interfaz adecuada. El elenco llama a QueryInterface debajo de las cubiertas y devuelve una referencia envuelta a la interfaz que desea.

Esta es la técnica que usaría en el escenario que presentó anteriormente. El objeto Documento que se devuelve desde el objeto de automatización de IE admite las interfaces IHTMLDocument, IHTMLDocument2, IHTMLDocument3, IHTMLDocument4 e IHTMLDocument5 (dependiendo de la versión de IE que esté utilizando). Debe enviar a la interfaz apropiada y luego llamar al método apropiado. Por ejemplo:

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

En el raro caso en que el objeto de automatización no sea compatible con una interfaz alternativa. Entonces deberías usar VB.Net para ajustar esa interfaz. Con la opción Strict configurada en desactivada (solo para la clase wrapper), puede usar el soporte integrado de VB para llamadas con retraso para simplemente llamar a los métodos IDispatch apropiados debajo de las cubiertas. En casos raros con tipos de argumentos inusuales, es posible que deba jugar un poco con la llamada, pero, en general, ¡en VB puede hacerlo! Incluso con las adiciones dinámicas a C # v4, VB probablemente tendrá una compatibilidad significativamente mejor para las llamadas COM vinculadas tarde.

Si por alguna razón no puede usar VB para envolver la interfaz de automatización, entonces aún puede hacer las llamadas necesarias desde C # usando la reflexión. No entraré en detalles ya que esta opción básicamente nunca debería usarse, pero aquí hay un pequeño ejemplo que involucra a Office automatización .

Ver este artículo: http://www.codeproject.com/KB/cs/IELateBindingAutowod.aspx

Automatización de enlace tardío de Internet Explorer Por yincekara

Código de muestra de automatización de Internet Explorer con enlace tardío, sin dependencia de Microsoft.mshtml y shdocvw.

para htmlDoc.write (htmlString); modificar

   [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);
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top