valid - param name c#
.NET2.0 C#Interop: ¿Cómo llamar código COM desde C#? (4)
Las respuestas en la publicación a la que se vincula son realmente incorrectas. En general, es muy fácil tratar con objetos basados en IDispatch en .Net. Básicamente, debes seguir 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 sin tipo de scripts (la interfaz IDispatch es en realidad una interfaz COM completa derivada de IDispatch o el objeto admite uno o más otras interfaces derivadas de IUnknown). En este caso, simplemente importaría la definición de interfaz COM apropiada y luego lanzaría el objeto a la interfaz adecuada. El elenco llama a QueryInterface bajo 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ó arriba. El objeto Document 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 transmitir a la interfaz adecuada 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 admite una interfaz alternativa. Entonces deberías usar VB.Net para envolver esa interfaz. Con Option Strict desactivado (solo para la clase contenedora), puede usar el soporte incorporado de VB para llamadas en espera tardías para simplemente llamar a los métodos IDispatch apropiados bajo las cubiertas. En casos raros con tipos de argumentos inusuales, es posible que tenga que juguetear un poco con la llamada, pero, en general, en VB ¡puede hacerlo! Incluso con las adiciones dinámicas a C # v4 VB probablemente todavía tenga un soporte significativamente mejor para llamadas COM de destino tardío.
Si por algún motivo no puede usar VB para envolver la interfaz de automatización, puede seguir realizando las llamadas necesarias desde C # utilizando la reflexión. No entraré en ningún detalle, ya que esta opción básicamente nunca debería usarse, pero aquí hay un pequeño ejemplo que involucra la automatización de Office .
En mi último entorno de desarrollo, pude interactuar fácilmente con COM, llamando 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 , cuya parte relativa 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; }
}
Creé la clase COM:
[ComImport]
[Guid("0002DF01-0000-0000-C000-000000000046")]
public class InternetExplorer
{
}
Así que ahora es el momento de mi transacción C # real:
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 límite de tiempo. Estamos usando 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 cierto nivel, solo admite IDispatch con límite de tiempo?
Una búsqueda rápida de Stack Overflow para usar IDispatch desde C # hace aparecer esta publicación diciendo que lo que quiero no es posible en .NET.
Entonces, ¿es posible usar COM desde C # .NET 2.0?
La cuestión es que hay un patrón de diseño aceptado que quiero usar en C # /. NET. Implica iniciar Internet Explorer fuera de proceso y proporcionarle contenido HTML, sin usar archivos temporales.
Una idea de diseño rechazada hospeda Internet Explorer en un WinForm.
Una alternativa aceptable es iniciar el navegador web registrado en el sistema, dándole HTML para mostrar, sin usar un archivo temporal.
El obstáculo continúa usando 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)
Actualización: en base a las actualizaciones de preguntas, eliminé 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 manera rápida y sucia de generar HTML en una aplicación de winforms y no requiera un IE en proceso, dejaré lo siguiente:
Posible escenario 1: el objetivo final es simplemente mostrar HTML a su usuario final y está usando formularios de Windows
System.Windows.Forms.WebBrowser
es el contenedor .NET cuidadosamente fácil para la interfaz que está tratando de implementar manualmente. Para obtenerlo, arrastre y suelte una instancia de ese objeto desde su barra de herramientas (en la lista como "Navegador web" en la sección "Todas las formas 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.
Vea este artículo: http://www.codeproject.com/KB/cs/IELateBindingAutowod.aspx
Automatización de enlaces finales de Internet Explorer por yincekara
Código de ejemplo de automatización de Internet Explorer utilizando 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);
El último enlace IDispatch llamado es relativly fácil en .NET, aunque pobre en orina:
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 que se hace referencia que originalmente decía "no es posible hasta C # 4.0" se modificó para mostrar cómo es posible en .NET 2.0.