sábado, 31 de marzo de 2012

Entendiendo la clase Process de .NET

La definición clásica de proceso [1] nos dice que un proceso "es un programa en ejecución" también nos señala que un proceso es más que un programa, ya que por programa se refiere únicamente al conjunto de instrucciones que se van ejecutando dentro del proceso, por lo que subraya que un proceso es una entidad activa que tiene un contador de programa (program counter), un conjunto de recursos asociados y ese conjunto de instrucciones que están representadas por el programa.
Un proceso tiene los siguientes estados durante su ejecución:

  1. Nuevo (New): El proceso se ha creado
  2. En ejecución (Running): Las instrucciones están siendo ejecutadas.
  3. En espera (Waiting): El proceso esta en espera de algún evento ocurra.
  4. Listo (Ready): El proceso esta en espera de una asignación del procesador.
  5. Terminado (Terminated): El proceso ha terminado su ejecución.

.NET proporciona la clase Process que representa un proceso del sistema operativo con todas sus propiedades y operaciones, esta clase se encuentra dentro del ensamblado System.Diagnostics.
A continuación mostramos el listado correspondiente a un proyecto GTK# de MonoDevelop, este programa sencillo muestra el uso de esta clase.


Primeramente obtenemos el nombre del host del cuál obtendremos sus procesos en ejecucción, este nombre lo asignamos a una variable de tipo string para después mostrarlo en una etiqueta de la interfaz gráfica


lbMachineName.Text = machineName = System.Environment.MachineName;

Teniendo el nombre del host, entonces con el metódo estático GetProcesses de la clase Process obtenemos un arreglo de procesos representados mediante la clase Process, este arreglo sera nuestro model (Model) para mostrar en el control GTK# TreeView de la interfaz gráfica, a continuación el código con el cuál realizamos esta función:

var resp = (from p in Process.GetProcesses(machineName)
where p.Id > 100
orderby p.ProcessName ascending select p);
Process[] arrProcesses = resp.ToArray();

Por útimo obtemos la información detallada del proceso, con el metódo estático GetProcessById un metódo que recibe como argumento un entero representando el pid (process identifier), esto se realiza con el código siguiente:


TreePath[] selected_paths = trProcessList.Selection.GetSelectedRows ();
TreeIter iter;
int pid;
foreach (TreePath p in selected_paths)
{
if (store.GetIter (out iter, p)) {
pid = Convert.ToInt32(store.GetValue(iter,1));
Process process = Process.GetProcessById(pid);
lbVirtualMemory.Text = Convert.ToString(process.WorkingSet64 / 1024) + " Kb";
lbPriority.Text = process.PriorityClass.ToString();
lbStartProcessorTime.Text = process.StartTime.ToString("HH:mm:ss.ffff");
}
}
Las siguientes imagenes muestran el programa en ejecucción



[1]Basado en el libro “Operating System Concepts”, Silberschatz Abraham, Baer Galvin Peter, Greg Gagne; John Wiley & Sons inc, 7 edición, 2005, Chapter 3

lunes, 19 de marzo de 2012

Flujos de entrada y salida en .NET

Todos los programas de computadora hacen uso de dispositivos de entrada y salida, los más clásicos para estos fines son el teclado (entrada estándar) y la consola salida (salida estándar).
.NET hace una abstracción de cada uno de estos dispositivos con el modelo de flujos (bajo el (concepto) de flujo), haciendo la analogía como si se tratase de una corriente de agua, solo que para el caso de las aplicaciones .NET se trata de corrientes de bytes. En resumen la comunicación entre .NET y el hardware de la computadora se realiza mediante el concepto de flujos.
La clase base para el tratamiento de estos flujos es la clase Stream, de la cuál derivan los flujos necesarios para la comunicación de los programas hacia el respaldo (persistencia) o en términos de .NET el Backing Store. .NET a diferencia de Java utiliza la misma clase para los flujos de entrada como para los de salida. A continuación la lista de los flujos básicos de entrada/salida:



  • FileStream: hacia archivos de disco

  • MemoryStream: hacia estructuras de memoria

  • NetworkStream: hacia conexiones de red


A continuación, mostramos como ejemplo un proyecto GTK# realizado en MonoDevelop cuyo diseño de pantalla se muestra en la siguiente imagen:


Ahora se muestra el listado que utiliza algunos de estos conceptos para leer un archivo binario .mp3 y obtener la información correspondiente al los datos del ID3 tag en estos archivos.


Primeramente en el código utilizamos las clases FileStream, BinaryReader, FileInfo del ensamblado System.IO, el cuál utilizamos en el encabezado:


using System.IO;

Con las siguientes lineas utilizamos la clase FileInfo para obtener algunas propiedades acerca del archivo, propiedades que mostramos en las etiquetas de la interfaz.

file = new FileInfo(fc.Filename);
int size = Convert.ToInt32(file.Length);
lbFileName.Text = file.Name;
lbWriteTime.Text = file.LastWriteTime.ToString();
lbFileSize.Text = size.ToString();

Ahora con el siguiente código implementamos toda la funcionalidad para la lectura de un archivo binario, utilizando un FileStream hacia un archivo creando un flujo de bytes como entrada que dirigimos hacia un BinaryReader (Lector binario) con el cuál utilizando el metódo ReadBytes para leer un arreglo de 128 bytes que son los bytes que contienen los datos del ID3 tag.

FileStream fis = new FileStream(file.FullName,FileMode.Open,
FileAccess.Read,FileShare.Read);
using(BinaryReader reader = new BinaryReader(fis))
{
int offset = size - 128;
reader.BaseStream.Position = offset;
b = reader.ReadBytes(128);
}

Una vez obtenido el arreglo de bytes,lo convertimos a caracteres, para que utilicemos la longitud y la posición correcta de cada dato según el estándar ID3, esto se logra con el siguiente código:

char[] c = new char[128];
for(int i = 0;i < b.Length;i++)
c[i] = (char)b[i];
string strTag = new string(c,0,3);
if(strTag.Equals("TAG")){
PutMsg("File loaded");
txtTitle.Text = new string(c,3,30);
txtArtist.Text = new string(c,33,30);
txtAlbum.Text = new string(c,63,30);
txtYear.Text = new string(c,93,4);
txtComments.Buffer.Text = new string(c,97,30);
}

Una vez compilada la aplicación, al ejecutarse utilizaremos el botón "open" para seleccionar un archivo mp3 del sistema de archivos.


Una vez que el archivo ha sido cargado se mostrará la información correspondiente:



martes, 28 de febrero de 2012

Sobrecarga de operadores en C# utilizando vectores

Además de los operadores para los tipos primitivos, C# tiene una característica conocida como sobrecarga de operadores la cuál permite que los operadores para tipos primitivos puedan utilizarse con objetos, permitiéndonos definir el tipo de operación, como se va a efectuar, los tipos involucrados y valor que devuelve, un ejemplo típico de esta funcionalidad lo tenemos en la concatenación de objetos String en donde se utiliza el símbolo "+" para la concatenación de cadenas que igualmente es utilizado para la adicción de enteros, como se muestra en los siguientes ejemplos:


Adicción de enteros
--------------------
int a = 18;
int b = 66;
Console.Write(a + b); //imprime 84

Concatenación de cadenas
-------------------------

String s1 = "Once upon ";
String s2 = "a hero ";
Console.Write(s1 + s2); //imprime Once upon a hero

Para ejemplificar como funciona la sobrecarga de operadores en objetos utilizaremos las operaciones con vectores que se estudian en el álgebra lineal, por lo que antes de codificar daremos algunas definiciones.

Un vector es un objeto perteneciente a un espacio vectorial, que para los casos particulares de espacios R2 y R3 podemos representarlos gráficamente como segmentos de línea dirigidos con un punto inicial y un punto final describiendo la asociación de una magnitud y una dirección.

Un espacio vectorial consiste de:

  1. Un campo F de escalares (en general los número reales).
  2. Un conjunto V de elementos llamados vectores.
  3. Un conjunto de reglas (u operaciones) llamadas suma y multiplicación que según los textos especialistas en la materia se dividen en dos categorías, una para la adicción y otra para la multiplicación.

Los axiomas para la adición espacios vectoriales son:
  • A1. Si u y v están en V, entonces u + v está dentro de V
  • A2. u + v = v + u para todos u y v que estan en V
  • A3. u + (v + w) = (u + v) + w para todos los u,v y w en V
  • A4. Un elemento 0 en V existe tal que v + 0 = v para cada v en V.
  • A5. Para cada v en V, existe un elemento -v en V tal que -v + v = 0 y v + (-v) = 0


Los axiomas para la multiplicación son:
  • S1. Si v esta en V entonces av esta en V para cada a en R.
  • S2. a(v + w) = av + aw para cada v y w en V y para cada a en R.
  • S3. (a+b)v = av + bv para cada v en V y para cada a y b en R.
  • S4. a(bv) = (ab)v para cada v en V y para todos cada a y b en R.
  • S5. 1v = v para cada v en V.

Ahora con estos conceptos pasemos al código, primeramente crearemos nuestra clase Vector en donde utilizando la palabra reservada operator definiremos las operaciones para demostrar algunos de los axiomas expuestos. A continuación el listado de dicha clase.


Como vemos en este código utilizamos la sobrecarga de operadores utilizando la palabra clave operator en los siguientes métodos:


public static Vector operator +(Vector u,Vector v)
public static Vector operator *(Vector u, Vector v)
public static Vector operator -(Vector u)
public static Vector operator *(double d,Vector v)
public static Vector operator *(Vector v,double d)

Aquí definimos la operación, el número de parámetros con los que se llevará a cabo y por supuesto su implementación.
Ahora con el siguiente listado mostraremos la utilización de clase Vector y el uso de la sobrecarga de operadores para vectores de números reales:

Si agregamos estas clases a un proyecto de consola en MonoDevelop podemos tener una solución lista para corregir y compilar.

Al ejecutar la solución, veremos el resultado como en la siguiente imagen:

martes, 21 de febrero de 2012

El Plan de pruebas

De las diferentes etapas del ciclo de desarrollo de sistemas, la etapa de pruebas es una de las que más recursos técnicos, administrativos y humanos puede necesitar, incluso hay ocasiones en donde dicha etapa supera o iguala el tiempo requerido para el resto de las etapas previas, por poner como ejemplo utilizando el tiempo como variable, supongamos que para el desarrollo de un sistema necesitamos x meses para las etapas de análisis, diseño y construcción entonces dependiendo del negocio y la criticidad de dicho sistema podemos requerir 2x de tiempo para las pruebas, lo cuál agrega costos adicionales al proyecto que afectan el presupuesto, sobretodo si no se esta seguro de que el sistema funcionará y se tiene una fecha compromiso.


Uno de los documentos iniciales de la fase de pruebas es el documento denominado Plan de pruebas el cuál y de forma general nos indica la estrategia de pruebas, los tipos, los roles y los criterios tanto de entrada como de salida de dichas pruebas.


A continuación como ejemplo, describo lo que dicho documento debe contener (pongo entre corchetes los párrafos que deben cambiar o complementarse ya que únicamente estan a manera de guía) lo siguiente:


Plan de pruebas



1-. Introducción

[Este plan de Pruebas o Programa de Pruebas describe la forma estándar de realizar las actividades de pruebas necesarias para cumplir con los controles de calidad y la verificación del funcionamiento del sistema XXX en cada uno de sus módulos y del sistema de manera global.]

1.1 Propósito

[Describe la estrategia de pruebas utilizada para el sistema XXX para establecer como se diseñan, ejecutan y se administran las pruebas, así también los tipos de pruebas y los recursos necesarios para su ejecución. Esto se realiza con el objetivo de identificar defectos y fallas, evaluar la calidad y determinar el cumplimiento de los requerimientos.]

1.2 Alcance

[Para esta versión del Sistema xxx se aplicarán los siguientes tipos de pruebas, las cuales se desarrollan conforme a los requisitos funcionales y a los controles de calidad.]

1.2.1 Pruebas funcionales

[Las pruebas funcionales deben focalizarse en cualquier requerimiento para probar que puedan ser rastreados directamente a los casos de uso, o reglas de negocio. Su objetivo es verificar la correcta aceptación, procesamiento y recuperación de los datos; así como la implementación apropiada de las reglas de negocio.]

1.2.2 Pruebas de Interfaz de usuario

[Estas pruebas verifican la interacción del usuario con el sistema. Su objetivo es asegurar que la interfaz gráfica de usuario (GUI) proporcione al usuario un acceso adecuado y una navegación a través de las pantallas del sistema.]

1.2.3 Pruebas de control de acceso

[Estas pruebas garantizan que, basado en la seguridad deseada, los actores estén restringidos a funciones/ casos de uso específicos o estén limitados a los datos que estén disponibles para ellos de acuerdo a su rol.]

2.1 Ambientes de prueba

[A continuación se listan los requerimientos mínimos de Hardware y Software.]

2.1.1 Hardware

[Listar todo el hardware necesario para el ambiente de pruebas, por ejemplo:
a) Estaciones de trabajo con y de RAM
b) Impresoras
c) Terminales con x,y ó z características.]

2.1.2 Software

[Listar todo el software necesario para el ambiente de pruebas, por ejemplo:
a) Sistemas Operativos x,y ó z
b) Navegadores x,y ó z a partir de la versión x
c) Suite de oficina versión x]

2.2.1 Roles y responsabilidades

[Aquí describir la participación de cada uno de los roles que participarán en la actividad de pruebas]

3 Actividades de prueba

3.1 Estrategia de pruebas

[La estrategia de pruebas se concentra en probar extensivamente el sistema para encontrar el mayor número de hallazgos inconsistencias, errores o excepciones en el sistema como parte de la ejecución de los casos de prueba.]

3.1.1 Procesos y actividades

3.1.1.1 Ciclo 1 Pruebas Unitarias

[Describir paso a paso cada una de las actividades de las pruebas unitarias]

3.1.1.2 Ciclo 2 Pruebas Funcionales

[Describir paso a paso cada una de las actividades de las pruebas funcionales]

3.1.1.3 Ciclo 3 Pruebas integrales

[Describir paso a paso cada una de las actividades de las pruebas integrales]

3.1.2.1 Pruebas Generales

[Documentar el conjunto de condiciones o variables bajo las cuáles el analista de pruebas determinará si el sistema xxx cumple con los requisitos solicitados.
Los requisitos generales que el sistema debe cumplir para su aprobación corresponden con: (aquí se sigue con la lista del tipos de pruebas que se efectuarán) ]

3.1.2.1.1 Interfaz de usuario

[a) Verificar la correspondencia entre el documento de diseño de interfaz aprobado y la implementación ejecutable del sistema.
b) Alineación de los elementos (márgenes).
c) Homogenización de estilos.
d) Corrección de ortografía.
e) Consistencia con diferentes cambios de resolución.
f) Correspondencia pantalla-fase en el Mapa de navegación.
g) Proporcionar mensajes apropiados para el manejo de errores.
h) Proporcionar (si aplica) mensajes con una retroalimentación y guías efectivos.
i) Consistencia en los elementos gráficos.
j) Organización y distribución apropiada de los elementos en la pantalla.
k) Verificar que los iconos sean significativos.]

3.1.2.1.2 Validación de las entradas

[a) Permitir solamente enteros o decimales en las entradas numéricas.
b) No permitir la entrada de caracteres especiales en las entradas para cadenas.
c) Validar los campos obligatorios.
d) Validar los límites mínimos o máximos de los campos.
e) Permitir únicamente fechas en las entradas de fechas.]

3.1.2.1.3 Funcionalidad

[a) Verificar que cada botón en la pantalla realice la funcionalidad para la que fue diseñada.
b) Que corresponda el componente con todos los requisitos solicitados por el usuario.
c) Que el sistema corresponda con los escenarios de funcionalidad esperados por el usuario.
d) Verificar el cumplimiento de la navegación entre componentes (pantallas, subprocesos, reportes).]

3.1.2.2 Documentación base para la ejecución de las pruebas

[Aquí va toda la documentación necesaria para esta etapa que se genero en las etapas anteriores.]

3.1.3 Criterios.

3.1.3.1 Criterios de Entrada

[Para comenzar la fase de pruebas del sistema, se deben de cumplir los siguientes criterios:
a) Construcción finalizada de cada componente
b) Ambiente de pruebas
c) Generación de los datos de prueba.]

3.1.3.2 Criterios de terminación

[a) Cuando no se encuentren hallazgos que impidan la operación del sistema. (ciclo 1)
b) Para los ciclos 2 y 3 se deberá contar con la aceptación del usuario.
c) El usuario deberá ser capaz de borrar los registros que haya creado.
d) Una vez que el usuario haya firmado de conformidad la funcionalidad del sistema. El área de desarrollo de sistemas no hará cambio alguno a la aplicación sin la previa solicitud correspondiente.]

4 Resultados

[Aquí se colocan todos los artefactos (documentos o reportes conteniendo el resultado de cada uno de los casos de prueba.]



El Plan de pruebas puede variar dependiendo de la metodología que se esté utilizando, pero a groso modo este es un ejemplo de como está estructurado este documento inicial e importante para la etapa de pruebas.

lunes, 30 de enero de 2012

Subnetting en redes IPV4 parte II, IPv4 de los números de hosts

Para sacar los números de host en una red con subneteo utilizamos los bits del último octeto, aquellos que no hayan sido utilizados para la subnet, por lo tanto estos bits serán utilizados para el número total de hosts.


Por ejemplo de la subred 130.3.12.128 necesitamos encontrar la IP número 20 o bien la vigésima IP de esa subred, bien la configuración de los octetos para esta subred se encuentran de la siguiente forma:



Bien en el último octeto entonces buscaremos el número 20 de acuerdo con los valores en decimal de los bits. Por lo que la configuración en el último octeto quedaría con los siguientes bits encendidos de acuerdo a su representación numérica.


Se realiza la suma decimal de los tres bits encendidos en el último octeto, dos de ellos representando el número de IP más el bit encendido del campo de subred.

128 + 16 + 4 = 148

Por lo que la vigésima IP de la subred 130.3.12.128 sería entonces la

130.3.12.148

Veamos ahora un segundo ejemplo, buscamos la última IP de host en la subred 400 de la red 130.3.0.0/25, por lo que la configuración de los octetos de esta subred queda de la siguiente forma:


Sumando la representación decimal de los bits encendidos del tercer octeto tenemos entonces:

128 + 64 + 8 = 200

Por lo que la IP para subred 400 es: 130.3.200.0.

Ahora vamos a utilizar el último octeto para obtener la última IP de esta subred, sabemos bien que la configuración para todos los hosts de esta subred es poniendo todos los bits encendidos excepto el del último octeto el cual representaría la IP del Gateway.
Por lo que la configuración del último host de esta subred en el último octeto queda de la siguiente forma:


Sumando su representación decimal

64 + 32 + 16 + 8 + 4 + 2 = 126

Por lo que la IP para el último host de la subred 400 es: 130.3.200.126


sábado, 31 de diciembre de 2011

Subnetting en redes IP v4 Parte I

Una dirección IPv4 (IPv4 address) tiene una longitud de 32 bits que se considera única para cada host, esta dirección se escribe en formato binario de izquierda a derecha donde se considera que el bit más significativo es el bit más hacia la izquierda, una IPv4 tiene dos partes, la parte de red (netid) y la parte de host (hostid), originalmente el espacio de direcciones IP fue dividido en tres clases diferentes clase A, clase B y clase C, cada clase se distingue por los primeros bits iniciales o bits de prefijo, como en la siguiente tabla:



La clase A tiene bit de prefijo en 0 y es seguido por 7 bits para la parte de red y 24 bits restantes para la parte de host, la clase B tiene dos bits de prefijo el primero en 1 y el segundo en 0 seguido de 14 bits para la parte de red y de 16 bits para la parte de host, la clase C tiene tres bits de prefijo el primero y el segundo en 1 y el tercero en 0 por lo que tiene 21 bits para la parte de red y 8 bits para la parte de host, existen también las clases D y E las cuales se usa para multicasting y para experimentación respectivamente.

En un principio este esquema pareció ser suficiente para el tamaño de las redes, pero conforme la Internet fue creciendo en número de redes y hosts, este esquema presento deficiencias, una de ellas fue el desperdicio y el agotamiento de direcciones, por lo que una de las soluciones a este problema fue el concepto de subneteo (subnetting) el cual nos permite tomar una dirección de red y crear subconjuntos de esa red o bien subredes esto se logra mediante los siguientes pasos:


Por ejemplo si se nos pide subneteo en una red clase B y se nos da un mascara de 27 bits, la clase B tiene 16 bits para el campo de red y 16 bits para el campo de host.
Se resta el número de mascara menos el número de bits del campo de red y el resultado es el número de bits que debemos de tomar de los octetos del campo de host.
En este caso:



Se utilizan los 11 bits para el subneteo y los 5 bits restantes se utilizan para el campo de host. Por lo que quedaría de la siguiente forma:


Bien ahora que ya tenemos identificados los bits de subred, vamos a poner en práctica como lo obtenemos las subredes en forma decimal.

Supongamos que tenemos una red 130.3.0.0 con una subnet mask de 25 o 130.3.0.0/25


Si necesitamos encontrar los números de subredes: 25 y 400 respectivamente por lo que en principio calculamos el número de bits que necesitaremos para la subred por lo que hacemos la resta:

25-16 = 9 bits para subneteo

Esquematizamos los octetos de la red 130.3.0.0/25, para una mejor comprensión:


En la parte inferior entre el tercero y el cuarto octeto vemos en color verde el número de subredes correspondientes a cada bit de la parte de subred, para la representación del valor en decimal utilizamos los valores de la parte superior, por lo que la suma de cada bit encedido en la parte inferior nos proporcione como resultado el número de subred y la suma de sus valores en la parte superior nos proporcione la representación decimal , así por ejemplo la subred número 25 será:


Hay que distinguir que para el número de subred sumamos los valores que nos den como resultado el número de subred que buscamos, sin importar que se encuentren en diferentes octetos y que para la parte decimal su representación es únicamente la suma de los valores de los bits que componen ese octeto.

Encendemos los valores de subred del tercer octeto en 16 y 8 y el valor de 1 del cuarto octeto para que la suma de sus valores de subred tengamos el número de 25.

16 + 8 + 1 = 25
Ahora entonces sumamos la parte superior del tercer octeto para obtener su representación decimal en este caso:

8 + 4 = 12
Y para el último octeto la representación decimal del bit encendido nos da 128, por lo que juntando todos los octetos obtenemos que la subred 25 se represente por la dirección Ipv4:

130.3.12.128 / 25
Ahora buscamos la representación decimal de la subred 400 utilizando el mismo método



En este caso como es un número par de subred, inferimos que el último octeto no tendrá valor por lo que nos concentramos en los bits del tercer octeto, prendemos el bit de subred 256, el bit 128 y por último el bit 16 de la suma obtenemos los bits de la subred 400.

256 + 128 + 16 = 400
Ahora obtenemos la representación decimal de los bits prendidos en el tercer octeto.

128 + 64 + 8 = 200
La subred 400 queda representada en formato decimal como:

130.3.200.0/25

martes, 27 de diciembre de 2011

Entendiendo Satellite Assemblies usando MonoDevelop - (parte 2)

En la primera parte de este tutorial, se mostró como crear Satellite Assemblies, ahora en este segunda parte se mostrará un listado donde se muestra el código que nos mostrará los pasos de como consumir los ensamblados satélite o ensamblados de recursos desde una aplicación GTK#.


using System;
using Gtk;
using System.IO;
using System.Resources;
using Gdk;
using System.Reflection;

namespace TestResource
{
class MainClass : Gtk.Window
{
DrawingArea darea = null;
Label label1 = null;
Button btnLoad = null;
Pixmap pixmap;
Pixbuf pngbuf;
public MainClass():base("Test Resources"){
BorderWidth = 8;
this.DeleteEvent += new DeleteEventHandler(OnWindowDelete);
Frame frame = new Frame("Load");
Add(frame);
VBox MainPanel = new VBox (false, 8);
label1 = new Label("Query is: ");
darea = new DrawingArea();
btnLoad = new Button("Load resources");
btnLoad.Clicked += AddResource_Clicked;
darea.SetSizeRequest (200, 200);
darea.ExposeEvent += Expose_Event;
darea.ConfigureEvent += Configure_Event;
MainPanel.Add(label1);
MainPanel.PackStart(darea);
MainPanel.Add(btnLoad);
frame.Add (MainPanel);
SetDefaultSize (320, 233);
Resizable = false;
ShowAll();
}
public void OnWindowDelete(object o, DeleteEventArgs args) {
Application.Quit(); }

public static void Main (string[] args)
{
Application.Init();
new MainClass();
Application.Run();
}

void PlacePixbuf (Gdk.Pixbuf buf)
{
pixmap.DrawPixbuf (darea.Style.BlackGC,buf, 0, 0, 0, 0,buf.Width,
buf.Height,RgbDither.None, 0, 0);
darea.QueueDrawArea (0, 0, buf.Width, buf.Height);
}

void LoadResources(){
try{
//find the assembly
string assem = "demo.resources.dll";
Assembly assembly = Assembly.LoadFrom(assem);
if(File.Exists(assem))
{
//Instance for resourcemanager
ResourceManager rm = new ResourceManager("demo",assembly);
//get the string for the resource
label1.Text += rm.GetString("query1");
//get the image for the resource
System.Drawing.Bitmap bitmap = (System.Drawing.Bitmap)rm.GetObject("pugme");
bitmap.Save("pugme.png",System.Drawing.Imaging.ImageFormat.Png);
pngbuf = new Pixbuf("pugme.png");
}
}catch(Exception e){
Console.WriteLine(e.Message);
}
}

void Configure_Event (object obj, ConfigureEventArgs args)
{
Gdk.EventConfigure ev = args.Event;
Gdk.Window window = ev.Window;
Gdk.Rectangle allocation = darea.Allocation;
pixmap = new Gdk.Pixmap (window, allocation.Width,allocation.Height, -1);
pixmap.DrawRectangle (darea.Style.WhiteGC, true, 0, 0,allocation.Width,
allocation.Height);
}

void Expose_Event (object obj, ExposeEventArgs args)
{
Gdk.Rectangle area = args.Event.Area;
args.Event.Window.DrawDrawable (darea.Style.WhiteGC,
pixmap,area.X, area.Y,area.X, area.Y,area.Width, area.Height);
}

void AddResource_Clicked (object obj, EventArgs args)
{
LoadResources();
PlacePixbuf (pngbuf);
}
}
}

Toda esta funcionalidad se encuentra en el método LoadResources() , este método comienza primeramente con la carga en tiempo de ejecución del ensamblado que contiene los recursos utilizando las líneas siguientes:

string assem = "demo.resources.dll";
Assembly assembly = Assembly.LoadFrom(assem);

A continuación creamos una instancia de la clase ResourceManager en la cual se encuentran los métodos para obtener los recursos del ensamblado, en este ejemplo obtenemos un recurso de tipo cadena y otro de tipo imagen, con el código de las líneas siguientes

label1.Text += rm.GetString("query1"); 
System.Drawing.Bitmap bitmap = (System.Drawing.Bitmap)rm.GetObject("pugme");

Por último únicamente se guarda la imagen en el directorio de la aplicación para crear un objeto Pixbuf el cual se dibujará en un control DrawingArea, esto ocurre en las siguientes líneas:

bitmap.Save("pugme.png",System.Drawing.Imaging.ImageFormat.Png);
pngbuf = new Pixbuf("pugme.png");

Los métodos presentados por este programa en GTK# aplican para cualquier otra aplicación .NET incluso si el lenguaje de programación utilizado no es C#.
>Compilamos la aplicación y ejecutamos la aplicación con los siguientes comandos desde una terminal:

$ mcs –pkg:gtk-sharp-2.0 –r:System.Drawing Main.cs
$ mono Main.exe


Al ejecutar la aplicación se mostrará como en la siguiente imagen:


Al presionar el botón Load resources deberán de cargarse los recursos de cadena e imagen respectivamente, antes de ejecutar la aplicación es importante verificar que el ensamblado demo.resources.dll se encuentre en el mismo directorio de la aplicación.

El resultado final se mostrará como en la siguiente imagen:

Parte del código de este programa se derivo del ejemplo 4-8 del capítulo 4 del libro Mono: A Developer's Notebook de Niel M. Bornstein y Edd Dumbill