Calificación:
  • 0 voto(s) - 0 Media
  • 1
  • 2
  • 3
  • 4
  • 5
[Tutorial] C# Interoperabilidad COM, parte 1
#1


La interoperabilidad COM proporciona el acceso a componentes COM existentes sin necesidad de modificar el componente original. Cuando desee incorporar código COM a una aplicación administrada, importe los tipos COM correspondientes mediante una herramienta de interoperabilidad COM (TlbImp.exe) destinada a ese fin. Una vez importados, los tipos COM están listos para usar.

Asimismo, la interoperabilidad COM también permite a los programadores de COM obtener acceso a los objetos administrados igual de fácilmente que con otros objetos COM. La interoperabilidad COM proporciona una herramienta especializada (RegAsm.exe) que exporta los tipos administrados a una biblioteca de tipos y registra el componente administrado como un componente COM tradicional.

En tiempo de ejecución, Common Language Runtime realiza el cálculo de referencias entre objetos COM y los objetos administrados, según sea necesario.

Este tutorial muestra cómo utilizar C# para interactuar con objetos COM.

El Tutorial de "Interoperabilidad COM, parte2: servidor de C#" explica el uso de un servidor de C# con un cliente COM de C++. Para ver información general sobre ambos tutoriales, vea Tutoriales de interoperabilidad COM.

Archivos de ejemplo

Vea Interoperabilidad COM, parte 1: Ejemplo para descargar y generar los archivos de ejemplo que se utilizan en este tutorial.

Información adicional

Atributos de C#
17. Atributos
Interoperabilidad COM avanzada
System.Runtime.InteropServices (Espacio de nombres)
Resumen de la conversión de una biblioteca de tipos en ensamblado
Herramienta Type Library Importer (Tlbimp.exe)
Herramienta MSIL Disassembler (Ildasm.exe)
Herramienta Assembly Registration (Regasm.exe)
Cálculo de referencias para interoperabilidad
Tutorial de "Interoperabilidad COM, parte 2: servidor de C#"
Tutorial

C# utiliza las herramientas de .NET Framework para implementar la interoperabilidad COM. C# admite las siguientes operaciones:

Crear objetos COM.
Determinar si una interfaz COM es implementada por un objeto.
Llamar a métodos en interfaces COM.
Implementar objetos e interfaces que puedan ser llamados por objetos COM.
.NET Framework controla las cuestiones de recuento de referencias con la interoperabilidad COM, por lo que no hay necesidad de llamar a (o implementar) AddRef y Release.

Este tutorial trata los siguientes temas:

Crear un contenedor de clases COM
Declarar una coclase COM
Crear un objeto COM
Declarar una interfaz COM
Utilizar conversiones de tipo explícitas en lugar de QueryInterface
Resumen de conceptos
Crear un contenedor de clases COM

Para que se pueda hacer referencia en el código C# a objetos e interfaces COM, es necesario incluir una definición de .NET Framework para las interfaces COM a la hora de generar dicho código. La forma más sencilla de hacerlo es utilizar TlbImp.exe (Type Library Importer), una herramienta de la línea de comandos incluida en .NET Framework SDK. TlbImp convierte una biblioteca de tipos COM en metadatos de .NET Framework, creando así un contenedor administrado al que es posible llamar desde cualquier lenguaje administrado. Los metadatos de .NET Framework creados mediante TlbImp pueden incluirse en una generación de código C# con la opción del compilador /R. Si utiliza el entorno de desarrollo de Visual Studio, sólo es necesario agregar una referencia a la biblioteca de tipos COM y la conversión se hará automáticamente.

TlbImp realiza las siguientes conversiones:

Las coclases COM se convierten en clases de C# con un constructor sin parámetros.
Las estructuras COM se convierten en estructuras de C# con campos públicos.
Una forma excelente de comprobar los resultados de la conversión que realiza TlbImp es ejecutar la herramienta de línea de comandos Ildasm.exe (Microsoft Intermediate Language Disassembler), de .NET Framework SDK.

Si bien TlbImp representa el método preferente para convertir definiciones COM a C#, a veces no es posible usarla (por ejemplo, cuando no hay biblioteca de tipos para las definiciones COM, o si TlbImp no es capaz de controlar las definiciones de la biblioteca de tipos). En esos casos, la alternativa consiste en definir manualmente las definiciones COM en código fuente de C# utilizando atributos de C#. Una vez creada la asignación de código fuente C#, basta con compilar el código fuente de C# para producir el contenedor administrado.

Los principales atributos que es necesario comprender para realizar la asignación COM son:

ComImport – marca una clase como clase COM implementada externamente.
Guid – se utiliza para especificar un identificador único universal (UUID) para una clase o interfaz.
InterfaceType – especifica si una interfaz se deriva de IUnknown o IDispatch.
PreserveSig – especifica si el valor devuelto nativo debe convertirse de un identificador HRESULT a una excepción de.NET Framework.
Cada uno de estos atributos se muestra en el contexto de un ejemplo real en este tutorial.

Declarar una coclase COM

Las coclases COM se representan en C# como clases. Estas clases deben tener asociado el atributo ComImport. Las restricciones siguientes se aplican sólo a estas clases:

La clase no se debe derivar de ninguna otra clase.
La clase no debe implementar ninguna interfaz.
La clase también debe tener un atributo Guid que establezca su identificador único global (GUID).
El ejemplo siguiente declara una coclase en C#:

//
// declare FilgraphManager as a COM coclass
//
[ComImport, Guid("E436EBB3-524F-11CE-9F53-0020AF0BA770")]
class FilgraphManager
{
}
El compilador de C# agregará un constructor sin parámetros al que se puede llamar para crear una instancia de la coclase COM.

Crear un objeto COM

Las coclases COM se representan en C# como clases con un constructor sin parámetros. Crear una instancia de esta clase mediante el operador new es el equivalente en C# a llamar a CoCreateInstance. Sirviéndose de la clase definida anteriormente, es sencillo crear una instancia de ella:

class MainClass
{
public static void Main()
{
//
// Create an instance of a COM coclass - calls
//
// CoCreateInstance(E436EBB3-524F-11CE-9F53-0020AF0BA770,
// NULL, CLSCTX_ALL,
// IID_IUnknown, &f)
//
// returns null on failure.
//
FilgraphManager f = new FilgraphManager();
}
}
Declarar una interfaz COM

Las interfaces COM se representan en C# como interfaces con atributos ComImport y Guid. No puede incluir ninguna interfaz en su lista de interfaces base, y deben declarar las funciones miembro de interfaz en el orden en que aparecen los métodos en la interfaz COM.

Las interfaces COM declaradas en C# deben incluir declaraciones de todos los miembros de sus interfaces base, con excepción de los miembros de IUnknown o IDispatch, ya que .NET Framework los agrega automáticamente. Las interfaces COM que se derivan de IDispatch deben marcarse con el atributo InterfaceType.

Al llamar a un método de interfaz COM desde código C#, Common Language Runtime debe realizar el cálculo de referencias desde los parámetros y valores devueltos al objeto COM y viceversa. Por cada tipo de .NET Framework hay un tipo predeterminado que Common Language usará para calcular las referencias en una llamada COM. Por ejemplo, el cálculo de referencias predeterminado para los valores de cadena de C# es al tipo nativo LPTSTR (puntero a un búfer de caracteres TCHAR). Se puede reemplazar el cálculo de referencias predeterminado utilizando el atributo MarshalAs en la declaración de C# de la interfaz COM.

Una forma habitual en COM de devolver un resultado con éxito o error es devolver un valor HRESULT y marcar un parámetro de salida como "retval" en MIDL, para el valor devuelto real del método. En C# (y en .NET Framework), la manera estándar de indicar que se ha producido un error es iniciar una excepción.

De forma predeterminada, .NET Framework proporciona una asignación automática entre los dos estilos de control de excepciones para los métodos de interfaces COM invocados por .NET Framework.

El valor devuelto cambia a la firma del parámetro marcado como retval (void si el método no tiene ningún parámetro marcado como retval).
El parámetro marcado como retval aparece a la izquierda de la lista de argumentos del método.
Cualquier valor devuelto diferente del éxito en la operación hará que se inicie una excepción System.COMException.

Este ejemplo muestra una interfaz COM declarada en MIDL y la misma interfaz declarada en C# (observe que los métodos utilizan el modelo de control de excepciones COM).

Esta es la versión original MIDL de la interfaz:

[
odl,
uuid(56A868B1-0AD4-11CE-B03A-0020AF0BA770),
helpstring("IMediaControl interface"),
dual,
oleautomation
]
interface IMediaControl : IDispatch
{
[id(0x60020000)]
HRESULT Run();

[id(0x60020001)]
HRESULT Pause();

[id(0x60020002)]
HRESULT Stop();

[id(0x60020003)]
HRESULT GetState( [in] long msTimeout, [out] long* pfs);

[id(0x60020004)]
HRESULT RenderFile([in] BSTR strFilename);

[id(0x60020005)]
HRESULT AddSourceFilter( [in] BSTR strFilename, [out] IDispatch** ppUnk);

[id(0x60020006), propget]
HRESULT FilterCollection([out, retval] IDispatch** ppUnk);

[id(0x60020007), propget]
HRESULT RegFilterCollection([out, retval] IDispatch** ppUnk);

[id(0x60020008)]
HRESULT StopWhenReady();
};
Éste es el equivalente en C# de la interfaz:

using System.Runtime.InteropServices;

// Declare IMediaControl as a COM interface which
// derives from the IDispatch interface.
[Guid("56A868B1-0AD4-11CE-B03A-0020AF0BA770"),
InterfaceType(ComInterfaceType.InterfaceIsDual)]
interface IMediaControl // cannot list any base interfaces here
{
// Note that the members of IUnknown and Interface are NOT
// listed here
//
void Run();

void Pause();

void Stop();

void GetState( [In] int msTimeout, [Out] out int pfs);

void RenderFile(
[In, MarshalAs(UnmanagedType.BStr)] string strFilename);

void AddSourceFilter(
[In, MarshalAs(UnmanagedType.BStr)] string strFilename,
[Out, MarshalAs(UnmanagedType.Interface)] out object ppUnk);

[return : MarshalAs(UnmanagedType.Interface)]
object FilterCollection();

[return : MarshalAs(UnmanagedType.Interface)]
object RegFilterCollection();

void StopWhenReady();
}
Observe que la interfaz C# ha asignado los casos de control de error. Si el método COM devuelve un error, se iniciará una excepción en la parte de C#.

Para impedir la conversión de HRESULT a excepciones COMException, asocie el atributo PreserveSig(true) al método en la declaración de C#. Para obtener información detallada, vea PreserveSigAttribute (Clase).

Utilizar conversiones de tipo explícitas en lugar de QueryInterface

Una coclase de C# no resulta de gran utilidad a menos que se disponga de acceso a una interfaz que la implemente. En C++ lo habitual es recorrer las interfaces de un objeto utilizando el método QueryInterface en la interfaz IUnknown. En C# podemos hacer lo mismo realizando una conversión explícita del objeto COM a la interfaz deseada. Si la conversión explícita no funciona, se inicia una excepción InvalidCastException:

// Create an instance of a COM coclass:
FilgraphManager graphManager = new FilgraphManager();

// See if it supports the IMediaControl COM interface.
// Note that this will throw a System.InvalidCastException if
// the cast fails. This is equivalent to QueryInterface for
// COM objects:
IMediaControl mc = (IMediaControl) graphManager;

// Now you call a method on a COM interface:
mc.Run();
Resumen de conceptos

A continuación se muestra un ejemplo completo que crea un visor de archivos AVI utilizando C#. El programa crea una instancia de una coclase COM, la convierte explícitamente a una interfaz COM y después llama a los métodos de la interfaz COM.

Los ejemplos de esta sección representan dos modelos:

Ejemplo 1 Se utiliza TlbImp para crear la clase de .NET Framework.
Ejemplo 2 Se crea código de C# que realiza manualmente la asignación COM.
Ejemplo 1: utilizar TlbImp

Este ejemplo muestra cómo crear un visor AVI con TlbImp. El programa lee un nombre de archivo AVI en la línea de comandos, crea una instancia del objeto COM Quartz y, a continuación, usa los métodos RenderFile y Run para mostrar el archivo AVI.

Estos son los pasos para generar el programa:

Ejecute TlbImp sobre la biblioteca de tipos. El Reproductor multimedia utilizado en este ejemplo se encuentra en Quartz.DLL, que debe hallarse en el directorio del sistema de Windows. Utilice el siguiente comando para crear la DLL de .NET Framework:
tlbimp c:\winnt\system32\quartz.dll /out:QuartzTypeLib.dll
Observe que la DLL resultante debe tener el nombre QuartzTypeLib, de manera que .NET Framework pueda cargar los tipos contenedores correctamente al ejecutarse.

Se puede utilizar la herramienta Ildasm para examinar la DLL resultante. Por ejemplo, para mostrar el contenido del archivo QuartzTypeLib.dll, utilice el comando siguiente:
Ildasm QuartzTypeLib.dll
Genere el programa con la opción del compilador de C# /R para incluir el archivo QuartzTypeLib.dll.
A continuación puede usar el programa para mostrar un archivo de película (puede intentarlo con Clock.avi, incluido en el directorio Windows).

// interop1.cs
// compile with: /R:QuartzTypeLib.dll
using System;
class MainClass
{
/************************************************************
Abstract: This method collects the file name of an AVI to
show then creates an instance of the Quartz COM object.
To show the AVI, the program calls RenderFile and Run on
IMediaControl. Quartz uses its own thread and window to
display the AVI.The main thread blocks on a ReadLine until
the user presses ENTER.
Input Parameters: the location of the AVI file it is
going to display
Returns: void
**************************************************************/
public static void Main(string[] args)
{
// Check to see if the user passed in a filename
if (args.Length != 1)
{
DisplayUsage();
return;
}

if (args[0] == "/?")
{
DisplayUsage();
return;
}

string filename = args[0];

// Check to see if the file exists
if (!System.IO.File.Exists(filename))
{
Console.WriteLine("File " + filename + " not found.");
DisplayUsage();
return;
}

// Create instance of Quartz
// (Calls CoCreateInstance(E436EBB3-524F-11CE-9F53-0020AF0BA770,
// NULL, CLSCTX_ALL, IID_IUnknown, &graphManager).):

try
{
QuartzTypeLib.FilgraphManager graphManager =
new QuartzTypeLib.FilgraphManager();

// QueryInterface for the IMediaControl interface:
QuartzTypeLib.IMediaControl mc =
(QuartzTypeLib.IMediaControl)graphManager;

// Call some methods on a COM interface
// Pass in file to RenderFile method on COM object.
mc.RenderFile(filename);

// Show file.
mc.Run();
}
catch(Exception ex)
{
Console.WriteLine("Unexpected COM exception: " + ex.Message);
}

// Wait for completion.
Console.WriteLine("Press Enter to continue.");
Console.ReadLine();
}

private static void DisplayUsage()
{
// User did not provide enough parameters.
// Display usage:
Console.WriteLine("VideoPlayer: Plays AVI files.");
Console.WriteLine("Usage: VIDEOPLAYER.EXE filename");
Console.WriteLine("where filename is the full path and");
Console.WriteLine("file name of the AVI to display.");
}
}
Ejecución de ejemplo

Para mostrar la película de ejemplo, Clock.avi, utilice el siguiente comando:

interop1 %windir%\clock.avi
Con ello verá la película en su pantalla al presionar ENTRAR.

Ejemplo 2: el modelo de código de C#

Este ejemplo usa el mismo método Main que el Ejemplo 1, pero en lugar de ejecutar TlbImp, asigna el objeto COM Reproductor multimedia mediante C#:

// interop2.cs
using System;
using System.Runtime.InteropServices;

namespace QuartzTypeLib
{
// Declare IMediaControl as a COM interface which
// derives from IDispatch interface:
[Guid("56A868B1-0AD4-11CE-B03A-0020AF0BA770"),
InterfaceType(ComInterfaceType.InterfaceIsDual)]
interface IMediaControl // Cannot list any base interfaces here
{
// Note that IUnknown Interface members are NOT listed here:


void Run();
void Pause();

void Stop();

void GetState( [In] int msTimeout, [Out] out int pfs);

void RenderFile(
[In, MarshalAs(UnmanagedType.BStr)] string strFilename);

void AddSourceFilter(
[In, MarshalAs(UnmanagedType.BStr)] string strFilename,
[Out, MarshalAs(UnmanagedType.Interface)]
out object ppUnk);

[return: MarshalAs(UnmanagedType.Interface)]
object FilterCollection();

[return: MarshalAs(UnmanagedType.Interface)]
object RegFilterCollection();

void StopWhenReady();
}
// Declare FilgraphManager as a COM coclass:
[ComImport, Guid("E436EBB3-524F-11CE-9F53-0020AF0BA770")]
class FilgraphManager // Cannot have a base class or
// interface list here.
{
// Cannot have any members here
// NOTE that the C# compiler will add a default constructor
// for you (no parameters).
}
}

class MainClass
{
/**********************************************************
Abstract: This method collects the file name of an AVI to
show then creates an instance of the Quartz COM object.
To show the AVI, the program calls RenderFile and Run on
IMediaControl. Quartz uses its own thread and window to
display the AVI.The main thread blocks on a ReadLine until
the user presses ENTER.
Input Parameters: the location of the AVI file it is
going to display
Returns: void
*************************************************************/

public static void Main(string[] args)
{
// Check to see if the user passed in a filename:
if (args.Length != 1)
{
DisplayUsage();
return;
}

if (args[0] == "/?")
{
DisplayUsage();
return;
}

String filename = args[0];

// Check to see if the file exists
if (!System.IO.File.Exists(filename))
{
Console.WriteLine("File " + filename + " not found.");
DisplayUsage();
return;
}

// Create instance of Quartz
// (Calls CoCreateInstance(E436EBB3-524F-11CE-9F53-0020AF0BA770,
// NULL, CLSCTX_ALL, IID_IUnknown,
// &graphManager).):
try
{
QuartzTypeLib.FilgraphManager graphManager =
new QuartzTypeLib.FilgraphManager();

// QueryInterface for the IMediaControl interface:
QuartzTypeLib.IMediaControl mc =
(QuartzTypeLib.IMediaControl)graphManager;

// Call some methods on a COM interface.
// Pass in file to RenderFile method on COM object.
mc.RenderFile(filename);

// Show file.
mc.Run();
}
catch(Exception ex)
{
Console.WriteLine("Unexpected COM exception: " + ex.Message);
}
// Wait for completion.
Console.WriteLine("Press Enter to continue.");
Console.ReadLine();
}

private static void DisplayUsage()
{
// User did not provide enough parameters.
// Display usage.
Console.WriteLine("VideoPlayer: Plays AVI files.");
Console.WriteLine("Usage: VIDEOPLAYER.EXE filename");
Console.WriteLine("where filename is the full path and");
Console.WriteLine("file name of the AVI to display.");
}
}
Ejecución de ejemplo

Para mostrar la película de ejemplo, Clock.avi, utilice el siguiente comando:

interop2 %windir%\clock.avi
Con ello verá la película en su pantalla al presionar ENTRAR.


Posibles temas similares...
Tema Autor Respuestas Vistas Último mensaje
Información [Tutorial] Certificado SSL Gratis StartSSL 2016 Tutorial DarkMaster 26 5,105 28-04-2016, 03:39 AM
Último mensaje: Lob3zNo
  [Tutorial] Trucos con CSS parte I JancoBH 2 362 13-03-2016, 01:00 AM
Último mensaje: JancoBH
Bombilla [Tutorial] SSL Gratis, Conseguir e Instalar Certificado DarkMaster 16 871 06-01-2016, 01:32 AM
Último mensaje: DarkMaster
  [PUBLICO] Tutorial Botones con perspectiva 3D AngelKrak 0 233 17-01-2015, 05:44 AM
Último mensaje: AngelKrak
  como dominar CSS sin salir del escritorio parte 1 postnix 20 2,144 02-06-2014, 12:33 PM
Último mensaje: piinbook
  [Tutorial] Aumentar el Tamaño Máximo al Cargar Archivos con PHP y Apache papanoel 0 667 22-01-2014, 04:38 AM
Último mensaje: papanoel
  50 Herramientas de Diseño Web Gratis! [Parte 3] lukas 2 707 03-07-2013, 09:45 PM
Último mensaje: lukas
  50 Herramientas de Diseño Web Gratis! [Parte 1] lukas 2 454 18-06-2013, 10:59 PM
Último mensaje: fercol
  50 Herramientas de Diseño Web Gratis! [Parte 2] lukas 0 502 18-06-2013, 07:35 PM
Último mensaje: lukas
  Guía de Referencia Rápida de CSS 3 [Parte 1] Static-X 14 2,049 13-04-2013, 11:05 PM
Último mensaje: Infamer



Usuarios navegando en este tema: 1 invitado(s)