´

August 2009 - Artículos

Conferencia – Silverlight como Plataforma de Desarrollo

Puedes ver el artículo original en mi blog:
http://juank.black-byte.com/eventos-silverlight-plataforma-desarrollo/

 

Fecha: Miércoles 2 de Septiembrea las 18:30 (-5 GMT)

Lugar: Auditorio Fundación Universitaria CAFAM
AK 68 N° 90-88 CAFAM Floresta, Bogotá – Colombia
¡¡¡..ENTRADA LIBRE…!!!!


Silverlight es una tecnología relativamente nueva en el mundo de las Aplicaciones Enriquecidas para Internet, la cual nos permite extender la funcionalidad de nuestras aplicaciones Web a través de un componente ligero, multi-plataforma y multi-navegador. En esta sesión podrás conocer cuáles son las características de la última versión de Silverlight que la hacen una excelente opción para construir este tipo de aplicaciones. Ven y conoce qué es Silverlight y cómo lo puedes utilizar como plataforma de desarrollo para aplicativos Web modernos.

C# – El extraño caso de la ventana sin borde que no se deja maximizar ni minimizar

Puedes ver el artículo original en  mi blog:

http://juank.black-byte.com/c-minimizar-maximizar-ventana-sin-borde/

 ---

En algunas aplicaciones llega a ser necesario tener una ventana sin borde en algún momento, una ventana sin borde se logra estableciendo la propiedad FormBorderStyle = None en el diseñador de Windows Forms o a través de código:

 
this.FormBorderStyle = FormBorderStyle.None;







Hasta ahí todo esta bien y no hay ningún problema al respecto, hasta que nos damos cuenta que una ventana sin borde no se deja maximizar ni minimizar.

De esto tratare en este artículo, el porqué de esta situación y como solucionarlo.

 

Como sabe una ventana que se debe maximizar o minimizar?

El bucle de mensajes

Las ventanas – y los demás controles – funcionan gracias a un bucle de mensajes, todo lo que manejamos nosotros como eventos : click del mouse, mover, cerrar, cambiar tamaño, maximizar etc, realmente es controlado por un bucle en donde se envían diferentes mensajes a la ventana, esta a su vez tiene un procedimiento que recibe estos mensajes y con base a los mensajes recibidos puede hacer una u otra cosa según se programe.

 

Si, para algunos esto ya debe estar sonando a cuento, pero las cosas son así por debajo de lo que usamos tradicionalmente. El tema del artículo no es explicar como funciona un ciclo de mensajes así que por el momento lo dejaremos hasta allí y quien quiera profundizar puede consultar esta fuente en internet http://www.winprog.org/tutorial/message_loop.html

 

Por el momento lo que si nos interesa del bucle de mensajes es que algunos de esos mensajes se utilizan para maximizar y minimizar las ventanas, es decir cuando uno utiliza alguna funcionalidad para minimizar una ventana, lo que ocurre realmente es que se envía el mensaje que dice: hey! minimízate y ya el manejador de la ventana hará lo necesario para minimizarse.

El problema de la ventana sin borde.

Resulta que cuando se crea una ventana el sistema de ventanas se encarga de asignar ciertas características de acuerdo a sus parámetros de creación, una de esas características es incluir llamados a las funciones internas de Windows Forms que minimizan y maximizan ventanas, pero cuando se esta creando una ventana sin borde, al no tener esta los botones de minimizar o maximizar simplemente se pasa por alto la necesidad de incluir llamados a esas funciones.

Tan es así que las ventanas sin borde ni siquiera reciben mensajes relacionados con maximizar y minimizar desde la barra de tareas de Windows, esto lo podemos verificar así:

  • Crear una ventana con borde en el diseñador
  • Sobre escribir el método WndProc ( que es el que procesa la cadena de mensajes enviados a la ventana)
  • Interceptar el mensaje de minimizar la ventana y lanzar un MessageBox:

 

Lo que sucederá es que el mensaje se mostrará, pero si creamos desde un comienzo la ventana como ventana sin borde nos daremos cuenta que el mensaje nunca se lanza puesto que la ventana nunca recibe el mensaje indicando que se minimize, y aunque lo recibiera no haría nada.

Este es el código de como se debe dejar el WndProc en la forma para hacer las pruebas con y sin borde.

 
const int WM_SYSCOMMAND = 0x112;
const int SC_MINIMIZE = 0xF020;
 
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_SYSCOMMAND)
{
if (m.WParam == (IntPtr)SC_MINIMIZE)
MessageBox.Show("Hacer lo que quieras en vez de minimizar");
 
base.WndProc(ref m);
}
else
base.WndProc(ref m);
}







Esto se convierte en un problema, pero como hacer para solucionarlo?

 

*Próximamente escribiré un breve artículo profundizando un poco más en la interceptación de mensajes y en el caso particular expuesto de minimizar la ventana.

Cambiar el comportamiento de la ventana

Como deshacernos de este problema y poder minimizar la ventana sin borde?

La respuesta esta escondida en mis líneas anteriores:

 

resulta que cuando se crea una ventana el sistema de ventanas se encarga de asignar ciertas características de acuerdo a sus parámetros de creación

Técnicamente hablando podríamos crear una ventana con borde y una vez inicializada cambiarle el estilo para que ahora estando sin borde se deje minimizar… ERROR eso no es posible porque apenas cambiamos el estilo de la ventana se llama una rutina que inicializa toda su estructura nuevamente… y al hacerlo elimina de nuevo la funcionalidad de minimizar y maximizar. :(

Para lograrlo hacer hay que hallar la forma de ‘engañar’ al sistema de ventanas del Windows Forms y hacerle creer que tiene una ventana con borde pero que realmente sea sin borde. Es decir debemos desde el comienzo crear una ventana con borde y luego volverla sin borde, PERO haciendo que el sistema de ventanas de Windows Forms no se entere, como es eso? con nuestra amiga la API de Windows haciendo llamados directamente al manejador de ventanas del sistema operativo sin pasar por Windows Forms.

Cuando una forma en Windows Forms es creada, esta inicializa todas sus estructuras de acuerdo a las propiedades establecidas y esto lo hace en un  método llamado CreateParams quien es el que internamente esta haciendo llamados a la API, bueno realmente es una propiedad, así que si reemplazamos  esta propiedad podemos hacer creer a Windows Forms que esta creando una ventana con bordes pero ya nos hemos encargado de quitarle dichos bordes “a la mala”.

Los pasos a seguir son los siguientes:

  1. Crear la forma con el estilo normal que incluye los botones minimizar y maximizar
  2. Sobre Escribir el método CreateParams
  3. Introducir modificaciones al estilo de la ventana  pero no utilizando las propiedades de Windows Forms sino modificando los parámetros con los cuales Windows Forms le pedirá al sistema operativo que cree la nueva ventana.

Todos los pasos son fáciles, el que es un poco críptico es el paso 3, así que lo analizare en más detalle.

CreateParams

Esta propiedad tiene a su vez su propia estructura, y parte de esa estructura es el campo Style de tipo int, cuando Windows esta inicializando la forma se revisa ese campo para determinar el estilo de la ventana, y de hecho cada vez que modificamos el estilo de la ventana esta recrea su apariencia modificando no solo el valor de Style sino también modificando comportamientos como ya lo hemos visto anteriormente. Sin embargo desde el propio manejador de ventanas de Windows cambiar el estilo no implica cambiar de una vez el comportamiento – como ya vimos que si sucede en Windows Forms  – así que podemos cambiar el valor de Style sin necesidad de cambiar nada más.

 

Como nuestra forma justo antes de comenzar el paso 3 ya esta lista para minimizarse y maximizarse, lo que haremos en el paso 3 será modificar la propiedad Style de CreateParams para suprimirle ‘la caja de titulo’ y ‘el borde de cambiar tamaño’, como hacemos esto si Style es un tipo int? pues lo haremos a través de mascaras como si fuera una enum en Windows Forms.  He definido WS_THICKFRAME nada más para preservar la definición inicial que se da en la API de Windows. El código quedaría así:

 

const int WS_CAPTION    = 0xC00000;
const int WS_THICKFRAME = 0x00040000;
const int WS_SIZEBOX = WS_THICKFRAME;
protected override CreateParams CreateParams
{
get
{
CreateParams p = base.CreateParams;
p.Style &= ~(WS_CAPTION | WS_SIZEBOX);
return p;
}
}







Simplemente estamos tomando los valores originales de CreateParams los cuales incluyen una ventana con bordes, pero reasignamos la propiedad Style para dejarla como estaba pero quitándole el borde de Resize y la barra de titulo.

Y Listo!!!

Eso es todo, ahora la ventana no tiene bordes y adicionalmente recibe el mensaje de minimizar, es más podemos combinar este código con el que veíamos en la primera parte y veremos como ahora si podemos interceptar el mensaje de minimizar !! ;)

Para profundizar un poco más acerca de como modificar el comportamiento de las ventanas les recomiendo revisar este link:

http://juank.black-byte.com/c-modificar-boton-minimizar-maximizar/

 

Happy Learning!

 

C# - la palabra clave volatile, explicación y ejemplos

Puedes ver el articulo original en  mi blog:

http://juank.black-byte.com/c-explicacion-ejemplo-volatile/

 ---

La palabra clave volatile es una de esas palabras clave muy pocas veces comprendidas, la documentación presente en msdn permite concluir que hay que utilizarla siempre que se manejen hilos, pero esto no siempre es así. Sin embargo lograr identificar que es lo que hace realmente esta palabra clave es una labor complicada así que dedicaré este artículo a explorar esta funcionalidad y a crear un ejemplo práctico que permita entender su verdadera naturaleza.

 

La documentación msdn

En msdn encontramos la siguiente definición de la palabra clave volatile:

La palabra clave volatile indica que varios subprocesos que se ejecutan a la vez pueden modificar un campo. Los campos que se declaran como volatile no están sujetos a optimizaciones del compilador que suponen el acceso por un subproceso único. Esto garantiza que el valor más actualizado está en todo momento presente en el campo

 

información completa

 

Debemos resaltar dos aspectos importantes de ese texto:

  1. Se menciona que los campos volatile no son susceptibles de optimizaciones por parte del compilador. Cuales optimizaciones?
  2. Dice que esto garantiza que el valor más actualizado siempre esta presente en el campo. No se supone que esto es así siempre?

A continuación revisaremos estas dos preguntas.

 

Optimizaciones del compilador

Siempre que compilamos un programa hecho con C# el compilador se encarga de convertir ese código C# en código de lenguaje IL, bueno realmente en OpCodes de IL. Esto es así de sencillo, pero resulta que cuando compilamos nuestro código en la configuración release o más específicamente cuando se marca la casilla de Optimizar código en el proyecto (ver imagen) el  compilador realiza una revisión general del código par determinar que cosas puede hacer funcionar de una manera mejor a la que codificó el programador inicialmente o incluso como puede cambiar las cosas en el ejecutable que no están en manos del programador ni del propio lenguaje para que a la hora de ejecutarse el programa sea más eficiente.

image

Estas optimizaciones son calculadas por el compilador haciendo uso de un complejo juego de reglas algunas de las cuales dependen de la arquitectura del procesador ( x86, x64, IA64, etc) y algunas otras del modelo de memoria que se esta trabajando. Los modelos de memoria son un tema complejo incluso desde sus fundamentos si bien no es algo imposible de aprender, pero quien quiera puede profundizar un poco más al respecto puede leer el libro de Sistemas Operativos Modernos de Andrew Tanembaum y desde luego buscar referencias adicionales en internet.

 

Adicionalmente a las optimizaciones realizadas por el compilador de C# tenemos una segunda etapa de compilación y optimizaciones realizadas por el JIT al momento de ejecutarse el programa, las cuales tiene su propio conjunto de reglas algunas de las cuales también están influenciadas por la arquitectura del procesador y el modelo de memoria.

 

El Valor Actualizado

Parte de las optimizaciones realizadas por el compilador pueden evitar que en algún momento determinado los campos de memoria donde se encuentran las variables no sean actualizados con su valor más reciente, porque?

 

Cada vez que el procesador hace una operación necesita colocar el valor de las variables en uno de sus registros, de tal forma que si tenemos por ejemplo un bucle con una suma de esta manera:


int i = 0;
while (condicion)
{
i = i + 1;
}

 

El procesador enviaría el valor de “i” a uno de sus registros y el valor ”1” en otro registro, al obtener el valor de la suma el procesador debe colocar este valor de nuevo en memoria en la variable “i” de tal forma que al continuar el bucle todo el proceso se repite.

 

Esta secuencia sin embargo puede ser optimizada por el compilador ya que se hace innecesario que el valor de “i” sea copiado de los registros de la CPU a la memoria y viceversa tantas veces como dure el bucle, así que el compilador genera un código ejecutable que permite que los cálculos sean hechos en su totalidad en los registros de la CPU y solo hasta salir del bucle estos datos serian copiados de nuevo a la memoria.

Este tipo de optimizaciones que usan ”cache”  trae una mejora considerable en la ejecución de procesos intensivos a nivel matemático y funcionan bastante bien en la mayoría de los escenarios.

 

Optimizaciones de cache y el problema de los hilos

Cuando el compilador esta realizando las optimizaciones analiza (de acuerdo a los estándares) todo lo que pueda hacer referencia a las variables en el contexto de ‘subproceso’ actual ( realmente en la mayoría de sistemas operativos no se ejecutan los procesos sino los subprocesos y se le llama proceso a un grupo de subprocesos que comparten la memoria ), así que con base a lo analizado realiza las optimizaciones, por ello el problema surge cuando un hilo trata de acceder a una variable que esta siendo modificada por un objeto que se ejecuta en un hilo diferente, ya que es posible que el hilo dueño de la variable se encuentre en  medio de un proceso optimizado para funcionar por cache en los registros y al no estar verificando el valor de la variable en memoria pues no vera los cambios realizados, de hecho los sobrescribirá al finalizar. Así que en escenarios donde existan múltiples hilos lo ideal es bloquear este tipo de optimizaciones y estar muy atento a los casos donde no sea conveniente inactivarlas pues hay casos para todo.

 

El Ejemplo

Bien después de la teoría veamos la práctica, veremos ejemplos donde volatile no sirve para nada ( y creo que son la mayoría de casos en el mundo real – al menos en CPUs x86 ya que en IA64 al parecer el tema es muy diferente), y finalmente un caso que de acuerdo a la teoría que acabamos de ver funcionara perfectamente desde que lo compiles para x86.

Sobra decir que para probar los ejemplos lo debes hacer con la versión Release del ejecutable.

Ejemplo 1

Este es el ejemplo típico, donde todos usamos volatile porque así lo dice la documentación pero la realidad es que en estos casos no sirve de NADA.


using System.Threading;
using System;

static class BackgroundTaskDemo
{
//volatile
static int i = 0;
private static bool stopping = false;

static void Main ()
{
new Thread(EfectuarTrabajo).Start();
Thread.Sleep(5000);
stopping = true;


Console.WriteLine("Main exit");
Console.ReadLine();
}

static void EfectuarTrabajo()
{
i++;
Console.WriteLine("Valor de i="+i);
Console.WriteLine("EfectuarTrabajo exit " + i);
}
}

 

Podríamos ponerle a "i" como volatile pero no habría diferencia puesto que básicamente el código no necesita ni tiene como ser optimizado. Así que volatile no bloquearía ninguna optimización.

Ejemplo 2

Este es el ejemplo tampoco sirve de NADA, si bien en el bucle se ve la necesidad de optimizar el rendimiento del código evitando que se copie el valor de "i" o de "stopping" de los registros de la CPU a la memoria y viceversa resulta que al convertir "i" a string estamos forzando al compilador a que no utilice el cache dado que necesita el valor mas reciente de "i" en la memoria para poder llamar el método ToString() y al método WriteLine, adicionalmente al llamar a alguno de esos métodos se requiere colocar en los registros de la CPU los valores necesarios para cambiar de contexto y para poder ejecutar las operaciones internas de dichos métodos, así que no hay manera de optimizar nada.


using System.Threading;
using System;

static class BackgroundTaskDemo
{
//volatile
private static bool stopping = false;

static void Main ()
{
new Thread(EfectuarTrabajo).Start();
Thread.Sleep(5000);
stopping = true;


Console.WriteLine("Main exit");
Console.ReadLine();
}

static void EfectuarTrabajo()
{
int i = 0;

while (!stopping)
{
i++;
Console.WriteLine("Valor de i=" + i.ToString());
}
Console.WriteLine("EfectuarTrabajo exit " + i);
}
}

 

Nuevamente podríamos ponerle a "i" como volatile pero no habría diferencia puesto que básicamente el código no necesita ni tiene como ser optimizado. Así que volatile no bloquearía ninguna optimización.

Ejemplo 3

En este SI que funcionan las optimizaciones, ya que el compilador optimizara el uso de "i" para que se apoye en los registros del procesador de principio a fin, eso es perfecto!, pero por otro lado "stopping" también es optimizado, trayendo como resultado que siempre este en uno de los registros y por tanto nunca se entere en que momento “stopping” cambio de valor para que saliera del bucle.


using System.Threading;
using System;

static class BackgroundTaskDemo
{
private static bool stopping = false;

static void Main ()
{
new Thread(EfectuarTrabajo).Start();
Thread.Sleep(5000);
stopping = true;


Console.WriteLine("Main exit");
Console.ReadLine();
}

static void EfectuarTrabajo() { int i = 0; Console.WriteLine("Valor de i=" + i.ToString()); while (!stopping) { i++; } Console.WriteLine("Valor de i=" + i.ToString()); Console.WriteLine("EfectuarTrabajo exit "); }
}

 

Al ejecutar la version release para x86 de este programa veremos como, aunque en codigo parece que todo saldra bien, al ejecutarse el programa este nunca terminara pues el hilo nunca se entera que "stopping" cambio su valor. En pantalla veriamos perpetuamente

Main Exit

Y nada más :(

 

Ese es el ejemplo perfecto de la funcionalidad de volatile, así que podemos ahora marcar el campo "stopping" como volatile, compilar y ejecutar la versión release... y AHORA SI FUNCIONA COMO DEBERIA DE SER!!!


using System.Threading;
using System;

static class BackgroundTaskDemo
{
private volatile static bool stopping = false;

static void Main ()
{
new Thread(EfectuarTrabajo).Start();
Thread.Sleep(5000);
stopping = true;


Console.WriteLine("Main exit");
Console.ReadLine();
}

static void EfectuarTrabajo() { int i = 0; Console.WriteLine("Valor de i=" + i.ToString()); while (!stopping) { i++; } Console.WriteLine("Valor de i=" + i.ToString()); Console.WriteLine("EfectuarTrabajo exit "); }
}

Ahora si en pantalla veríamos

Main Exit
EfectuarTrabajo exit xxxxxxxx

:)

Ejemplo 4

Este es el mismo ejemplo un poco mas elaborado haciendo uso de algunas operaciones de System.Math las cuales son suceptibles de optimización:


using System.Threading;
using System;

static class BackgroundTaskDemo
{
//volatile
private static bool stopping = false;

static void Main ()
{
new Thread(EfectuarTrabajo).Start();
Thread.Sleep(5000);
stopping = true;


Console.WriteLine("Main exit");
Console.ReadLine();
}

static void EfectuarTrabajo()
{
int i = 0;
int j = 2;

while (!stopping)
{
i++;
j = 100 + (int)Math.Sin((double)i * 10d);
}
Console.WriteLine("EfectuarTrabajo exit " + i);
}
}

Prueben quitando y poniendo volatile y verán la diferencia.

Eso es todo.

 

Hasta pronto.

C# - Cuando la precisión que da el StopWatch no es suficiente

Puedes ver el articulo original en  mi blog:

http://juank.black-byte.com/c-medir-nanosegundos/

 ---

Medir tiempo en nanosegundos

Hola!

He observado que es muy frecuente cuando alguien quiere hacer una prueba de rendimiento (sobre todo a nivel académico) que la resolución que da el objeto StopWatch (System.Diagnostics.Stopwatch) de Milisegundos resulta no ser siempre suficiente.

En esos casos lo mejor es recurrir a las funciones de la API para crear algo más de acuerdo a nuestras necesidades, de tal forma que podamos medir el tiempo transcurrido con una precisión mayor a la que da  - por alguna razón – el objeto StopWatch, así que creare algo sencillo que permitirá lograr la precisión deseada, pero primero – como siempre –algo de teoría al respecto.

 

CÓMO CALCULAR EL TIEMPO

Para calcular el tiempo dentro de un computador debemos valernos de la información que nos brinda el procesador, como todos sabemos el procesador posee un atributo llamado frecuencia, el cual nos indica cuantos ciclos de reloj realiza el procesador cada segundo. De esta forma encontramos que hay procesadores 1 Ghz (un millon de ciclos de reloj por segundo) y hay de muchos diferentes valores.

Por otro lado un procesador posee un contador de ciclos es decir un registro el cual informa de cuantos ciclos ha procesado.

Así que tenemos dos fuentes de información que utilizaremos para calcular el tiempo transcurrido ya que si dividimos la cantidad de ciclos que han pasado de un momento a otro entre la frecuencia, obtendremos el tiempo transcurrido con una precisión bastante grande (double).

 

Tenemos:

Frecuencia= ciclos por segundo

Ticks= ciclos procesados de un instante a otro

Tiempo = Ticks / Frecuencia (   ciclos / ciclos por segundo  )

De tal forma que las unidades resultantes serán: segundos.

 

Que!!! segundos? si pero esos segundos están expresados con una gran precisión decimal por lo cual podemos llegar a la precisión de nanosegundos tan solo multiplicando por 1.000‘000.000 (mil millones), y con un tipo de dato double tenemos espacio mas que suficiente para manejar estas cifras.

 

DE DONDE OBTENEMOS LOS DATOS?

Para ello utilizaremos dos funciones de la API de Windows:

  • QueryPerformanceCounter: Retorna el valor almacenado en el registro contador de ciclos del procesador en un momento dado
  • QueryPerformanceFrequency: Retorna la velocidad del procesador

 

Como ven ya esta todo lo necesario, ahora la implementación.

 

IMPLEMENTACIÓN

Lo primero es poder hacer uso de las funciones API  para lo cual nos ayudaremos con DllImport, ya saben la mejor fuente para saber como hacer declaraciones de la API de manera rápida es: http://www.pinvoke.net/. Todo esto lo hare dentro de la clase NanoTemporizador

using System;
using System.Runtime.InteropServices;

public class Temporizador
{
    /// 
    /// Obtiene la frecuencia del procesador 
    /// 
    /// variable donde retorna la frecuencia
    /// True si el procesador tiene contador de frecuencia, false sino
    [DllImport"kernel32.dll", SetLastError = true)]
    static extern bool QueryPerformanceFrequency(out long frequency);

    /// 
    /// Obtiene l evalor actual del contador de alto rendimiento del ptrocesador
    /// 
    /// variable donde retorna el valor
    /// True si todo salio OK, false sino
    [DllImport("kernel32.dll", SetLastError = true)]
    static extern bool QueryPerformanceCounter(out long lpPerformanceCount);
}

Ahora en el constructor de nuestra clase no haremos nada :). Vale la pena recordar que siempre se deben crear componentes eficientes – según yo :P – por lo que es mejor que tengamos un constructor estático ya que realmente la frecuencia del procesador no cambiara nunca, así que solo es necesario calcularla una sola vez para todas las instancias.

    /// Almacena la frecuencia del contador de alto rendimiento
    private static long _frecuencia;

    static NanoTemporizador()
    {
        if (!QueryPerformanceFrequency(out _frecuencia))
            throw new NullReferenceException(
                "Este componente se hizo para utilizar contadores de alto rendimiento. Como no los hay mejor utiliza StopWatch"
             );
    }

Para que funcione realmente como un contador de tiempo necesitamos poder establecer si el contador esta andando o no, para lo cual crearemos una propiedad. Adicionalmente en el método Start del temporizador vams a calcular el valor de contador actual y a cambiar el valor de nuestro indicador a true:

    /// Almacena el valor de conteo inicial
    private long _conteoInicial;
    /// Indica si ya se ha inicializado el timer
    private bool _isRunning = false;
    /// Indica si ya se ha inicializado el timer
    public bool IsRunning { get { return _isRunning; } }

    public void Start()
    {
        if (!_isRunning)
        {
            QueryPerformanceCounter(out _conteoInicial);
            _isRunning = true;
        }
    }

De igual forma se establece el método Stop:

    /// Almacena el valor de conteo final
    private long _conteoFinal;

    public void Stop()
    {
        if (_isRunning)
        {
            QueryPerformanceCounter(out _conteoFinal);
            _isRunning = false;
        }
    }

Finalmente se crea una propiedad a travez de la cual podamos hallar el valor en nanosegundos:

    ///Retorna la cantidad de nanosegundos contados
    public double ElapsedNanoseconds
    {
        get
        {
            return (_conteoFinal - _conteoInicial) * 1000000000L
                   / (double)_frecuencia;
        }
    }

 

Perfecto, eso es todo. Esta es la versión completa:

 
using System;
using System.Runtime.InteropServices;

public class NanoTemporizador
{
    /// 
    /// Obtiene la frecuencia del procesador 
    /// 
    /// variable donde retorna la frecuencia
    /// True si el procesador tiene contador de frecuencia, false sino
    [DllImport("kernel32.dll", SetLastError = true)]
    static extern bool QueryPerformanceFrequency(out long frequency);

    /// 
    /// Obtiene l evalor actual del contador de alto rendimiento del ptrocesador
    /// 
    /// variable donde retorna el valor
    /// True si todo salio OK, false sino
    [DllImport("kernel32.dll", SetLastError = true)]
    static extern bool QueryPerformanceCounter(out long lpPerformanceCount);

    /// Almacena la frecuencia del contador de alto rendimiento
    private static long _frecuencia;
    
    /// Almacena el valor de conteo inicial
    private long _conteoInicial;
    /// Almacena el valor de conteo final
    private long _conteoFinal;

    /// Indica si ya se ha inicializado el timer
    private bool _isRunning = false;
    /// Indica si ya se ha inicializado el timer
    public bool IsRunning { get { return _isRunning; } }

    /// Valor por el cual se multiplican segundos para pasarlos a nanosegundos
    private const long NANOSEGUNDOS = 1000000000L;

    /// Valor por el cual se multiplican segundos para pasarlos a milisegundos
    private const long MILISEGUNDOS = 1000L;
    
    static NanoTemporizador()
    {
        if (!QueryPerformanceFrequency(out _frecuencia))
            throw new NullReferenceException(
               "Este componente se hizo para utilizar contadores de alto rendimiento. Como no los hay mejor utiliza StopWatch."
            );
    }

    /// Inicia el conteo del temporizador
    public void Start()
    {
        if (!_isRunning)
        {
            QueryPerformanceCounter(out _conteoInicial);
            _isRunning = true;
        }
    }
    
    /// Detiene el conteo del temporizador
    public void Stop()
    {
        if (_isRunning)
        {
            QueryPerformanceCounter(out _conteoFinal);
            _isRunning = false;
        }
    }

    ///Retorna la cantidad de nanosegundos contados
    public double ElapsedNanoseconds
    {
        get
        {
            return (_conteoFinal - _conteoInicial) * NANOSEGUNDOS
                   / (double)_frecuencia;
        }
    }

    ///Retorna la cantidad de milisegundos contados
    public double ElapsedMilliseconds
    {
        get
        {
            return (_conteoFinal - _conteoInicial) * MILISEGUNDOS
                   / (double)_frecuencia;
        }
    }

    ///Retorna la cantidad de segundos contados
    public double ElapsedSeconds
    {
        get
        {
            return (_conteoFinal - _conteoInicial) / (double)_frecuencia;
        }
    }
}

CÓMO USARLO?

Bien este es un ejemplo tontisimo, pero muy ilustrativo:


using System;

namespace Prueba
{
    class Program
    {
        static void Main(string[] args)
        {
            NanoTemporizador temporizador = new NanoTemporizador();

            Probador(temporizador, 1000);
            Probador(temporizador, 1000);
            Probador(temporizador, 5000);
            Probador(temporizador, 2358);
            Probador(temporizador, 3541);
            Probador(temporizador, 10000);

            Console.ReadLine();
        }

        private static void Probador(NanoTemporizador temporizador, int espera )
        {
            temporizador.Start();
            System.Threading.Thread.Sleep(espera);
            temporizador.Stop();

            Console.WriteLine("Tiempo transcurrido: {0} ns> ", temporizador.ElapsedNanoseconds);
            Console.WriteLine("Tiempo transcurrido: {0} ms> ", temporizador.ElapsedMilliseconds);
            Console.WriteLine("Tiempo transcurrido: {0} sg> ", temporizador.ElapsedSeconds);
            Console.WriteLine("=====================================================");
        }
    }
}

Hasta pronto.

Service Behavior con WCF y C#

Puedes ver el articulo original en  mi blog:

http://juank.black-byte.com/c-service-behavior-wcf-c/

---

 

Hola, he creado este video ( screencast ) donde se enseña de manera sencilla a manipular el comportamiento de un servicio WCF con lenguaje C#. Si desean verlo en alta resolucion, ya saben, basta con hacer click.

Posted: 18/8/2009 15:55 por Juan Carlos Ruiz Pacheco | con no comments |
Archivado en: ,
DataContract - Tipos Personalizados con WCF y C#

Puedes ver el articulo original en  mi blog:

http://juank.black-byte.com/c-datacontract-wcf/

 ---

Hola, he creado este video ( screencast ) donde se enseña como manejat tipos personalizados en sun servicio WCF con lenguaje C#. Si desean verlo en alta resolución, ya saben, basta con hacer click.

Posted: 18/8/2009 15:54 por Juan Carlos Ruiz Pacheco | con no comments |
Archivado en: ,
Como crear un Servicio con C# y WCF

Puedes ver el articulo original en  mi blog:

http://juank.black-byte.com/c-crear-servicio-c-wcf/

--

 Hola, he creado este video ( screencast ) donde se enseña de manera sencilla a crear un servicio WCF con lenguaje C#. Si desean verlo en alta resolucion, ya saben, basta con hacer click.

Posted: 18/8/2009 15:53 por Juan Carlos Ruiz Pacheco | con no comments |
Archivado en: ,
Como cambiar el texto de los botones de un MessageBox - C#

Puedes ver el articulo original en  mi blog:

http://juank.black-byte.com/c-cambiar-texto-botones-messagebox/

 ---

Por razones que aún no me son del todo claras, el .NET Framework no tiene textos localizables para los MessageBox, razón por la cual no siempre se muestran en el lenguaje que necesitamos, sino que se muestran en el lenguaje del Framework instalado.

 

Puede que nuestra aplicación este en español pero probablemente nuestros MessageBox siempre salgan con los labels de los botones en ingles:

image

Además no siempre los labels de los batones tienen textos convenientes para nuestras aplicación.

El Framework no nos ofrece ninguna manera de modificar dichos textos más allá de las opciones predeterminadas, ¿Qué podemos hacer?

No es una tarea tan sencilla como uno se imaginaria inicialmente, requiere del uso de la API de Windows y un poco de ingenio.

He creado una utilidad que te ahorrará mucho trabajo y podrás cambiar los textos de los botones de manera muy sencilla tal como te lo muestra el siguiente ejemplo:


MsgBoxUtil.HackMessageBox("SI","NO", "CANCELAR");
MessageBox.Show("hola", "hola", MessageBoxButtons.YesNoCancel);

MsgBoxUtil.HackMessageBox("REINTENTAR", "CANCELAR");
MessageBox.Show("hola2", "hola2", MessageBoxButtons.RetryCancel);

MsgBoxUtil.HackMessageBox("Descartar", "Reintentar", "Ignorar");
MessageBox.Show("hola3", "hola3", MessageBoxButtons.AbortRetryIgnore);

MsgBoxUtil.UnHackMessageBox();

MessageBox.Show("Normal", "Normal", MessageBoxButtons.AbortRetryIgnore);

 

El código completo de la utilidad lo puedes descargar aquí: MsgBoxUtil.cs

 

Cómo se hace?

Bueno hay que hacer las cosas al derecho, primero lo primero.

Análisis

Planteamiento inicial

  • Para modificar los textos hay que encontrar la manera de llegar hasta los botones del MessageBox
  • Para llegar a los botones del MessageBox primero hay que llegar al MessageBox

 

Dado que el .NET Framework no nos permite manipular la ventana del MessageBox debemos recurrir a la API de Windows.

Para Windows, todo control que se dibuje en pantalla es una ventana ( si, así es), así que si tuviéramos una forma, esta es una ventana que contiene paneles, los panales son también ventanas que contienen por ejemplo GroupBox que a su vez son ventanas que contienen botones , que a su vez también son ventanas.

Con base a estos puntos entonces detallemos un poco más el planteamiento.

  • Se deben buscar las ventanas hijas de la forma actual
  • Cuando se encuentre la ventana del MessageBox se deben buscar los botones
  • Con base a la ventana del  MessageBox se deben buscar sus ventanas hijas
  • A las ventanas que sean botones se les debe cambiar el texto
  • Dado que el MessageBox es una clase estática, la podemos dar por creada desde un comienzo ( el CLR la creara a la primera referencia que se haga).

Referencias a la API de Windows

Dado que esta tarea requiere de la API de Windows, y de acuerdo a nuestro planteamiento detallado, estas son las funciones de la API que utilizaremos:

  • EnumThreadWindows: Ejecuta un proceso por cada una de las ventanas del hilo actual, según se le indique. Lo usaremos para encontrar cual es la ventana del MessageBox.
  • GetCurrentThreadId: Obtiene el id del Thread en ejecución.
  • GetClassName: Obtiene el nombre de la clase a partir de un handler. Se utilizara junto con EnumThreadWindows para determinar si la ventana procesada es un MessageBox, dado que se desconoce el nombre que tendrán las ventanas del MessageBox, se debe determinar utilizando el ClassName de las ventanas. Más adelante veremos como hallar el ClassName de un MessageBox.
  • EnumChildWindows: Con base al handler de una ventana recorre cada una de sus ventanas hijas y ejecuta un proceso. Solo enumera las ventanas de primer nivel. Lo usaremos para encontrar los botones del MessageBox.
  • SetWindowText: Establece el texto asociado a una ventana, lo usaremos para modificar el texto de los Botones.

 

Diseño

Tenemos una única clase, utilicemos algo sencillo que ilustre el proceso de manera general, con esto bastará.

Diseno

  • Iniciaremos el proceso con EnumThreadWindows al cual le pasaremos un delegado que internamente revisará cada ventana enumerada para determinar si es o no un MessageBox.
  • Sino es un MessageBox revisará la siguiente ventana enumerada hasta que lo encuentre. Sino encuentra es que no hay MessageBox.
  • Cuando encuentra el MessageBox verifica a través de sus ventanas hijas con EnumChildWindows al cual se se pasa un delegado que internamente revisará cada ventana enumerada para determinar si es o no un Botón.
  • Cuando encuentra que es un botón modificará el texto asociado.

 

Implementación

Lo primero que debemos hacer es preparar las funciones a las que accederemos a través de interoperabilidad:


public class MsgBoxUtil
{
#region Interoperabilidad
private delegate bool EnumWindowDelegate(IntPtr handler, IntPtr longPointer);

[DllImport("user32.dll")]
private static extern bool SetWindowText(IntPtr handler, string texto);

[DllImport("user32.dll")]
private static extern int GetClassName(IntPtr handler, StringBuilder nombre, int tamañoMaximo);

[DllImport("user32.dll")]
private static extern bool EnumChildWindows(IntPtr handler, EnumWindowDelegate callback, IntPtr longPointer);

[DllImport("user32.dll")]
private static extern bool EnumThreadWindows(int threadID, EnumWindowDelegate callback, IntPtr longPointer);

[DllImport("kernel32.dll")]
private static extern int GetCurrentThreadId();

#endregion Interoperabilidad
}

 

No hay nada nuevo en lo que acabamos de ver, son las funciones que hemos mencionado desde el comienzo,  es de destacar únicamente EnumWindowDelegate que es un delegado (puntero a una función) que debemos usar cuando llamemos a EnumChildWindows y a EnumThreadWindows.

 

HackMessageBox

Acto seguido crearemos el método HackMessageBox, el cual recibe como parámetro las cadenas de texto que representan los labels de los botones del MessageBox. Esta función guarda una referencia estática a ese array de textos para poderlos usar en otros lugares de la clase, también se determina si hay o no un windows form y de ser así inicia el procesamiento llamando asíncronamente a EnumThreadWindow, sin embargo no llamaremos directamente a EnumThreadWindows porque requiere demasiados parámetros lo cual nos complica un poco el paso de parámetros en el llamado asíncrono (BeginInvoke), en su lugar llamaremos a BuscaMessageBox que es una función sin parámetros que llamará internamente a EnumThreadWindows con los parámetros necesarios.Dado que BeginInvoque requiere enviar un delegado, debemos definirlo previamente.

 

En el llamado a EnumThreadWindows le debemos pasar el ThreadId el cual obtenemos con un llamado a GetCurrentThreadId, el segundo parámetro es una función ProcesaMessageBoxEnForms que se ejecutará cada por cada ventana que se enumere, el tercer parámetro no lo necesitamos así que será Zero.


private static string[] textoBotones;
private delegate void BuscarMsgBoxDelegate();

public static void HackMessageBox(params string[] textoBotones)
{
MsgBoxUtil.textoBotones = textoBotones;

if (Application.OpenForms.Count > 0)
Application.OpenForms[0].BeginInvoke(new BuscarMsgBoxDelegate(BuscaMessageBox));
}

private static void BuscaMessageBox()
{
EnumThreadWindows(GetCurrentThreadId(), ProcesaMessageBoxEnForms, IntPtr.Zero);
}

 

ProcesaMessageBoxEnForms

Revisemos ProcesaMessageBoxEnForms, como esta funcion es llamada por EnumThreadWindows, esta le pasa siempre como parámetro el handler de la ventana que se esta enumerando, así que con ese handler buscaremos el nombre de la clase de la ventana enumerada para así poder compararlo con el nombre de la clase de un MessageBox, sino es un MessageBox entonces se devuelve true y así EnumThreadWindows vuelve a llamar a ProcesaMessageBoxEnForms pero pasando como parámetro la siguiente ventana enumerada, el procedimiento se repite hasta que se logra hallar la ventana del MessageBox. En ese momento se establece la variable indiceTexto en 0, esta variable la utilizaremos para recorrer el array de textos de acuerdo a los botones que encontremos dentro del MessageBox.

Seguidamente llamamos a EnumChildWindows pasándole como parámetro el handle de la ventana que desde luego es el MessageBox que acabamos de encontrar, así que las ventanas que se enumeraran son las subventanas del MessageBox entre ellas... los botones. Como ya encontro el MessageBox y realizo el proceso de busqueda de los botones se retorna false, de tal forma que EnumThreadWindows no siga enumerando ventanas.


private static int indiceTexto;
private const string MBOX_CLASSNAME = "#32770";
private const int STRING_BUILDER_CAPACITY = 256;

private static bool ProcesaMessageBoxEnForms(IntPtr handler, IntPtr longPointer)
{
StringBuilder nombreClase = new StringBuilder(STRING_BUILDER_CAPACITY);
GetClassName(handler, nombreClase, nombreClase.Capacity);

if (nombreClase.ToString() != MBOX_CLASSNAME)
return true;
else
{
indiceTexto = 0;
EnumChildWindows(handler, CambiaTextoBotonMessageBox, IntPtr.Zero);
return false;
}
}

 

BREAK> Y de dónde saqué que MBOX_CLASSNAME = "#32770"?

Algunos, o espero que todos los que no sepan se estarán haciendo esa pregunta.

Hay que utilizar herramientas que nos permitan hacer ingeniería inversa, una de estas herramientas es Spy++ que viene incluida con Visual Studio, no haremos un curso completo de Spy++ pero veremos como usarlo para obtener el ClassName de un MessageBox.

  1. Abrir un MessageBox de un programa de .NET Framework
  2. Abrir Spy++
  3. Ahora en Spy++ presionaremos esta tecla Spy
  4. Esto hace que se despliegue el siguiente dialogo:Spy  2
  5. Allí damos click sostenido en la figura señalada en rojo y la arrastramos hasta la ventana del MessageBox, teniendo cuidado de no seleccionar los botones u otras figuras, solo el marco principal. Soltamos el click y damos OK.
  6. Se abre un cuadro de dialogo,vamos a la pestaña Class y allí esta!!! ClassName = #32770.

Spy  3

END BREAK>

CambiaTextoBotonMessageBox

Una vez se ha encontrado el MessageBox se hace el llamado a EnumChildWindows pasándole como parámetro el handler del MessageBox, el segundo parámetro es la función CambiaTextoBotonMessageBox, el tercer parámetro es Zero.

 

EnumChildWindows invoca a CambiaTextoBotonMessageBox por cada una de las ventanas enumeradas, internamente CambiaTextoBotonMessageBox revisa si la ventana procesada es de clase Button, si no lo es entonces se devuelve true para que EnumChildWindow envie la siguiente ventana a proceso, esto continua así hasta encontrar tantos botones como cadenas de texto se hallan enviado en HackMessageBox como parámetro, a cada uno de los botones encontrados se le asigna su cadena de texto en el orden correspondiente.


private const string BUTTON_CLASSNAME = "Button";

private static bool CambiaTextoBotonMessageBox(IntPtr handler, IntPtr longPointer)
{
StringBuilder nombreClase = new StringBuilder(STRING_BUILDER_CAPACITY);
GetClassName(handler, nombreClase, nombreClase.Capacity);

if (nombreClase.ToString() == BUTTON_CLASSNAME && indiceTexto < textoBotones.Length)
{
SetWindowText(handler, textoBotones[indiceTexto]);
indiceTexto++;
}
return true;
}

Ejemplo de uso

Listo ya la funcionalidad esta terminada, pero solo para utilizarla dentro de Windows Forms, ya que si lo tratamos de usar por consola fallará. Sin embargo eso es fácil de solucionar y lo haremos en un próximo artículo. Esta es la manera correcta de utilizarlo.


MsgBoxUtil.HackMessageBox("SI","NO", "CANCELAR");
MessageBox.Show("hola", "hola", MessageBoxButtons.YesNoCancel);

MsgBoxUtil.HackMessageBox("REINTENTAR", "CANCELAR");
MessageBox.Show("hola2", "hola2", MessageBoxButtons.RetryCancel);

MsgBoxUtil.HackMessageBox("Descartar", "Reintentar", "Ignorar");
MessageBox.Show("hola3", "hola3", MessageBoxButtons.AbortRetryIgnore);

 

Les dejo este MessageBox con Botones Personalizados:


MsgBoxUtil.HackMessageBox("Acepto", "Lo Pensare", "Olvidalo");
MessageBox.Show("hola3", "hola3", MessageBoxButtons.AbortRetryIgnore);

 

MsgBox2

Reversar el MessageBox a su estado original también es posible de hacer, pero lo dejaremos para el próximo artículo, mientras pueden intentarlo si quieren.

 

Cordial saludo a Todos.

Como abrir la puerta del cd rom desde C# y VB.NET

Puedes ver el articulo original en  mi blog:

http://juank.black-byte.com/c-abrir-puerta-cd-rom/

 ---

Dando solución a una de las inquietudes más comunes de las personas en los foros he creado este post para mostrarles la funcionalidad de la clase ExpulsarCDRom para explicarles como se hizo.

 

CÓMO UTILIZAR ESTA CLASE?

Para utilizarla, las explicaciones sobran, este es un código de ejemplo que muestra como abrir todas las unidades de CDRom en el PC ,he utilizado linq y DriveInfo para obtener el listado de unidades de CDRom disponibles y seguidamente utilizo el método ExpulsarCDRom.Expulsar:


class Program
{
    static void Main(string[] args)
    {
        //Obtener la lista de dispositivos de CDRom   
        var CDRomDrives = from drive in System.IO.DriveInfo.GetDrives()
                          where drive.DriveType == System.IO.DriveType.CDRom
                          select drive;

        
        //A cada uno de ellos hacerlo abrir   
        foreach (DriveInfo cdRom in CDRomDrives)
            ExpulsarCDRom.Expulsar(cdRom.Name);             
    }
}

Esta es la implementación final de ExpulsarCDRom en C#


//Constantes usadas en la API 
/// Indica que se se hara lectura genérica del archvo
const uint GENERICREAD = 0x80000000;
/// Indica que se debe abrir un archivo existente, no crear uno nuevo
const uint OPENEXISTING = 3;
/// Comando enviado al dispositivo para abrir la puerta
const uint IOCTL_STORAGE_EJECT_MEDIA = 0x2D4808;
/// Indica que la operaciónj no finalizo adecuadamente 
const int INVALID_HANDLE = -1;


/// Puntero que se usara para apuntar al archivo (unidad) de CDRom
private static IntPtr fileHandle;
/// Indica el número de bytes leidos cmo rspuesta de un proceso
private static uint returnedBytes;

/// 
/// esta función sirve para crear archivos pero también para abrir archivos existentes, 
/// así que se utilizará para abrir un archivo, la unidad del CD hace parte del sistema 
/// global de archivos así que podemos llegar a ella desde este medio
/// 
/// Puntero que sirve como manejador del archivo
[DllImport("kernel32", SetLastError = true)]
static extern IntPtr CreateFile(string fileName,
                                uint desiredAccess,
                                uint shareMode,
                                IntPtr attributes,
                                uint creationDisposition,
                                uint flagsAndAttributes,
                                IntPtr templateFile);

/// 
/// Cierra un manejador a un objeto, 
/// en este caso el objeto será la unidad de CD que hemos accedido a través de CreateFile 
/// 
/// 
/// Entero que indica la respuesta del proceso
[DllImport("kernel32", SetLastError = true)]
static extern int CloseHandle(IntPtr driveHandle);


/// 
/// Nos permite enviar comandos de I/O a un dispositivo. 
/// 
/// Indica si fue o no enviado el comando al dispositivo
[DllImport("kernel32", SetLastError = true)]
static extern bool DeviceIoControl(IntPtr driveHandle,
                                    uint IoControlCode,
                                    IntPtr lpInBuffer,
                                    uint inBufferSize,
                                    IntPtr lpOutBuffer,
                                    uint outBufferSize,
                                    ref uint lpBytesReturned,
                                    IntPtr lpOverlapped);

///    
/// Expulsa el drive de acuerdo a la letra asignada   
///    
/// Letra del drive   
public static void Expulsar(string driveLetter)
{
    //Modificar el nombre de la unidad de acuerdo a como lo entiende el 
    //sistema de archivos
    driveLetter = @"\\.\" + driveLetter.Substring(0, 2);
    try
    {
        //Crea el puntero al archivo (dispositivo)   
        fileHandle = CreateFile(driveLetter, GENERICREAD, 0,
                                IntPtr.Zero, OPENEXISTING,
                                0, IntPtr.Zero);

        //Si es una unidad valida   
        if (fileHandle.ToInt32() != INVALID_HANDLE)
        {
            //Intenta expulsar el dispositivo   
            DeviceIoControl(fileHandle, IOCTL_STORAGE_EJECT_MEDIA,
                            IntPtr.Zero, 0, IntPtr.Zero, 0,
                            ref returnedBytes, IntPtr.Zero);
        }
    }
    catch
    {
        //Sino lo pudo expulsar   
        throw new Exception(Marshal.GetLastWin32Error().ToString());
    }
    finally
    {
        //Asegurarse de siempre cerrar el puntero del archvo   
        CloseHandle(fileHandle);
        fileHandle = IntPtr.Zero;
    }
}
//Constantes usadas en la API 
/// Indica que se se hara lectura genérica del archvo
const uint GENERICREAD = 0x80000000;
/// Indica que se debe abrir un archivo existente, no crear uno nuevo
const uint OPENEXISTING = 3;
/// Comando enviado al dispositivo para abrir la puerta
const uint IOCTL_STORAGE_EJECT_MEDIA = 0x2D4808;
/// Indica que la operaciónj no finalizo adecuadamente 
const int INVALID_HANDLE = -1;


/// Puntero que se usara para apuntar al archivo (unidad) de CDRom
private static IntPtr fileHandle;
/// Indica el número de bytes leidos cmo rspuesta de un proceso
private static uint returnedBytes;

/// 
/// esta función sirve para crear archivos pero también para abrir archivos existentes, 
/// así que se utilizará para abrir un archivo, la unidad del CD hace parte del sistema 
/// global de archivos así que podemos llegar a ella desde este medio
/// 
/// Puntero que sirve como manejador del archivo
[DllImport("kernel32", SetLastError = true)]
static extern IntPtr CreateFile(string fileName,
                                uint desiredAccess,
                                uint shareMode,
                                IntPtr attributes,
                                uint creationDisposition,
                                uint flagsAndAttributes,
                                IntPtr templateFile);

/// 
/// Cierra un manejador a un objeto, 
/// en este caso el objeto será la unidad de CD que hemos accedido a través de CreateFile 
/// 
/// 
/// Entero que indica la respuesta del proceso
[DllImport("kernel32", SetLastError = true)]
static extern int CloseHandle(IntPtr driveHandle);


/// 
/// Nos permite enviar comandos de I/O a un dispositivo. 
/// 
/// Indica si fue o no enviado el comando al dispositivo
[DllImport("kernel32", SetLastError = true)]
static extern bool DeviceIoControl(IntPtr driveHandle,
                                    uint IoControlCode,
                                    IntPtr lpInBuffer,
                                    uint inBufferSize,
                                    IntPtr lpOutBuffer,
                                    uint outBufferSize,
                                    ref uint lpBytesReturned,
                                    IntPtr lpOverlapped);

///    
/// Expulsa el drive de acuerdo a la letra asignada   
///    
/// Letra del drive   
public static void Expulsar(string driveLetter)
{
    //Modificar el nombre de la unidad de acuerdo a como lo entiende el 
    //sistema de archivos
    driveLetter = @"\\.\" + driveLetter.Substring(0, 2);
    try
    {
        //Crea el puntero al archivo (dispositivo)   
        fileHandle = CreateFile(driveLetter, GENERICREAD, 0,
                                IntPtr.Zero, OPENEXISTING,
                                0, IntPtr.Zero);

        //Si es una unidad valida   
        if (fileHandle.ToInt32() != INVALID_HANDLE)
        {
            //Intenta expulsar el dispositivo   
            DeviceIoControl(fileHandle, IOCTL_STORAGE_EJECT_MEDIA,
                            IntPtr.Zero, 0, IntPtr.Zero, 0,
                            ref returnedBytes, IntPtr.Zero);
        }
    }
    catch
    {
        //Sino lo pudo expulsar   
        throw new Exception(Marshal.GetLastWin32Error().ToString());
    }
    finally
    {
        //Asegurarse de siempre cerrar el puntero del archvo   
        CloseHandle(fileHandle);
        fileHandle = IntPtr.Zero;
    }
}

Para Acceder a la versión en VB.NET pueden hacer la conversión del código anterior en este link:
http://www.developerfusion.com/tools/convert/csharp-to-vb/

 

COMO SE HACE?

Hay que utilizar la API de Windows

Si, sucede que abrir la puerta de la unidad de CDRom es una actividad altamente dependiente del sistema operativo pues requiere acceder al dispositivo y esto en cada sistema operativo se hace de manera diferente incluso puede ser diferente de un dispositivo a otro por lo que utilizar la API del sistema operativo nos cae bastante bien para no caer en complejidades exageradas.

Qué cosas hay que usar de la API?

Para poder abrir la unidad de CD necesitamos lo siguiente

  • CreateFile = esta función sirve para crear archivos pero también para abrir archivos existentes, así que se utilizará para abrir un archivo, la unidad del CD hace parte del sistema global de archivos así que podemos llegar a ella desde este medio.
  • CloseHandle = Cierra un manejador a un objeto, en este caso el objeto será la unidad de CD que hemos accedido a través de CreateFile .
  • DeviceIoControl = Nos permite enviar comandos de I/O a un dispositivo.

 

Para poder utilizar estos llamados a la API de Windows desde C# o VB necesitamos utilizar el atributo DLLImport que se encuentra definido en System.Runtime.InteropServices y que nos sirve para importar librerías creadas en código nativo.

 

Sin embargo no es tan sencillo como usar las funciones y ya, así que sino tienes experiencia en el uso de componentes nativos, puedes pegarte una pasada en internet para entender todo de manera más completa.

 

Por el momento basta con decir que debemos definir los tipos de dato y constantes que se utilizan en la API para así hacer un uso correcto de las funciones importadas.

 

Cómo funciona / Cómo Crearla.

Creamos una clase ExpulsarCDRom en la cual importaremos las funciones necesarias de la API de Windows:


class ExpulsarCDRom
{

    /// 
    /// esta función sirve para crear archivos pero también para abrir archivos existentes, 
    /// así que se utilizará para abrir un archivo, la unidad del CD hace parte del sistema 
    /// global de archivos así que podemos llegar a ella desde este medio
    /// 
    /// Puntero que sirve como manejador del archivo
    [DllImport("kernel32", SetLastError = true)]
    static extern IntPtr CreateFile(string fileName,
                                    uint desiredAccess,
                                    uint shareMode,
                                    IntPtr attributes,
                                    uint creationDisposition,
                                    uint flagsAndAttributes,
                                    IntPtr templateFile);

    /// 
    /// Cierra un manejador a un objeto, 
    /// en este caso el objeto será la unidad de CD que hemos accedido a través de CreateFile 
    /// 
    /// 
    /// Entero que indica la respuesta del proceso
    [DllImport("kernel32", SetLastError = true)]
    static extern int CloseHandle(IntPtr driveHandle);

    
    /// 
    /// Nos permite enviar comandos de I/O a un dispositivo. 
    /// 
    /// Indica si fue o no enviado el comando al dispositivo
    [DllImport("kernel32", SetLastError = true)]
    static extern bool DeviceIoControl(IntPtr driveHandle,
                                        uint IoControlCode,
                                        IntPtr lpInBuffer,
                                        uint inBufferSize,
                                        IntPtr lpOutBuffer,
                                        uint outBufferSize,
                                        ref uint lpBytesReturned,
                                        IntPtr lpOverlapped);

}

 

Una vez hecho esto, continuamos creando las constantes que necesitamos para llamar las funciones y desde luego las variables que nos permitirán controlar la respuesta de cada una de ellas, así que agregamos:


class ExpulsarCDRom
{

    //Constantes usadas en la API 
    /// Indica que se se hara lectura genérica del archvo
    const uint GENERICREAD = 0x80000000;
    /// Indica que se debe abrir un archivo existente, no crear uno nuevo
    const uint OPENEXISTING = 3;
    /// Comando enviado al dispositivo para abrir la puerta
    const uint IOCTL_STORAGE_EJECT_MEDIA = 0x2D4808;
    /// Indica que la operaciónj no finalizo adecuadamente 
    const int INVALID_HANDLE = -1;

    /// Puntero que se usara para apuntar al archivo (unidad) de CDRom
    private static IntPtr fileHandle;
    /// Indica el número de bytes leidos cmo rspuesta de un proceso
    private static uint returnedBytes;   

...
...

 

Tenemos los preparativos para comenzar a codificar, creamos una función estática llamada ExpulsarUnidad la cual vemos a continuación y explicamos más abajo:


///    
/// Expulsa el drive de acuerdo a la letra asignada   
///    
/// Letra del drive   
public static void Expulsar(string driveLetter)   
{   
    //1. Modificar el nombre de la unidad de acuerdo a como lo entiende el 
    //sistema de archivos
    driveLetter = @"\\.\" + driveLetter.Substring(0, 2);   
    try  
    {   
        //2.Crea el puntero al archivo (dispositivo)   
        fileHandle = CreateFile(driveLetter, GENERICREAD, 0, 
                                IntPtr.Zero, OPENEXISTING,
                                0,IntPtr.Zero);   

        //3.Si es una unidad valida   
        if (fileHandle.ToInt32() != INVALID_HANDLE)   
        {   
            //4.Intenta expulsar el dispositivo   
            DeviceIoControl(fileHandle, IOCTL_STORAGE_EJECT_MEDIA,   
                            IntPtr.Zero, 0, IntPtr.Zero, 0,   
                            ref returnedBytes,IntPtr.Zero);   
        }   
    }   
    catch  
    {   
        //5.Sino lo pudo expulsar   
        throw new Exception(Marshal.GetLastWin32Error().ToString());   
    }   
    finally  
    {   
        //6.Asegurarse de siempre cerrar el puntero del archvo   
        CloseHandle(fileHandle);   
        fileHandle = IntPtr.Zero;   
    }   
}

 

  1. Lo primero que hacemos es modificar el nombre del archivo que vamos a abrir, ya que por facilidad de consumo hemos creado la función para que reciba como parámetro el nombre de la unidad a abrir, pero este no es el nombre que entiende el sistema de archivos, así que modificamos levemente el nombre de la unidad para poder trabajar más cómodamente en adelante.
  2. Abrimos el archivo con ayuda de la función CreateFile de la API de Windows, lo cual nos devuelve el manejador del archivo. Los parámetros pasados son el nombre del archivo (la unidad de CD), la forma en que se abrirá el archivo, en  este caso solo lectura, y le indicamos que el archivo ya existe es decir que no debería crear uno nuevo, los demás parámetros no los necesitamos por lo cual envió 0 o apuntador a cero.
  3. Verificamos que en efecto que el sistema haya podido abrir el archivo (es decir direccionar un manejador a la unidad de CD), si no pudo el valor devuelto será INVALID_HANDLE (una de nuestras constantes creadas) en cuyo caso no continuaremos con el programa y no haremos nada.
  4. Si la respuesta no fue INVALID_HANDLE procedemos a enviar un comando a la unidad de CDRom, esto lo hacemos con la función DeviceIoControl , pasándole como parámetros: el manejador del archivo que representa al dispositivo (filehandle), el comando IOCTL_STORAGE_EJECT_MEDIA  (otra de nuestras constantes) y los demás parámetros que nuevamente no representan importancia para nuestro caso.
  5. Nos aseguramos de hacer un control básico de las excepciones que se pueden presentar .
  6. Y siempre, sin importar lo que suceda, intentamos cerrar el manejador al archivo y establecemos ese manejador en cero.

Para desarrolladores en Windows Vista y posteriores (sistema seguros)

Deben crear un manifestó en su aplicación, pues por defecto UAC requiere elevar privilegios para que este código funcione, entonces en su proyecto agreguen un nuevo manifestó que indique elevar privilegios para efectuar esta operación.

 

Hay más para decir al respecto, por lo cual a algunos no les funcionará, pero esperemos las preguntas :P

 

Happy Learning!

Optimización de Código - Diferencia entre usar Convert y usar cast - C#

Puedes ver el articulo original en  mi blog:

http://juank.black-byte.com/c-optimizacion-codigo-diferencia-convert-toint32-cast/

 

 ---

Esta es una duda frecuente cuando quieres convertir de un tipo numérico a otro.

En ese caso hay diferencias importantes sobre todo en cuanto a velocidad de procesamiento.

Veamos 2 ejemplos.

 

1 - CONVERTIR DE TIPO DECIMAL (DECIMAL) A TIPO INT (INT32):

Si utilizas el operador de cast, es decir : (int)

Explicación básica:

El runtime lo que hará es simplemente tomar la parte entera del número descartando completamente la parte decimal.

Explicación más completa:

del tipo decimal es de 16 bytes mientras que el tipo int es de 4 bytes (casi siempre), así que lo que sucede es que el runtime toma la parte entera del tipo decimal y la coloca en los 4 bytes enteros, pero atención, eso solo siempre y cuando la parte entera del dato tipo decimal quepa en 2^32 , de lo contrario se genera una excepción de overflow.

 

Si utilizas el Convert.ToInt32

Explicación básica:

Cuando el método estático Convert.ToInt32()  (en adelante solo ToInt32) recibe como parámetro un tipo de dato Decimal hace más que solo retornar la parte entera, de hecho redondea el resultado al entero más cercano. Esto genera una sobrecarga adicional que lo hace mucho más lento.

Explicación más completa:

ToInt32 cuando recibe un tipo Decimal llama al método interno Decimal.FCallToInt32 que a su vez es un wrapper de un método implementado en código nativo que hace los cálculos necesarios para producir  un valor redondeado. Como se observa entonces hay invocación a 3 métodos como mínimo lo cual implica 3 cambios de contexto: el primero al llamar a ToInt32, luego al llamar a Decimal.FCallToInt32 y luego cuando este hace el wrapper a la función de código nativo, hasta allí ya es mucho más lento que simplemente usar el cast (int), ahora ,  la lógica usada internamente para convertir el tipo de 16 bytes al tipo de 4 bytes y tener en cuenta el redondeo y los casos especiales como por ejemplo cuando el parámetro es null, convierte a esta función en una muchísimas veces más lenta que solo hacer (int).

 

2 - CONVERTIR DE TIPO FLOAT (SINGLE) A TIPO INT (INT32):

Si utilizas el operador de cast, es decir : (int)

Explicación básica:

El runtime lo que hará es simplemente tomar la parte entera del número descartando completamente la parte decimal.

Explicación más completa:

del tipo float es de 4 bytes mientras que el tipo int también es de 4 bytes (casi siempre), así que lo que sucede es que el runtime toma la parte entera del tipo float y la coloca en los 4 bytes enteros.

Si utilizas el Convert.ToInt32

Explicación básica:

Cuando el método estático ToInt32() recibe como parámetro un tipo de dato Single (float) hace más que solo retornar la parte entera, de hecho redondea el resultado al entero más cercano. Esto genera una sobrecarga adicional que lo hace mucho más lento.

Explicación más completa:

ToInt32 cuando recibe un tipo Single (float) llama al método interno Convert.ToInt32 (si, de nuevo, pero llama al que recibe parámetro tipo double ya que la lógica para double es la misma que para Single (float)) que es finalmente quien resuelve la lógica para hacer el redondeo. Como se observa entonces hay invocación a 2 métodos mínimo lo cual implica 2 cambios de contexto: el primero al llamar a ToInt32(Single), luego al llamar a ToInt32(double) que al final es que tiene en cuenta el redondeo y los casos especiales como por ejemplo cuando el parámetro es null, convierte a esta función en una muchísimas veces más lenta que solo hacer (int) pero comparativamente con el caso de Decimal no es tan lenta.

Conclusión: Si necesitas velocidad siempre usa operadores de cast, ejemplo: (int); si requieres aproximación y que te tengan en cuenta los casos especiales usa siempre Convert.

Optimización de Código - Cómo Convertir un Entero en Binario - C#

Puedes ver el articulo original en  mi blog:

http://juank.black-byte.com/c-entero-a-binario/ 

---

El título de este post debería ser realmente: "Cómo formatear una cadena para mostrar un entero en formato binario"... pero créanme que nadie lo buscaría así en un buscador :P.

Bien esto parece ser un problema recurrente alrededor de los foros en internet, existen muchas soluciones diferentes a este problema, incluso hay algunos lenguajes que ya incluyen soporte para hacer esto fácilmente,  el CLR no se caracteriza por tener una funcionalidad fácil de utilizar sin embargo en este artículo exploraremos varias soluciones (desde luego no todas )posibles hasta llegar a la solución ideal que espero les sea de provecho a todos.

Estas son las opciones que exploraremos, son básicamente 2.

La primera opción no requiere de mucho análisis pero tiene sus inconvenientes digamos menores. La segunda opción es la que más nos importa, aprenderemos a deducir el algoritmo a implementarlo y subsecuentemente lo iremos optimizando hasta lograr una versión digna de un excelente programador:

  1. Convertir con BitVector32 (mala solución pero rápida de implementar)
  2. Convertir creando un algoritmo eficiente
    • Utilizando string
    • Utilizando StringBuilder
    • Utilizando punteros con codigo inseguro

 

1.  CONVERTIR CON BITVECTOR32

Esta es la solución más rápida al problema, pero en mi opinión la menos profesional....

BitVector32 es una colección especializada que se encuentra en:

System.Collections.Specialized

Esta clase provee una estructura simple que almacena valores boléanos que representan un entero de 32 bit.  Que que?

Ok esta explicación no necesariamente es clara para todos, hagámoslo muy simple veamos un entero de 32 bits, imaginemos que en memoria se ve así:

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31

Es decir cada casilla representa un bit, para completar así 32 bits.

Bien entonces esto a final de cuentas es una colección bits, es decir en cada 'celda' podemos tener dos valores posibles 1 o 0... true o false.

Bien eso es lo hace BitVector toma un entero y lo permite manejar como un array de booleanos.

El método ToString() permite convertir todo esto a 1s y 0s para poder ver los valores de cada 'celda' en su representación numérica, sin embargo esto tiene un inconveniente pues si utilizamos elObjetoBitVector.ToString() recibiremos algo así como esto:

BitVector32{00000000000000000000000001101111}

Nos sobra algo al principio y al final... a nosotros solo nos interesan los datos binarios así que la solución es hacer un substring de la parte de la cadena que nos interesa, finalmente el código quedaría así:


public static string EB_BitVector32(Int32 entero)
{
    BitVector32 bv = new BitVector32(entero);
    return bv.ToString().Substring(12, 32);
}

Esto nos produciría este resultado similar a:

00000000000000000000000001101111

 

2.  CONVERTIR CREANDO UN ALGORITMO EFICIENTE

Ahora si entremos en materia hagamos el algoritmo.

Una de las primeras ideas que a uno se le viene a la mente es utilizar el conocido algoritmo que nos permite convertir un numero decimal a base 2, para el que no lo recuerde o no lo conozca: es un algoritmo que toma como base divisiones anidadas del valor entre 2 y recupera los residuos. En este link tienen una explicación detallada al respecto: Decimal a Binario

Sin embargo dicho algoritmo puede ser ineficiente para nuestro gusto.

Retomemos conceptos como puntos clave que nos permitan llegar a un algoritmo óptimo:

  1. Básicamente un entero es un arreglo de 4 bytes (32 bits)
  2. Por cada bit se debe producir su equivalente como cadena ("0" , "1") según si el bit esta prendido(1) o apagado(0)
  3. Podemos acceder a todos los bit como un arreglo de boolean a través de BitVector32, pero en ese caso la solución sería aún más lenta que utilizando la propuesta inicial que usa BitVector32
  4. Hay que encontrar el mecanismo adecuado para recorrer los bit

De los conceptos clave tenemos que los dos primeros nos son útiles, el tercero no nos aporta nada nuevo y el cuarto es nuestra necesidad más inmediata: Averiguar como recorrer los bit sin necesidad de usar BitVector32.

Lo primero que me salta a la mente es usar operadores binarios especialmente corrimientos de bit (<<) y and (&) para las máscaras.

 

Veamos un poco de teoría acerca de lo que haremos.

  1. Crear un bucle e 32 ciclos
  2. Leemos siempre el valor del primer bit del entero de 32 bit, esto lo podemos hacer efectuando un and binario (&) del entero con un numero que tenga el primer bit prendido únicamente, es decir con : 0x80000000 (es decir 2147483648 )... porqué este número?  bueno coloquemos este 0x80000000 en nuestro arreglo de 32 bits:
    1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
    8       0       0       0       0       0       0       0      

    Cada 4 bits representan uno de los dígitos hexa, de esta forma el 0x8 esta representado por los primeros 4 bits, en efecto 1000 = 0x8, así que si se fijan en nuestro arreglo de bits solo tenemos un bit prendido, de tal forma que cualquier cosa que haga & con 0x80000000 solo podría llegar a tener prendido el bit 1, así podemos hallar que valor tiene el bit 1 de un número dado ya que si ese bit esta en 0 al hacer and quedaría en 0, mientras que si esta en 1 al hacer and quedaría en uno nuevamente. Concretando, cuando hacemos un and entre dos enteros y uno de los enteros es 0x80000000 el resultado siempre será el valor del primer bit del otro entero. ( espero haber sido claro).
  3. En cada iteración corremos 1 bit posición a la izquierda (<< 1), de tal forma que el primer bit es reemplazado por el bit siguiente
  4. Se regresa al paso 2

IMPLEMENTACION CRUDA Utilizando String

Este es el código resultante de la implementación más simpe del algoritmo:


public static string EB_String(int entero)
{
    //La máscara y el # de iteraciones
    const uint mascara = 0x80000000;
    const int iteraciones = 32;
    //el contador y el resultado
    int contador = 0;
    string resultado = "";

    //Se recorren los 32 bit
    while (contador++ < iteraciones)
    {
        /*Si el entero and la mascara = 0 quiere decir
         *que el bit 1 esta apagado*/
        if ((entero & mascara) == 0)
            resultado += "0";
        else
            resultado += "1";

        /*correr un bit a la izquierda para poner
         *el siguiente bit en la posicion del primero*/
        entero = entero << 1;
    }
    return resultado;
}
 

OPTIMIZACIÓN DE CÓDIGO Utilizando StringBuilder

La implementación anterior es susceptible de una sencilla mejora utilizando StringBuilder ya que al ser string un tipo inmutable , las recurrentes concatenaciones generadas al utilizar masivamente el método sobrecargarían el GC, sin contar el overhead producido por las frecuentes reservas de memoria (1 adicional por cada cambio al string). StringBuilder se encuentra en el namespace System.Text.

Código optimizado utilizando StringBuilder:


public static string EB_StringBuilder(int entero)
{
    //La máscara y el # de iteraciones
    const uint mascara = 0x80000000;
    const int iteraciones = 32;
    //el contador y el resultado
    int contador = 0;
    StringBuilder resultado = new StringBuilder(iteraciones);

    //Se recorren los 32 bit
    while (contador++ < iteraciones)
    {
        /*Si el entero and la mascara = 0 quiere decir
         *que el bit 1 esta apagado*/
        if ((entero & mascara) == 0)
            resultado.Append('0');
        else
            resultado.Append('1');

        /*correr un bit a la izquierda para poner
         *el siguiente bit en la posicion del primero*/
        entero = entero << 1;
    }
    return resultado.ToString();
}

OPTIMIZACIÓN DE CÓDIGO Utilizando punteros - código unsafe

Aunque StringBuilder ya proporciona una mejora muy importante, dadas las características de nuestro problema(concatenación uno a uno) resulta mucho más idóneo generar código que utilice punteros. Es importante tener en cuenta que para que este código funcione se requiere modificar las propiedades del proyecto para que admita código unsafe.

Esta es la versión básica con manejo de apuntadores:

 


public static unsafe string EB_Unsafe(int entero)
{
    const uint mascara = 0x80000000;
    const int iteraciones = 32;

    int contador = 0;

    //Se reservan 32 posiciones y uno adicional para 
    //terminacion en null
    char* resultado = stackalloc char[iteraciones + 1];
    //puntero de trabajo
    char* aux = resultado;

    while (contador++ < iteraciones)
    {
        if ((entero & mascara) == 0)
            *aux = '0';
        else
            *aux = '1';

        //Mover el puntero una posicion dentro de la cadena
        aux++;

        entero = entero << 1;
    }
    return new string(resultado);
}

Esta es la versión final de "bonus" totalmente optimizada para velocidad de procesamiento, es levemente diferente de la anterior para realizar la menor cantidad de validaciones posibles y para que ignore los 0s a la izquierda, lo cual la hace excepcionalmente rápida en la mayoría de los casos. Revísenla...:


public static unsafe string EB_Unsafe_Opt(int entero)
{
    const uint mascara = 0x1;
    const int iteraciones = 32;

    char* resultado = stackalloc char[iteraciones + 1];
    char* aux = resultado + iteraciones - 1;

    do
    {
        if ((entero & mascara) == 0)
            *(aux--) = '0';
        else
            *(aux--) = '1';

        entero = entero >> 1;
    } while (entero != 0);

    return new string(++aux);
}

Si alguien tiene una solución mejor por favor no dude en compartirla!!

En unos días publicare un programa especialmente diseñado para medir la velocidad de cada una de estas alternativas... les adelanto que las diferencias de tiempo van casi hasta de 20 o 30 veces más velocidad.

 

Happy Learning!!!

OOOHH OLvidaba decirles Si quieren pueden usar:

System.Convert.ToString(elValor, 2);

jajajajaja, bueno el ejercicio que hicimos produce un resultado 30% más rápido. :P

Caracter de Salto de linea - C#

Puedes ver el articulo original en  mi blog:

http://juank.black-byte.com/c-caracter-salto-linea/

 ---

Un salto de linea en windows no es es realmente un caracter sino que realmente son dos caracteres el 13 y luego el caracter 10.  En detalle: \r = 13 = CR = Carriage return = Retorno de carro \n = 10 = LF = Line Feed       = Avance de linea


Mientras que en unix podria ser igual o diferente dependiendo de la configuracion.

La más usual es  que en unix sea solo CR, razon por la cual cuando abres en windows un archivo de texto creado en unix , ves que te sale todo en una sola linea ( falta el LF  - el salto de linea).

Como ves esto depende de la plataforma asi que lo ideal es que en nuestros programas que usen el CLR  es hacer uso de: System.Environment.NewLine


En las base de datos utiliza necesariamente CHR(13)+CHR(10)  ya que esto te dara compatibilidad con varias plataformas. Complemento: oscardo comenta en este hilo este aporte de gran utilidad, que puede ser útil cuando se esta trabajando con Web Mobile: char.ConvertFromUtf32(13) + char.ConvertFromUtf32(10)

Happy learning!

Posted: 18/8/2009 15:36 por Juan Carlos Ruiz Pacheco | con 2 comment(s) |
Archivado en: ,
Cómo encontrar los archivos pst de outlook? - C#

Puedes ver el articulo original en  mi blog:

http://juank.black-byte.com/c-pst-outlook/

 ---

Con ayuda de las facilidades de interoperabilidad que ofrece .Net Framework y desde luego con ayuda de Visual Studio este proceso es bastante sencillo.

El primer paso es desde luego agregar una referencia al objeto COM de Outlook para que Visual Studio nos importe el componente y esto nos permita acceder al objeto COM como lo haríamos normalmente con cualquiero otra librería para usar con el framework. El componente COM a que debemos acceder es (o equivalente de acuerdo a la versión de office instalada): 

image

Como buena práctica resulta importante colocarle un alias al namespace, por ejemplo:

using Outlook = Microsoft.Office.Interop.Outlook;

Los pasos importantes siguientes se resumen así:

  1. Instanciar un objeto de la aplicación (outlook)
  2. Obtener los stores (almacenamientos de datos) para la sesión actual
  3. Verificar que exista un FilePath, esto es necesario puesto que si el store esta en exchange el FilePath es nulo.
  4. Verificar que el FilePath termine en ".pst", pues algunos stores de caché tienen extensiones diferentes.
  5. El resto es maquillaje

using System;
using System.Text;
using Outlook = Microsoft.Office.Interop.Outlook;

class Program
{
    static void Main(string[] args)
    {
        StringBuilder sb = new StringBuilder();
        Outlook._Application outlookObj = new Outlook.Application();

        foreach (Outlook.Store store in outlookObj.Session.Stores)
            if (store.FilePath != null && store.FilePath.EndsWith(".pst"))
                sb.AppendLine(store.FilePath);

        Console.WriteLine(sb.ToString());
        Console.ReadLine();
    }
}

Happy Learning!

Cómo obtener el SID de un usuario local? - C#

Puedes ver el articulo original en  mi blog:

http://juank.black-byte.com/c-obtener-sid-usuario-local/

 ---

Obtener el SID del usuario que se encuentra ejecutando la aplicación es bastante sencillo:

  1. Adicionar using a System.Security.Principal
  2. Instanciar un objeto WindowsIdentity
  3. Construirlo a partir de WindowsIdentity.GetCurret()
  4. Usar la propiedad Value

using System;
using System.Security.Principal;

class Program
{
    static void Main(string[] args)
    {
        WindowsIdentity currentUser = WindowsIdentity.GetCurrent();
        Console.WriteLine(currentUser.User.Value);
        Console.ReadLine();
    }
}

Sin embargo tratar de acceder a la información de los usuarios diferentes del logueado actualmente ( y sin usar impersonación ) puede ser un poco más complejo. Una alternativa viable es hacer uso de WMI.

  1. Adicionar la referencia a System.Management y el respectivo using
  2. A traves de WMI se debe hacer un query al objeto Win32_UserAccount
  3. Especificar el nombre del dominio, si es local es el nombre de la máquina el cual se peude obtener a traves de System.Environment
  4. Hacer una búsqueda sobre el query
  5. Usar el indizador accediento con la cadena "SID"

using System;
using System.Management;

class Program
{
    static void Main(string[] args)
    {
       SelectQuery sQuery = new SelectQuery("Win32_UserAccount", "Domain='" 
                                      + System.Environment.MachineName + "'");
        ManagementObjectSearcher mSearcher = new ManagementObjectSearcher(sQuery);
        
        Console.WriteLine("User Accounts");
        Console.WriteLine("");
        foreach (ManagementObject mObject in mSearcher.Get())
            Console.WriteLine(mObject["SID"]);

        Console.ReadLine();
    }
}

Happy Learning!

Posted: 18/8/2009 15:19 por Juan Carlos Ruiz Pacheco | con no comments |
Archivado en: ,,