Vous êtes sur la page 1sur 38

Marco Besteiro y Miguel Rodrguez

Cdigo Inseguro

Cdigo inseguro (Unsafe Code) e interoperabilidad.


Cdigo inseguro
Referencias y punteros.
En C#, para acceder a memoria se utilizan referencias. Las referencias son similares a los punteros, con la salvedad de que no permiten manipular el valor de la direccin a la que apuntan (valor de la direccin, no contenido). Es decir, si una referencia apunta a una variable que est en la direccin 0x004FFFA8, a travs de la referencia es posible utilizar la variable pero no se puede obtener el valor 0x004FFFA8 (que es la direccin de la variable) y tampoco se le pueden sumar valores a la direccin para apuntar a otra direccin (por ejemplo: sumarle 4 a 0x004FFFA8 para acceder a la variable contenida a partir de la posicin 0x004FFFAB). La gran ventaja que ofrecen las referencias es que evitan muchos errores derivados del mal uso de los punteros, pero tambin tiene desventajas: Al no permitir acceder directamente a la memoria, limitan el rendimiento que podra dar a una aplicacin hacerlo. Ciertas aplicaciones como, por ejemplo, un depurador o debugger, necesitan manejar direcciones de memoria. Las dll antiguas (el API de Windows, por ejemplo) tienen funciones que utilizan punteros como parmetros, de modo que no podran ser invocadas si no se permite la utilizacin de punteros.

De lo comentado se deduce que, en ocasiones, puede ser interesante utilizar punteros. Un primer acercamiento a los punteros son los delegates, que pertenecen al llamado safe code o cdigo seguro pero nicamente se comportan como punteros a funciones. En C# se permite utilizar punteros y al cdigo en el que se utilizan punteros se le denomina cdigo inseguro o unsafe code. El cdigo inseguro debe ser marcado utilizando la palabra reservada unsafe. Es posible marcar como unsafe: Una clase Un miembro de una clase Un bloque de cdigo

En C# no se permite declarar punteros a tipos referencia. Tampoco es posible marcar como unsafe una variable local (ha de marcarse el mtodo o bloque de cdigo en el que est). Adems de lo comentado, al compilar cdigo inseguro es necesario indicrselo al compilador utilizando el flag unsafe (independientemente de si se compila desde el Visual Studio .NET o desde la lnea de comandos). 1/38

Marco Besteiro y Miguel Rodrguez

Cdigo Inseguro

Desde la lnea de comandos es tan sencillo como indicar /unsafe al compilar:

csc /unsafe ...

Desde Visual Studio .NET se ha de mostrar la ventana de propiedades:

Figura 13.1

2/38

Marco Besteiro y Miguel Rodrguez

Cdigo Inseguro

Figura 13.2 En caso de que no est seleccionado, se ha de seleccionar el proyecto en desarrollo desde la explorador de soluciones:

3/38

Marco Besteiro y Miguel Rodrguez

Cdigo Inseguro

Figura 13.3 Al hacerlo, en la ventana propiedades se mostrar la informacin sobre el proyecto y se activarn los botones e iconos del cuadro de la ventana. Pulse sobre el cuarto icono de la ventana de propiedades y aparecer el cuadro de dilogo con las pgina de propiedades del proyecto. En propiedades de configuracin se ha de elegir Generar y Permitir bloques de cdigo no seguros a True.

4/38

Marco Besteiro y Miguel Rodrguez

Cdigo Inseguro

Figura 13.4 De este modo ser posible compilar cdigo inseguro. En caso contrario el compilador no lo permitir (genera error en tiempo de compilacin). Por lo dems, el uso y declaracin de punteros en C# es igual que en C y C++, con una excepcin: en C y C++, cuando se declaran varias variables de tipo puntero en una misma lnea se separan por comas y van precedidas todas ellas por *. Por ejemplo:
int *p1, *p2;

En C# slo la primera variable de tipo puntero ha de ir precedida por *. Por ejemplo:


int *p1, p2;

Ejemplo:
int num = 5; //la variable num ocupa 4 bytes de memoria //(stack o pila), por ejemplo, a partir //de la posicin 0x004FFFA8 hasta la //0x004FFFAB. El contenido de esas posiciones //es el valor 5. //la variable num2 ocupa 4 bytes //de memoria (stack o pila), por //ejemplo, a partir de la posicin 0x004FFFA4 hasta //la 0x004FFFA7. El contenido de esas //posiciones es el valor 10. // pNum es una variable de tipo puntero a entero y //ocupa 4 bytes de memoria (stack o pila), por //ejemplo, a partir de la 0x004FFFA0 hasta la //0x004FFFA3. El contenido de esas posiciones no est //definido (basura).

int num2 = 10;

int *pNum;

5/38

Marco Besteiro y Miguel Rodrguez

Cdigo Inseguro

*pNum = &num2;

//al asignar la direccin de la variable num2 al //contenido de la variable pNum, las posiciones //correspondientes a la variable de tipo puntero pNum //(0x004FFFA0 a 0x004FFFA3) pasan a //contener la direccin de la variable num2, es //decir, 0x004FFFA4.

Aritmtica de punteros.
Es posible aadir o restar enteros a un puntero utilizando los operadores +, -, ++, --, += y -=. Por ejemplo, supngase que se aade la instruccin pNum++ al ejemplo anterior:
int num = 5; int num2 = 10; int *pNum; *pNum = &num2; pNum++;

Al incrementar en una unidad pNum lo que sucede es que su contenido pasa a ser el actual aumentado en cuatro unidades, ya que el tamao de una variable de tipo entero es 4 bytes y el compilador entiende que se quiere que pNum apunte a la siguiente variable de tipo entero y no al siguiente byte. De este modo, el contenido de pNum ser 0x004FFFA4 + 4, es decir 0x004FFFA8, que es la direccin de la variable num. Es importante notar que si el cdigo hubiese sido:
int num = 5; int num2 = 10; int *pNum; *pNum = # pNum++;

Al incrementar pNum pasara a valer 0x004FFFA8 + 4, que es 0x004FFFAC. Aqu se presenta el problema inherente al manejo directo de punteros. Qu hay en 0x004FFFAC? Lo ms posible es que no haya un entero sino algo desconocido, lo cual implicar un malfuncionamiento del programa cuyos efectos no son fcilmente predecibles. El siguiente ejemplo muestra cmo desarrollar cdigo inseguro y utilizar punteros y variables, indicando informacin sobre stos.
using System; class CodigoInseguroPunteros { static unsafe void Main (string[] args) { int int_num = 15; int *p_int_num = &int_num; Console.WriteLine("El valor de int_num es:" + *p_int_num); Console.WriteLine("La direccin de int_num es: " + (uint) p_int_num); Console.WriteLine("El tamao de int_num es:"+sizeof (int)); Console.WriteLine("El valor de p_int_num es: " +

6/38

Marco Besteiro y Miguel Rodrguez

Cdigo Inseguro

(uint) p_int_num); Console.WriteLine("La direccin de p_int_num es: " + (uint) &p_int_num); Console.WriteLine("El tamao de p_int_num es: " + sizeof (int*)); } }

El resultado es:

Figura 13.5 Tanto int_num como p_int_num son almacenados en la pila o stack ya que son datos de tipo primitivo (valor). Un int (entero) tiene un tamao de 4 bytes. Un puntero tiene un tamao de 4 bytes (independientemente del tipo de datos al que apunte). El contenido de p_int_num es la direccin de int_num. Se puede observar que la direccin de p_int_num es 1243464, es decir, anterior en 4 bytes (que es lo que ocupa el puntero) a int_num. Puesto que la pila crece hacia abajo y las variables se le aaden en el orden en que han sido declaradas, lo lgico es que p_int_num ocupe las posiciones ms bajas.

7/38

Marco Besteiro y Miguel Rodrguez

Cdigo Inseguro

Figura 13.6 El siguiente ejemplo es similar al anterior pero abarca ms tipos de datos de tipo valor (todos primitivos).
using System; class ManejoPunteros { static unsafe void Main (string[] args) { bool bool_val = true; char char_val = 'a'; sbyte sbyte_num = 5; short short_num = 10; int int_num = 15; long long_num = 20; double double_num = 25; float float_num = 30; bool *p_bool_val = &bool_val; char *p_char_val = &char_val; sbyte *p_sbyte_num = &sbyte_num; short *p_short_num = &short_num; int *p_int_num = &int_num; long *p_long_num = &long_num; double *p_double_num = &double_num; float *p_float_num = &float_num;

Console.WriteLine("El valor de bool_val es: " + *p_bool_val); //podra ponerse bool_val Console.WriteLine("La direccin de bool_val es: " + (uint) p_bool_val); //podra ponerse &bool_val Console.WriteLine("El tamao de bool_val es: " + sizeof (bool)); Console.WriteLine("El valor de char_val es: " + *p_char_val);

8/38

Marco Besteiro y Miguel Rodrguez

Cdigo Inseguro

Console.WriteLine("La direccin de char_val es: " + (uint) p_char_val); Console.WriteLine("El tamao de char_val es: " + sizeof (char)); Console.WriteLine("El valor de sbyte_num es: " + *p_sbyte_num); Console.WriteLine("La direccin de sbyte_num es: (uint) p_sbyte_num); Console.WriteLine("El tamao de sbyte_num es: " + sizeof (sbyte)); Console.WriteLine("El valor de short_num es: " + *p_short_num); Console.WriteLine("La direccin de short_num es: (uint) p_short_num); Console.WriteLine("El tamao de short_num es: " + sizeof (short)); Console.WriteLine("El valor de int_num es: *p_int_num); Console.WriteLine("La direccin de int_num es: " + (uint) p_int_num); Console.WriteLine("El tamao de int_num es: " + sizeof (int)); Console.WriteLine("El valor de long_num es: " + *p_long_num); Console.WriteLine("La direccin de long_num es: " + (uint) p_long_num); Console.WriteLine("El tamao de long_num es: " + sizeof (long)); Console.WriteLine("El valor de double_num es: " *p_double_num); Console.WriteLine("La direccin de double_num es: (uint) p_double_num); Console.WriteLine("El tamao de double_num es: " + sizeof (double)); Console.WriteLine("El valor de float_num es: " + *p_float_num); Console.WriteLine("La direccin de float_num es: (uint) p_float_num); Console.WriteLine("El tamao de float_num es: " + sizeof (float)); } }

" +

" +

" +

+ " +

" +

El resultado ser:

9/38

Marco Besteiro y Miguel Rodrguez

Cdigo Inseguro

Figura 13.7 Aqu puede observarse tambin la colocacin de las variables en la pila (en este caso no se muestra la informacin sobre los punteros).

Casting de punteros.
Es posible convertir punteros a un tipo de datos a punteros a otro tipo diferente de datos. Por ejemplo:
int num = 5; int *pNum; byte *pOcteto; pOcteto = # pNum = # System.Console.WriteLine(Contenido apuntado por pNum {0}, *pNum); System.Console.WriteLine(Contenido apuntado por pOcteto {0}, *pOcteto);

Tanto pOcteto como pNum apuntan a num, que es una variable que ocupa 4 bytes en memoria (stack), ya que es una variable de tipo int. Cuando se acceda a num mediante pNum, se permitir manejar los 4 bytes de num como lo que es (un entero), en cambio, cuando se acceda a num a travs de pOcteto, slo podr manipularse el primer byte u octeto de los 4 que ocupa num, porque a travs de un puntero a byte slo puede manejarse un byte, independientemente del tipo de datos real al que se refiera la direccin contenida en el puntero a byte. Es importante deducir de esto que dependiendo del casting que se haga es posible que no tenga utilidad alguna. Un casting lgico puede ser convertir cualquier tipo de puntero en un puntero a sbyte, para poder recorrer byte a byte el contenido de una variable. Tambin es posible convertir un puntero a otro tipo de datos no puntero, como puede ser int, uint, long... De todos estos casting los ms lgicos son a int o uint, ya que un dato de tipo int o uint ocupa 4 bytes y uno de tipo long 8, etc...

10/38

Marco Besteiro y Miguel Rodrguez

Cdigo Inseguro

Adems, si se desea mostrar por consola el valor de un puntero, por ejemplo pNum, no puede hacerse:
//error en compilacin System.Console.WriteLine(Contenido de pNum {0}, pNum);

Ha de hacerse lo siguiente:
System.Console.WriteLine(Contenido de pNum {0}, (uint) pNum);

Nota: en C#, al igual que en C y C++ puede utilizarse el operador sizeof para obtener el tamao de un tipo de datos.

Punteros void.
No es una buena idea utilizar punteros a void en C# pero en ocasiones puede ser necesario. El caso ms comn es cuando se desea invocar una funcin del API que necesita recibir parmetros de tipo void*. Se puede declarar punteros a void y hacer casting entre punteros de otros tipos y void:
void * pVoid; int * pInt; int num = 5; *pInt = &num pVoid = (void*) pInt;

pero no es posible desreferenciar punteros a void utilizando el operador *, es decir *pVoid causar un error de compilacin en cualquier expresin en que aparezca (en las declaraciones no, por supuesto).

Punteros a estructuras y a miembros de estructuras.


C# permite declarar punteros a estructuras con la condicin de que la estructura para la que se declare el puntero no posea miembros de tipo referencia. Si se salta esta norma, el compilador da un error. Salvada esta restriccin, los punteros a estructuras funcionan de modo similar a los punteros a los tipos valor predefinidos (byte, int, uint, long). Por ejemplo:
struct Punto { public int X; public int Y; } Punto * pPunto; Punto p1 = new Punto(); pPunto = &p1; (*pPunto).X = 5; //tambin es vlido pPunto->X = 5; (*pPunto).Y = 10; //tambin es vlido pPunto->Y = 10; System.Console.WriteLine (X: + (*pPunto).X );

11/38

Marco Besteiro y Miguel Rodrguez

Cdigo Inseguro

System.Console.WriteLine (Y: + (*pPunto).Y );

Por supuesto, tambin se pueden crear punteros a los miembros de una estructura. Por ejemplo:
Punto * pPunto; int * pX, pY; Punto p1 = new Punto(); pX = &(p1.X); //tambin pY = &(p1.Y); //tambin *pX = 5; *pY = 10; System.Console.WriteLine System.Console.WriteLine

es vlido pX = &(p1->X); es vlido pY = &(p1->Y);

(X: + pX ); (Y: + pY );

Punteros a clases y a miembros de clases.


No es posible crear punteros a clases (una clase es un tipo referencia) ya que se almacenan, como toda variable de tipo referencia, en el heap y el garbage collector elimina las variables del heap cuando dejan de estar referenciadas por referencias, aunque queden punteros apuntndolas. En cambio, es posible que un puntero apunte a los miembros de tipo valor de una clase (slo a estos). No obstante, se plantea un problema: los miembros de una variable de una clase sern eliminados cuando el garbage collector elimine la variable del heap (ya que el garbage collector no tiene en cuenta los punteros). La solucin es utilizar la palabra reservada fixed cuando se declara un puntero a un miembro de una clase. fixed le indica al compilador que el cdigo que genere ha de asegurar que el garbage collector no eliminar el objeto al que pertenece el miembro fijado mientras se est utilizando. Por ejemplo:
class { Punto public int X; public int Y; }

int * pX, pY; Punto p1 = new Punto(); fixed (pX = &(p1.X)) { //manejar pX *pX = 5; System.Console.WriteLine (X: + pX ); } ... fixed (pY = &(p1.Y)) { //manejar pY *pY = 10; System.Console.WriteLine (Y: + pY ); }

12/38

Marco Besteiro y Miguel Rodrguez

Cdigo Inseguro

Lo anterior podra haberse programado de modos alternativos. Modo 1 (en grupo):

... ... int * Punto fixed fixed {

pX, pY; p1 = new Punto(); (pX = &(p1.X)) (pY = &(p1.Y)) //manejar pX *pX = 5; System.Console.WriteLine (X: + pX ); //manejar pY *pY = 10; System.Console.WriteLine (Y: + pY );

... ... Modo 2 (anidado):

... ... int * pX, pY; Punto p1 = new Punto(); fixed (pX = &(p1.X)) { //manejar pX *pX = 5; System.Console.WriteLine (X: + pX ); fixed (pY = &(p1.Y)) { //manejar pY *pY = 10; System.Console.WriteLine (Y: + pY ); } } ... ...

13/38

Marco Besteiro y Miguel Rodrguez

Cdigo Inseguro

Interoperabilidad.
Se denomina managed code al cdigo que se puede ejecutar bajo el control del CLR y unmanaged code a aqul cuya ejecucin no puede ser controlada por el CLR y por tanto no pueden aplicrsele las reglas de control de seguridad del CLR ni la recoleccin de basura (garbage collector). El managed code necesita en muchas ocasiones operar con el unmanaged code (cdigo nativo) existente. Incluso habr casos en los que sea necesario desarrollar una parte de un proyecto en cdigo nativo e invocar tal cdigo nativo desde el cdigo IL que es ejecutado, de modo controlado (managed code), por el CLR. Para permitir esto, el CLR soporta dos formas de operacin con el cdigo nativo: Platform Invocation Services (P/Invoke). COM Interoperability.

Es importante no confundir unmanaged code con unsafe code. Este ltimo es managed code precedido por la palabra clave unsafe al que se le permite utilizar caractersticas no seguras (unsafe) del lenguaje C++, como son los punteros.

Platform Invocation Services.


Invoke o Platform Invocation Services es el nombre que se da a la tecnologa que permite invocar unmanaged code desde el cdigo .NET o managed code. Utilizacin de dll nativas desde .NET. Para poder invocar una funcin de una dll desde cdigo .NET basta con utilizar el atributo DllImport o SysImport. El atributo DllImport se coment en el tema referente a los atributos y el atributo SysImport es similar, slo que se utiliza para invocar funciones de las dll del sistema (user32.dll, gdi32.dll y kernel32.dll). Al explicar el atributo DllImport se utiliz un ejemplo en el que se invocaba a la funcin MessageBox de la librera user32.dll:
using System; using System.Reflection; using Clases_NameSpace; //Para utilizar DllImport es necesario el namespace //System.Runtime.InteropServices using System.Runtime.InteropServices; class PruebaAtributosReservados { [DllImport("User32.dll")] public static extern int MessageBox(int hParent, string Message, string Caption, int Type); public static void Main() {

14/38

Marco Besteiro y Miguel Rodrguez

Cdigo Inseguro

Clase2.FunClase2 (); Clase3.FunClase3 (); System.Console.WriteLine(" "); System.Console.WriteLine("Para llamar a MessageBox pulse Enter: "); System.Console.ReadLine(); MessageBox(0, "Hola desde el API de Windows", "Mensaje invocado desde .NET", 0); } }

El mismo ejemplo, utilizando SysImport es:


using System; using System.Reflection; using Clases_NameSpace; //Para utilizar DllImport es necesario el namespace //System.Runtime.InteropServices using System.Runtime.InteropServices; class PruebaAtributosReservados { [SysImport(dll="User32.dll")] public static extern int MessageBox(int hParent, string Message, string Caption, int Type); public static void Main() {

... ... El resultado de ejecutar este ejemplo es:

Figura 13.8 Tras pulsar Enter se mostrar el cuadro de mensaje:

15/38

Marco Besteiro y Miguel Rodrguez

Cdigo Inseguro

Figura 13.9

COM interoperability.
El CLR ofrece soporte tanto para utilizar componentes COM desde C# como para utilizar objetos C# desde componentes COM y desde cdigo nativo en general como si los objetos C# fuesen componentes COM. La interoperabilidad entre componentes COM y C# puede realizarse mediante enlace temprano (en compilacin) o tardo (en ejecucin). Utilizacin de componentes COM desde .NET. El uso de componentes COM desde C# es diferente dependiendo de si se utiliza enlace temprano o tardo. Utilizacin de componentes COM desde .NET mediante enlace temprano. Para poder utilizar un componente COM desde .NET de modo que queden todas las referencias resueltas en tiempo de compilacin (enlace temprano o early binding) es necesario que exista un objeto que siga la tecnologa .NET (para poder incluirlo en el cdigo .NET) y que exponga las interfaces del componente COM. A este objeto se le llama Runtime Callable Wrapper (RCW) y acta como proxy que utiliza .NET para acceder al componente COM. El RCW se encarga de traducir las llamadas que desde .NET se hagan a sus mtodos en las llamadas correctas al componente COM. Tambin se encarga de traducir la respuesta del componente COM de modo que pueda ser utilizado desde .NET y de controlar el ciclo de vida del componente COM (Addref, Release...). Para crear el RCW correspondiente a un componente COM se ha de utilizar TlbImp.exe. Por ejemplo: supnga que se dispone de un componente COM contenido en la dll quartz.dll (C:\Winnt\Sustem32\quartz.dll) y que se desea utilizar desde .NET, en concreto desde una aplicacin C#. Los pasos a seguir son: 1) Crear el RCW correspondiente al componente contenido en quartz.dll y copiarlo en el directorio de la aplicacin .NET que lo va a utilizar (no tiene porque ser as):
tlbimp c:\winnt\system32\quartz.dll /out:QuartzTypeLib.dll

16/38

Marco Besteiro y Miguel Rodrguez

Cdigo Inseguro

QuartzTypeLib.dll es el RCW, que realmente es una clase .NET, la cual podr

utilizarse en la aplicacin C# que llamar al componente contenido en


quartz.dll.

2) Crear la aplicacin que utiliza el componente contenido en quartz.dll


//Fichero InteropCOM_Enlace_Temprano_Consola.cs using System; using QuartzTypeLib; namespace COMInterop_Enlace_Temprano_Consola { /// <summary> /// Summary description for Aplicacion_COMInterop_Consola. /// </summary> public class Aplicacion_COMInterop_Consola { public Aplicacion_COMInterop_Consola() { // // TODO: Add constructor logic here // } public static void Main() { QuartzTypeLib.FilgraphManager fgm = new FilgraphManager(); // La siguiente instruccin equivale a una llamada // a QueryInterface pidiendo el interface IMediaControl QuartzTypeLib.IMediaControl mc = (QuartzTypeLib.IMediaControl)fgm; // El mtodo RenderFile recibe como parmetro el fichero a //reproducir mc.RenderFile(@"C:\Archivos de programa\Microsoft.NET\FrameworkSDK\Samples\ technologies\remoting\advanced\ remotingcom\mediaplayer\client\clock.avi"); //Run reproduce el fichero. mc.Run(); Console.WriteLine("Pulse Enter para continuar."); Console.ReadLine(); } } }

3) Compilar la aplicacin incluyendo la dll del RCW como referencia:


csc /r:QuartzTypeLib.dll InteropCOM_Enlace_Temprano_Consola.cs

4) Invocar al ejecutable de la aplicacin:


InteropCOM_Enlace_Temprano_Consola

17/38

Marco Besteiro y Miguel Rodrguez

Cdigo Inseguro

El resultado es:

Figura 13.10 Este mismo ejemplo, hecho desde el Visual Studio .NET es ms sencillo gracias al cuadro de dilogo del men principal Proyecto/Agregar Referencia:

18/38

Marco Besteiro y Miguel Rodrguez

Cdigo Inseguro

Figura 13.11 Tras aceptar la librera seleccionada quartz.dll Visual Studio muestra un mensaje indicando que no existe el RCW para el componente contenido en quartz.dll y preguntando si se desea que lo cree.

Figura 13.12 Suponiendo que se acepta, Visual Studio .NET crea una dll llamada

QuartzTypeLib.dll (RCW) y la almacena en el directorio de la aplicacin. A partir de

este momento, la aplicacin puede utilizar la clase o, mejor dicho, las clases del paquete
QuartzTypeLib (puede haber ms de una clase o componente en una dll). En el explorador de soluciones se ve que aparece una referencia a QuartzTypeLib.

19/38

Marco Besteiro y Miguel Rodrguez

Cdigo Inseguro

Figura 13.13 Una de las ventajas que da Visual Studio .NET es que muestra ayuda contextual al escribir el cdigo que utiliza la referencia aadida (en este caso QuartzTypeLib).

Figura 13.14 Nota: En caso de que el componente est en una dll no incluida por defecto en la pestaa COM del cuadro de dilogo Agregar referencia puede utilizarse el botn Examinar para buscar e incluir la dll que se desee. 20/38

Marco Besteiro y Miguel Rodrguez

Cdigo Inseguro

Utilizacin de componentes COM desde .NET mediante enlace tardo. Cuando se utiliza enlace tardo para invocar los mtodos de un componente COM desde una aplicacin, sta ha de obtener la direccin de tales mtodos durante la ejecucin. Es decir, al compilar la aplicacin nada se sabe sobre la direccin de los componentes que va a utilizar la aplicacin. En enlace tardo no es necesario crear el RCW para el componente COM. Se ha de utilizar el mtodo GetTypefromProgID de la clase Type. Los pasos a seguir para utilizar un componente COM mediante enlace tardo son: 1) Incluir el namespace System.Interop.InteropSevices. 2) Crear un objeto Type para el componente COM utilizando Type.GetTypeFromProgID() o Type.GetTypeFromCLSID(). 3) Crear un componente COM utilizando Activator.CreateInstance(). 4) Llamar a los mtodos del componente COM utilizando el mtodo InvokeMember del objeto Type. El enlace tardo ya es conocido de un tema anterior y se puede observar que se sigue una idea similar al llamar a componentes COM mediante enlace tardo. Es importante saber que cuando se invoca a los mtodos de un componente COM mediante enlace tardo, la invocacin se hace utilizando la interface IDispatch. En el siguiente ejemplo va a intentarse invocar utilizando enlace tardo a los mtodos RenderFile y Run del componente FilgraphManager, contenido en Quartz.dll. Por supuesto, no hay que aadir una referencia a Quartz.dll en el proyecto.
using System; using System.Reflection; using System.Runtime.InteropServices; namespace COMInterop_Enlace_Tardio_Consola { /// <summary> /// Summary description for Aplicacion_COMInterop_Consola. /// </summary> public class Aplicacion_COMInterop_Consola_Enlace_Tardio { public Aplicacion_COMInterop_Consola_Enlace_Tardio() { // // TODO: Add constructor logic here // } public static void Main() { Type objTypeQuartz; object objQuartz; object[] arObjs = {@"C:\Archivos de programa\ Microsoft.NET\FrameworkSDK\Samples\ technologies\remoting\advanced\ remotingcom\mediaplayer\client\clock.avi"};

21/38

Marco Besteiro y Miguel Rodrguez

Cdigo Inseguro

objTypeQuartz = Type.GetTypeFromProgID("Quartz.FilgraphManager"); objQuartz = Activator.CreateInstance(objTypeQuartz); objTypeQuartz.InvokeMember("RenderFile", BindingFlags.InvokeMethod, null, objQuartz, arObjs); objTypeQuartz.InvokeMember("Run", BindingFlags.InvokeMethod, null, objQuartz, null); Console.WriteLine("Pulse Enter para continuar."); Console.ReadLine(); } } }

Si se compila esta aplicacin, al ejecutarse lanza una excepcin.

Figura 13.15 Si se pulsa No:

22/38

Marco Besteiro y Miguel Rodrguez

Cdigo Inseguro

Figura 13.16 La excepcin se lanza al intentar crear un objeto del tipo FilgraphManager, es decir, al ejecutar la lnea:
objQuartz = Activator.CreateInstance(objTypeQuartz);

ya que objTypeQuartz es null y no una referencia a un objeto. El problema real esta en la lnea anterior:
objTypeQuartz= Type.GetTypeFromProgID("Quartz.FilgraphManager");

El problema es que no est registrado el ProgID de FilgraphManager. La verdad es que no est registrado ni siquiera FilgraphManager. Para comprobarlo no hay ms que mostrar el registro del sistema y buscarlo.

Figura 13.17

23/38

Marco Besteiro y Miguel Rodrguez

Cdigo Inseguro

Figura 13.18 El resultado ser que no se encuentra:

24/38

Marco Besteiro y Miguel Rodrguez

Cdigo Inseguro

Figura 13.19 Si se busca FilgraphManager, tampoco se encontrar. Esto puede hacer sospechar que no est registrado, pero la verdad es que s lo est (puede ejecutarse regsvr32 C:\Winnt\System32\Quartz.dll para asegurarse). Si se busca Quartz:

Figura 13.20 Se encontraran varias coincidencias. La que interesa es:

25/38

Marco Besteiro y Miguel Rodrguez

Cdigo Inseguro

Figura 13.21 Es decir, Quartz.dll s est registrada y se conoce su CLSID. Podra pensarse que tal vez valga este CLSID, aunque el que se quiere es el de FilgraphManager, que no se encuentra y por tanto cambiar el cdigo de modo que se utilice el CLSID, en lugar del ProgID:
using System; using System.Reflection; using System.Runtime.InteropServices; namespace COMInterop_Enlace_Tardio_Consola { /// <summary> /// Summary description for Aplicacion_COMInterop_Consola. /// </summary> public class Aplicacion_COMInterop_Consola_Enlace_Tardio { public Aplicacion_COMInterop_Consola_Enlace_Tardio() { // // TODO: Add constructor logic here // } public static void Main() { Type objTypeQuartz; object objQuartz;

26/38

Marco Besteiro y Miguel Rodrguez

Cdigo Inseguro

object[] arObjs = {@"C:\Archivos de programa\Microsoft.NET\FrameworkSDK\Samples\technolog ies\remoting\advanced\remotingcom\mediaplayer\client\ clock.avi"}; System.Guid clasid = new System.Guid("70E102B0-555611CE-97C0-00AA0055595A"); objTypeQuartz = Type.GetTypeFromCLSID(clasid); objQuartz = Activator.CreateInstance(objTypeQuartz); objTypeQuartz.InvokeMember("RenderFile", BindingFlags.InvokeMethod, null, objQuartz, arObjs); objTypeQuartz.InvokeMember("Run", BindingFlags.InvokeMethod, null, objQuartz, null);

Pero en tiempo de ejecucin sigue lanzando una excepcin (en modo no debug):

Figura 13.22 Podra utilizarse el CLSID pero: Tendra que ser el CLSID del componente deseado. El componente deseado tendra que soportar la interface IDispatch.

Llegar aqu y no realizar un ejemplo que funcione deja la duda en el aire, de modo que habr que realizar un ejemplo vlido.Hay que buscar un componente registrado que tenga un progID (o un versionIndependentProgID) o un CLSID y que soporte la interface COM IDispatch. Powerpoint.Application puede ser el componente que cumpla tal ejemplo. 27/38

Marco Besteiro y Miguel Rodrguez

Cdigo Inseguro

Figura 13.23 El cdigo de este ejemplo ser:


using System; using System.Reflection; using System.Runtime.InteropServices; namespace COMInterop_Enlace_Tardio_Consola_Funciona { /// <summary> /// Summary description for Aplicacion_COMInterop_Consola. /// </summary> public class Aplicacion_COMInterop_Consola_Enlace_Tardio_Funciona { public Aplicacion_COMInterop_Consola_Enlace_Tardio_Funciona() { // // TODO: Add constructor logic here // } public static void Main() { Type objTypeApplicationPowerPoint; object objApplicationPowerPoint; Console.WriteLine("Pulse PowerPoint."); Console.ReadLine(); Enter para lanzar

28/38

Marco Besteiro y Miguel Rodrguez

Cdigo Inseguro

objTypeApplicationPowerPoint = Type.GetTypeFromProgID("PowerPoint.Application"); objApplicationPowerPoint = Activator.CreateInstance(objTypeApplicationPowerPoint ); objTypeApplicationPowerPoint.InvokeMember("Activate", BindingFlags.InvokeMethod, null, objApplicationPowerPoint, null); Console.WriteLine("Pulse Enter para continuar."); Console.ReadLine(); } } }

Y el resultado de ejecutarla:

Figura 13.24 Al pulsar Enter aparecer Powerpoint.

29/38

Marco Besteiro y Miguel Rodrguez

Cdigo Inseguro

Figura 13.25 Utilizacin de componentes .NET desde componentes COM y dll nativas. Lo ms comn es que al desarrollar cdigo .NET se necesite, en ocasiones, utilizar componentes COM existentes o nuevos. No obstante, tambin puede darse el caso contrario, es decir, la necesidad de invocar componentes .NET desde cdigo nativo Windows (componentes COM o funciones de dll). Para poder utilizar componentes .NET como si fuesen componentes COM es necesario que los componentes .NET estn registrados (en el registro del sistema o registry) y exista un proxy al que se invoque a travs de los datos registrados, siendo el proxy el que realmente invocar los mtodos del objeto o componente .NET. La aplicacin RegAsm (Register Assembly) registra un assembly y todos sus clases (componentes), creando adems el proxy. Se puede decir que es la inversa de TlbImp. Una vez registrado un assembly hay que realizar varios pasos para que sea realmente accesible como un componente COM: 1. Crear un nombre fuerte (strong name) para asignarlo al assembly. El nombre fuerte identificar al assembly (consta de un nombre de tipo texto, un nmero de versin, informacin sobre la cultura si se ha indicado, una clave pblica y una firma digital).
sn k assemblyx.snk

30/38

Marco Besteiro y Miguel Rodrguez

Cdigo Inseguro

2. Crear un fichero AssemblyInfo.cs que contenga una referencia al fichero con el nombre fuerte:
using System.Reflection; [assembly: AssemblyKeyFile(assemblyx.snk)]

donde assemblyx.snk es el nombre fuerte del assembly. 3. Compilarlo, generando un mdulo:


csc /t:module /out:AssemblyInfo.dll AssemblyInfo.cs

4. Compilar assemblyx.cs utilizando el mdulo assemblyinfo.dll generando una librera:


csc /t:library /addmodule:assemblyinfo.dll assemblyx.cs

5. Copiar la librera assemblyx.dll a la cach global (si no se hace, no ser accesible desde el cdigo nativo (COM y funciones de dll no COM):
gacutil /i assemblyx.dll

En el siguiente ejemplo se crea una clase muy sencilla llamada Doble (en el fichero N_Doble.cs), que contiene un mtodo Doblar que multiplica por dos el valor que recibe como parmetro.
namespace N_Doble { using system; public class Doble { public int Doblar(int num) { return (num * 2); } } }

Lo primero que se ha de hacer es compilar N_Doble.cs y generar N_Doble.dll.

31/38

Marco Besteiro y Miguel Rodrguez

Cdigo Inseguro

Figura 13.26 Una vez generada la librera, se utiliza regasm para registrarla en el registro del sistema.

Figura 13.27 En el registro del sistema ha de encontrarse una tentrada para el componente Doble.

32/38

Marco Besteiro y Miguel Rodrguez

Cdigo Inseguro

Figura 13.28 Ahora es necesario copiar N_Doble.dll a la cach global para que pueda ser accedida desde el cdigo Windows nativo. El primer paso es generar un strong name o nombre fuerte (es una buena idea generarlo y depositarlo en el archivo N_Doble.snk).

Figura 13.29

33/38

Marco Besteiro y Miguel Rodrguez

Cdigo Inseguro

Una vez se tiene el fichero con el nombre fuerte, ha de ser referenciado desde un fichero que luego se aadir al assembly N_Doble.dll. En este caso se llama AssemblyInfo.cs y contiene:
using System.Reflection; [assembly: AssemblyKeyFile("N_Doble.snk")]

Antes de aadir esta informacin a N_Doble.dll habr que generar un mdulo a partir de AssemblyInfo.cs.

Figura 13.30 Para aadir la informacin de tal mdulo a N_Doble.dll lo que realmente se har es regenerar N_Doble.dll a partir de N_Doble.cs indicando que se aada el mdulo AssemblyInfo.dll.

Figura 13.31

34/38

Marco Besteiro y Miguel Rodrguez

Cdigo Inseguro

Una vez se ha obtenido N_Doble.dll con la informacin de AssemblyInfo.dll slo resta copiarlo a la cach global.

Figura 13.32 A partir de este momento es posible utilizar desde cdigo Windows nativo el componente Doble. Un posible ejemplo puede ser un script (VBScript) contenido, por ejemplo, en una pgina Web:
//Fichero PrueballamarNETdesdeCOM.html <html> <head> <title> Prueba de llamada a .NET desde Windows nativo </title> </head> <body> <script language=VBScript> Option Explicit Dim objeto_N_Doble Dim Numero Dim Resultado Set objeto_N_Doble = CreateObject("N_Doble.Doble") Numero = InputBox("Nmero a doblar") Resultado = objeto_N_Doble.Doblar(CLng(Numero)) Call MsgBox(Resultado) </script> </body> </html>

Al cargar la pgina:

35/38

Marco Besteiro y Miguel Rodrguez

Cdigo Inseguro

Figura 13.33

36/38

Marco Besteiro y Miguel Rodrguez

Cdigo Inseguro

Figura

13.34

37/38

Marco Besteiro y Miguel Rodrguez

Cdigo Inseguro

38/38

Vous aimerez peut-être aussi