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: